@vibgrate/cli 2026.803.3 → 2026.803.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (105) hide show
  1. package/dist/agent-PQEODATY.js +26 -0
  2. package/dist/{agent-JTA4YWMQ.js.map → agent-PQEODATY.js.map} +1 -1
  3. package/dist/baseline-SU2PCQV2.js +9 -0
  4. package/dist/{baseline-2V7PKA7Q.js.map → baseline-SU2PCQV2.js.map} +1 -1
  5. package/dist/chunk-2A7XCANX.js +375 -0
  6. package/dist/chunk-2A7XCANX.js.map +1 -0
  7. package/dist/{chunk-SJFBXCII.js → chunk-2U4SXS5U.js} +77 -9
  8. package/dist/chunk-2U4SXS5U.js.map +1 -0
  9. package/dist/{chunk-QMME6TB4.js → chunk-4LGDQBYK.js} +3 -102
  10. package/dist/chunk-4LGDQBYK.js.map +1 -0
  11. package/dist/{chunk-XK5LKOBF.js → chunk-5WMFX7TY.js} +4 -4
  12. package/dist/{chunk-XK5LKOBF.js.map → chunk-5WMFX7TY.js.map} +1 -1
  13. package/dist/{chunk-KYX2YCXT.js → chunk-72XI4MTL.js} +9 -9
  14. package/dist/{chunk-KYX2YCXT.js.map → chunk-72XI4MTL.js.map} +1 -1
  15. package/dist/{chunk-WGPS3NZQ.js → chunk-COZEHQTK.js} +33 -11
  16. package/dist/chunk-COZEHQTK.js.map +1 -0
  17. package/dist/{chunk-6DFV4WCQ.js → chunk-CPGTEDFD.js} +3 -3
  18. package/dist/{chunk-6DFV4WCQ.js.map → chunk-CPGTEDFD.js.map} +1 -1
  19. package/dist/{chunk-M452MRWI.js → chunk-H4XQQ4N2.js} +9 -8
  20. package/dist/chunk-H4XQQ4N2.js.map +1 -0
  21. package/dist/{chunk-PXM3VZYR.js → chunk-HE2TEFWY.js} +4 -4
  22. package/dist/{chunk-PXM3VZYR.js.map → chunk-HE2TEFWY.js.map} +1 -1
  23. package/dist/{chunk-NJFQCDGA.js → chunk-INIULQEP.js} +3 -3
  24. package/dist/{chunk-NJFQCDGA.js.map → chunk-INIULQEP.js.map} +1 -1
  25. package/dist/{chunk-K6Q6HTFD.js → chunk-JZGVUH57.js} +4 -4
  26. package/dist/{chunk-K6Q6HTFD.js.map → chunk-JZGVUH57.js.map} +1 -1
  27. package/dist/{chunk-TM5XQY3L.js → chunk-KFK7KCAH.js} +3 -3
  28. package/dist/{chunk-TM5XQY3L.js.map → chunk-KFK7KCAH.js.map} +1 -1
  29. package/dist/{chunk-L5PUNO3T.js → chunk-LOV44ROX.js} +451 -22
  30. package/dist/chunk-LOV44ROX.js.map +1 -0
  31. package/dist/{chunk-NSWEJPLV.js → chunk-MKDM2KLO.js} +9 -9
  32. package/dist/{chunk-NSWEJPLV.js.map → chunk-MKDM2KLO.js.map} +1 -1
  33. package/dist/{chunk-3Q3ZXJZP.js → chunk-OCINHIPN.js} +5 -4
  34. package/dist/chunk-OCINHIPN.js.map +1 -0
  35. package/dist/chunk-RBMKBYGV.js +102 -0
  36. package/dist/chunk-RBMKBYGV.js.map +1 -0
  37. package/dist/{chunk-NUZM7UOR.js → chunk-SJC3VKQ2.js} +59 -9
  38. package/dist/chunk-SJC3VKQ2.js.map +1 -0
  39. package/dist/{chunk-M7773OUN.js → chunk-TKV3GAKX.js} +5 -5
  40. package/dist/{chunk-M7773OUN.js.map → chunk-TKV3GAKX.js.map} +1 -1
  41. package/dist/{chunk-HC44772H.js → chunk-TVRM57CE.js} +5 -373
  42. package/dist/chunk-TVRM57CE.js.map +1 -0
  43. package/dist/{chunk-TNADGCOX.js → chunk-UH2WHRKJ.js} +8 -8
  44. package/dist/{chunk-TNADGCOX.js.map → chunk-UH2WHRKJ.js.map} +1 -1
  45. package/dist/{chunk-CPLG4GFB.js → chunk-UJGWQ6YA.js} +4 -4
  46. package/dist/{chunk-CPLG4GFB.js.map → chunk-UJGWQ6YA.js.map} +1 -1
  47. package/dist/chunk-USO67ZEE.js +6 -0
  48. package/dist/{chunk-7KNFG44U.js.map → chunk-USO67ZEE.js.map} +1 -1
  49. package/dist/{chunk-4ACXDVON.js → chunk-UXWN5YQW.js} +4 -4
  50. package/dist/{chunk-4ACXDVON.js.map → chunk-UXWN5YQW.js.map} +1 -1
  51. package/dist/{chunk-UXR7FBE3.js → chunk-VJHNGNCT.js} +6 -6
  52. package/dist/{chunk-UXR7FBE3.js.map → chunk-VJHNGNCT.js.map} +1 -1
  53. package/dist/{chunk-6JP7HGIF.js → chunk-Y4JO6LYP.js} +3 -3
  54. package/dist/{chunk-6JP7HGIF.js.map → chunk-Y4JO6LYP.js.map} +1 -1
  55. package/dist/cli.js +498 -340
  56. package/dist/cli.js.map +1 -1
  57. package/dist/embed-worker-main.d.ts +2 -0
  58. package/dist/embed-worker-main.js +64 -0
  59. package/dist/embed-worker-main.js.map +1 -0
  60. package/dist/ensure-map-Z6UXBPI7.js +31 -0
  61. package/dist/{ensure-map-TAVYMPEG.js.map → ensure-map-Z6UXBPI7.js.map} +1 -1
  62. package/dist/graph-backend-3A2M22QN.js +13 -0
  63. package/dist/{graph-backend-WF4LGBGD.js.map → graph-backend-3A2M22QN.js.map} +1 -1
  64. package/dist/index.d.ts +1 -1
  65. package/dist/index.js +16 -14
  66. package/dist/index.js.map +1 -1
  67. package/dist/{interactive-6PN237LX.js → interactive-6TCXGIMA.js} +31 -29
  68. package/dist/interactive-6TCXGIMA.js.map +1 -0
  69. package/dist/load-QHU4WMUN.js +11 -0
  70. package/dist/{load-T2FOJUPI.js.map → load-QHU4WMUN.js.map} +1 -1
  71. package/dist/{run-outcome-AMIKAASL.js → run-outcome-NHD3NXTL.js} +7 -6
  72. package/dist/run-outcome-NHD3NXTL.js.map +1 -0
  73. package/dist/runtime-session-W65P4OXR.js +22 -0
  74. package/dist/{runtime-session-QAVOPSSH.js.map → runtime-session-W65P4OXR.js.map} +1 -1
  75. package/dist/session-NZKZXBX4.js +17 -0
  76. package/dist/{session-SCHQQTHN.js.map → session-NZKZXBX4.js.map} +1 -1
  77. package/dist/{stream-json-A6K2MONY.js → stream-json-MLWEIQNK.js} +13 -11
  78. package/dist/stream-json-MLWEIQNK.js.map +1 -0
  79. package/dist/{vg-mcp-bridge-3TSFOL4V.js → vg-mcp-bridge-2DEZF3JM.js} +12 -10
  80. package/dist/vg-mcp-bridge-2DEZF3JM.js.map +1 -0
  81. package/dist/{vgd-B67JCNJP.js → vgd-W7IHKGNU.js} +12 -10
  82. package/dist/vgd-W7IHKGNU.js.map +1 -0
  83. package/package.json +1 -1
  84. package/scripts/postinstall.mjs +22 -1
  85. package/dist/agent-JTA4YWMQ.js +0 -24
  86. package/dist/baseline-2V7PKA7Q.js +0 -9
  87. package/dist/chunk-3Q3ZXJZP.js.map +0 -1
  88. package/dist/chunk-7KNFG44U.js +0 -6
  89. package/dist/chunk-HC44772H.js.map +0 -1
  90. package/dist/chunk-L5PUNO3T.js.map +0 -1
  91. package/dist/chunk-M452MRWI.js.map +0 -1
  92. package/dist/chunk-NUZM7UOR.js.map +0 -1
  93. package/dist/chunk-QMME6TB4.js.map +0 -1
  94. package/dist/chunk-SJFBXCII.js.map +0 -1
  95. package/dist/chunk-WGPS3NZQ.js.map +0 -1
  96. package/dist/ensure-map-TAVYMPEG.js +0 -29
  97. package/dist/graph-backend-WF4LGBGD.js +0 -11
  98. package/dist/interactive-6PN237LX.js.map +0 -1
  99. package/dist/load-T2FOJUPI.js +0 -10
  100. package/dist/run-outcome-AMIKAASL.js.map +0 -1
  101. package/dist/runtime-session-QAVOPSSH.js +0 -20
  102. package/dist/session-SCHQQTHN.js +0 -15
  103. package/dist/stream-json-A6K2MONY.js.map +0 -1
  104. package/dist/vg-mcp-bridge-3TSFOL4V.js.map +0 -1
  105. package/dist/vgd-B67JCNJP.js.map +0 -1
