@neat.is/core 0.10.6-dev.20261007 → 0.10.6-dev.20261009

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 (46) hide show
  1. package/dist/{chunk-VL7ANDPI.js → chunk-ECBRGTB2.js} +10 -8
  2. package/dist/{chunk-VL7ANDPI.js.map → chunk-ECBRGTB2.js.map} +1 -1
  3. package/dist/{chunk-KTJK2PAH.js → chunk-H2BAPN33.js} +311 -611
  4. package/dist/chunk-H2BAPN33.js.map +1 -0
  5. package/dist/chunk-MR64ICXX.js +19 -0
  6. package/dist/chunk-MR64ICXX.js.map +1 -0
  7. package/dist/{chunk-EFBWQIKG.js → chunk-QPG2QXCW.js} +11 -27
  8. package/dist/chunk-QPG2QXCW.js.map +1 -0
  9. package/dist/chunk-RGYC5U2Q.js +511 -0
  10. package/dist/chunk-RGYC5U2Q.js.map +1 -0
  11. package/dist/{chunk-LXQCL4MP.js → chunk-TZXSWSHX.js} +2 -2
  12. package/dist/{chunk-BC53SCT7.js → chunk-VFZ6CXLI.js} +20 -6
  13. package/dist/chunk-VFZ6CXLI.js.map +1 -0
  14. package/dist/cli.cjs +409 -77
  15. package/dist/cli.cjs.map +1 -1
  16. package/dist/cli.d.cts +2 -0
  17. package/dist/cli.d.ts +2 -0
  18. package/dist/cli.js +251 -77
  19. package/dist/cli.js.map +1 -1
  20. package/dist/index.cjs +165 -17
  21. package/dist/index.cjs.map +1 -1
  22. package/dist/index.d.cts +3 -99
  23. package/dist/index.d.ts +3 -99
  24. package/dist/index.js +18 -15
  25. package/dist/neatd.cjs +165 -17
  26. package/dist/neatd.cjs.map +1 -1
  27. package/dist/neatd.js +7 -4
  28. package/dist/neatd.js.map +1 -1
  29. package/dist/otel-grpc-CCCI772O.js +11 -0
  30. package/dist/registry.cjs +570 -0
  31. package/dist/registry.cjs.map +1 -0
  32. package/dist/registry.d.cts +220 -0
  33. package/dist/registry.d.ts +220 -0
  34. package/dist/registry.js +62 -0
  35. package/dist/registry.js.map +1 -0
  36. package/dist/server.cjs +185 -23
  37. package/dist/server.cjs.map +1 -1
  38. package/dist/server.js +10 -6
  39. package/dist/server.js.map +1 -1
  40. package/package.json +8 -3
  41. package/dist/chunk-BC53SCT7.js.map +0 -1
  42. package/dist/chunk-EFBWQIKG.js.map +0 -1
  43. package/dist/chunk-KTJK2PAH.js.map +0 -1
  44. package/dist/otel-grpc-B7ABHLV7.js +0 -10
  45. /package/dist/{chunk-LXQCL4MP.js.map → chunk-TZXSWSHX.js.map} +0 -0
  46. /package/dist/{otel-grpc-B7ABHLV7.js.map → otel-grpc-CCCI772O.js.map} +0 -0
package/dist/server.cjs CHANGED
@@ -5678,7 +5678,29 @@ function ensureFrontierNode(graph, host, ts) {
5678
5678
  graph.addNode(id, node);
5679
5679
  return id;
5680
5680
  }
