@vibgrate/cli 2026.803.1 → 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.
Files changed (106) hide show
  1. package/dist/agent-WYBWUBCC.js +26 -0
  2. package/dist/{agent-HNV2VUCO.js.map → agent-WYBWUBCC.js.map} +1 -1
  3. package/dist/baseline-3HECX4Q6.js +9 -0
  4. package/dist/{baseline-6WTULHM5.js.map → baseline-3HECX4Q6.js.map} +1 -1
  5. package/dist/{chunk-FKFRASAB.js → chunk-23UPIFRU.js} +8 -8
  6. package/dist/{chunk-FKFRASAB.js.map → chunk-23UPIFRU.js.map} +1 -1
  7. package/dist/chunk-2A7XCANX.js +375 -0
  8. package/dist/chunk-2A7XCANX.js.map +1 -0
  9. package/dist/{chunk-AJ2KE3NT.js → chunk-4LGDQBYK.js} +14 -102
  10. package/dist/chunk-4LGDQBYK.js.map +1 -0
  11. package/dist/{chunk-B2KLJPFY.js → chunk-4ZMJKR74.js} +451 -22
  12. package/dist/chunk-4ZMJKR74.js.map +1 -0
  13. package/dist/{chunk-DIU3MXB3.js → chunk-537JBQVB.js} +4 -4
  14. package/dist/{chunk-DIU3MXB3.js.map → chunk-537JBQVB.js.map} +1 -1
  15. package/dist/{chunk-4HFSUIIN.js → chunk-5TS7N7J2.js} +59 -9
  16. package/dist/chunk-5TS7N7J2.js.map +1 -0
  17. package/dist/{chunk-BWMMR5XA.js → chunk-5WMFX7TY.js} +4 -4
  18. package/dist/{chunk-BWMMR5XA.js.map → chunk-5WMFX7TY.js.map} +1 -1
  19. package/dist/{chunk-SNKTD23L.js → chunk-66B4ZQA3.js} +9 -8
  20. package/dist/chunk-66B4ZQA3.js.map +1 -0
  21. package/dist/{chunk-536J6HUR.js → chunk-7BU7CWXC.js} +39 -12
  22. package/dist/chunk-7BU7CWXC.js.map +1 -0
  23. package/dist/{chunk-N4IDP2M2.js → chunk-7TOIESUT.js} +5 -4
  24. package/dist/chunk-7TOIESUT.js.map +1 -0
  25. package/dist/{chunk-CE45STY5.js → chunk-BRUMBQV7.js} +4 -4
  26. package/dist/{chunk-CE45STY5.js.map → chunk-BRUMBQV7.js.map} +1 -1
  27. package/dist/{chunk-CC6F75GI.js → chunk-BUCNKYTW.js} +5 -5
  28. package/dist/{chunk-CC6F75GI.js.map → chunk-BUCNKYTW.js.map} +1 -1
  29. package/dist/{chunk-2TLQDF6C.js → chunk-FAA7RBIG.js} +9 -9
  30. package/dist/{chunk-2TLQDF6C.js.map → chunk-FAA7RBIG.js.map} +1 -1
  31. package/dist/chunk-FZ5Y4KEI.js +6 -0
  32. package/dist/{chunk-QT2PMGN3.js.map → chunk-FZ5Y4KEI.js.map} +1 -1
  33. package/dist/{chunk-S4NSF5AZ.js → chunk-HBQC4XQJ.js} +6 -6
  34. package/dist/{chunk-S4NSF5AZ.js.map → chunk-HBQC4XQJ.js.map} +1 -1
  35. package/dist/{chunk-O7MTBC52.js → chunk-HVHCXM2E.js} +9 -9
  36. package/dist/{chunk-O7MTBC52.js.map → chunk-HVHCXM2E.js.map} +1 -1
  37. package/dist/{chunk-NJFQCDGA.js → chunk-INIULQEP.js} +3 -3
  38. package/dist/{chunk-NJFQCDGA.js.map → chunk-INIULQEP.js.map} +1 -1
  39. package/dist/{chunk-Y7YKUZ3T.js → chunk-JZGVUH57.js} +30 -10
  40. package/dist/chunk-JZGVUH57.js.map +1 -0
  41. package/dist/{chunk-JUSXLCSE.js → chunk-KFK7KCAH.js} +3 -3
  42. package/dist/{chunk-JUSXLCSE.js.map → chunk-KFK7KCAH.js.map} +1 -1
  43. package/dist/{chunk-6I4TXAI6.js → chunk-OVDL76LB.js} +3 -3
  44. package/dist/{chunk-6I4TXAI6.js.map → chunk-OVDL76LB.js.map} +1 -1
  45. package/dist/{chunk-SJFBXCII.js → chunk-QUI3E3TQ.js} +54 -9
  46. package/dist/chunk-QUI3E3TQ.js.map +1 -0
  47. package/dist/chunk-RBMKBYGV.js +102 -0
  48. package/dist/chunk-RBMKBYGV.js.map +1 -0
  49. package/dist/{chunk-5VH3Y4QC.js → chunk-TVRM57CE.js} +78 -381
  50. package/dist/chunk-TVRM57CE.js.map +1 -0
  51. package/dist/{chunk-BBFQZ7CA.js → chunk-UWVFLVZX.js} +4 -4
  52. package/dist/{chunk-BBFQZ7CA.js.map → chunk-UWVFLVZX.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 +555 -351
  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-DZ53QXRT.js +31 -0
  61. package/dist/{ensure-map-5YEI7R7V.js.map → ensure-map-DZ53QXRT.js.map} +1 -1
  62. package/dist/graph-backend-3A2M22QN.js +13 -0
  63. package/dist/{graph-backend-CPHLUKUC.js.map → graph-backend-3A2M22QN.js.map} +1 -1
  64. package/dist/index.d.ts +27 -2
  65. package/dist/index.js +16 -14
  66. package/dist/index.js.map +1 -1
  67. package/dist/{interactive-TNRE4OEK.js → interactive-DB7IC3BL.js} +35 -29
  68. package/dist/interactive-DB7IC3BL.js.map +1 -0
  69. package/dist/load-6KVGUQMU.js +11 -0
  70. package/dist/{load-MWG6WZTK.js.map → load-6KVGUQMU.js.map} +1 -1
  71. package/dist/{run-outcome-375HMSQG.js → run-outcome-YOYRC32U.js} +7 -6
  72. package/dist/run-outcome-YOYRC32U.js.map +1 -0
  73. package/dist/runtime-session-XEP7IPGP.js +22 -0
  74. package/dist/{runtime-session-3MPWXNRL.js.map → runtime-session-XEP7IPGP.js.map} +1 -1
  75. package/dist/session-5WKEACIF.js +17 -0
  76. package/dist/{session-2NGJLI5Y.js.map → session-5WKEACIF.js.map} +1 -1
  77. package/dist/{stream-json-AUMU3NLV.js → stream-json-3OJCPBWY.js} +13 -11
  78. package/dist/stream-json-3OJCPBWY.js.map +1 -0
  79. package/dist/{vg-mcp-bridge-D463EHQI.js → vg-mcp-bridge-PM3C4FQL.js} +12 -10
  80. package/dist/vg-mcp-bridge-PM3C4FQL.js.map +1 -0
  81. package/dist/{vgd-Z23PXSP4.js → vgd-UTLMMPIZ.js} +12 -10
  82. package/dist/vgd-UTLMMPIZ.js.map +1 -0
  83. package/package.json +1 -1
  84. package/scripts/postinstall.mjs +22 -1
  85. package/dist/agent-HNV2VUCO.js +0 -24
  86. package/dist/baseline-6WTULHM5.js +0 -9
  87. package/dist/chunk-4HFSUIIN.js.map +0 -1
  88. package/dist/chunk-536J6HUR.js.map +0 -1
  89. package/dist/chunk-5VH3Y4QC.js.map +0 -1
  90. package/dist/chunk-AJ2KE3NT.js.map +0 -1
  91. package/dist/chunk-B2KLJPFY.js.map +0 -1
  92. package/dist/chunk-N4IDP2M2.js.map +0 -1
  93. package/dist/chunk-QT2PMGN3.js +0 -6
  94. package/dist/chunk-SJFBXCII.js.map +0 -1
  95. package/dist/chunk-SNKTD23L.js.map +0 -1
  96. package/dist/chunk-Y7YKUZ3T.js.map +0 -1
  97. package/dist/ensure-map-5YEI7R7V.js +0 -29
  98. package/dist/graph-backend-CPHLUKUC.js +0 -11
  99. package/dist/interactive-TNRE4OEK.js.map +0 -1
  100. package/dist/load-MWG6WZTK.js +0 -10
  101. package/dist/run-outcome-375HMSQG.js.map +0 -1
  102. package/dist/runtime-session-3MPWXNRL.js +0 -20
  103. package/dist/session-2NGJLI5Y.js +0 -15
  104. package/dist/stream-json-AUMU3NLV.js.map +0 -1
  105. package/dist/vg-mcp-bridge-D463EHQI.js.map +0 -1
  106. package/dist/vgd-Z23PXSP4.js.map +0 -1
@@ -1,13 +1,15 @@
1
- import { listHostSessions, hostSessionPoolSize, acquireHostSession, evictIdleSessions, clearHostSessionPool, dropHostSession, generateOnSession } from './chunk-3LOWJSAF.js';
2
1
  import { vgdSocketPath, vgdPidPath } from './chunk-RBIRXQR6.js';
2
+ import { listHostSessions, hostSessionPoolSize, acquireHostSession, evictIdleSessions, clearHostSessionPool, dropHostSession, generateOnSession } from './chunk-3LOWJSAF.js';
3
3
  import { resolveOne } from './chunk-J7LKEB2R.js';
4
- import { impactOf, queryGraph } from './chunk-5VH3Y4QC.js';
5
- import { loadGraph } from './chunk-DIU3MXB3.js';
4
+ import { impactOf, queryGraph } from './chunk-TVRM57CE.js';
5
+ import { loadGraph } from './chunk-537JBQVB.js';
6
6
  import { repositoryIdFromRoot, globalGraphPathForRef, globalGraphPath } from './chunk-BWPBB45M.js';
7
7
  import { activeGraphSlotKey, detectGitRef } from './chunk-BPF3YX2U.js';
8
8
  import * as path from 'path';
9
- import * as fs from 'fs';
9
+ import * as fs2 from 'fs';
10
10
  import * as net from 'net';
11
+ import { spawn } from 'child_process';
12
+ import { fileURLToPath } from 'url';
11
13
 
12
14
  // src/runtime/vgd/protocol.ts
13
15
  var VGD_PROTOCOL_VERSION = "vgd/0";
@@ -56,6 +58,28 @@ function parseRequest(line) {
56
58
  graphPath: typeof graphPath === "string" && graphPath.trim() ? graphPath.trim() : void 0
57
59
  };
58
60
  }