@@ -0,0 +1,26 @@
1
+ export { CONTEXT_POLICY_VERSION, DISCOVERY_TOOLS, MUTATION_TOOLS, compact, compactWithModel, runAgent } from './chunk-COZEHQTK.js';
2
+ import './chunk-543E6WDJ.js';
3
+ import './chunk-2U4SXS5U.js';
4
+ import './chunk-REIGWTTH.js';
5
+ import './chunk-KFK7KCAH.js';
6
+ import './chunk-RBIRXQR6.js';
7
+ import './chunk-5HYCB55P.js';
8
+ export { summarizeCapsule } from './chunk-JZGVUH57.js';
9
+ import './chunk-VIWZ4QT7.js';
10
+ import './chunk-5WMFX7TY.js';
11
+ import './chunk-3LSG7Z4I.js';
12
+ import './chunk-J7LKEB2R.js';
13
+ import './chunk-TVRM57CE.js';
14
+ import './chunk-UTNNGKKX.js';
15
+ import './chunk-RXP66R2E.js';
16
+ import './chunk-2A7XCANX.js';
17
+ import './chunk-OCINHIPN.js';
18
+ import './chunk-USO67ZEE.js';
19
+ import './chunk-4LGDQBYK.js';
20
+ import './chunk-RBMKBYGV.js';
21
+ import './chunk-BWPBB45M.js';
22
+ import './chunk-JFGYT6BI.js';
23
+ import './chunk-BPF3YX2U.js';
24
+ import './chunk-Z6ZWNWWR.js';
25
+ //# sourceMappingURL=agent-PQEODATY.js.map
26
+ //# sourceMappingURL=agent-PQEODATY.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":[],"names":[],"mappings":"","file":"agent-JTA4YWMQ.js"}
1
+ {"version":3,"sources":[],"names":[],"mappings":"","file":"agent-PQEODATY.js"}
@@ -0,0 +1,9 @@
1
+ export { baselineCommand, runBaseline } from './chunk-Y4JO6LYP.js';
2
+ import './chunk-INIULQEP.js';
3
+ import './chunk-DT4KIVRI.js';
4
+ import './chunk-CRAMOEBE.js';
5
+ import './chunk-RXP66R2E.js';
6
+ import './chunk-USO67ZEE.js';
7
+ import './chunk-Z6ZWNWWR.js';
8
+ //# sourceMappingURL=baseline-SU2PCQV2.js.map
9
+ //# sourceMappingURL=baseline-SU2PCQV2.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":[],"names":[],"mappings":"","file":"baseline-2V7PKA7Q.js"}
1
+ {"version":3,"sources":[],"names":[],"mappings":"","file":"baseline-SU2PCQV2.js"}
@@ -0,0 +1,375 @@
1
+ import { cacheDir } from './chunk-RBMKBYGV.js';
2
+ import { hashString } from './chunk-JFGYT6BI.js';
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';
8
+
9
+ var DEFAULT_LOCK_STALE_MS = 15 * 60 * 1e3;
10
+ function isProcessAlive(pid) {
11
+ try {
12
+ process.kill(pid, 0);
13
+ return true;
14
+ } catch (e) {
15
+ return e.code === "EPERM";
16
+ }
17
+ }
18
+ function lockIsStale(file, staleMs) {
19
+ try {
20
+ const { pid, at } = JSON.parse(fs2.readFileSync(file, "utf8"));
21
+ if (typeof at === "number" && Date.now() - at > staleMs) return true;
22
+ if (typeof pid === "number" && !isProcessAlive(pid)) return true;
23
+ return false;
24
+ } catch {
25
+ return true;
26
+ }
27
+ }
28
+ function acquireLock(file, staleMs = DEFAULT_LOCK_STALE_MS) {
29
+ const write = () => {
30
+ try {
31
+ fs2.mkdirSync(path2.dirname(file), { recursive: true });
32
+ const fd = fs2.openSync(file, "wx");
33
+ fs2.writeFileSync(fd, JSON.stringify({ pid: process.pid, at: Date.now() }));
34
+ fs2.closeSync(fd);
35
+ return true;
36
+ } catch {
37
+ return false;
38
+ }
39
+ };
40
+ if (write()) return true;
41
+ if (lockIsStale(file, staleMs)) {
42
+ try {
43
+ fs2.rmSync(file, { force: true });
44
+ } catch {
45
+ }
46
+ return write();
47
+ }
48
+ return false;
49
+ }
50
+ function releaseLock(file) {
51
+ try {
52
+ fs2.rmSync(file, { force: true });
53
+ } catch {
54
+ }
55
+ }
56
+
57
+ // src/engine/embeddings.ts
58
+ function resolveEmbedModel(model) {
59
+ return model ?? "bge-small-en-v1.5";
60
+ }
61
+ function modelCacheDir() {
62
+ const base = process.env.XDG_CACHE_HOME || path2.join(os.homedir(), ".cache");
63
+ return path2.join(base, "vibgrate", "models");
64
+ }
65
+ function modelReadyMarker(modelId) {
66
+ return path2.join(modelCacheDir(), `.ready-${safe(modelId)}`);
67
+ }
68
+ function isModelReady(modelId = resolveEmbedModel()) {
69
+ return fs2.existsSync(modelReadyMarker(modelId));
70
+ }
71
+ function unavailableMessage(reason) {
72
+ switch (reason) {
73
+ case "no-permission":
74
+ 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.)`;
75
+ case "download-failed":
76
+ 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.`;
77
+ case "not-installed":
78
+ return `the optional semantic backend isn't available here \u2014 using fast lexical search.`;
79
+ case "init-failed":
80
+ default:
81
+ return `the semantic model couldn't load \u2014 using fast lexical search.`;
82
+ }
83
+ }
84
+ function modelCacheInfo(modelId = resolveEmbedModel()) {
85
+ const dir = modelCacheDir();
86
+ return { dir, present: isModelReady(modelId) || fs2.existsSync(dir), bytes: dirSize(dir) };
87
+ }
88
+ function clearModelCache() {
89
+ const dir = modelCacheDir();
90
+ const bytes = dirSize(dir);
91
+ try {
92
+ fs2.rmSync(dir, { recursive: true, force: true });
93
+ } catch {
94
+ }
95
+ return bytes;
96
+ }
97
+ function dirSize(dir) {
98
+ let total = 0;
99
+ let entries;
100
+ try {
101
+ entries = fs2.readdirSync(dir, { withFileTypes: true });
102
+ } catch {
103
+ return 0;
104
+ }
105
+ for (const e of entries) {
106
+ const p = path2.join(dir, e.name);
107
+ if (e.isDirectory()) total += dirSize(p);
108
+ else {
109
+ try {
110
+ total += fs2.statSync(p).size;
111
+ } catch {
112
+ }
113
+ }
114
+ }
115
+ return total;
116
+ }
117
+ function isPermissionError(e) {
118
+ const code = e?.code;
119
+ return code === "EACCES" || code === "EPERM" || code === "EROFS";
120
+ }
121
+ function embeddingsCached(root, modelId) {
122
+ return fs2.existsSync(path2.join(cacheDir(root), `embeddings-${safe(modelId)}.json`));
123
+ }
124
+ async function loadEmbedder(options = {}) {
125
+ if (options.local) return null;
126
+ const modelId = resolveEmbedModel(options.model);
127
+ if (options.noDownload && !isModelReady(modelId)) return null;
128
+ const fail = (reason) => {
129
+ options.onUnavailable?.(reason);
130
+ return null;
131
+ };
132
+ const mod = await importEmbedBackend();
133
+ if (!mod?.FlagEmbedding) return fail("not-installed");
134
+ const cache = modelCacheDir();
135
+ try {
136
+ fs2.mkdirSync(cache, { recursive: true });
137
+ } catch (e) {
138
+ return fail(isPermissionError(e) ? "no-permission" : "init-failed");
139
+ }
140
+ try {
141
+ const model = await withTimeout(
142
+ mod.FlagEmbedding.init({
143
+ model: mapModel(mod, modelId),
144
+ cacheDir: cache,
145
+ showDownloadProgress: options.showDownloadProgress ?? false
146
+ }),
147
+ embedInitTimeoutMs(),
148
+ "embedding model init timed out"
149
+ );
150
+ try {
151
+ fs2.writeFileSync(modelReadyMarker(modelId), (/* @__PURE__ */ new Date(0)).toISOString());
152
+ } catch {
153
+ }
154
+ return {
155
+ id: modelId,
156
+ async embed(texts) {
157
+ const out = [];
158
+ for await (const batch of model.embed(texts, 64)) {
159
+ for (const v of batch) out.push(Array.from(v));
160
+ }
161
+ return out;
162
+ },
163
+ async embedQuery(text) {
164
+ const v = await model.queryEmbed(text);
165
+ return Array.from(v);
166
+ }
167
+ };
168
+ } catch (e) {
169
+ return fail(isPermissionError(e) ? "no-permission" : "download-failed");
170
+ }
171
+ }
172
+ async function importEmbedBackend() {
173
+ const hostDir = process.env.VIBGRATE_EMBEDDER_PATH;
174
+ if (hostDir) {
175
+ try {
176
+ const req = createRequire(path2.join(hostDir, "package.json"));
177
+ const mod = await import(pathToFileURL(req.resolve("fastembed")).href);
178
+ const resolved = mod?.FlagEmbedding ? mod : mod?.default;
179
+ if (resolved?.FlagEmbedding) return resolved;
180
+ } catch {
181
+ }
182
+ }
183
+ return await import('fastembed').catch(() => null);
184
+ }
185
+ function embedInitTimeoutMs() {
186
+ const n = Number(process.env.VG_EMBED_TIMEOUT_MS);
187
+ return Number.isFinite(n) && n > 0 ? n : 6e4;
188
+ }
189
+ async function withTimeout(promise, ms, message) {
190
+ let timer;
191
+ try {
192
+ return await Promise.race([
193
+ promise,
194
+ new Promise((_, reject) => {
195
+ timer = setTimeout(() => reject(new Error(message)), ms);
196
+ timer.unref?.();
197
+ })
198
+ ]);
199
+ } finally {
200
+ clearTimeout(timer);
201
+ }
202
+ }
203
+ function mapModel(mod, id) {
204
+ const m = mod.EmbeddingModel ?? {};
205
+ if (/bge-small/i.test(id)) return m.BGESmallENV15 ?? m.BGESmallEN ?? id;
206
+ if (/bge-base/i.test(id)) return m.BGEBaseENV15 ?? id;
207
+ if (/all-minilm/i.test(id)) return m.AllMiniLML6V2 ?? id;
208
+ return id;
209
+ }
210
+ var PATH_NOISE = /* @__PURE__ */ new Set([
211
+ "src",
212
+ "app",
213
+ "apps",
214
+ "lib",
215
+ "libs",
216
+ "dist",
217
+ "build",
218
+ "out",
219
+ "index",
220
+ "main",
221
+ "packages",
222
+ "pkg",
223
+ "test",
224
+ "tests",
225
+ "__tests__",
226
+ "spec",
227
+ "node_modules",
228
+ "internal",
229
+ "com",
230
+ "example",
231
+ "demo",
232
+ "java",
233
+ "net",
234
+ "org",
235
+ "www"
236
+ ]);
237
+ function pathContext(file) {
238
+ const parts = file.split("/");
239
+ const stem = (parts.pop() ?? "").replace(/\.[^.]+$/, "");
240
+ const dirs = parts.filter((p) => p && !PATH_NOISE.has(p.toLowerCase()));
241
+ return [...dirs.slice(-2), stem].filter(Boolean).join(" ");
242
+ }
243
+ function nodeEmbedText(node, areaLabel) {
244
+ if (node.kind === "document") {
245
+ const category = (node.signature ?? "document").replace(/^document:/, "");
246
+ const categoryHints = {
247
+ manifest: "package dependencies scripts install dependencies versions",
248
+ docker: "dockerfile container image build runtime",
249
+ compose: "docker-compose services containers networking volumes",
250
+ ci: "continuous integration github actions workflow pipeline test build deploy",
251
+ "build-config": "typescript tsconfig bundler vite webpack eslint prettier test",
252
+ "api-contract": "openapi swagger graphql protobuf api schema endpoints",
253
+ "task-runner": "makefile justfile tasks build test run commands",
254
+ workspace: "monorepo workspace packages pnpm turbo nx",
255
+ infra: "deploy infrastructure kubernetes kubernetes terraform helm kubernetes",
256
+ "env-example": "environment variables configuration secrets template",
257
+ markdown: "documentation readme guide"
258
+ };
259
+ const hint = categoryHints[category] ?? category;
260
+ return [
261
+ node.qualifiedName,
262
+ "document",
263
+ category,
264
+ hint,
265
+ pathContext(node.file),
266
+ (node.doc ?? "").slice(0, 5e3)
267
+ ].filter(Boolean).join("\n");
268
+ }
269
+ return [node.qualifiedName, node.kind, node.signature ?? "", node.doc ?? "", pathContext(node.file), areaLabel ?? ""].filter(Boolean).join(" ");
270
+ }
271
+ function cosine(a, b) {
272
+ let dot = 0;
273
+ let na = 0;
274
+ let nb = 0;
275
+ const n = Math.min(a.length, b.length);
276
+ for (let i = 0; i < n; i++) {
277
+ dot += a[i] * b[i];
278
+ na += a[i] * a[i];
279
+ nb += b[i] * b[i];
280
+ }
281
+ if (na === 0 || nb === 0) return 0;
282
+ return dot / (Math.sqrt(na) * Math.sqrt(nb));
283
+ }
284
+ var EMBED_CHUNK = 256;
285
+ var CACHE_WRITE_INTERVAL_MS = 1500;
286
+ function vectorCachePath(root, modelId) {
287
+ return path2.join(cacheDir(root), `embeddings-${safe(modelId)}.json`);
288
+ }
289
+ function countPending(graph, root, modelId) {
290
+ const entries = readCacheEntries(vectorCachePath(root, modelId), modelId);
291
+ const areaLabel = new Map(graph.areas.map((a) => [a.id, a.label]));
292
+ let pending = 0;
293
+ for (const n of graph.nodes) {
294
+ if (n.kind === "file" || n.kind === "external") continue;
295
+ const h = hashString(nodeEmbedText(n, areaLabel.get(n.area)));
296
+ const cached = entries[n.id];
297
+ if (!cached || cached.hash !== h) pending++;
298
+ }
299
+ return pending;
300
+ }
301
+ function readCacheEntries(file, modelId) {
302
+ if (!fs2.existsSync(file)) return {};
303
+ try {
304
+ const loaded = JSON.parse(fs2.readFileSync(file, "utf8"));
305
+ if (loaded.model === modelId && loaded.entries) return loaded.entries;
306
+ } catch {
307
+ }
308
+ return {};
309
+ }
310
+ async function getNodeEmbeddings(graph, embedder, root, onProgress) {
311
+ const file = vectorCachePath(root, embedder.id);
312
+ let cache = { model: embedder.id, entries: {} };
313
+ if (fs2.existsSync(file)) {
314
+ try {
315
+ const loaded = JSON.parse(fs2.readFileSync(file, "utf8"));
316
+ if (loaded.model === embedder.id && loaded.entries) cache = loaded;
317
+ } catch {
318
+ }
319
+ }
320
+ const areaLabel = new Map(graph.areas.map((a) => [a.id, a.label]));
321
+ const targets = graph.nodes.filter((n) => n.kind !== "file" && n.kind !== "external");
322
+ const toEmbed = [];
323
+ const vectors = /* @__PURE__ */ new Map();
324
+ for (const n of targets) {
325
+ const text = nodeEmbedText(n, areaLabel.get(n.area));
326
+ const h = hashString(text);
327
+ const cached = cache.entries[n.id];
328
+ if (cached && cached.hash === h) vectors.set(n.id, cached.vec);
329
+ else toEmbed.push({ id: n.id, text, hash: h });
330
+ }
331
+ const persist = () => {
332
+ try {
333
+ fs2.mkdirSync(cacheDir(root), { recursive: true });
334
+ const tmp = `${file}.tmp.${process.pid}`;
335
+ fs2.writeFileSync(tmp, JSON.stringify(cache));
336
+ fs2.renameSync(tmp, file);
337
+ } catch {
338
+ }
339
+ };
340
+ if (toEmbed.length) {
341
+ const lock = `${file}.lock`;
342
+ if (!acquireLock(lock)) return vectors;
343
+ try {
344
+ onProgress?.(0, toEmbed.length);
345
+ let lastWrite = Date.now();
346
+ for (let i = 0; i < toEmbed.length; i += EMBED_CHUNK) {
347
+ const slice = toEmbed.slice(i, i + EMBED_CHUNK);
348
+ const vecs = await embedder.embed(slice.map((t) => t.text));
349
+ slice.forEach((t, j) => {
350
+ const vec = vecs[j] ?? [];
351
+ vectors.set(t.id, vec);
352
+ cache.entries[t.id] = { hash: t.hash, vec };
353
+ });
354
+ onProgress?.(Math.min(i + EMBED_CHUNK, toEmbed.length), toEmbed.length);
355
+ if (Date.now() - lastWrite >= CACHE_WRITE_INTERVAL_MS) {
356
+ persist();
357
+ lastWrite = Date.now();
358
+ }
359
+ }
360
+ const live = new Set(targets.map((n) => n.id));
361
+ for (const id of Object.keys(cache.entries)) if (!live.has(id)) delete cache.entries[id];
362
+ persist();
363
+ } finally {
364
+ releaseLock(lock);
365
+ }
366
+ }
367
+ return vectors;
368
+ }
369
+ function safe(id) {
370
+ return id.replace(/[^a-z0-9.-]+/gi, "_");
371
+ }
372
+
373
+ export { acquireLock, clearModelCache, cosine, countPending, embeddingsCached, getNodeEmbeddings, isModelReady, loadEmbedder, modelCacheInfo, nodeEmbedText, releaseLock, resolveEmbedModel, unavailableMessage, withTimeout };
374
+ //# sourceMappingURL=chunk-2A7XCANX.js.map
375
+ //# sourceMappingURL=chunk-2A7XCANX.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/engine/lock.ts","../src/engine/embeddings.ts"],"names":["fs","path"],"mappings":"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","file":"chunk-2A7XCANX.js","sourcesContent":["import * as fs from 'node:fs';\nimport * as path from 'node:path';\n\n/**\n * Cross-process single-writer lock (O_EXCL create), shared by the embedding\n * cache writer and the auto-refresh rebuild so two vg processes never write\n * the same repo-local artifact at once. A lock whose owning process is dead,\n * or that is older than `staleMs`, is presumed abandoned and reclaimed.\n */\n\n/** A lock older than this is presumed dead (crashed process). */\nexport const DEFAULT_LOCK_STALE_MS = 15 * 60 * 1000;\n\nfunction isProcessAlive(pid: number): boolean {\n try {\n process.kill(pid, 0);\n return true;\n } catch (e) {\n return (e as NodeJS.ErrnoException).code === 'EPERM'; // exists, just not ours to signal\n }\n}\n\nfunction lockIsStale(file: string, staleMs: number): boolean {\n try {\n const { pid, at } = JSON.parse(fs.readFileSync(file, 'utf8')) as { pid?: number; at?: number };\n if (typeof at === 'number' && Date.now() - at > staleMs) return true;\n if (typeof pid === 'number' && !isProcessAlive(pid)) return true;\n return false;\n } catch {\n return true; // unreadable/corrupt → reclaim it\n }\n}\n\n/** Take the lock (O_EXCL), reclaiming a stale/dead one. Returns success. */\nexport function acquireLock(file: string, staleMs = DEFAULT_LOCK_STALE_MS): boolean {\n const write = (): boolean => {\n try {\n fs.mkdirSync(path.dirname(file), { recursive: true });\n const fd = fs.openSync(file, 'wx');\n fs.writeFileSync(fd, JSON.stringify({ pid: process.pid, at: Date.now() }));\n fs.closeSync(fd);\n return true;\n } catch {\n return false;\n }\n };\n if (write()) return true;\n if (lockIsStale(file, staleMs)) {\n try {\n fs.rmSync(file, { force: true });\n } catch {\n /* another process may have reclaimed it */\n }\n return write();\n }\n return false;\n}\n\nexport function releaseLock(file: string): void {\n try {\n fs.rmSync(file, { force: true });\n } catch {\n /* best-effort */\n }\n}\n","import * as fs from 'node:fs';\nimport { createRequire } from 'node:module';\nimport * as os from 'node:os';\nimport * as path from 'node:path';\nimport { pathToFileURL } from 'node:url';\nimport { hashString } from './hash.js';\nimport { cacheDir } from './cache.js';\nimport { acquireLock, releaseLock } from './lock.js';\nimport type { GraphNode, VgGraph } from '../schema.js';\n\n/**\n * Local-embedding semantic search for `vg ask --semantic`/`--deep` (no API key).\n *\n * The embedding backend is an OPTIONAL, lazily-loaded dependency (`fastembed`,\n * Apache-2.0, local ONNX) so the core install stays lean and `ask` never breaks:\n * if the backend or model isn't available it degrades to (prefix-fuzzy) lexical\n * search with a clear note. Embeddings are query-time + cached under\n * `.vibgrate/cache/` (keyed by node-text hash); they are NEVER written into the\n * committed `graph.json`, so the artifact stays byte-deterministic.\n *\n * `--local` disables the model download (semantic is skipped unless already\n * cached via an injected backend), keeping the air-gapped guarantee.\n */\n\nexport interface Embedder {\n /** Stable model id (recorded with cached vectors so a model change invalidates them). */\n id: string;\n /** Embed documents → unit-or-raw vectors (cosine handles normalization). */\n embed(texts: string[]): Promise<number[][]>;\n /** Embed a single query string. */\n embedQuery(text: string): Promise<number[]>;\n}\n\n/** Why semantic fell back to lexical — for calm, specific messaging. */\nexport type EmbedUnavailable = 'not-installed' | 'no-permission' | 'download-failed' | 'init-failed';\n\nexport interface LoadEmbedderOptions {\n local?: boolean; // --local: do not download a model\n model?: string; // override the embedding model id\n noDownload?: boolean; // only load if the model is already cached (never fetch)\n showDownloadProgress?: boolean; // let fastembed print its one-time download progress\n onUnavailable?: (reason: EmbedUnavailable) => void; // called (not thrown) when it can't load\n}\n\n/** The embedding model id in effect (explicit override → default). */\nexport function resolveEmbedModel(model?: string): string {\n return model ?? 'bge-small-en-v1.5';\n}\n\n/**\n * The **central, per-user** model cache — downloaded once per machine and shared\n * across every repo/folder (devs have many repos; the ~tens-of-MB model should\n * not re-download per project). Uses the XDG standard:\n * `$XDG_CACHE_HOME/vibgrate/models` (or `~/.cache/vibgrate/models`); relocate via\n * the standard `XDG_CACHE_HOME`. NB: fastembed's own default is the relative\n * `./local_cache`, i.e. per-CWD — which we override.\n */\nexport function modelCacheDir(): string {\n const base = process.env.XDG_CACHE_HOME || path.join(os.homedir(), '.cache');\n return path.join(base, 'vibgrate', 'models');\n}\n\n/** A machine-global marker written once the model has loaded successfully. */\nfunction modelReadyMarker(modelId: string): string {\n return path.join(modelCacheDir(), `.ready-${safe(modelId)}`);\n}\n\n/**\n * Whether the embedding model is already present on this machine (loaded at least\n * once), so it can be used with **no network**. Lets the background warm-up\n * trigger only when it won't surprise-download.\n */\nexport function isModelReady(modelId = resolveEmbedModel()): boolean {\n return fs.existsSync(modelReadyMarker(modelId));\n}\n\n/**\n * A calm, specific, actionable one-liner for why semantic isn't available right\n * now — built to reassure (lexical still works) and inform, never to alarm. No\n * stack traces, no scary words; always names the off switch and the fix.\n */\nexport function unavailableMessage(reason: EmbedUnavailable): string {\n switch (reason) {\n case 'no-permission':\n return `semantic search couldn't write its model cache at ${modelCacheDir()} — 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 — the cache lives in your home folder.)`;\n case 'download-failed':\n return `the semantic model isn't downloaded yet and couldn't be fetched (offline?) — using fast lexical search. It'll try again next time; run with --local to keep lexical without retrying.`;\n case 'not-installed':\n return `the optional semantic backend isn't available here — using fast lexical search.`;\n case 'init-failed':\n default:\n return `the semantic model couldn't load — using fast lexical search.`;\n }\n}\n\nexport interface ModelCacheInfo {\n dir: string;\n present: boolean;\n bytes: number;\n}\n\n/** Where the shared model lives, whether it's present, and how big it is. */\nexport function modelCacheInfo(modelId = resolveEmbedModel()): ModelCacheInfo {\n const dir = modelCacheDir();\n return { dir, present: isModelReady(modelId) || fs.existsSync(dir), bytes: dirSize(dir) };\n}\n\n/** Remove the shared, re-downloadable model from the central cache. Returns bytes freed. */\nexport function clearModelCache(): number {\n const dir = modelCacheDir();\n const bytes = dirSize(dir);\n try {\n fs.rmSync(dir, { recursive: true, force: true });\n } catch {\n /* best-effort */\n }\n return bytes;\n}\n\nfunction dirSize(dir: string): number {\n let total = 0;\n let entries: fs.Dirent[];\n try {\n entries = fs.readdirSync(dir, { withFileTypes: true });\n } catch {\n return 0;\n }\n for (const e of entries) {\n const p = path.join(dir, e.name);\n if (e.isDirectory()) total += dirSize(p);\n else {\n try {\n total += fs.statSync(p).size;\n } catch {\n /* skip unreadable */\n }\n }\n }\n return total;\n}\n\nfunction isPermissionError(e: unknown): boolean {\n const code = (e as NodeJS.ErrnoException | undefined)?.code;\n return code === 'EACCES' || code === 'EPERM' || code === 'EROFS';\n}\n\n/**\n * Whether this repo already has a cached vector set for `modelId` — i.e. semantic\n * search has run here before, so the next run is fast (no first-use download/embed).\n * Lets `ask` show the one-time setup note only when it's actually warranted.\n */\nexport function embeddingsCached(root: string, modelId: string): boolean {\n return fs.existsSync(path.join(cacheDir(root), `embeddings-${safe(modelId)}.json`));\n}\n\n/**\n * Try to load the optional local embedding backend. Returns null (→ caller falls\n * back to lexical) when running `--local`, when the dependency isn't installed,\n * or when the model can't initialize.\n */\nexport async function loadEmbedder(options: LoadEmbedderOptions = {}): Promise<Embedder | null> {\n // `--local` forces the deterministic lexical floor with no model load/download\n // (air-gapped guarantee; also how tests stay offline).\n if (options.local) return null;\n const modelId = resolveEmbedModel(options.model);\n // noDownload (used by the background warm-up): never fetch — bail if not cached.\n if (options.noDownload && !isModelReady(modelId)) return null;\n const fail = (reason: EmbedUnavailable): null => {\n options.onUnavailable?.(reason);\n return null;\n };\n\n // Lazy, optional dependency (native ONNX): loaded only for `vg ask`/`embed`,\n // never by build/verify, so the graph artifact stays deterministic.\n const mod: any = await importEmbedBackend();\n if (!mod?.FlagEmbedding) return fail('not-installed');\n\n const cache = modelCacheDir(); // central, shared across all repos\n try {\n fs.mkdirSync(cache, { recursive: true });\n } catch (e) {\n return fail(isPermissionError(e) ? 'no-permission' : 'init-failed');\n }\n\n try {\n // First use fetches the model. A refused connection fails fast, but a\n // black-holing proxy (common on corporate networks) would hang the first\n // `vg ask` / MCP query_graph forever — bound the wait and fall back to\n // lexical instead. Cached loads are local disk and get the same generous\n // ceiling without ever hitting it.\n const model: any = await withTimeout(\n mod.FlagEmbedding.init({\n model: mapModel(mod, modelId),\n cacheDir: cache,\n showDownloadProgress: options.showDownloadProgress ?? false,\n }) as Promise<unknown>,\n embedInitTimeoutMs(),\n 'embedding model init timed out',\n );\n // Record machine-global readiness so future builds can warm up offline.\n try {\n fs.writeFileSync(modelReadyMarker(modelId), new Date(0).toISOString());\n } catch {\n /* marker is best-effort */\n }\n return {\n id: modelId,\n async embed(texts) {\n const out: number[][] = [];\n for await (const batch of model.embed(texts, 64)) {\n for (const v of batch) out.push(Array.from(v as ArrayLike<number>));\n }\n return out;\n },\n async embedQuery(text) {\n const v = await model.queryEmbed(text);\n return Array.from(v as ArrayLike<number>);\n },\n };\n } catch (e) {\n return fail(isPermissionError(e) ? 'no-permission' : 'download-failed');\n }\n}\n\n/**\n * Import the optional native backend. A host can supply it out-of-tree via\n * `VIBGRATE_EMBEDDER_PATH` — a directory whose `node_modules` contains\n * `fastembed`. Editor integrations use this when their bundled engine ships\n * without the native optional dependency (the VS Code extension installs it\n * into its own storage on consent); the host dir wins over this package's own\n * dependency tree so the copy the user consented to is the one that loads.\n * Any resolution failure falls through — never throws.\n */\nasync function importEmbedBackend(): Promise<any> {\n const hostDir = process.env.VIBGRATE_EMBEDDER_PATH;\n if (hostDir) {\n try {\n const req = createRequire(path.join(hostDir, 'package.json'));\n const mod: any = await import(pathToFileURL(req.resolve('fastembed')).href);\n // A CJS entry imported over ESM may nest its exports under `default`.\n const resolved = mod?.FlagEmbedding ? mod : mod?.default;\n if (resolved?.FlagEmbedding) return resolved;\n } catch {\n /* fall through to normal resolution */\n }\n }\n return await import('fastembed' as string).catch(() => null);\n}\n\n/** Ceiling for the one-time model fetch/init; override via VG_EMBED_TIMEOUT_MS. */\nfunction embedInitTimeoutMs(): number {\n const n = Number(process.env.VG_EMBED_TIMEOUT_MS);\n return Number.isFinite(n) && n > 0 ? n : 60_000;\n}\n\n/** Reject `promise` if it hasn't settled within `ms` (the work itself is not cancelled). */\nexport async function withTimeout<T>(promise: Promise<T>, ms: number, message: string): Promise<T> {\n let timer: ReturnType<typeof setTimeout> | undefined;\n try {\n return await Promise.race([\n promise,\n new Promise<never>((_, reject) => {\n timer = setTimeout(() => reject(new Error(message)), ms);\n timer.unref?.();\n }),\n ]);\n } finally {\n clearTimeout(timer);\n }\n}\n\nfunction mapModel(mod: any, id: string): unknown {\n const m = mod.EmbeddingModel ?? {};\n if (/bge-small/i.test(id)) return m.BGESmallENV15 ?? m.BGESmallEN ?? id;\n if (/bge-base/i.test(id)) return m.BGEBaseENV15 ?? id;\n if (/all-minilm/i.test(id)) return m.AllMiniLML6V2 ?? id;\n return id;\n}\n\n/** Path segments that carry no semantic signal — dropped from embed context. */\nconst PATH_NOISE = new Set([\n 'src', 'app', 'apps', 'lib', 'libs', 'dist', 'build', 'out', 'index', 'main',\n 'packages', 'pkg', 'test', 'tests', '__tests__', 'spec', 'node_modules', 'internal',\n 'com', 'example', 'demo', 'java', 'net', 'org', 'www',\n]);\n\n/** Meaningful path context for a node: a couple of parent dirs + the filename stem. */\nfunction pathContext(file: string): string {\n const parts = file.split('/');\n const stem = (parts.pop() ?? '').replace(/\\.[^.]+$/, '');\n const dirs = parts.filter((p) => p && !PATH_NOISE.has(p.toLowerCase()));\n return [...dirs.slice(-2), stem].filter(Boolean).join(' ');\n}\n\n/**\n * The text we embed for a node. Alongside identity + signature we add the\n * strongest available signal — the node's **doc-comment / docstring** summary —\n * plus lightweight context already on the graph (file-path words, area label), so\n * a tersely-named symbol (`Table`, `NotificationJob`) a concept query can reach.\n *\n * `document` nodes (markdown, manifests, Docker, CI, OpenAPI, …) put the\n * scrubbed body in `doc` — that body is the primary embed signal so `vg ask`\n * can answer project-context questions, not only code symbols.\n */\nexport function nodeEmbedText(node: GraphNode, areaLabel?: string): string {\n if (node.kind === 'document') {\n const category = (node.signature ?? 'document').replace(/^document:/, '');\n // Category keywords help ops/config queries land on the right file type.\n const categoryHints: Record<string, string> = {\n manifest: 'package dependencies scripts install dependencies versions',\n docker: 'dockerfile container image build runtime',\n compose: 'docker-compose services containers networking volumes',\n ci: 'continuous integration github actions workflow pipeline test build deploy',\n 'build-config': 'typescript tsconfig bundler vite webpack eslint prettier test',\n 'api-contract': 'openapi swagger graphql protobuf api schema endpoints',\n 'task-runner': 'makefile justfile tasks build test run commands',\n workspace: 'monorepo workspace packages pnpm turbo nx',\n infra: 'deploy infrastructure kubernetes kubernetes terraform helm kubernetes',\n 'env-example': 'environment variables configuration secrets template',\n markdown: 'documentation readme guide',\n };\n const hint = categoryHints[category] ?? category;\n return [\n node.qualifiedName,\n 'document',\n category,\n hint,\n pathContext(node.file),\n (node.doc ?? '').slice(0, 5000),\n ]\n .filter(Boolean)\n .join('\\n');\n }\n return [node.qualifiedName, node.kind, node.signature ?? '', node.doc ?? '', pathContext(node.file), areaLabel ?? '']\n .filter(Boolean)\n .join(' ');\n}\n\nexport function cosine(a: number[], b: number[]): number {\n let dot = 0;\n let na = 0;\n let nb = 0;\n const n = Math.min(a.length, b.length);\n for (let i = 0; i < n; i++) {\n dot += a[i] * b[i];\n na += a[i] * a[i];\n nb += b[i] * b[i];\n }\n if (na === 0 || nb === 0) return 0;\n return dot / (Math.sqrt(na) * Math.sqrt(nb));\n}\n\ninterface EmbedCache {\n model: string;\n entries: Record<string, { hash: string; vec: number[] }>;\n}\n\n/** Reports embedding progress: how many of `total` nodes are done so far. */\nexport type EmbedProgress = (done: number, total: number) => void;\n\n/** Nodes embedded per chunk — bounds memory and gives progress granularity. */\nconst EMBED_CHUNK = 256;\n/** Min gap between incremental cache writes (ms) — caps IO on big repos. */\nconst CACHE_WRITE_INTERVAL_MS = 1500;\n\nfunction vectorCachePath(root: string, modelId: string): string {\n return path.join(cacheDir(root), `embeddings-${safe(modelId)}.json`);\n}\n\n/**\n * How many searchable nodes still need embedding for `modelId` — cheap (hashes\n * the embed-text, reads only the cache; never loads the model). Lets the embed\n * command / background warm-up exit instantly when nothing changed.\n */\nexport function countPending(graph: VgGraph, root: string, modelId: string): number {\n const entries = readCacheEntries(vectorCachePath(root, modelId), modelId);\n const areaLabel = new Map(graph.areas.map((a) => [a.id, a.label] as const));\n let pending = 0;\n for (const n of graph.nodes) {\n // file/external stay out of the index; document (docs/env examples) are in.\n if (n.kind === 'file' || n.kind === 'external') continue;\n const h = hashString(nodeEmbedText(n, areaLabel.get(n.area)));\n const cached = entries[n.id];\n if (!cached || cached.hash !== h) pending++;\n }\n return pending;\n}\n\nfunction readCacheEntries(file: string, modelId: string): EmbedCache['entries'] {\n if (!fs.existsSync(file)) return {};\n try {\n const loaded = JSON.parse(fs.readFileSync(file, 'utf8')) as EmbedCache;\n if (loaded.model === modelId && loaded.entries) return loaded.entries;\n } catch {\n /* treat as empty */\n }\n return {};\n}\n\n// Single-writer lock (shared helper — see ./lock.ts) so a foreground `ask`\n// and a background `embed` never both embed the same repo at once (which\n// would race on the cache file).\n\n/**\n * Node embeddings for the searchable (non-file/external) nodes, cache-backed:\n * only nodes whose embed-text changed are re-embedded. The first run embeds in\n * chunks, **persists the cache incrementally** (so an interrupted/timed-out run\n * resumes instead of wasting the work), and reports progress via `onProgress`.\n */\nexport async function getNodeEmbeddings(\n graph: VgGraph,\n embedder: Embedder,\n root: string,\n onProgress?: EmbedProgress,\n): Promise<Map<string, number[]>> {\n const file = vectorCachePath(root, embedder.id);\n let cache: EmbedCache = { model: embedder.id, entries: {} };\n if (fs.existsSync(file)) {\n try {\n const loaded = JSON.parse(fs.readFileSync(file, 'utf8')) as EmbedCache;\n if (loaded.model === embedder.id && loaded.entries) cache = loaded;\n } catch {\n /* rebuild */\n }\n }\n\n const areaLabel = new Map(graph.areas.map((a) => [a.id, a.label] as const));\n // Include document nodes (markdown/txt/env examples) so ask can retrieve docs.\n const targets = graph.nodes.filter((n) => n.kind !== 'file' && n.kind !== 'external');\n const toEmbed: { id: string; text: string; hash: string }[] = [];\n const vectors = new Map<string, number[]>();\n for (const n of targets) {\n const text = nodeEmbedText(n, areaLabel.get(n.area));\n const h = hashString(text);\n const cached = cache.entries[n.id];\n if (cached && cached.hash === h) vectors.set(n.id, cached.vec);\n else toEmbed.push({ id: n.id, text, hash: h });\n }\n\n // Atomic write (temp + rename) so a reader never sees a half-written cache.\n const persist = (): void => {\n try {\n fs.mkdirSync(cacheDir(root), { recursive: true });\n const tmp = `${file}.tmp.${process.pid}`;\n fs.writeFileSync(tmp, JSON.stringify(cache));\n fs.renameSync(tmp, file);\n } catch {\n /* cache write best-effort */\n }\n };\n\n if (toEmbed.length) {\n // Single-writer: if another process (e.g. a background warm-up) is already\n // embedding this repo, don't double-work or race the cache — return what's\n // cached so far (lexical floor still applies). It will be complete next run.\n const lock = `${file}.lock`;\n if (!acquireLock(lock)) return vectors;\n try {\n onProgress?.(0, toEmbed.length);\n let lastWrite = Date.now();\n for (let i = 0; i < toEmbed.length; i += EMBED_CHUNK) {\n const slice = toEmbed.slice(i, i + EMBED_CHUNK);\n const vecs = await embedder.embed(slice.map((t) => t.text));\n slice.forEach((t, j) => {\n const vec = vecs[j] ?? [];\n vectors.set(t.id, vec);\n cache.entries[t.id] = { hash: t.hash, vec };\n });\n onProgress?.(Math.min(i + EMBED_CHUNK, toEmbed.length), toEmbed.length);\n // Persist periodically so a crash/timeout resumes from here next run.\n if (Date.now() - lastWrite >= CACHE_WRITE_INTERVAL_MS) {\n persist();\n lastWrite = Date.now();\n }\n }\n // prune entries for nodes no longer present, then a final authoritative write\n const live = new Set(targets.map((n) => n.id));\n for (const id of Object.keys(cache.entries)) if (!live.has(id)) delete cache.entries[id];\n persist();\n } finally {\n releaseLock(lock);\n }\n }\n\n return vectors;\n}\n\nfunction safe(id: string): string {\n return id.replace(/[^a-z0-9.-]+/gi, '_');\n}\n"]}
@@ -479,6 +479,67 @@ function redactSecrets(s) {
479
479
  function httpError(label, status, hint) {
480
480
  return new Error(`${label} returned HTTP ${status} \u2014 ${hint}`);
481
481
  }
482
+ function unwrapNestedErrorJson(text) {
483
+ const trimmed = (text ?? "").trim();
484
+ if (!trimmed.startsWith("{")) return trimmed;
485
+ let parsed;
486
+ try {
487
+ parsed = JSON.parse(trimmed);
488
+ } catch {
489
+ return trimmed;
490
+ }
491
+ const obj = parsed ?? {};
492
+ const err = obj.error;
493
+ const errObj = typeof err === "object" && err ? err : {};
494
+ const message = typeof err === "string" ? err : typeof errObj.message === "string" ? errObj.message : typeof obj.message === "string" ? obj.message : null;
495
+ if (!message?.trim()) return trimmed;
496
+ const metadata = typeof errObj.metadata === "object" && errObj.metadata ? errObj.metadata : {};
497
+ const names = (value) => Array.isArray(value) ? value.filter((v) => typeof v === "string" && !!v.trim()) : [];
498
+ const requested = names(metadata.requested_providers);
499
+ const available = names(metadata.available_providers);
500
+ const parts = [message.trim()];
501
+ if (requested.length) parts.push(`requested provider(s): ${requested.join(", ")}`);
502
+ if (available.length) parts.push(`permitted provider(s): ${available.join(", ")}`);
503
+ return parts.join(" \u2014 ");
504
+ }
505
+ function relayErrorDetail(body) {
506
+ if (!body) return null;
507
+ let parsed;
508
+ try {
509
+ parsed = JSON.parse(body);
510
+ } catch {
511
+ return nonJsonErrorDetail(body);
512
+ }
513
+ const obj = parsed;
514
+ const err = obj?.error;
515
+ const errObj = typeof err === "object" && err ? err : {};
516
+ const message = typeof err === "string" ? err : typeof errObj.message === "string" ? errObj.message : typeof obj?.message === "string" ? obj.message : null;
517
+ const detailValue = typeof obj?.detail === "string" ? obj.detail : typeof errObj.detail === "string" ? errObj.detail : null;
518
+ const detail = detailValue && detailValue.trim() ? unwrapNestedErrorJson(detailValue) : null;
519
+ const parts = [message, detail].filter((p) => !!p && p.trim().length > 0);
520
+ if (!parts.length) return null;
521
+ const correlationId = typeof obj?.correlation_id === "string" && obj.correlation_id ? obj.correlation_id : typeof errObj.correlation_id === "string" && errObj.correlation_id ? errObj.correlation_id : null;
522
+ const id = correlationId ? ` (correlation id ${correlationId})` : "";
523
+ return `${[...new Set(parts)].join(" \u2014 ").slice(0, 400)}${id}`;
524
+ }
525
+ function nonJsonErrorDetail(body) {
526
+ const text = body.trim();
527
+ if (!text) return null;
528
+ if (text.length <= 200) return text;
529
+ const title = /<title[^>]*>([\s\S]*?)<\/title>/i.exec(text)?.[1]?.replace(/\s+/g, " ").trim();
530
+ if (title) return title.slice(0, 200);
531
+ const code = /\bError\s+\d{3,4}\b/.exec(text)?.[0];
532
+ return code ?? null;
533
+ }
534
+ function statusHint(status, model, apiKeyEnv) {
535
+ if (status === 401 || status === 403) return `the key in ${apiKeyEnv ?? "(none)"} was rejected \u2014 check it has access to "${model}"`;
536
+ if (status === 402) return "the account behind this endpoint is out of credit \u2014 top it up, or switch backend with --provider / --local";
537
+ if (status === 404) return `this endpoint has no model "${model}" \u2014 check the id (\`vg models catalog\`) and the base URL`;
538
+ if (status === 408 || status === 504) return "the request timed out before the model replied \u2014 retry, shorten the task, or use --local";
539
+ if (status === 429) return "rate limited \u2014 wait and retry, or switch backend with --provider / --local";
540
+ if (status >= 500) return `the endpoint failed to serve the request \u2014 this is an upstream/gateway failure, not the model id "${model}"; retry, or use --provider / --local if it persists`;
541
+ return `check the model id "${model}" and the endpoint`;
542
+ }
482
543
  async function* streamLines(res) {
483
544
  const reader = res.body?.getReader();
484
545
  if (!reader) return;
@@ -499,6 +560,8 @@ async function* streamLines(res) {
499
560
  }
500
561
  function accumulateOpenAiDelta(acc, chunk, onToken) {
501
562
  const c = chunk;
563
+ const reason = c.choices?.[0]?.finish_reason;
564
+ if (typeof reason === "string" && reason) acc.finishReason = reason;
502
565
  const delta = c.choices?.[0]?.delta;
503
566
  if (delta?.content) {
504
567
  acc.text += delta.content;
@@ -570,6 +633,7 @@ var OllamaProvider = class {
570
633
  if (streaming) {
571
634
  let text = "";
572
635
  let toolCallsRaw;
636
+ let truncated = false;
573
637
  const usage = {};
574
638
  for await (const line of streamLines(res)) {
575
639
  try {
@@ -582,10 +646,11 @@ var OllamaProvider = class {
582
646
  if (obj.message?.tool_calls) toolCallsRaw = obj.message.tool_calls;
583
647
  if (typeof obj.prompt_eval_count === "number") usage.promptTokens = obj.prompt_eval_count;
584
648
  if (typeof obj.eval_count === "number") usage.completionTokens = obj.eval_count;
649
+ if (obj.done_reason === "length") truncated = true;
585
650
  } catch {
586
651
  }
587
652
  }
588
- return { text, model: this.model, provider: this.id, toolCalls: parseToolCalls(toolCallsRaw), usage };
653
+ return { text, model: this.model, provider: this.id, toolCalls: parseToolCalls(toolCallsRaw), usage, truncated };
589
654
  }
590
655
  const data = await res.json();
591
656
  return {
@@ -593,7 +658,8 @@ var OllamaProvider = class {
593
658
  model: this.model,
594
659
  provider: this.id,
595
660
  toolCalls: parseToolCalls(data.message?.tool_calls),
596
- usage: { promptTokens: data.prompt_eval_count, completionTokens: data.eval_count }
661
+ usage: { promptTokens: data.prompt_eval_count, completionTokens: data.eval_count },
662
+ truncated: data.done_reason === "length"
597
663
  };
598
664
  }
599
665
  };
@@ -637,11 +703,12 @@ var OpenAiCompatibleProvider = class {
637
703
  throw new Error(`couldn't reach ${this.label} at ${this.config.baseUrl} \u2014 check the endpoint is up (or use --local).`);
638
704
  }
639
705
  if (!res.ok) {
640
- const hint = res.status === 401 || res.status === 403 ? `the key in ${this.config.apiKeyEnv ?? "(none)"} was rejected \u2014 check it has access to "${this.model}"` : `check the model id "${this.model}" and the endpoint`;
706
+ const detail = relayErrorDetail(await res.text().catch(() => ""));
707
+ const hint = detail ?? statusHint(res.status, this.model, this.config.apiKeyEnv);
641
708
  throw httpError(this.label, res.status, hint);
642
709
  }
643
710
  if (streaming) {
644
- const acc = { text: "", tools: /* @__PURE__ */ new Map(), usage: {} };
711
+ const acc = { text: "", tools: /* @__PURE__ */ new Map(), usage: {}, finishReason: void 0 };
645
712
  for await (const line of streamLines(res)) {
646
713
  if (!line.startsWith("data:")) continue;
647
714
  const payload = line.slice(5).trim();
@@ -652,7 +719,7 @@ var OpenAiCompatibleProvider = class {
652
719
  }
653
720
  }
654
721
  const tools = finalizeTools(acc.tools);
655
- return { text: acc.text, model: this.model, provider: this.id, toolCalls: tools.length ? tools : void 0, usage: acc.usage };
722
+ return { text: acc.text, model: this.model, provider: this.id, toolCalls: tools.length ? tools : void 0, usage: acc.usage, truncated: acc.finishReason === "length" };
656
723
  }
657
724
  const data = await res.json();
658
725
  const message = data.choices?.[0]?.message;
@@ -661,7 +728,8 @@ var OpenAiCompatibleProvider = class {
661
728
  model: this.model,
662
729
  provider: this.id,
663
730
  toolCalls: parseToolCalls(message?.tool_calls),
664
- usage: { promptTokens: data.usage?.prompt_tokens, completionTokens: data.usage?.completion_tokens }
731
+ usage: { promptTokens: data.usage?.prompt_tokens, completionTokens: data.usage?.completion_tokens },
732
+ truncated: data.choices?.[0]?.finish_reason === "length"
665
733
  };
666
734
  }
667
735
  };
@@ -680,7 +748,7 @@ var ScriptedProvider = class {
680
748
  async chat(_messages, _opts) {
681
749
  const step = this.steps[Math.min(this.turn, this.steps.length - 1)];
682
750
  this.turn++;
683
- return { text: step.text ?? "", model: this.model, provider: this.id, toolCalls: step.toolCalls };
751
+ return { text: step.text ?? "", model: this.model, provider: this.id, toolCalls: step.toolCalls, truncated: step.truncated };
684
752
  }
685
753
  };
686
754
  var OPENAI_COMPATIBLE = {
@@ -835,5 +903,5 @@ var LocalLlamaProvider = class _LocalLlamaProvider {
835
903
  };
836
904
 
837
905
  export { LocalLlamaProvider, MockProvider, OPENAI_COMPATIBLE, OllamaProvider, OpenAiCompatibleProvider, ScriptedProvider, applyEdit, applyEdits, buildAgentMessages, buildMessages, parseEdits, redactSecrets, summarizeDiffs, unifiedDiff };
838
- //# sourceMappingURL=chunk-SJFBXCII.js.map
839
- //# sourceMappingURL=chunk-SJFBXCII.js.map
906
+ //# sourceMappingURL=chunk-2U4SXS5U.js.map
907
+ //# sourceMappingURL=chunk-2U4SXS5U.js.map