5681
- function upsertObservedEdge(graph, type, source, target, ts, isError = false, evidence, durationMs) {
5681
+ var EDGE_ERROR_MESSAGE_MAX = 512;
5682
+ function buildEdgeErrorExemplar(detail, ts) {
5683
+ return {
5684
+ ...detail.exceptionType !== void 0 ? { exceptionType: detail.exceptionType } : {},
5685
+ ...detail.message !== void 0 ? { message: detail.message.slice(0, EDGE_ERROR_MESSAGE_MAX) } : {},
5686
+ ...detail.httpStatusCode !== void 0 ? { httpStatusCode: detail.httpStatusCode } : {},
5687
+ at: ts
5688
+ };
5689
+ }
5690
+ function edgeErrorDetailFromSpan(span) {
5691
+ const exceptionType = span.exception?.type;
5692
+ const message = span.exception?.message;
5693
+ const httpStatusCode = httpResponseStatus(span);
5694
+ if (exceptionType === void 0 && message === void 0 && httpStatusCode === void 0) {
5695
+ return void 0;
5696
+ }
5697
+ return {
5698
+ ...exceptionType !== void 0 ? { exceptionType } : {},
5699
+ ...message !== void 0 ? { message } : {},
5700
+ ...httpStatusCode !== void 0 ? { httpStatusCode } : {}
5701
+ };
5702
+ }
5703
+ function upsertObservedEdge(graph, type, source, target, ts, isError = false, evidence, durationMs, errorDetail) {
5682
5704
  if (!graph.hasNode(source) || !graph.hasNode(target)) return null;
5683
5705
  const grain = source.startsWith("file:") || source.startsWith("symbol:") ? "file" : "service";
5684
5706
  const id = makeObservedEdgeId(type, source, target);
@@ -5688,13 +5710,15 @@ function upsertObservedEdge(graph, type, source, target, ts, isError = false, ev
5688
5710
  const newErrorCount = (existing.signal?.errorCount ?? 0) + (isError ? 1 : 0);
5689
5711
  const latencyHist2 = durationMs !== void 0 ? recordLatency({ ...existing.signal?.latencyHist ?? {} }, durationMs) : existing.signal?.latencyHist;
5690
5712
  const latencyMs2 = latencyPercentiles(latencyHist2) ?? existing.signal?.latencyMs;
5713
+ const lastError2 = isError && errorDetail ? buildEdgeErrorExemplar(errorDetail, ts) : existing.signal?.lastError;
5691
5714
  const newSignal = {
5692
5715
  spanCount: newSpanCount,
5693
5716
  errorCount: newErrorCount,
5694
5717
  lastObservedAgeMs: 0,
5695
5718
  ...latencyHist2 ? { latencyHist: latencyHist2 } : {},
5696
5719
  ...latencyMs2 ? { latencyMs: latencyMs2 } : {},
5697
- ...existing.signal?.anomalous !== void 0 ? { anomalous: existing.signal.anomalous } : {}
5720
+ ...existing.signal?.anomalous !== void 0 ? { anomalous: existing.signal.anomalous } : {},
5721
+ ...lastError2 ? { lastError: lastError2 } : {}
5698
5722
  };
5699
5723
  const updated = {
5700
5724
  ...existing,
@@ -5711,12 +5735,14 @@ function upsertObservedEdge(graph, type, source, target, ts, isError = false, ev
5711
5735
  }
5712
5736
  const latencyHist = durationMs !== void 0 ? recordLatency({}, durationMs) : void 0;
5713
5737
  const latencyMs = latencyHist ? latencyPercentiles(latencyHist) : void 0;
5738
+ const lastError = isError && errorDetail ? buildEdgeErrorExemplar(errorDetail, ts) : void 0;
5714
5739
  const signal = {
5715
5740
  spanCount: 1,
5716
5741
  errorCount: isError ? 1 : 0,
5717
5742
  lastObservedAgeMs: 0,
5718
5743
  ...latencyHist ? { latencyHist } : {},
5719
- ...latencyMs ? { latencyMs } : {}
5744
+ ...latencyMs ? { latencyMs } : {},
5745
+ ...lastError ? { lastError } : {}
5720
5746
  };
5721
5747
  const edge = {
5722
5748
  id,
@@ -6049,6 +6075,7 @@ async function handleSpan(ctx, span) {
6049
6075
  const sourceId = ensureServiceNode(ctx.graph, span.service, env);
6050
6076
  const isError = spanRecordsError(span);
6051
6077
  const durationMs = span.durationNanos > 0n && !spanIsStreaming(span) ? Number(span.durationNanos) / 1e6 : void 0;
6078
+ const errorDetail = isError ? edgeErrorDetailFromSpan(span) : void 0;
6052
6079
  const sourceServiceNode = ctx.graph.getNodeAttributes(sourceId);
6053
6080
  const callSite = callSiteFromSpan(span, sourceServiceNode, ctx.scanPath);
6054
6081
  cacheSpanService(span, nowMs, callSite);
@@ -6088,7 +6115,8 @@ async function handleSpan(ctx, span) {
6088
6115
  ts,
6089
6116
  isError,
6090
6117
  callSiteEvidence,
6091
- durationMs
6118
+ durationMs,
6119
+ errorDetail
6092
6120
  );
6093
6121
  if (result) affectedNode = targetId;
6094
6122
  if (span.dbSystem === "mongodb" && span.dbCollection) {
@@ -6101,7 +6129,8 @@ async function handleSpan(ctx, span) {
6101
6129
  ts,
6102
6130
  isError,
6103
6131
  callSiteEvidence,
6104
- durationMs
6132
+ durationMs,
6133
+ errorDetail
6105
6134
  );
6106
6135
  }
6107
6136
  if (span.dbTable) {
@@ -6114,7 +6143,8 @@ async function handleSpan(ctx, span) {
6114
6143
  ts,
6115
6144
  isError,
6116
6145
  callSiteEvidence,
6117
- durationMs
6146
+ durationMs,
6147
+ errorDetail
6118
6148
  );
6119
6149
  mergeObservedColumns(ctx.graph, tableId, span.dbColumns);
6120
6150
  }
@@ -6134,7 +6164,8 @@ async function handleSpan(ctx, span) {
6134
6164
  ts,
6135
6165
  isError,
6136
6166
  callSiteEvidence,
6137
- durationMs
6167
+ durationMs,
6168
+ errorDetail
6138
6169
  );
6139
6170
  if (result) affectedNode = targetId;
6140
6171
  } else if (span.graphqlOperationName && span.graphqlOperationType && spanServesGraphqlOperation(span.kind)) {
@@ -6152,7 +6183,8 @@ async function handleSpan(ctx, span) {
6152
6183
  ts,
6153
6184
  isError,
6154
6185
  callSiteEvidence,
6155
- durationMs
6186
+ durationMs,
6187
+ errorDetail
6156
6188
  );
6157
6189
  if (result) affectedNode = targetId;
6158
6190
  } else if (span.rpcSystem === "grpc" && span.rpcService && span.rpcMethod && spanServesGrpcMethod(span.kind)) {
@@ -6165,7 +6197,8 @@ async function handleSpan(ctx, span) {
6165
6197
  ts,
6166
6198
  isError,
6167
6199
  callSiteEvidence,
6168
- durationMs
6200
+ durationMs,
6201
+ errorDetail
6169
6202
  );
6170
6203
  if (result) affectedNode = targetId;
6171
6204
  } else if (span.websocketChannel && spanServesWebsocketChannel(span.kind)) {
@@ -6182,7 +6215,8 @@ async function handleSpan(ctx, span) {
6182
6215
  ts,
6183
6216
  isError,
6184
6217
  callSiteEvidence,
6185
- durationMs
6218
+ durationMs,
6219
+ errorDetail
6186
6220
  );
6187
6221
  if (result) affectedNode = targetId;
6188
6222
  } else {
@@ -6199,7 +6233,8 @@ async function handleSpan(ctx, span) {
6199
6233
  ts,
6200
6234
  isError,
6201
6235
  callSiteEvidence,
6202
- durationMs
6236
+ durationMs,
6237
+ errorDetail
6203
6238
  );
6204
6239
  affectedNode = targetId;
6205
6240
  resolvedViaAddress = true;
@@ -6213,7 +6248,8 @@ async function handleSpan(ctx, span) {
6213
6248
  ts,
6214
6249
  isError,
6215
6250
  callSiteEvidence,
6216
- durationMs
6251
+ durationMs,
6252
+ errorDetail
6217
6253
  );
6218
6254
  affectedNode = frontierNodeId;
6219
6255
  resolvedViaAddress = true;
@@ -6240,7 +6276,8 @@ async function handleSpan(ctx, span) {
6240
6276
  ts,
6241
6277
  isError,
6242
6278
  fallbackEvidence,
6243
- durationMs
6279
+ durationMs,
6280
+ errorDetail
6244
6281
  );
6245
6282
  }
6246
6283
  }
@@ -6255,7 +6292,7 @@ async function handleSpan(ctx, span) {
6255
6292
  );
6256
6293
  if (routeNodeId) {
6257
6294
  const routeSvc = ctx.graph.getNodeAttributes(routeNodeId).service;
6258
- upsertObservedEdge(ctx.graph, import_types7.EdgeType.CONTAINS, (0, import_types7.serviceId)(routeSvc), routeNodeId, ts, isError, void 0, durationMs);
6295
+ upsertObservedEdge(ctx.graph, import_types7.EdgeType.CONTAINS, (0, import_types7.serviceId)(routeSvc), routeNodeId, ts, isError, void 0, durationMs, errorDetail);
6259
6296
  }
6260
6297
  }
6261
6298
  if (span.statusCode === 2) {
@@ -8123,6 +8160,11 @@ function detectObservedFailingEdge(graph, bucket) {
8123
8160
  spanCount,
8124
8161
  errorCount,
8125
8162
  errorRate: clampConfidence(errorRate),
8163
+ // The bounded last-error exemplar rides the finding as evidence (ADR-236):
8164
+ // the error rate says HOW MUCH is failing, the exemplar says WHAT failed —
8165
+ // a timeout vs a deadlock vs a connection-refused, which `errorCount`
8166
+ // alone cannot name. Present only when the observed edge carries one.
8167
+ ...signal.lastError ? { lastError: signal.lastError } : {},
8126
8168
  confidence: OBSERVED_FAILING_CONFIDENCE,
8127
8169
  reason: `Code declares ${bucket.source} \u2192 ${bucket.target} (${bucket.type}) and production observes it, but ${errorCount}/${spanCount} observed calls fail (${pct}% error rate) \u2014 the declared dependency is predominantly failing. The declared intent (EXTRACTED) and the observed behaviour (OBSERVED) diverge.`,
8128
8170
  recommendation: "Treat this as a broken declared dependency, not a coverage gap: production runs the call the code declares and most calls error. Check the dependency\u2019s health, recent deploys, and the observed error responses on this edge."
@@ -17598,10 +17640,11 @@ function locusOf(graph, ev) {
17598
17640
  const rawLine = ev.attributes?.[CODE_LINENO_ATTR2];
17599
17641
  const line = typeof rawLine === "number" ? rawLine : Number(rawLine);
17600
17642
  const node = graph.hasNode(ev.affectedNode) ? graph.getNodeAttributes(ev.affectedNode) : void 0;
17643
+ const symbol = node?.type === "SymbolNode" ? node.qualname : void 0;
17601
17644
  return {
17602
17645
  file,
17603
17646
  ...Number.isFinite(line) ? { lineStart: line, lineEnd: line } : {},
17604
- ...node?.name ? { symbol: node.name } : {},
17647
+ ...symbol ? { symbol } : {},
17605
17648
  service: node?.service ?? ev.service,
17606
17649
  provenance: import_types60.Provenance.OBSERVED
17607
17650
  };
@@ -17697,7 +17740,8 @@ function buildIncidentCard(graph, errorEvent, incidents, policies) {
17697
17740
  const policyCards = applicable.map((p) => ({
17698
17741
  policyName: p.policyName,
17699
17742
  severity: p.severity,
17700
- message: p.reason
17743
+ message: p.reason,
17744
+ onViolation: p.onViolation
17701
17745
  }));
17702
17746
  const divergences = inGraph ? computeDivergences(graph, { node: affected, incidents }).divergences.map((d) => ({
17703
17747
  type: d.type,
@@ -17724,8 +17768,111 @@ function buildIncidentCard(graph, errorEvent, incidents, policies) {
17724
17768
  ...divergences.length > 0 ? { divergence: divergences } : {},
17725
17769
  headline: renderHeadline(graph, errorEvent, locus, rootCause?.node ?? null)
17726
17770
  };
17771
+ card.grade = gradeIncidentCard(card);
17727
17772
  return import_types60.IncidentCardSchema.parse(card);
17728
17773
  }
17774
+ var GRADE_PRIORS_VERSION = "sniper-architecture-priors-2026-10-08";
17775
+ var GRADE_WEIGHTS = {
17776
+ evidence: 3,
17777
+ locus: 2,
17778
+ tests: 2,
17779
+ reach: 1.5,
17780
+ kind: 1.5,
17781
+ chain: 1,
17782
+ recur: 1,
17783
+ div: 1
17784
+ };
17785
+ var PROVENANCE_WEIGHT = {
17786
+ OBSERVED: 1,
17787
+ EXTRACTED: 0.8,
17788
+ INFERRED: 0.4,
17789
+ FRONTIER: 0.3,
17790
+ STALE: 0.2
17791
+ };
17792
+ var REACH_NODES_MAX = 50;
17793
+ var TIMEOUT = /timeout|timed out|deadline/i;
17794
+ var round2 = (n) => Math.round(n * 1e4) / 1e4;
17795
+ function factor(name, value, evidence, reason) {
17796
+ return {
17797
+ value: value === null ? null : round2(value),
17798
+ weight: GRADE_WEIGHTS[name],
17799
+ evidence,
17800
+ ...reason ? { reason } : {}
17801
+ };
17802
+ }
17803
+ function gradeIncidentCard(card) {
17804
+ const rc = card.rootCause;
17805
+ const chain = rc?.chain ?? [];
17806
+ const gates = {
17807
+ policyNotBlock: {
17808
+ passed: !(card.policies ?? []).some((p) => p.onViolation === "block"),
17809
+ evidence: ["policies[].onViolation"]
17810
+ },
17811
+ notSymptomOnly: {
17812
+ passed: rc?.classification !== "symptom-only",
17813
+ evidence: ["rootCause.classification"]
17814
+ },
17815
+ locusResolves: { passed: card.locus !== null, evidence: ["locus"] }
17816
+ };
17817
+ const gamma = Object.values(gates).every((g) => g.passed) ? 1 : 0;
17818
+ const carrying = chain.some((h) => h.carriesSignal === true) ? chain.filter((h) => h.carriesSignal === true) : chain;
17819
+ const hopWeights = carrying.map((h) => PROVENANCE_WEIGHT[h.provenance]);
17820
+ const evidence = hopWeights.length === 0 ? factor("evidence", null, ["rootCause.chain[].provenance"], "no root-cause chain on the card") : hopWeights.some((w) => w === void 0) ? factor("evidence", null, ["rootCause.chain[].provenance"], "a chain hop has a provenance with no weight") : factor("evidence", Math.min(...hopWeights), ["rootCause.chain[].provenance"]);
17821
+ const locus = card.locus ? factor("locus", card.locus.symbol ? 1 : 0.6, ["locus.symbol", "locus.file"]) : factor("locus", null, ["locus"], "no locus on the card (the locus gate fails)");
17822
+ const tests = factor(
17823
+ "tests",
17824
+ null,
17825
+ [],
17826
+ "the card carries no test-file classification or test-to-symbol edges"
17827
+ );
17828
+ const reach = card.blastRadius ? factor(
17829
+ "reach",
17830
+ Math.max(0, 1 - card.blastRadius.totalAffected / REACH_NODES_MAX),
17831
+ ["blastRadius.totalAffected"],
17832
+ `nodes in the blast radius against a prior bound of ${REACH_NODES_MAX}`
17833
+ ) : factor("reach", null, ["blastRadius"], "no blast radius on the card");
17834
+ const timeout = TIMEOUT.test(`${card.exceptionType ?? ""} ${card.message}`);
17835
+ const kindValue = timeout ? 0.3 : card.incidentKind === "exception" ? 1 : card.incidentKind === "5xx" || card.incidentKind === "status-error" ? 0.7 : card.incidentKind === "connector" ? 0.6 : 0.4;
17836
+ const kind = factor("kind", kindValue, ["incidentKind", "exceptionType", "message"]);
17837
+ const chainFactor = rc ? factor(
17838
+ "chain",
17839
+ 1 / (1 + 0.25 * (Math.max(1, chain.length - 1) - 1)),
17840
+ ["rootCause.chain"]
17841
+ ) : factor("chain", null, ["rootCause"], "no root cause on the card");
17842
+ const occurrences = card.count ?? 1;
17843
+ const recurValue = Math.min(1, Math.log(1 + occurrences) / Math.log(11));
17844
+ const recur = factor(
17845
+ "recur",
17846
+ recurValue,
17847
+ ["count"],
17848
+ card.count === void 0 ? "no coalesced count; one recorded occurrence" : void 0
17849
+ );
17850
+ const div = factor("div", (card.divergence ?? []).length > 0 ? 1 : 0.7, ["divergence"]);
17851
+ const factors = { evidence, locus, tests, reach, kind, chain: chainFactor, recur, div };
17852
+ let weighted = 0;
17853
+ let total = 0;
17854
+ for (const f of Object.values(factors)) {
17855
+ if (f.value === null) continue;
17856
+ weighted += f.weight * f.value;
17857
+ total += f.weight;
17858
+ }
17859
+ const C = round2(weighted / total);
17860
+ const G = round2(gamma * C);
17861
+ const band = gamma === 0 || G < 0.5 ? "out" : G >= 0.75 ? "full" : "diagnose-only";
17862
+ return {
17863
+ G,
17864
+ gamma,
17865
+ gates,
17866
+ C,
17867
+ factors,
17868
+ band,
17869
+ urgency: {
17870
+ value: null,
17871
+ reason: "f_fresh needs the time of the last OBSERVED signal at the node, which the card does not carry"
17872
+ },
17873
+ priorsVersion: GRADE_PRIORS_VERSION
17874
+ };
17875
+ }
17729
17876
 
17730
17877
  // src/ask.ts
17731
17878
  init_cjs_shims();
@@ -19498,7 +19645,7 @@ async function computePrVerdict(req, ctx, deps, deadline) {
19498
19645
  const found = observedBreakFrom(node, getObservedDependencies(ctx.liveGraph, node.id));
19499
19646
  if (found) observedBreaks.push(found);
19500
19647
  }
19501
- const { marker, body } = renderVerdict({
19648
+ const { marker, body: rendered } = renderVerdict({
19502
19649
  graph: head,
19503
19650
  delta,
19504
19651
  changedFiles,
@@ -19506,6 +19653,7 @@ async function computePrVerdict(req, ctx, deps, deadline) {
19506
19653
  observedBreaks,
19507
19654
  tone: req.tone ?? "loud"
19508
19655
  });
19656
+ const body = rendered.startsWith(marker) ? rendered.slice(marker.length).replace(/^(?:\r?\n)+/, "") : rendered;
19509
19657
  return {
19510
19658
  marker,
19511
19659
  body,
@@ -24403,13 +24551,25 @@ async function makeTransformersEmbedder() {
24403
24551
  }
24404
24552
  };
24405
24553
  }
24406
- async function pickEmbedder() {
24407
- const host = ollamaHost();
24408
- if (host && await ollamaReachable(host)) {
24409
- return makeOllamaEmbedder(host);
24554
+ async function pickEmbedder(provider) {
24555
+ if (provider !== "transformers") {
24556
+ const host = ollamaHost();
24557
+ if (host && await ollamaReachable(host)) {
24558
+ return makeOllamaEmbedder(host);
24559
+ }
24410
24560
  }
24561
+ if (provider === "ollama") return null;
24411
24562
  return makeTransformersEmbedder();
24412
24563
  }
24564
+ function searchProviderFromEnv(env = process.env, warn = console.warn) {
24565
+ const value = env.NEAT_SEARCH_PROVIDER;
24566
+ if (!value) return void 0;
24567
+ if (value === "substring" || value === "ollama" || value === "transformers") return value;
24568
+ warn(
24569
+ `semantic_search: NEAT_SEARCH_PROVIDER="${value}" is not substring, ollama, or transformers; using automatic selection`
24570
+ );
24571
+ return void 0;
24572
+ }
24413
24573
  async function readCache(cachePath) {
24414
24574
  try {
24415
24575
  const raw = await import_node_fs44.promises.readFile(cachePath, "utf8");
@@ -24573,7 +24733,8 @@ async function buildSearchIndex(graph, options = {}) {
24573
24733
  if (options.embedder) {
24574
24734
  embedder = options.embedder;
24575
24735
  } else if (options.forceProvider !== "substring") {
24576
- const factory = options.embedderFactory ?? pickEmbedder;
24736
+ const forcedProvider = options.forceProvider;
24737
+ const factory = options.embedderFactory ?? (() => pickEmbedder(forcedProvider));
24577
24738
  embedder = await resolveEmbedderBounded(
24578
24739
  factory,
24579
24740
  options.initTimeoutMs ?? searchInitTimeoutMs()
@@ -24617,7 +24778,8 @@ async function bootProject(registry, name, scanPath, baseDir) {
24617
24778
  startPersistLoop(graph, paths.snapshotPath);
24618
24779
  startStalenessLoop(graph, { staleEventsPath: paths.staleEventsPath });
24619
24780
  const searchIndex = await buildSearchIndex(graph, {
24620
- cachePath: paths.embeddingsCachePath
24781
+ cachePath: paths.embeddingsCachePath,
24782
+ forceProvider: searchProviderFromEnv()
24621
24783
  }).catch((err) => {
24622
24784
  console.warn(
24623
24785
  `[${name}] semantic_search: index build failed (${err.message}); falling back to inline substring`