@equationalapplications/core-llm-wiki 7.5.0 → 7.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.mts CHANGED
@@ -1,5 +1,5 @@
1
- import { M as MemoryBundle, F as FormatContextOptions, G as GraphNeighborhood, a as MemoryDump, b as FormattedMemoryDump, R as ReadOptions, S as SQLiteAdapter, W as WikiOptions, c as WikiMemory } from './testing-ClpOUiLI.mjs';
2
- export { C as ChunkFailure, d as ClassifierAnswer, e as ClassifierQuestion, f as ClassifyRequest, g as ClassifyResponse, D as DegradedRecord, h as DraftPage, E as EmbedFactResult, i as EmbedFailureKind, j as EmbeddingMarkerKind, k as EntityStatus, l as ExtractedFact, m as ExtractedFactEdge, n as ExtractedFactWithOntology, o as ExtractedTask, p as GraphTraversalOptions, H as HEAL_BATCH_SIZE, q as HEAL_RECHECK_MS, r as HOOK_TIMEOUT_MARKER, s as HealResult, I as IngestDocumentResult, L as LLMProvider, O as ONTOLOGY_BACKFILL_BATCH_SIZE, t as ONTOLOGY_BACKFILL_MAX_PROMPT_CHARS, u as ONTOLOGY_BACKFILL_RECHECK_MS, v as OntologyBackfillResult, w as OntologyConfig, x as OntologyEdgeType, y as OntologyManifest, z as OntologyMode, A as OntologyNodeType, B as OntologyPromptContext, J as OntologyUpdates, P as PromptOverrides, K as PromptService, N as PrunePartialFailureError, Q as ReembedResult, V as VectorRanker, T as VectorRankerFallback, U as VectorRankerRankArgs, X as VectorRankerSemanticResult, Y as WikiBusyError, Z as WikiBusyOperation, _ as WikiCheckpoint, $ as WikiConfig, a0 as WikiDiagnostic, a1 as WikiDiagnosticCode, a2 as WikiDiagnosticDetail, a3 as WikiDiagnosticOperation, a4 as WikiDiagnosticSeverity, a5 as WikiDiagnosticTrigger, a6 as WikiDraftNotFound, a7 as WikiDuplicateHashError, a8 as WikiEdge, a9 as WikiEvent, aa as WikiFact, ab as WikiGraphNodeOwnershipConflict, ac as WikiIngestEmptyError, ad as WikiInvalidReadOptions, ae as WikiMemoryTestAccess, af as WikiOutboxEvent, ag as WikiParseError, ah as WikiSourceRefHashCollision, ai as WikiStrictOntologyViolation, aj as WikiTask, ak as WikiTransactionError, al as validateManifest } from './testing-ClpOUiLI.mjs';
1
+ import { M as MemoryBundle, F as FormatContextOptions, G as GraphNeighborhood, a as MemoryDump, b as FormattedMemoryDump, R as ReadOptions, S as SQLiteAdapter, W as WikiOptions, c as WikiMemory } from './testing-d1SrpDvg.mjs';
2
+ export { C as ChunkFailure, d as ClassifierAnswer, e as ClassifierQuestion, f as ClassifyRequest, g as ClassifyResponse, D as DegradedRecord, h as DraftPage, E as EmbedFactResult, i as EmbedFailureKind, j as EmbeddingMarkerKind, k as EntityStatus, l as ExtractedFact, m as ExtractedFactEdge, n as ExtractedFactWithOntology, o as ExtractedTask, p as GraphTraversalOptions, q as GroundingConfig, H as HEAL_BATCH_SIZE, r as HEAL_RECHECK_MS, s as HOOK_TIMEOUT_MARKER, t as HealResult, I as IngestDocumentResult, L as LLMProvider, O as ONTOLOGY_BACKFILL_BATCH_SIZE, u as ONTOLOGY_BACKFILL_MAX_PROMPT_CHARS, v as ONTOLOGY_BACKFILL_RECHECK_MS, w as OntologyBackfillResult, x as OntologyConfig, y as OntologyEdgeType, z as OntologyManifest, A as OntologyMode, B as OntologyNodeType, J as OntologyPromptContext, K as OntologyUpdates, P as PromptOverrides, N as PromptService, Q as PrunePartialFailureError, T as ReembedResult, V as VectorRanker, U as VectorRankerFallback, X as VectorRankerRankArgs, Y as VectorRankerSemanticResult, Z as WikiBusyError, _ as WikiBusyOperation, $ as WikiCheckpoint, a0 as WikiConfig, a1 as WikiDiagnostic, a2 as WikiDiagnosticCode, a3 as WikiDiagnosticDetail, a4 as WikiDiagnosticOperation, a5 as WikiDiagnosticSeverity, a6 as WikiDiagnosticTrigger, a7 as WikiDraftNotFound, a8 as WikiDuplicateHashError, a9 as WikiEdge, aa as WikiEvent, ab as WikiFact, ac as WikiGraphNodeOwnershipConflict, ad as WikiIngestEmptyError, ae as WikiInvalidReadOptions, af as WikiMemoryTestAccess, ag as WikiOutboxEvent, ah as WikiParseError, ai as WikiSourceRefHashCollision, aj as WikiStrictOntologyViolation, ak as WikiTask, al as WikiTransactionError, am as validateManifest } from './testing-d1SrpDvg.mjs';
3
3
  import { OkfFile } from '@equationalapplications/core-okf';
4
4
  import 'minisearch';
5
5
 