61
+ if (op === "embed-status") {
62
+ const repositoryId = raw.repositoryId;
63
+ return {
64
+ op: "embed-status",
65
+ repositoryId: typeof repositoryId === "string" && repositoryId.trim() ? repositoryId.trim() : void 0
66
+ };
67
+ }
68
+ if (op === "embed-query") {
69
+ const text = raw.text;
70
+ if (typeof text !== "string" || !text.trim()) return { error: "embed-query requires text" };
71
+ return { op: "embed-query", text: text.trim().slice(0, 8e3) };
72
+ }
73
+ if (op === "embed-index") {
74
+ const repositoryId = raw.repositoryId;
75
+ const gitRef = raw.gitRef;
76
+ if (typeof repositoryId !== "string" || !repositoryId.trim()) return { error: "embed-index requires repositoryId" };
77
+ return {
78
+ op: "embed-index",
79
+ repositoryId: repositoryId.trim(),
80
+ gitRef: typeof gitRef === "string" && gitRef.trim() ? gitRef.trim() : void 0
81
+ };
82
+ }
59
83
  if (op === "query-graph") {
60
84
  const repositoryId = raw.repositoryId;
61
85
  const query = raw.query;
@@ -186,7 +210,9 @@ var ActiveGraphCache = class {
186
210
  maxPerRepo;
187
211
  maxTotal;
188
212
  now;
213
+ onEvict;
189
214
  constructor(options = {}) {
215
+ this.onEvict = options.onEvict;
190
216
  this.idleTimeoutMs = options.idleTimeoutMs ?? DEFAULT_ACTIVE_GRAPH_IDLE_MS;
191
217
  this.maxPerRepo = options.maxPerRepo ?? 8;
192
218
  this.maxTotal = options.maxTotal ?? 32;
@@ -276,9 +302,17 @@ var ActiveGraphCache = class {
276
302
  );
277
303
  }
278
304
  clear() {
305
+ for (const slot of this.slots.values()) this.notifyEvict(slot);
279
306
  this.slots.clear();
280
307
  this.currentRef.clear();
281
308
  }
309
+ notifyEvict(slot) {
310
+ if (!this.onEvict) return;
311
+ try {
312
+ this.onEvict({ repositoryId: slot.repositoryId, gitRef: slot.gitRef });
313
+ } catch {
314
+ }
315
+ }
282
316
  evict(now, forceOverflow) {
283
317
  const removed = [];
284
318
  const removeKey = (key) => {
@@ -286,6 +320,7 @@ var ActiveGraphCache = class {
286
320
  if (!slot) return;
287
321
  this.slots.delete(key);
288
322
  removed.push(key);
323
+ this.notifyEvict(slot);
289
324
  };
290
325
  const idle = this.idleCandidates(now, this.idleTimeoutMs);
291
326
  idle.sort((a, b) => a.loadedAt - b.loadedAt);
@@ -343,8 +378,21 @@ var ActiveGraphCache = class {
343
378
  // src/runtime/vgd/registry.ts
344
379
  var WorkspaceRegistry = class {
345
380
  byId = /* @__PURE__ */ new Map();
381
+ /**
382
+ * Anything keyed by the same (repositoryId, gitRef) slot as the graph — the
383
+ * semantic index — attaches here. Wiring it through the registry rather than
384
+ * the cache directly means every put/select/evict passes one funnel, so an
385
+ * index can never describe a ref its graph no longer holds.
386
+ */
387
+ slotListener;
346
388
  /** Multi-branch in-memory graphs (shared across registered workspaces). */
347
- graphs = new ActiveGraphCache();
389
+ graphs = new ActiveGraphCache({
390
+ onEvict: (slot) => this.slotListener?.onGraphEvict(slot.repositoryId, slot.gitRef)
391
+ });
392
+ /** Attach the semantic index (or any slot-scoped resource) to graph lifecycle. */
393
+ setSlotListener(listener) {
394
+ this.slotListener = listener;
395
+ }
348
396
  register(root, now = () => /* @__PURE__ */ new Date(), meta = {}) {
349
397
  const resolved = path.resolve(root);
350
398
  const id = repositoryIdFromRoot(resolved);
@@ -370,10 +418,13 @@ var WorkspaceRegistry = class {
370
418
  putGraph(repositoryId, gitRef, graph) {
371
419
  this.graphs.put(repositoryId, gitRef, graph);
372
420
  this.graphs.select(repositoryId, gitRef);
421
+ this.slotListener?.onGraphPut(repositoryId, gitRef, graph);
422
+ this.slotListener?.onGraphSelect(repositoryId, gitRef);
373
423
  }
374
424
  /** Switch the current branch slot for a repository (no load). */
375
425
  selectGitRef(repositoryId, gitRef) {
376
426
  this.graphs.select(repositoryId, gitRef);
427
+ this.slotListener?.onGraphSelect(repositoryId, gitRef);
377
428
  }
378
429
  /** Register every member of a federation (primary + members). */
379
430
  registerFederation(members, now = () => /* @__PURE__ */ new Date()) {
@@ -430,8 +481,8 @@ var VgdHostBroker = class {
430
481
  }
431
482
  status() {
432
483
  const leases = {};
433
- for (const [path3, clients] of this.leases) {
434
- leases[path3] = clients.size;
484
+ for (const [path4, clients] of this.leases) {
485
+ leases[path4] = clients.size;
435
486
  }
436
487
  return {
437
488
  poolSize: hostSessionPoolSize(),
@@ -530,6 +581,338 @@ function getVgdHostBroker() {
530
581
  if (!defaultBroker) defaultBroker = new VgdHostBroker();
531
582
  return defaultBroker;
532
583
  }
584
+ function nodeText(node) {
585
+ const parts = [node.qualifiedName || node.name, node.kind, node.file, node.doc].filter((p) => typeof p === "string" && p.length > 0);
586
+ return parts.join(" \xB7 ").slice(0, 512);
587
+ }
588
+ var EmbedBroker = class {
589
+ child = null;
590
+ starting = false;
591
+ gaveUp = false;
592
+ crashes = 0;
593
+ reason;
594
+ model;
595
+ nextId = 1;
596
+ buffer = "";
597
+ pending = /* @__PURE__ */ new Map();
598
+ indexes = /* @__PURE__ */ new Map();
599
+ /** Slot keys with a build in flight, so warms are not duplicated. */
600
+ building = /* @__PURE__ */ new Set();
601
+ /** The slot most recently made current — logged on change. */
602
+ currentKey;
603
+ graphProvider;
604
+ log;
605
+ spawnWorker;
606
+ maxCrashes;
607
+ requestTimeoutMs;
608
+ batchSize;
609
+ now;
610
+ constructor(options = {}) {
611
+ this.log = options.log ?? (() => {
612
+ });
613
+ this.spawnWorker = options.spawnWorker ?? defaultSpawnWorker;
614
+ this.maxCrashes = options.maxCrashes ?? 3;
615
+ this.requestTimeoutMs = options.requestTimeoutMs ?? 12e4;
616
+ this.batchSize = options.batchSize ?? 256;
617
+ this.now = options.now ?? (() => Date.now());
618
+ }
619
+ status() {
620
+ return {
621
+ worker: this.gaveUp ? "unavailable" : this.child ? "ready" : this.starting ? "starting" : "stopped",
622
+ pid: this.child?.pid,
623
+ model: this.model,
624
+ crashes: this.crashes,
625
+ reason: this.reason,
626
+ slots: this.listSlots()
627
+ };
628
+ }
629
+ listSlots(repositoryId) {
630
+ const out = [];
631
+ for (const idx of this.indexes.values()) {
632
+ if (repositoryId && idx.repositoryId !== repositoryId) continue;
633
+ out.push({
634
+ repositoryId: idx.repositoryId,
635
+ gitRef: idx.gitRef,
636
+ state: idx.state,
637
+ vectors: idx.vectors.size,
638
+ nodeCount: idx.nodeCount,
639
+ builtAt: idx.builtAt,
640
+ buildMs: idx.buildMs,
641
+ error: idx.error
642
+ });
643
+ }
644
+ return out.sort((a, b) => a.repositoryId.localeCompare(b.repositoryId) || a.gitRef.localeCompare(b.gitRef));
645
+ }
646
+ slot(repositoryId, gitRef) {
647
+ return this.indexes.get(activeGraphSlotKey(repositoryId, gitRef));
648
+ }
649
+ /** True once the broker has stopped trying — callers should go lexical. */
650
+ unavailable() {
651
+ return this.gaveUp;
652
+ }
653
+ /**
654
+ * A graph slot was published or replaced. The vectors for that slot no
655
+ * longer describe its graph, so they are dropped and a rebuild is queued.
656
+ * Called from the same funnel that stores the graph, so the two can never
657
+ * disagree about which ref they belong to.
658
+ */
659
+ onGraphPut(repositoryId, gitRef, graph) {
660
+ const key = activeGraphSlotKey(repositoryId, gitRef);
661
+ const existing = this.indexes.get(key);
662
+ const nodeCount = graph.nodes?.length ?? 0;
663
+ if (existing && existing.state === "ready") {
664
+ this.log(`semantic: ${repositoryId}@${gitRef} graph republished \u2014 index stale (${existing.vectors.size} vectors dropped)`);
665
+ }
666
+ this.indexes.set(key, {
667
+ repositoryId,
668
+ gitRef,
669
+ state: "stale",
670
+ vectors: /* @__PURE__ */ new Map(),
671
+ nodeCount
672
+ });
673
+ }
674
+ /**
675
+ * Supplies the graph for a slot so a select can warm without the caller
676
+ * threading it through. Set by the daemon to read its own cache.
677
+ */
678
+ setGraphProvider(provider) {
679
+ this.graphProvider = provider;
680
+ }
681
+ /**
682
+ * A slot became current — a branch switch, a new repo, a fresh publish.
683
+ * The matching index is swapped in, and warmed in the background if it is
684
+ * not ready, so the first Ask after a switch is not the one that pays.
685
+ */
686
+ onGraphSelect(repositoryId, gitRef) {
687
+ const key = activeGraphSlotKey(repositoryId, gitRef);
688
+ if (this.currentKey !== key) {
689
+ this.currentKey = key;
690
+ const idx2 = this.indexes.get(key);
691
+ this.log(
692
+ `semantic: current slot \u2192 ${repositoryId}@${gitRef} \xB7 index ${idx2 ? `${idx2.state} (${idx2.vectors.size} vectors)` : "absent"}`
693
+ );
694
+ }
695
+ if (this.gaveUp) return;
696
+ const idx = this.indexes.get(key);
697
+ if (idx?.state === "ready" || this.building.has(key)) return;
698
+ const graph = this.graphProvider?.(repositoryId, gitRef);
699
+ if (!graph) return;
700
+ void this.ensureIndex(repositoryId, gitRef, graph);
701
+ }
702
+ /** A graph slot was evicted — its index goes with it, never outliving it. */
703
+ onGraphEvict(repositoryId, gitRef) {
704
+ const key = activeGraphSlotKey(repositoryId, gitRef);
705
+ const idx = this.indexes.get(key);
706
+ if (!idx) return;
707
+ this.indexes.delete(key);
708
+ this.log(`semantic: ${repositoryId}@${gitRef} index evicted with its graph slot (${idx.vectors.size} vectors freed)`);
709
+ }
710
+ /** Drop everything (daemon shutdown / registry clear). */
711
+ clear() {
712
+ this.indexes.clear();
713
+ this.building.clear();
714
+ }
715
+ /**
716
+ * Ensure the slot has vectors, building them if needed. Resolves when the
717
+ * index is ready or has definitively failed; never throws.
718
+ */
719
+ async ensureIndex(repositoryId, gitRef, graph) {
720
+ const key = activeGraphSlotKey(repositoryId, gitRef);
721
+ let idx = this.indexes.get(key);
722
+ if (!idx) {
723
+ idx = { repositoryId, gitRef, state: "stale", vectors: /* @__PURE__ */ new Map(), nodeCount: graph.nodes?.length ?? 0 };
724
+ this.indexes.set(key, idx);
725
+ }
726
+ if (idx.state === "ready") return idx;
727
+ if (this.gaveUp) {
728
+ idx.state = "failed";
729
+ idx.error = this.reason ?? "embedding worker unavailable";
730
+ return idx;
731
+ }
732
+ if (this.building.has(key)) return idx;
733
+ this.building.add(key);
734
+ idx.state = "building";
735
+ const nodes = (graph.nodes ?? []).filter((n) => !!n?.id);
736
+ const started = this.now();
737
+ this.log(`semantic: building index for ${repositoryId}@${gitRef} (${nodes.length} nodes)\u2026`);
738
+ try {
739
+ const vectors = /* @__PURE__ */ new Map();
740
+ for (let i = 0; i < nodes.length; i += this.batchSize) {
741
+ const batch = nodes.slice(i, i + this.batchSize);
742
+ const out = await this.embed(batch.map((n) => nodeText(n)));
743
+ batch.forEach((n, j) => {
744
+ const v = out[j];
745
+ if (v) vectors.set(n.id, v);
746
+ });
747
+ const live = this.indexes.get(key);
748
+ if (!live || live !== idx) {
749
+ this.log(`semantic: index build for ${repositoryId}@${gitRef} abandoned \u2014 slot changed underneath it`);
750
+ return idx;
751
+ }
752
+ }
753
+ idx.vectors = vectors;
754
+ idx.nodeCount = nodes.length;
755
+ idx.state = "ready";
756
+ idx.builtAt = this.now();
757
+ idx.buildMs = idx.builtAt - started;
758
+ idx.error = void 0;
759
+ this.log(`semantic: index ready for ${repositoryId}@${gitRef} \u2014 ${vectors.size} vectors in ${idx.buildMs}ms`);
760
+ } catch (err) {
761
+ idx.state = "failed";
762
+ idx.error = err instanceof Error ? err.message : String(err);
763
+ this.log(`semantic: index build failed for ${repositoryId}@${gitRef} \u2014 ${idx.error}; callers fall back to lexical`);
764
+ } finally {
765
+ this.building.delete(key);
766
+ }
767
+ return idx;
768
+ }
769
+ /** Embed one query string. Returns null when semantic is not available. */
770
+ async embedQuery(text) {
771
+ if (this.gaveUp) return null;
772
+ try {
773
+ const [vector] = await this.embed([text], true);
774
+ return vector ?? null;
775
+ } catch (err) {
776
+ this.log(`semantic: query embedding failed \u2014 ${err instanceof Error ? err.message : String(err)}; using lexical`);
777
+ return null;
778
+ }
779
+ }
780
+ /** Stop the worker (daemon shutdown). Safe to call when already stopped. */
781
+ stop() {
782
+ const child = this.child;
783
+ this.child = null;
784
+ this.starting = false;
785
+ for (const [, p] of this.pending) {
786
+ clearTimeout(p.timer);
787
+ p.reject(new Error("embedding worker stopped"));
788
+ }
789
+ this.pending.clear();
790
+ if (child) {
791
+ try {
792
+ child.kill();
793
+ } catch {
794
+ }
795
+ }
796
+ }
797
+ // ── worker plumbing ────────────────────────────────────────────────────
798
+ ensureWorker() {
799
+ if (this.child) return this.child;
800
+ this.starting = true;
801
+ const child = this.spawnWorker();
802
+ this.child = child;
803
+ this.starting = false;
804
+ this.buffer = "";
805
+ this.log(`semantic: embedding worker started (pid ${child.pid ?? "?"})`);
806
+ child.stdout?.setEncoding("utf8");
807
+ child.stdout?.on("data", (chunk) => this.onData(chunk));
808
+ child.stderr?.setEncoding("utf8");
809
+ child.stderr?.on("data", (chunk) => {
810
+ const text = String(chunk).trim();
811
+ if (text) this.log(`semantic worker: ${text.slice(0, 500)}`);
812
+ });
813
+ child.on("exit", (code, signal) => this.onExit(code, signal));
814
+ child.on("error", (err) => {
815
+ this.log(`semantic: embedding worker could not start \u2014 ${err.message}`);
816
+ this.onExit(null, null);
817
+ });
818
+ return child;
819
+ }
820
+ onData(chunk) {
821
+ this.buffer += chunk;
822
+ let nl;
823
+ while ((nl = this.buffer.indexOf("\n")) >= 0) {
824
+ const line = this.buffer.slice(0, nl).trim();
825
+ this.buffer = this.buffer.slice(nl + 1);
826
+ if (!line) continue;
827
+ let res;
828
+ try {
829
+ res = JSON.parse(line);
830
+ } catch {
831
+ continue;
832
+ }
833
+ if (typeof res.model === "string") this.model = res.model;
834
+ const id = res.id;
835
+ if (typeof id !== "number") continue;
836
+ const p = this.pending.get(id);
837
+ if (!p) continue;
838
+ this.pending.delete(id);
839
+ clearTimeout(p.timer);
840
+ if (res.ok && Array.isArray(res.vectors)) p.resolve(res.vectors);
841
+ else p.reject(new Error(res.error || "embedding worker returned no vectors"));
842
+ }
843
+ }
844
+ onExit(code, signal) {
845
+ const wasRunning = !!this.child;
846
+ this.child = null;
847
+ if (!wasRunning) return;
848
+ this.crashes++;
849
+ const how = signal ? `signal ${signal}` : `code ${code ?? "null"}`;
850
+ for (const [, p] of this.pending) {
851
+ clearTimeout(p.timer);
852
+ p.reject(new Error(`embedding worker exited (${how})`));
853
+ }
854
+ this.pending.clear();
855
+ if (this.crashes >= this.maxCrashes) {
856
+ this.gaveUp = true;
857
+ this.reason = `the embedding worker exited (${how}) ${this.crashes} times \u2014 semantic search is off for this daemon; everything falls back to lexical. A native backend that aborts like this is usually a blocked onnxruntime-node postinstall (see \`vg embed --where\`).`;
858
+ this.log(`semantic: ${this.reason}`);
859
+ return;
860
+ }
861
+ this.log(`semantic: embedding worker exited (${how}) \u2014 restarting on next use (${this.crashes}/${this.maxCrashes})`);
862
+ }
863
+ embed(texts, query = false) {
864
+ if (this.gaveUp) return Promise.reject(new Error(this.reason ?? "embedding worker unavailable"));
865
+ if (!texts.length) return Promise.resolve([]);
866
+ let child;
867
+ try {
868
+ child = this.ensureWorker();
869
+ } catch (err) {
870
+ const error = err instanceof Error ? err : new Error(String(err));
871
+ this.crashes++;
872
+ if (this.crashes >= this.maxCrashes) {
873
+ this.gaveUp = true;
874
+ this.reason = `the embedding worker could not start (${error.message}) \u2014 semantic search is off for this daemon`;
875
+ this.log(`semantic: ${this.reason}`);
876
+ } else {
877
+ this.log(`semantic: embedding worker could not start \u2014 ${error.message} (${this.crashes}/${this.maxCrashes})`);
878
+ }
879
+ return Promise.reject(error);
880
+ }
881
+ const id = this.nextId++;
882
+ return new Promise((resolve2, reject) => {
883
+ const timer = setTimeout(() => {
884
+ this.pending.delete(id);
885
+ reject(new Error(`embedding request timed out after ${this.requestTimeoutMs}ms`));
886
+ }, this.requestTimeoutMs);
887
+ timer.unref?.();
888
+ this.pending.set(id, { resolve: resolve2, reject, timer });
889
+ try {
890
+ child.stdin?.write(JSON.stringify({ id, op: "embed", texts, query }) + "\n");
891
+ } catch (err) {
892
+ this.pending.delete(id);
893
+ clearTimeout(timer);
894
+ reject(err instanceof Error ? err : new Error(String(err)));
895
+ }
896
+ });
897
+ }
898
+ /** A successful exchange clears the crash streak — used after a good build. */
899
+ noteHealthy() {
900
+ this.crashes = 0;
901
+ }
902
+ };
903
+ function defaultSpawnWorker() {
904
+ const here = path.dirname(fileURLToPath(import.meta.url));
905
+ const entry = path.join(here, "embed-worker-main.js");
906
+ if (!fs2.existsSync(entry)) {
907
+ throw new Error(`embedding worker not found at ${entry} \u2014 semantic runs lexically`);
908
+ }
909
+ return spawn(process.execPath, [entry], {
910
+ stdio: ["pipe", "pipe", "pipe"],
911
+ // Never inherit loader flags or a debugger port: this child exists to be
912
+ // expendable, and anything that changes how it loads defeats the point.
913
+ env: { ...process.env, NODE_OPTIONS: "" }
914
+ });
915
+ }
533
916
 
534
917
  // src/runtime/vgd/server.ts
535
918
  async function startVgdServer(options = {}) {
@@ -537,18 +920,23 @@ async function startVgdServer(options = {}) {
537
920
  const pidPath = options.pidPath ?? vgdPidPath();
538
921
  const registry = options.registry ?? new WorkspaceRegistry();
539
922
  const hostBroker = options.hostBroker ?? getVgdHostBroker();
923
+ const log = options.log ?? (() => {
924
+ });
925
+ const embedBroker = options.embedBroker ?? new EmbedBroker({ log });
926
+ embedBroker.setGraphProvider((repositoryId, gitRef) => registry.graphs.get(repositoryId, gitRef)?.graph);
927
+ registry.setSlotListener(embedBroker);
540
928
  const onShutdownRequest = options.onShutdownRequest;
541
929
  const now = options.now ?? (() => /* @__PURE__ */ new Date());
542
930
  const pid = options.pid ?? process.pid;
543
931
  const startedAt = Date.now();
544
932
  if (!socketPath.startsWith("\\\\.\\pipe")) {
545
- fs.mkdirSync(path.dirname(socketPath), { recursive: true });
933
+ fs2.mkdirSync(path.dirname(socketPath), { recursive: true });
546
934
  try {
547
- fs.unlinkSync(socketPath);
935
+ fs2.unlinkSync(socketPath);
548
936
  } catch {
549
937
  }
550
938
  } else {
551
- fs.mkdirSync(path.dirname(pidPath), { recursive: true });
939
+ fs2.mkdirSync(path.dirname(pidPath), { recursive: true });
552
940
  }
553
941
  const server = net.createServer((socket) => {
554
942
  let buffer = "";
@@ -561,7 +949,7 @@ async function startVgdServer(options = {}) {
561
949
  buffer = buffer.slice(nl + 1);
562
950
  if (!line) continue;
563
951
  void Promise.resolve(
564
- handleLine(line, { registry, hostBroker, now, pid, startedAt, socketPath, onShutdownRequest })
952
+ handleLine(line, { registry, hostBroker, embedBroker, now, pid, startedAt, socketPath, onShutdownRequest, log })
565
953
  ).then(
566
954
  (response) => {
567
955
  socket.write(JSON.stringify(response) + "\n");
@@ -578,8 +966,8 @@ async function startVgdServer(options = {}) {
578
966
  });
579
967
  });
580
968
  try {
581
- fs.mkdirSync(path.dirname(pidPath), { recursive: true });
582
- fs.writeFileSync(pidPath, `${pid}
969
+ fs2.mkdirSync(path.dirname(pidPath), { recursive: true });
970
+ fs2.writeFileSync(pidPath, `${pid}
583
971
  `, "utf8");
584
972
  } catch {
585
973
  }
@@ -588,14 +976,15 @@ async function startVgdServer(options = {}) {
588
976
  registry,
589
977
  startedAt,
590
978
  async close() {
979
+ embedBroker.stop();
591
980
  await new Promise((resolve2) => server.close(() => resolve2()));
592
981
  try {
593
- fs.unlinkSync(pidPath);
982
+ fs2.unlinkSync(pidPath);
594
983
  } catch {
595
984
  }
596
985
  if (!socketPath.startsWith("\\\\.\\pipe")) {
597
986
  try {
598
- fs.unlinkSync(socketPath);
987
+ fs2.unlinkSync(socketPath);
599
988
  } catch {
600
989
  }
601
990
  }
@@ -630,6 +1019,37 @@ async function handleLine(line, ctx) {
630
1019
  setTimeout(() => hook(), 150);
631
1020
  return { ok: true, stopping: true };
632
1021
  }
1022
+ case "embed-status":
1023
+ return { ok: true, semantic: ctx.embedBroker.status() };
1024
+ case "embed-query": {
1025
+ const vector = await ctx.embedBroker.embedQuery(req.text);
1026
+ if (!vector) {
1027
+ return {
1028
+ ok: false,
1029
+ error: "semantic unavailable \u2014 rank lexically",
1030
+ code: "semantic_unavailable"
1031
+ };
1032
+ }
1033
+ return { ok: true, vector, model: ctx.embedBroker.status().model };
1034
+ }
1035
+ case "embed-index": {
1036
+ const gitRef = req.gitRef ?? ctx.registry.graphs.selectedRef(req.repositoryId);
1037
+ if (!gitRef) return { ok: false, error: "no current gitRef for this repository", code: "no_slot" };
1038
+ const slot = ctx.registry.graphs.get(req.repositoryId, gitRef);
1039
+ if (!slot) {
1040
+ return { ok: false, error: `no graph slot for ${req.repositoryId}@${gitRef} \u2014 publish the map first`, code: "no_slot" };
1041
+ }
1042
+ const idx = await ctx.embedBroker.ensureIndex(req.repositoryId, gitRef, slot.graph);
1043
+ return {
1044
+ ok: true,
1045
+ indexed: true,
1046
+ repositoryId: req.repositoryId,
1047
+ gitRef,
1048
+ state: idx.state,
1049
+ vectors: idx.vectors.size,
1050
+ buildMs: idx.buildMs
1051
+ };
1052
+ }
633
1053
  case "host-status":
634
1054
  return { ok: true, host: ctx.hostBroker.status() };
635
1055
  case "host-load": {
@@ -670,11 +1090,14 @@ async function handleLine(line, ctx) {
670
1090
  }
671
1091
  case "list":
672
1092
  return { ok: true, workspaces: ctx.registry.list() };
673
- case "register":
674
- return {
675
- ok: true,
676
- workspace: ctx.registry.register(req.root, ctx.now, { label: req.label, role: req.role })
677
- };
1093
+ case "register": {
1094
+ const known = ctx.registry.size();
1095
+ const workspace = ctx.registry.register(req.root, ctx.now, { label: req.label, role: req.role });
1096
+ if (ctx.registry.size() > known) {
1097
+ ctx.log(`register: new workspace ${workspace.id} (${workspace.root}${workspace.gitRef ? ` @${workspace.gitRef}` : ""})`);
1098
+ }
1099
+ return { ok: true, workspace };
1100
+ }
678
1101
  case "unregister":
679
1102
  return { ok: true, removed: ctx.registry.unregister(req.root) };
680
1103
  case "register-federation": {
@@ -686,12 +1109,17 @@ async function handleLine(line, ctx) {
686
1109
  return { ok: true, slots: ctx.registry.graphs.list(req.repositoryId) };
687
1110
  }
688
1111
  case "select-git-ref": {
1112
+ const before = ctx.registry.graphs.selectedRef(req.repositoryId);
1113
+ if (before !== req.gitRef) {
1114
+ ctx.log(`select-git-ref: ${req.repositoryId} ${before ?? "(none)"} \u2192 ${req.gitRef}`);
1115
+ }
689
1116
  ctx.registry.selectGitRef(req.repositoryId, req.gitRef);
690
1117
  return { ok: true, selected: true, repositoryId: req.repositoryId, gitRef: req.gitRef };
691
1118
  }
692
1119
  case "put-graph": {
693
1120
  try {
694
1121
  const graph = req.graph;
1122
+ ctx.log(`put-graph: ${req.repositoryId}@${req.gitRef} \xB7 ${graph.nodes?.length ?? 0} nodes`);
695
1123
  ctx.registry.putGraph(req.repositoryId, req.gitRef, graph);
696
1124
  return {
697
1125
  ok: true,
@@ -707,6 +1135,7 @@ async function handleLine(line, ctx) {
707
1135
  case "load-graph": {
708
1136
  const record = ctx.registry.register(req.root, ctx.now, { gitRef: req.gitRef });
709
1137
  const gitRef = req.gitRef ?? record.gitRef ?? "HEAD";
1138
+ ctx.log(`load-graph: ${record.id}@${gitRef} from ${req.root}`);
710
1139
  const graph = loadGraph(req.root, req.graphPath);
711
1140
  if (!graph) {
712
1141
  return {
@@ -885,5 +1314,5 @@ var BranchGraphSession = class {
885
1314
  };
886
1315
 
887
1316
  export { ActiveGraphCache, BranchGraphSession, DEFAULT_ACTIVE_GRAPH_IDLE_MS, VGD_PROTOCOL_VERSION, WorkspaceRegistry, parseRequest, startVgdServer };
888
- //# sourceMappingURL=chunk-B2KLJPFY.js.map
889
- //# sourceMappingURL=chunk-B2KLJPFY.js.map
1317
+ //# sourceMappingURL=chunk-4ZMJKR74.js.map
1318
+ //# sourceMappingURL=chunk-4ZMJKR74.js.map