package/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
- import { M as MemoryBundle, F as FormatContextOptions, G as GraphNeighborhood, a as MemoryDump, b as FormattedMemoryDump, R as ReadOptions, S as SQLiteAdapter, W as WikiOptions, c as WikiMemory } from './testing-ClpOUiLI.js';
2
- export { C as ChunkFailure, d as ClassifierAnswer, e as ClassifierQuestion, f as ClassifyRequest, g as ClassifyResponse, D as DegradedRecord, h as DraftPage, E as EmbedFactResult, i as EmbedFailureKind, j as EmbeddingMarkerKind, k as EntityStatus, l as ExtractedFact, m as ExtractedFactEdge, n as ExtractedFactWithOntology, o as ExtractedTask, p as GraphTraversalOptions, H as HEAL_BATCH_SIZE, q as HEAL_RECHECK_MS, r as HOOK_TIMEOUT_MARKER, s as HealResult, I as IngestDocumentResult, L as LLMProvider, O as ONTOLOGY_BACKFILL_BATCH_SIZE, t as ONTOLOGY_BACKFILL_MAX_PROMPT_CHARS, u as ONTOLOGY_BACKFILL_RECHECK_MS, v as OntologyBackfillResult, w as OntologyConfig, x as OntologyEdgeType, y as OntologyManifest, z as OntologyMode, A as OntologyNodeType, B as OntologyPromptContext, J as OntologyUpdates, P as PromptOverrides, K as PromptService, N as PrunePartialFailureError, Q as ReembedResult, V as VectorRanker, T as VectorRankerFallback, U as VectorRankerRankArgs, X as VectorRankerSemanticResult, Y as WikiBusyError, Z as WikiBusyOperation, _ as WikiCheckpoint, $ as WikiConfig, a0 as WikiDiagnostic, a1 as WikiDiagnosticCode, a2 as WikiDiagnosticDetail, a3 as WikiDiagnosticOperation, a4 as WikiDiagnosticSeverity, a5 as WikiDiagnosticTrigger, a6 as WikiDraftNotFound, a7 as WikiDuplicateHashError, a8 as WikiEdge, a9 as WikiEvent, aa as WikiFact, ab as WikiGraphNodeOwnershipConflict, ac as WikiIngestEmptyError, ad as WikiInvalidReadOptions, ae as WikiMemoryTestAccess, af as WikiOutboxEvent, ag as WikiParseError, ah as WikiSourceRefHashCollision, ai as WikiStrictOntologyViolation, aj as WikiTask, ak as WikiTransactionError, al as validateManifest } from './testing-ClpOUiLI.js';
1
+ import { M as MemoryBundle, F as FormatContextOptions, G as GraphNeighborhood, a as MemoryDump, b as FormattedMemoryDump, R as ReadOptions, S as SQLiteAdapter, W as WikiOptions, c as WikiMemory } from './testing-d1SrpDvg.js';
2
+ export { C as ChunkFailure, d as ClassifierAnswer, e as ClassifierQuestion, f as ClassifyRequest, g as ClassifyResponse, D as DegradedRecord, h as DraftPage, E as EmbedFactResult, i as EmbedFailureKind, j as EmbeddingMarkerKind, k as EntityStatus, l as ExtractedFact, m as ExtractedFactEdge, n as ExtractedFactWithOntology, o as ExtractedTask, p as GraphTraversalOptions, q as GroundingConfig, H as HEAL_BATCH_SIZE, r as HEAL_RECHECK_MS, s as HOOK_TIMEOUT_MARKER, t as HealResult, I as IngestDocumentResult, L as LLMProvider, O as ONTOLOGY_BACKFILL_BATCH_SIZE, u as ONTOLOGY_BACKFILL_MAX_PROMPT_CHARS, v as ONTOLOGY_BACKFILL_RECHECK_MS, w as OntologyBackfillResult, x as OntologyConfig, y as OntologyEdgeType, z as OntologyManifest, A as OntologyMode, B as OntologyNodeType, J as OntologyPromptContext, K as OntologyUpdates, P as PromptOverrides, N as PromptService, Q as PrunePartialFailureError, T as ReembedResult, V as VectorRanker, U as VectorRankerFallback, X as VectorRankerRankArgs, Y as VectorRankerSemanticResult, Z as WikiBusyError, _ as WikiBusyOperation, $ as WikiCheckpoint, a0 as WikiConfig, a1 as WikiDiagnostic, a2 as WikiDiagnosticCode, a3 as WikiDiagnosticDetail, a4 as WikiDiagnosticOperation, a5 as WikiDiagnosticSeverity, a6 as WikiDiagnosticTrigger, a7 as WikiDraftNotFound, a8 as WikiDuplicateHashError, a9 as WikiEdge, aa as WikiEvent, ab as WikiFact, ac as WikiGraphNodeOwnershipConflict, ad as WikiIngestEmptyError, ae as WikiInvalidReadOptions, af as WikiMemoryTestAccess, ag as WikiOutboxEvent, ah as WikiParseError, ai as WikiSourceRefHashCollision, aj as WikiStrictOntologyViolation, ak as WikiTask, al as WikiTransactionError, am as validateManifest } from './testing-d1SrpDvg.js';
3
3
  import { OkfFile } from '@equationalapplications/core-okf';
4
4
  import 'minisearch';
5
5
 
package/dist/index.js CHANGED
@@ -1144,15 +1144,16 @@ var EntryRepository = class extends BaseRepository {
1144
1144
  * fact, not only immutable_document rows, so the source-type restriction has
1145
1145
  * to be applied here, after retrieval.
1146
1146
  */
1147
- async findAnchorRowsByIds(entityId, ids, tx) {
1147
+ async findAnchorRowsByIds(entityId, ids, tx, opts) {
1148
1148
  if (ids.length === 0) return [];
1149
1149
  const executor = this.getExecutor(tx);
1150
+ const columns = opts?.withBody ? "id, title, source_ref, body, lifecycle_status" : "id, title, source_ref";
1150
1151
  const rows = [];
1151
1152
  for (let i = 0; i < ids.length; i += this.chunkSize) {
1152
1153
  const chunk = ids.slice(i, i + this.chunkSize);
1153
1154
  const placeholders = chunk.map(() => "?").join(", ");
1154
1155
  const chunkRows = await executor.getAllAsync(
1155
- `SELECT id, title, source_ref FROM ${this.prefix}entries
1156
+ `SELECT ${columns} FROM ${this.prefix}entries
1156
1157
  WHERE entity_id = ? AND deleted_at IS NULL
1157
1158
  AND source_type = 'immutable_document'
1158
1159
  AND id IN (${placeholders})`,
@@ -4154,6 +4155,19 @@ function validateTags(tags) {
4154
4155
  if (!Array.isArray(tags)) return [];
4155
4156
  return tags.filter((t) => typeof t === "string").map((t) => t.trim().toLowerCase()).filter((t) => t.length > 0 && t.length <= 40).slice(0, 6);
4156
4157
  }
4158
+ var MAX_EVIDENCE_QUOTES = 10;
4159
+ function normalizeEvidence(raw) {
4160
+ if (!Array.isArray(raw)) return void 0;
4161
+ const out = [];
4162
+ for (const entry of raw) {
4163
+ if (typeof entry !== "string") continue;
4164
+ const trimmed = entry.trim();
4165
+ if (!trimmed) continue;
4166
+ out.push(trimmed);
4167
+ if (out.length > MAX_EVIDENCE_QUOTES) break;
4168
+ }
4169
+ return out;
4170
+ }
4157
4171
  function validateFact(fact) {
4158
4172
  if (typeof fact?.title !== "string" || typeof fact?.body !== "string") return null;
4159
4173
  const title = clip(fact.title, 80);
@@ -4161,13 +4175,17 @@ function validateFact(fact) {
4161
4175
  if (!title || !body) return null;
4162
4176
  let confidence = fact.confidence;
4163
4177
  if (confidence !== "certain" && confidence !== "tentative") confidence = "inferred";
4164
- return {
4178
+ const valid = {
4165
4179
  ...fact,
4166
4180
  title,
4167
4181
  body,
4168
4182
  confidence,
4169
4183
  tags: validateTags(fact.tags)
4170
4184
  };
4185
+ const evidence = normalizeEvidence(fact.evidence);
4186
+ if (evidence) valid.evidence = evidence;
4187
+ else delete valid.evidence;
4188
+ return valid;
4171
4189
  }
4172
4190
  function validateTask(task) {
4173
4191
  if (typeof task?.description !== "string") return null;
@@ -4212,6 +4230,60 @@ function jaccardScore(a, b) {
4212
4230
  return intersection.size / union.size;
4213
4231
  }
4214
4232
 
4233
+ // src/utils/grounding.ts
4234
+ var GROUNDING_VERIFIER = "process:grounding-check";
4235
+ var WRITERS = ["ingest", "librarian", "heal"];
4236
+ function positiveInt(value, fallback) {
4237
+ return typeof value === "number" && Number.isFinite(value) && value >= 1 ? Math.floor(value) : fallback;
4238
+ }
4239
+ function resolveGrounding(config) {
4240
+ if (!config || config.mode !== "draft") return null;
4241
+ const writers = Array.isArray(config.writers) ? config.writers.filter((w) => WRITERS.includes(w)) : ["ingest"];
4242
+ return {
4243
+ writers: new Set(writers),
4244
+ minEvidenceChars: positiveInt(config.minEvidenceChars, 20),
4245
+ // Never ask for more quotes than checkGrounding accepts (MAX_EVIDENCE_QUOTES).
4246
+ maxEvidence: Math.min(MAX_EVIDENCE_QUOTES, positiveInt(config.maxEvidence, 3)),
4247
+ maxEvidenceChars: positiveInt(config.maxEvidenceChars, 300)
4248
+ };
4249
+ }
4250
+ function normalizeForGrounding(text) {
4251
+ return text.normalize("NFKC").replace(/\s+/g, " ").trim();
4252
+ }
4253
+ function buildGroundingCorpus(parts) {
4254
+ return parts.filter((p) => typeof p === "string").map(normalizeForGrounding);
4255
+ }
4256
+ function checkGrounding(evidence, normalizedCorpus, cfg) {
4257
+ const quotes = evidence ?? [];
4258
+ if (quotes.length > MAX_EVIDENCE_QUOTES) return { status: "failed", reason: "too_many_quotes" };
4259
+ if (quotes.length === 0) return { status: "missing", reason: "no_evidence" };
4260
+ const qualifying = quotes.map(normalizeForGrounding).filter((q) => q.length >= cfg.minEvidenceChars);
4261
+ if (qualifying.length === 0) return { status: "missing", reason: "evidence_too_short" };
4262
+ if (qualifying.some((q) => !normalizedCorpus.some((part) => part.includes(q)))) {
4263
+ return { status: "failed", reason: "quote_not_found" };
4264
+ }
4265
+ return {
4266
+ status: "grounded",
4267
+ retained: qualifying.slice(0, cfg.maxEvidence).map((q) => safeSlice(q, 0, cfg.maxEvidenceChars))
4268
+ };
4269
+ }
4270
+ function groundingOutcome(verdict, now) {
4271
+ if (verdict.status === "grounded") {
4272
+ return {
4273
+ trust: {
4274
+ lifecycle_status: "stable",
4275
+ okf_verified: [{ by: GROUNDING_VERIFIER, at: new Date(now).toISOString() }],
4276
+ last_verified_at: now,
4277
+ last_verified_by: GROUNDING_VERIFIER
4278
+ }
4279
+ };
4280
+ }
4281
+ return {
4282
+ trust: { lifecycle_status: "draft" },
4283
+ diagnostic: { code: verdict.status === "missing" ? "grounding_missing" : "grounding_failed", reason: verdict.reason }
4284
+ };
4285
+ }
4286
+
4215
4287
  // src/utils/diagnostics.ts
4216
4288
  var SEVERITY = {
4217
4289
  ingest_chunk_failed: "warn",
@@ -4341,17 +4413,40 @@ Return ONLY a valid JSON object matching this schema:
4341
4413
  }
4342
4414
  If no manifest type fits a fact, omit that fact from "classifications" entirely \u2014 do not guess.
4343
4415
  When echoing an existing fact's title verbatim into "target_title", preserve every JSON escape sequence (\\", \\n, \\\\, \\/) exactly as it appeared in the input body \u2014 do not strip backslashes, do not add unescaped quotes. Do not return markdown, just raw JSON.`;
4416
+ var GROUNDING_SOURCE = {
4417
+ ingest: { key: "facts", source: "the document chunk" },
4418
+ librarian: { key: "facts", source: 'the "summary" text of the events' },
4419
+ heal: { key: "newFacts", source: 'the "summary" text of the recent events or the "body" text of the document anchors' }
4420
+ };
4421
+ function groundingEvidenceBlock(writer, cfg) {
4422
+ const { key, source } = GROUNDING_SOURCE[writer];
4423
+ return `EVIDENCE REQUIREMENT: every object in "${key}" must also carry an "evidence" array of 1 to ${cfg.maxEvidence} quotes. Each quote must be an exact substring copied character-for-character from ${source} (the SOURCE section), at least ${cfg.minEvidenceChars} characters long. Do not paraphrase. Do not quote these instructions, the ontology manifest, or any existing fact. A fact whose quotes cannot be found in the SOURCE section is stored as an unreviewed draft.
4424
+ "evidence": ["exact substring copied from the SOURCE section"]`;
4425
+ }
4344
4426
 
4345
4427
  // src/utils/healConstants.ts
4346
4428
  var HEAL_MAX_ANCHORS = 50;
4347
4429
  var HEAL_ANCHORS_PER_CANDIDATE = 4;
4348
4430
  var HEAL_MAX_FACT_BODY_CHARS_L3 = 4e3;
4349
4431
  var HEAL_MAX_TASKS = 50;
4432
+ var HEAL_ANCHOR_BODY_CHARS = 800;
4350
4433
 
4351
4434
  // src/services/PromptService.ts
4352
4435
  var PromptService = class {
4353
- constructor(globalOverrides) {
4436
+ constructor(globalOverrides, grounding = null) {
4354
4437
  this.globalOverrides = globalOverrides;
4438
+ this.grounding = grounding;
4439
+ }
4440
+ /** The resolved grounding config when `writer` is in `grounding.writers`; otherwise null (spec §6.2). */
4441
+ groundingFor(writer) {
4442
+ return this.grounding?.writers.has(writer) ? this.grounding : null;
4443
+ }
4444
+ /** Appended after any override and after ontology context, so it is always last. */
4445
+ appendGrounding(systemPrompt, writer) {
4446
+ const cfg = this.groundingFor(writer);
4447
+ return cfg ? `${systemPrompt}
4448
+
4449
+ ${groundingEvidenceBlock(writer, cfg)}` : systemPrompt;
4355
4450
  }
4356
4451
  hydrate(template, variables) {
4357
4452
  return template.replace(/\{\{\s*(\w+)\s*\}\}/g, (_match, key) => {
@@ -4381,13 +4476,13 @@ ${ctx.ontologyModeInstructions}`;
4381
4476
  const hasDocumentChunk = /\{\{\s*documentChunk\s*\}\}/.test(template);
4382
4477
  if (hasDocumentChunk || this.hasOntologyPlaceholders(template)) {
4383
4478
  return {
4384
- systemPrompt: this.buildSystemPrompt(template, { documentChunk }, ontologyContext),
4479
+ systemPrompt: this.appendGrounding(this.buildSystemPrompt(template, { documentChunk }, ontologyContext), "ingest"),
4385
4480
  userPrompt: hasDocumentChunk ? "Please extract the facts." : `Document Chunk:
4386
4481
  ${documentChunk}`
4387
4482
  };
4388
4483
  }
4389
4484
  return {
4390
- systemPrompt: this.appendOntology(template, ontologyContext),
4485
+ systemPrompt: this.appendGrounding(this.appendOntology(template, ontologyContext), "ingest"),
4391
4486
  userPrompt: `Document Chunk:
4392
4487
  ${documentChunk}`
4393
4488
  };
@@ -4396,23 +4491,27 @@ ${documentChunk}`
4396
4491
  const template = runtimeOverride ?? this.globalOverrides?.librarianSystemPrompt ?? LIBRARIAN_SYSTEM_PROMPT;
4397
4492
  const hasEvents = /\{\{\s*events\s*\}\}/.test(template);
4398
4493
  const hasCurrentFacts = /\{\{\s*currentFacts\s*\}\}/.test(template);
4494
+ const eventsShown = hasEvents || !hasCurrentFacts;
4495
+ const corpusField = this.groundingFor("librarian") ? { groundingCorpus: buildGroundingCorpus(eventsShown ? events.map(summaryOf) : []) } : {};
4399
4496
  if (hasEvents || hasCurrentFacts || this.hasOntologyPlaceholders(template)) {
4400
4497
  return {
4401
- systemPrompt: this.buildSystemPrompt(template, { events, currentFacts }, ontologyContext),
4498
+ systemPrompt: this.appendGrounding(this.buildSystemPrompt(template, { events, currentFacts }, ontologyContext), "librarian"),
4402
4499
  userPrompt: hasEvents || hasCurrentFacts ? "Please synthesize the context." : `Events:
4403
4500
  ${JSON.stringify(events, null, 2)}
4404
4501
 
4405
4502
  Current Facts:
4406
- ${JSON.stringify(currentFacts, null, 2)}`
4503
+ ${JSON.stringify(currentFacts, null, 2)}`,
4504
+ ...corpusField
4407
4505
  };
4408
4506
  }
4409
4507
  return {
4410
- systemPrompt: this.appendOntology(template, ontologyContext),
4508
+ systemPrompt: this.appendGrounding(this.appendOntology(template, ontologyContext), "librarian"),
4411
4509
  userPrompt: `Events:
4412
4510
  ${JSON.stringify(events, null, 2)}
4413
4511
 
4414
4512
  Current Facts:
4415
- ${JSON.stringify(currentFacts, null, 2)}`
4513
+ ${JSON.stringify(currentFacts, null, 2)}`,
4514
+ ...corpusField
4416
4515
  };
4417
4516
  }
4418
4517
  /**
@@ -4442,40 +4541,54 @@ ${JSON.stringify(currentFacts, null, 2)}`
4442
4541
  const effectiveEvents = attemptLevel >= 2 ? [] : recentEvents;
4443
4542
  const maxAnchors = Math.max(1, Math.min(HEAL_MAX_ANCHORS, healCandidates.length * HEAL_ANCHORS_PER_CANDIDATE));
4444
4543
  const effectiveAnchors = documentAnchors.slice(0, maxAnchors);
4544
+ const template = runtimeOverride ?? this.globalOverrides?.healSystemPrompt ?? HEAL_SYSTEM_PROMPT;
4545
+ const hasRecentEvents = /\{\{\s*recentEvents\s*\}\}/.test(template);
4546
+ const hasDocumentAnchors = /\{\{\s*documentAnchors\s*\}\}/.test(template);
4547
+ const usesPlaceholders = /\{\{\s*healCandidates\s*\}\}/.test(template) || hasDocumentAnchors || /\{\{\s*allTasks\s*\}\}/.test(template) || hasRecentEvents;
4548
+ const healGrounding = this.groundingFor("heal");
4549
+ const promptAnchors = healGrounding ? effectiveAnchors.map(toGroundingAnchor) : effectiveAnchors;
4550
+ const eventsShown = !usesPlaceholders || hasRecentEvents;
4551
+ const anchorsShown = !usesPlaceholders || hasDocumentAnchors;
4552
+ const groundingCorpus = healGrounding ? buildGroundingCorpus([
4553
+ ...eventsShown ? effectiveEvents.map(summaryOf) : [],
4554
+ ...anchorsShown ? effectiveAnchors.map((a, i) => ({ status: a?.lifecycle_status, shown: promptAnchors[i] })).filter((x) => x.status !== "draft").map((x) => x.shown?.body) : []
4555
+ ]) : null;
4445
4556
  const { shapedCandidates, degraded } = applyBodyTruncation(
4446
4557
  healCandidates,
4447
4558
  attemptLevel,
4448
4559
  bodyTruncationChars
4449
4560
  );
4450
- const template = runtimeOverride ?? this.globalOverrides?.healSystemPrompt ?? HEAL_SYSTEM_PROMPT;
4451
- if (/\{\{\s*healCandidates\s*\}\}/.test(template) || /\{\{\s*documentAnchors\s*\}\}/.test(template) || /\{\{\s*allTasks\s*\}\}/.test(template) || /\{\{\s*recentEvents\s*\}\}/.test(template)) {
4561
+ const corpusField = groundingCorpus !== null ? { groundingCorpus } : {};
4562
+ if (usesPlaceholders) {
4452
4563
  return {
4453
4564
  prompts: {
4454
- systemPrompt: this.hydrate(template, {
4565
+ systemPrompt: this.appendGrounding(this.hydrate(template, {
4455
4566
  healCandidates: shapedCandidates,
4456
- documentAnchors: effectiveAnchors,
4567
+ documentAnchors: promptAnchors,
4457
4568
  allTasks: effectiveTasks,
4458
4569
  recentEvents: effectiveEvents
4459
- }),
4570
+ }), "heal"),
4460
4571
  userPrompt: "Please heal the memory graph."
4461
4572
  },
4462
- degraded
4573
+ degraded,
4574
+ ...corpusField
4463
4575
  };
4464
4576
  }
4465
4577
  return {
4466
4578
  prompts: {
4467
- systemPrompt: template,
4579
+ systemPrompt: this.appendGrounding(template, "heal"),
4468
4580
  userPrompt: `Heal Candidates:
4469
4581
  ${JSON.stringify(shapedCandidates, null, 2)}
4470
4582
  Document Anchors (DO NOT MODIFY OR DELETE):
4471
- ${JSON.stringify(effectiveAnchors, null, 2)}
4583
+ ${JSON.stringify(promptAnchors, null, 2)}
4472
4584
  All Tasks:
4473
4585
  ${JSON.stringify(effectiveTasks, null, 2)}
4474
4586
  Recent Events:
4475
4587
  ${JSON.stringify(effectiveEvents, null, 2)}
4476
4588
  The following document anchors are provided for contradiction detection only. Do not include them in \`downgraded\`, \`deleted\`, or \`newFacts\`.`
4477
4589
  },
4478
- degraded
4590
+ degraded,
4591
+ ...corpusField
4479
4592
  };
4480
4593
  }
4481
4594
  buildOntologyBackfillPrompt(facts, runtimeOverride, ontologyContext) {
@@ -4525,6 +4638,19 @@ function applyBodyTruncation(candidates, attemptLevel, bodyTruncationChars) {
4525
4638
  }
4526
4639
  return { shapedCandidates, degraded };
4527
4640
  }
4641
+ function summaryOf(event) {
4642
+ return event?.summary;
4643
+ }
4644
+ function toGroundingAnchor(anchor) {
4645
+ if (typeof anchor !== "object" || anchor === null) return anchor;
4646
+ const a = anchor;
4647
+ return {
4648
+ id: a.id,
4649
+ title: a.title,
4650
+ source_ref: a.source_ref,
4651
+ body: typeof a.body === "string" ? safeSlice(a.body, 0, HEAL_ANCHOR_BODY_CHARS) : ""
4652
+ };
4653
+ }
4528
4654
 
4529
4655
  // src/utils/chunkingDefaults.ts
4530
4656
  var DEFAULT_MAX_CHUNK_LENGTH = 12e3;
@@ -4554,7 +4680,7 @@ var IngestionService = class {
4554
4680
  this.jobManager = jobManager;
4555
4681
  this.embeddingService = embeddingService;
4556
4682
  this.ontologyService = ontologyService;
4557
- this.promptService = promptService ?? new PromptService(this.options.config?.prompts);
4683
+ this.promptService = promptService ?? new PromptService(this.options.config?.prompts, resolveGrounding(this.options.config?.grounding));
4558
4684
  }
4559
4685
  async ingestDocument(entityId, params, opts) {
4560
4686
  const sourceRef = normalizeSourceRef(params.sourceRef);
@@ -4587,6 +4713,7 @@ var IngestionService = class {
4587
4713
  const { chunks, truncated } = chunkText(params.documentChunk, maxChunkLength, chunkOverlap);
4588
4714
  if (chunks.length === 0) return zeroChunkResult();
4589
4715
  const ontologyContext = await this.ontologyService?.buildPromptContext(entityId) ?? null;
4716
+ const ingestGrounding = this.promptService.groundingFor("ingest");
4590
4717
  const chunkResults = await withConcurrency(
4591
4718
  chunks.map((chunk, chunkIndex) => async () => {
4592
4719
  const { systemPrompt, userPrompt } = this.promptService.buildIngestPrompt(
@@ -4610,11 +4737,16 @@ var IngestionService = class {
4610
4737
  rejected.push({ itemIndex, reason: factRejectionReason(raw) });
4611
4738
  }
4612
4739
  });
4740
+ const verdicts = ingestGrounding ? (() => {
4741
+ const corpus = buildGroundingCorpus([chunk]);
4742
+ return facts.map((f) => checkGrounding(f.evidence, corpus, ingestGrounding));
4743
+ })() : [];
4613
4744
  return {
4614
4745
  status: "ok",
4615
4746
  facts,
4616
4747
  itemIndexes,
4617
4748
  rejected,
4749
+ verdicts,
4618
4750
  ontology_updates: result2.ontology_updates
4619
4751
  };
4620
4752
  } catch (e) {
@@ -4659,6 +4791,7 @@ var IngestionService = class {
4659
4791
  const failures = [];
4660
4792
  const seen = /* @__PURE__ */ new Set();
4661
4793
  const orderedChunkFacts = [];
4794
+ const groundingLedger = /* @__PURE__ */ new Map();
4662
4795
  const diagBuffer = new DiagnosticBuffer();
4663
4796
  const diagBase = { entityId, operation: "ingest", trigger: "call" };
4664
4797
  for (const [chunkIndex, slot] of chunkResults.entries()) {
@@ -4677,6 +4810,7 @@ var IngestionService = class {
4677
4810
  if (!seen.has(normalizedTitle)) {
4678
4811
  seen.add(normalizedTitle);
4679
4812
  dedupedFacts.push(fact);
4813
+ if (ingestGrounding) groundingLedger.set(fact, { verdict: slot.verdicts[k], chunkIndex, itemIndex: slot.itemIndexes[k] });
4680
4814
  } else {
4681
4815
  diagBuffer.push({
4682
4816
  ...diagBase,
@@ -4713,7 +4847,7 @@ var IngestionService = class {
4713
4847
  try {
4714
4848
  if (failedChunks === 0) {
4715
4849
  const fullResult = await this.db.withTransactionAsync(async (tx) => {
4716
- return await this.runFullUpsertGraph(entityId, sourceRef, sourceHash, orderedChunkFacts, tx, diagBuffer);
4850
+ return await this.runFullUpsertGraph(entityId, sourceRef, sourceHash, orderedChunkFacts, tx, diagBuffer, groundingLedger);
4717
4851
  });
4718
4852
  deletedSourceFactIds.push(...fullResult.deletedSourceFactIds);
4719
4853
  insertedFacts.push(...fullResult.insertedFacts);
@@ -4721,7 +4855,7 @@ var IngestionService = class {
4721
4855
  const partialResult = await this.db.withTransactionAsync(async (tx) => {
4722
4856
  const flat = [];
4723
4857
  for (const slot of orderedChunkFacts) flat.push(...slot.facts);
4724
- return await this.appendPartialFacts(entityId, sourceRef, flat, tx, diagBuffer);
4858
+ return await this.appendPartialFacts(entityId, sourceRef, flat, tx, diagBuffer, groundingLedger);
4725
4859
  });
4726
4860
  insertedFacts.push(...partialResult.insertedDescriptors);
4727
4861
  }
@@ -4863,7 +4997,8 @@ var IngestionService = class {
4863
4997
  last_accessed_at: null,
4864
4998
  access_count: 0,
4865
4999
  deleted_at: null,
4866
- okf_type: normalized.okf_type
5000
+ okf_type: normalized.okf_type,
5001
+ ...opts?.nodeTrust?.get(node.id)
4867
5002
  };
4868
5003
  wikiFacts.push(wikiFact);
4869
5004
  }
@@ -4954,9 +5089,10 @@ var IngestionService = class {
4954
5089
  * On the partial path the caller never invokes this method; the empty
4955
5090
  * arrays stay empty.
4956
5091
  */
4957
- async runFullUpsertGraph(entityId, sourceRef, sourceHash, orderedChunkFacts, tx, diagBuffer) {
5092
+ async runFullUpsertGraph(entityId, sourceRef, sourceHash, orderedChunkFacts, tx, diagBuffer, groundingLedger = /* @__PURE__ */ new Map()) {
4958
5093
  const deletedSourceFactIds = [];
4959
5094
  const insertedFacts = [];
5095
+ const nodeTrust = /* @__PURE__ */ new Map();
4960
5096
  deletedSourceFactIds.push(...await this.entryRepo.findIdsBySource(entityId, sourceRef, null, tx, false));
4961
5097
  const titleIndex = /* @__PURE__ */ new Map();
4962
5098
  const existingFacts = await this.entryRepo.findRecentByEntityId(entityId, 500, tx, sourceRef);
@@ -5000,6 +5136,20 @@ var IngestionService = class {
5000
5136
  tags: fact.tags,
5001
5137
  confidence: fact.confidence
5002
5138
  });
5139
+ const grounding = groundingLedger.get(fact);
5140
+ if (grounding) {
5141
+ const outcome = groundingOutcome(grounding.verdict, now);
5142
+ nodeTrust.set(id, outcome.trust);
5143
+ if (outcome.diagnostic) {
5144
+ diagBuffer.push({
5145
+ entityId,
5146
+ operation: "ingest",
5147
+ trigger: "call",
5148
+ code: outcome.diagnostic.code,
5149
+ detail: { factId: id, sourceRef, chunkIndex: grounding.chunkIndex, itemIndex: grounding.itemIndex, reason: outcome.diagnostic.reason }
5150
+ });
5151
+ }
5152
+ }
5003
5153
  insertedFacts.push({ id, entity_id: entityId, title: fact.title, body: fact.body, tags: JSON.stringify(fact.tags) });
5004
5154
  titleIndex.set(normalizeTitleKey(fact.title), { id, okf_type: normalized.okf_type });
5005
5155
  if (normalized.edges.length > 0) {
@@ -5031,7 +5181,7 @@ var IngestionService = class {
5031
5181
  entityId,
5032
5182
  { sourceRef, sourceHash, nodes: hostNodes, edges: hostEdges },
5033
5183
  tx,
5034
- { strict: false, diag: { buffer: diagBuffer, operation: "ingest" } }
5184
+ { strict: false, diag: { buffer: diagBuffer, operation: "ingest" }, ...nodeTrust.size > 0 ? { nodeTrust } : {} }
5035
5185
  );
5036
5186
  return { deletedSourceFactIds, insertedFacts };
5037
5187
  }
@@ -5055,7 +5205,7 @@ var IngestionService = class {
5055
5205
  * Runs INSIDE the caller's `tx`. Does not open a nested transaction.
5056
5206
  * Returns `{ inserted, skippedDuplicate }` for observability.
5057
5207
  */
5058
- async appendPartialFacts(entityId, sourceRef, dedupedFacts, tx, diagBuffer) {
5208
+ async appendPartialFacts(entityId, sourceRef, dedupedFacts, tx, diagBuffer, groundingLedger = /* @__PURE__ */ new Map()) {
5059
5209
  const liveIds = await this.entryRepo.findIdsBySource(entityId, sourceRef, null, tx, false);
5060
5210
  const liveFacts = liveIds.length === 0 ? [] : await this.entryRepo.findByIds(liveIds, void 0, tx);
5061
5211
  const liveTitles = new Set(liveFacts.map((f) => normalizeTitleKey(f.title)));
@@ -5078,6 +5228,17 @@ var IngestionService = class {
5078
5228
  }
5079
5229
  liveTitles.add(normalizedTitle);
5080
5230
  const id = generateId("fact_");
5231
+ const grounding = groundingLedger.get(fact);
5232
+ const outcome = grounding ? groundingOutcome(grounding.verdict, now) : null;
5233
+ if (grounding && outcome?.diagnostic) {
5234
+ diagBuffer.push({
5235
+ entityId,
5236
+ operation: "ingest",
5237
+ trigger: "call",
5238
+ code: outcome.diagnostic.code,
5239
+ detail: { factId: id, sourceRef, chunkIndex: grounding.chunkIndex, itemIndex: grounding.itemIndex, reason: outcome.diagnostic.reason }
5240
+ });
5241
+ }
5081
5242
  const wikiFact = {
5082
5243
  id,
5083
5244
  entity_id: entityId,
@@ -5096,7 +5257,8 @@ var IngestionService = class {
5096
5257
  last_accessed_at: null,
5097
5258
  access_count: 0,
5098
5259
  deleted_at: null,
5099
- okf_type: null
5260
+ okf_type: null,
5261
+ ...outcome?.trust
5100
5262
  };
5101
5263
  await this.entryRepo.upsert(wikiFact, tx);
5102
5264
  insertedDescriptors.push({
@@ -5264,7 +5426,7 @@ async function runBatched(args) {
5264
5426
  }
5265
5427
  let result;
5266
5428
  try {
5267
- result = parse(responseText, batch);
5429
+ result = parse(responseText, batch, prompts);
5268
5430
  } catch (err) {
5269
5431
  await onFailure(batch, err, attemptLevel, false);
5270
5432
  return;
@@ -5345,7 +5507,7 @@ var MaintenanceService = class {
5345
5507
  this.jobManager = jobManager;
5346
5508
  this.embeddingService = embeddingService;
5347
5509
  this.ontologyService = ontologyService;
5348
- this.promptService = promptService ?? new PromptService(this.options.config?.prompts);
5510
+ this.promptService = promptService ?? new PromptService(this.options.config?.prompts, resolveGrounding(this.options.config?.grounding));
5349
5511
  }
5350
5512
  async runPrune(entityId, options) {
5351
5513
  this.jobManager.acquireLock("prune", entityId);
@@ -5650,8 +5812,10 @@ var MaintenanceService = class {
5650
5812
  };
5651
5813
  });
5652
5814
  const ontologyContext = await this.ontologyService?.buildPromptContext(entityId) ?? null;
5653
- const { systemPrompt, userPrompt } = this.promptService.buildLibrarianPrompt(
5654
- events.reverse(),
5815
+ const promptEvents = events.reverse();
5816
+ const librarianGrounding = this.promptService.groundingFor("librarian");
5817
+ const { systemPrompt, userPrompt, groundingCorpus: librarianCorpus = [] } = this.promptService.buildLibrarianPrompt(
5818
+ promptEvents,
5655
5819
  currentFacts,
5656
5820
  promptOverride,
5657
5821
  ontologyContext
@@ -5720,6 +5884,10 @@ var MaintenanceService = class {
5720
5884
  const validationDrops = [];
5721
5885
  const normalized = this.ontologyService?.validateAndNormalizeFact(ontologyFact, manifest, { strict: false, drops: validationDrops }) ?? { okf_type: null, edges: [] };
5722
5886
  for (const drop of validationDrops) diagBuffer.push(edgeDropDiagnostic(drop, { ...diagBase, factId: id }));
5887
+ const grounding = librarianGrounding ? groundingOutcome(checkGrounding(fact.evidence, librarianCorpus, librarianGrounding), now) : null;
5888
+ if (grounding?.diagnostic) {
5889
+ diagBuffer.push({ ...diagBase, code: grounding.diagnostic.code, detail: { factId: id, itemIndex: validFactItemIndexes[k], reason: grounding.diagnostic.reason } });
5890
+ }
5723
5891
  const factObj = {
5724
5892
  id,
5725
5893
  entity_id: entityId,
@@ -5735,7 +5903,8 @@ var MaintenanceService = class {
5735
5903
  last_accessed_at: null,
5736
5904
  access_count: 0,
5737
5905
  deleted_at: null,
5738
- okf_type: normalized.okf_type
5906
+ okf_type: normalized.okf_type,
5907
+ ...grounding?.trust
5739
5908
  };
5740
5909
  await this.entryRepo.upsert(factObj, tx);
5741
5910
  insertedFacts.push({ id, entity_id: entityId, title: fact.title, body: fact.body, tags: JSON.stringify(fact.tags) });
@@ -5851,11 +6020,14 @@ var MaintenanceService = class {
5851
6020
  }
5852
6021
  const allTasks = await this.taskRepo.findAllPending([entityId], HEAL_MAX_TASKS);
5853
6022
  const recentEvents = await this.eventRepo.getRecent(entityId, 20);
6023
+ const healGrounding = this.promptService.groundingFor("heal");
5854
6024
  const toPromptShape = (f) => {
5855
6025
  const { embedding: _embedding, embedding_blob: _blob, ...rest } = f;
5856
- return { ...rest, tags: typeof rest.tags === "string" ? JSON.parse(rest.tags) : rest.tags };
6026
+ const shown = healGrounding ? (({ lifecycle_status: _l, okf_verified: _v, last_verified_at: _a, last_verified_by: _b, ...r }) => r)(rest) : rest;
6027
+ return { ...shown, tags: typeof shown.tags === "string" ? JSON.parse(shown.tags) : shown.tags };
5857
6028
  };
5858
6029
  const anchorCache = /* @__PURE__ */ new Map();
6030
+ const corpusByPrompt = /* @__PURE__ */ new WeakMap();
5859
6031
  const degraded = [];
5860
6032
  const outcome = await runBatched({
5861
6033
  items: healCandidates,
@@ -5868,9 +6040,10 @@ var MaintenanceService = class {
5868
6040
  // not overfetch the same 200 keyword hits a 25-fact batch once did.
5869
6041
  // buildHealPrompt applies the matching cap on its side — values must match.
5870
6042
  Math.min(HEAL_MAX_ANCHORS, batch.length * HEAL_ANCHORS_PER_CANDIDATE),
5871
- anchorCache
6043
+ anchorCache,
6044
+ healGrounding !== null
5872
6045
  );
5873
- const { prompts, degraded: batchDegraded } = await this.promptService.buildHealPrompt(
6046
+ const { prompts, degraded: batchDegraded, groundingCorpus } = await this.promptService.buildHealPrompt(
5874
6047
  batch.map(toPromptShape),
5875
6048
  documentAnchors,
5876
6049
  allTasks,
@@ -5880,16 +6053,18 @@ var MaintenanceService = class {
5880
6053
  bodyTruncationChars
5881
6054
  );
5882
6055
  degraded.push(...batchDegraded);
6056
+ if (groundingCorpus !== void 0) corpusByPrompt.set(prompts, groundingCorpus);
5883
6057
  return prompts;
5884
6058
  },
5885
6059
  call: (prompts) => this.options.llmProvider.generateText(prompts),
5886
- parse: (responseText, batch) => {
6060
+ parse: (responseText, batch, prompts) => {
5887
6061
  const result = parseJsonResponse(responseText);
5888
6062
  return {
5889
6063
  batch,
5890
6064
  downgraded: Array.isArray(result.downgraded) ? result.downgraded : [],
5891
6065
  deleted: Array.isArray(result.deleted) ? result.deleted : [],
5892
- newFacts: Array.isArray(result.newFacts) ? result.newFacts : []
6066
+ newFacts: Array.isArray(result.newFacts) ? result.newFacts : [],
6067
+ corpus: corpusByPrompt.get(prompts) ?? null
5893
6068
  };
5894
6069
  },
5895
6070
  maxOutputTokens: this.options.llmProvider.maxOutputTokens,
@@ -5904,6 +6079,7 @@ var MaintenanceService = class {
5904
6079
  const safeDeletedSet = /* @__PURE__ */ new Set();
5905
6080
  const newFacts = [];
5906
6081
  const newFactItemIndexes = [];
6082
+ const newFactCorpora = [];
5907
6083
  for (const batchResult of outcome.results) {
5908
6084
  const mutableIds = new Set(batchResult.batch.map((f) => f.id));
5909
6085
  for (const id of batchResult.downgraded) if (mutableIds.has(id)) safeDowngradedSet.add(id);
@@ -5911,6 +6087,7 @@ var MaintenanceService = class {
5911
6087
  batchResult.newFacts.forEach((raw, itemIndex) => {
5912
6088
  newFacts.push(raw);
5913
6089
  newFactItemIndexes.push(itemIndex);
6090
+ newFactCorpora.push(batchResult.corpus);
5914
6091
  });
5915
6092
  }
5916
6093
  const safeDowngraded = Array.from(safeDowngradedSet);
@@ -5918,12 +6095,14 @@ var MaintenanceService = class {
5918
6095
  const diagBuffer = new DiagnosticBuffer();
5919
6096
  const validNewFacts = [];
5920
6097
  const validNewFactItemIndexes = [];
6098
+ const validNewFactCorpora = [];
5921
6099
  newFacts.forEach((raw, k) => {
5922
6100
  const itemIndex = newFactItemIndexes[k];
5923
6101
  const valid = validateFact(raw);
5924
6102
  if (valid) {
5925
6103
  validNewFacts.push(valid);
5926
6104
  validNewFactItemIndexes.push(itemIndex);
6105
+ validNewFactCorpora.push(newFactCorpora[k]);
5927
6106
  } else {
5928
6107
  diagBuffer.push({ ...diagBase, code: "fact_rejected", detail: { itemIndex, reason: factRejectionReason(raw) } });
5929
6108
  }
@@ -5953,6 +6132,10 @@ var MaintenanceService = class {
5953
6132
  continue;
5954
6133
  }
5955
6134
  const id = generateId("fact_");
6135
+ const grounding = healGrounding ? groundingOutcome(checkGrounding(fact.evidence, validNewFactCorpora[k] ?? [], healGrounding), now) : null;
6136
+ if (grounding?.diagnostic) {
6137
+ diagBuffer.push({ ...diagBase, code: grounding.diagnostic.code, detail: { factId: id, itemIndex: validNewFactItemIndexes[k], reason: grounding.diagnostic.reason } });
6138
+ }
5956
6139
  const factObj = {
5957
6140
  id,
5958
6141
  entity_id: entityId,
@@ -5967,7 +6150,8 @@ var MaintenanceService = class {
5967
6150
  updated_at: now,
5968
6151
  last_accessed_at: null,
5969
6152
  access_count: 0,
5970
- deleted_at: null
6153
+ deleted_at: null,
6154
+ ...grounding?.trust
5971
6155
  };
5972
6156
  await this.entryRepo.upsert(factObj, tx);
5973
6157
  insertedFacts.push({ id, entity_id: entityId, title: fact.title, body: fact.body, tags: JSON.stringify(fact.tags) });
@@ -6331,7 +6515,7 @@ var MaintenanceService = class {
6331
6515
  * batches that reduce to the same query share one lookup. Caller-owned and
6332
6516
  * per-pass — see the call site in doRunHeal.
6333
6517
  */
6334
- async _selectHealAnchors(entityId, batch, cap = HEAL_MAX_ANCHORS, cache) {
6518
+ async _selectHealAnchors(entityId, batch, cap = HEAL_MAX_ANCHORS, cache, withBodies = false) {
6335
6519
  const query = batch.map((f) => f.title).join(" ").trim();
6336
6520
  if (!query) return [];
6337
6521
  const cached = cache?.get(query);
@@ -6344,7 +6528,7 @@ var MaintenanceService = class {
6344
6528
  const hitIds = hits.map((h) => h.id);
6345
6529
  const anchors = [];
6346
6530
  if (hitIds.length > 0) {
6347
- const rows = await this.entryRepo.findAnchorRowsByIds(entityId, hitIds);
6531
+ const rows = withBodies ? await this.entryRepo.findAnchorRowsByIds(entityId, hitIds, void 0, { withBody: true }) : await this.entryRepo.findAnchorRowsByIds(entityId, hitIds);
6348
6532
  const byId = new Map(rows.map((r) => [r.id, r]));
6349
6533
  for (const id of hitIds) {
6350
6534
  const row = byId.get(id);
@@ -8279,7 +8463,7 @@ var WikiMemory = class {
8279
8463
  this.embeddingService = new EmbeddingService(this.db, this.options, this.entryRepo, this.metadataRepo);
8280
8464
  this.searchService = new SearchService(this.entryRepo);
8281
8465
  this.jobManager = new JobManager(this.prefix);
8282
- this.promptService = new PromptService(options.config?.prompts);
8466
+ this.promptService = new PromptService(options.config?.prompts, resolveGrounding(options.config?.grounding));
8283
8467
  this.ingestionService = new IngestionService(
8284
8468
  this.db,
8285
8469
  this.prefix,