@absolutejs/absolute 0.19.0-beta.614 → 0.19.0-beta.616

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 (49) hide show
  1. package/dist/ai/client/index.js +92 -43
  2. package/dist/ai/client/index.js.map +7 -7
  3. package/dist/ai/client/ui.js +37 -1
  4. package/dist/ai/client/ui.js.map +3 -3
  5. package/dist/ai/index.js +1271 -237
  6. package/dist/ai/index.js.map +17 -14
  7. package/dist/ai/rag/quality.js +16 -3
  8. package/dist/ai/rag/quality.js.map +4 -4
  9. package/dist/ai/rag/ui.js +37 -1
  10. package/dist/ai/rag/ui.js.map +3 -3
  11. package/dist/ai-client/angular/ai/index.js +51 -40
  12. package/dist/ai-client/react/ai/index.js +53 -42
  13. package/dist/ai-client/vue/ai/index.js +51 -40
  14. package/dist/angular/ai/index.js +78 -42
  15. package/dist/angular/ai/index.js.map +6 -6
  16. package/dist/angular/index.js +2 -2
  17. package/dist/angular/index.js.map +1 -1
  18. package/dist/angular/server.js +2 -2
  19. package/dist/angular/server.js.map +1 -1
  20. package/dist/build.js +2 -2
  21. package/dist/build.js.map +1 -1
  22. package/dist/index.js +2 -2
  23. package/dist/index.js.map +1 -1
  24. package/dist/react/ai/index.js +96 -47
  25. package/dist/react/ai/index.js.map +9 -9
  26. package/dist/src/ai/client/actions.d.ts +1 -1
  27. package/dist/src/ai/index.d.ts +2 -2
  28. package/dist/src/ai/protocol.d.ts +1 -1
  29. package/dist/src/ai/rag/accessControl.d.ts +2 -0
  30. package/dist/src/ai/rag/chat.d.ts +21 -23
  31. package/dist/src/ai/rag/collection.d.ts +2 -1
  32. package/dist/src/ai/rag/index.d.ts +5 -2
  33. package/dist/src/ai/rag/ingestion.d.ts +12 -3
  34. package/dist/src/ai/rag/jobState.d.ts +2 -0
  35. package/dist/src/ai/rag/retrievalStrategies.d.ts +6 -0
  36. package/dist/src/react/ai/useRAG.d.ts +2 -0
  37. package/dist/src/svelte/ai/createRAG.d.ts +2 -0
  38. package/dist/src/vue/ai/useRAG.d.ts +44 -0
  39. package/dist/src/vue/ai/useRAGChunkPreview.d.ts +4 -0
  40. package/dist/src/vue/ai/useRAGDocuments.d.ts +4 -0
  41. package/dist/src/vue/ai/useRAGEvaluate.d.ts +20 -0
  42. package/dist/src/vue/ai/useRAGIndexAdmin.d.ts +2 -0
  43. package/dist/src/vue/ai/useRAGSearch.d.ts +12 -0
  44. package/dist/svelte/ai/index.js +92 -43
  45. package/dist/svelte/ai/index.js.map +7 -7
  46. package/dist/types/ai.d.ts +199 -1
  47. package/dist/vue/ai/index.js +92 -43
  48. package/dist/vue/ai/index.js.map +7 -7
  49. package/package.json +7 -7
package/dist/ai/index.js CHANGED
@@ -187,7 +187,7 @@ var parseAIMessage = (raw) => {
187
187
  return null;
188
188
  }
189
189
  };
190
- var serializeAIMessage = (msg) => JSON.stringify(msg);
190
+ var serializeAIMessage = (message) => JSON.stringify(message);
191
191
 
192
192
  // src/ai/rag/grounding.ts
193
193
  var getContextString = (value) => typeof value === "string" && value.trim().length > 0 ? value.trim() : undefined;
@@ -1275,7 +1275,10 @@ var evaluateRAGCollectionCases = async ({
1275
1275
  const expectedIds = normalizeExpectedIds(mode === "chunkId" ? caseInput.expectedChunkIds ?? [] : mode === "source" ? caseInput.expectedSources ?? [] : caseInput.expectedDocumentIds ?? []);
1276
1276
  const topK = typeof caseInput.topK === "number" ? caseInput.topK : typeof input.topK === "number" ? input.topK : defaultTopK;
1277
1277
  const searchInput = {
1278
- filter: typeof caseInput.filter === "object" ? caseInput.filter : input.filter,
1278
+ filter: caseInput.corpusKey ? {
1279
+ ...(typeof caseInput.filter === "object" ? caseInput.filter : input.filter) ?? {},
1280
+ corpusKey: caseInput.corpusKey
1281
+ } : typeof caseInput.filter === "object" ? caseInput.filter : input.filter,
1279
1282
  model: caseInput.model ?? input.model,
1280
1283
  query,
1281
1284
  rerank,
@@ -1677,6 +1680,7 @@ var buildEvaluationCaseTraceSnapshot = ({
1677
1680
  return {
1678
1681
  candidateTopK: currentTrace?.candidateTopK ?? 0,
1679
1682
  caseId: caseResult.caseId,
1683
+ corpusKey: caseResult.corpusKey,
1680
1684
  inputFilter: filter,
1681
1685
  finalCount: currentTrace?.resultCounts.final ?? 0,
1682
1686
  label: caseResult.label,
@@ -3229,11 +3233,15 @@ var persistRAGRetrievalReleaseLaneEscalationPolicyHistory = async ({
3229
3233
  };
3230
3234
  var buildRAGEvaluationResponse = (cases) => {
3231
3235
  const totalCases = cases.length;
3236
+ const corpusKeys = [
3237
+ ...new Set(cases.flatMap((entry) => entry.corpusKey ?? []))
3238
+ ];
3232
3239
  const passedCases = cases.filter((entry) => entry.status === "pass").length;
3233
3240
  const partialCases = cases.filter((entry) => entry.status === "partial").length;
3234
3241
  const failedCases = cases.filter((entry) => entry.status === "fail").length;
3235
3242
  return {
3236
3243
  cases,
3244
+ ...corpusKeys.length > 0 ? { corpusKeys } : {},
3237
3245
  elapsedMs: cases.reduce((sum, result) => sum + result.elapsedMs, 0),
3238
3246
  ok: true,
3239
3247
  passingRate: totalCases > 0 ? passedCases / totalCases * 100 : 0,
@@ -3459,6 +3467,9 @@ var compareRAGRetrievalStrategies = async ({
3459
3467
  traceSummary: entry.traceSummary
3460
3468
  })));
3461
3469
  return {
3470
+ corpusKeys: [
3471
+ ...new Set(entries.flatMap((entry) => entry.response.corpusKeys ?? []))
3472
+ ],
3462
3473
  entries,
3463
3474
  leaderboard,
3464
3475
  summary: summarizeRAGRetrievalComparison(entries),
@@ -3491,6 +3502,7 @@ var compareRAGRetrievalTraceSummaries = (current, previous) => ({
3491
3502
  });
3492
3503
  var buildSearchTraceResultSnapshots = (results) => results.map((result) => ({
3493
3504
  chunkId: result.chunkId,
3505
+ corpusKey: result.corpusKey ?? (typeof result.metadata?.corpusKey === "string" ? result.metadata.corpusKey : undefined),
3494
3506
  documentId: typeof result.metadata?.documentId === "string" ? result.metadata.documentId : undefined,
3495
3507
  score: result.score,
3496
3508
  source: result.source,
@@ -3690,6 +3702,7 @@ var summarizeRAGEvaluationCase = ({
3690
3702
  const status = expectedCount === 0 ? "partial" : matchedCount === expectedCount ? "pass" : matchedCount > 0 ? "partial" : "fail";
3691
3703
  return {
3692
3704
  caseId: caseInput.id ?? `case-${caseIndex + 1}`,
3705
+ corpusKey: caseInput.corpusKey,
3693
3706
  elapsedMs,
3694
3707
  expectedCount,
3695
3708
  expectedIds,
@@ -3772,10 +3785,35 @@ var buildRAGRetrievalTracePresentation = (trace) => {
3772
3785
  ];
3773
3786
  const details = [
3774
3787
  { label: "Transformed query", value: trace.transformedQuery },
3788
+ ...trace.queryTransformLabel || trace.queryTransformProvider ? [
3789
+ {
3790
+ label: "Query transform",
3791
+ value: trace.queryTransformLabel ?? trace.queryTransformProvider ?? "configured"
3792
+ },
3793
+ ...trace.queryTransformReason ? [
3794
+ {
3795
+ label: "Query transform reason",
3796
+ value: trace.queryTransformReason
3797
+ }
3798
+ ] : []
3799
+ ] : [],
3775
3800
  {
3776
3801
  label: "Variant queries",
3777
3802
  value: trace.variantQueries.length > 0 ? trace.variantQueries.join(" \xB7 ") : "none"
3778
3803
  },
3804
+ ...trace.requestedMode && trace.requestedMode !== trace.mode ? [{ label: "Requested mode", value: trace.requestedMode }] : [],
3805
+ ...trace.routingLabel || trace.routingProvider ? [
3806
+ {
3807
+ label: "Routing decision",
3808
+ value: trace.routingLabel ?? trace.routingProvider ?? "configured"
3809
+ },
3810
+ ...trace.routingReason ? [
3811
+ {
3812
+ label: "Routing reason",
3813
+ value: trace.routingReason
3814
+ }
3815
+ ] : []
3816
+ ] : [],
3779
3817
  { label: "Candidate topK", value: String(trace.candidateTopK) },
3780
3818
  { label: "Lexical topK", value: String(trace.lexicalTopK) }
3781
3819
  ];
@@ -4264,10 +4302,14 @@ var buildProvenanceLabel2 = (metadata) => {
4264
4302
  const transcriptSource = getContextString2(metadata.transcriptSource);
4265
4303
  const pdfTextMode = getContextString2(metadata.pdfTextMode);
4266
4304
  const ocrEngine = getContextString2(metadata.ocrEngine);
4305
+ const extractorRegistryMatch = getContextString2(metadata.extractorRegistryMatch);
4306
+ const chunkingProfile = getContextString2(metadata.chunkingProfile);
4267
4307
  const ocrConfidence = getContextNumber2(metadata.ocrRegionConfidence) ?? getContextNumber2(metadata.ocrConfidence);
4268
4308
  const labels = [
4269
4309
  pdfTextMode ? `PDF ${pdfTextMode}` : "",
4270
4310
  ocrEngine ? `OCR ${ocrEngine}` : "",
4311
+ extractorRegistryMatch ? `Extractor ${extractorRegistryMatch}` : "",
4312
+ chunkingProfile ? `Chunking ${chunkingProfile}` : "",
4271
4313
  typeof ocrConfidence === "number" ? `Confidence ${ocrConfidence.toFixed(2)}` : "",
4272
4314
  mediaKind ? `Media ${mediaKind}` : "",
4273
4315
  transcriptSource ? `Transcript ${transcriptSource}` : "",
@@ -4878,7 +4920,14 @@ var buildRAGSectionRetrievalDiagnostics = (sources, trace) => {
4878
4920
  transformedHits: section.transformedHits,
4879
4921
  variantHits: section.variantHits
4880
4922
  },
4923
+ requestedMode: trace?.requestedMode,
4881
4924
  retrievalMode: trace?.mode,
4925
+ routingLabel: trace?.routingLabel,
4926
+ routingProvider: trace?.routingProvider,
4927
+ routingReason: trace?.routingReason,
4928
+ queryTransformLabel: trace?.queryTransformLabel,
4929
+ queryTransformProvider: trace?.queryTransformProvider,
4930
+ queryTransformReason: trace?.queryTransformReason,
4882
4931
  reasons,
4883
4932
  rerankApplied: trace?.steps.some((step) => step.stage === "rerank" && step.metadata?.applied === true),
4884
4933
  scoreShare,
@@ -6709,12 +6758,12 @@ var checkBackpressure = async (socket) => {
6709
6758
  await delay(BACKPRESSURE_DELAY);
6710
6759
  }
6711
6760
  };
6712
- var sendMessage = async (socket, msg) => {
6761
+ var sendMessage = async (socket, message) => {
6713
6762
  if (socket.readyState !== WS_OPEN) {
6714
6763
  return false;
6715
6764
  }
6716
6765
  await checkBackpressure(socket);
6717
- socket.send(serializeAIMessage(msg));
6766
+ socket.send(serializeAIMessage(message));
6718
6767
  return true;
6719
6768
  };
6720
6769
  var buildToolDefinitions = (tools) => Object.entries(tools).map(([name, def]) => ({
@@ -8356,7 +8405,17 @@ var STOP_WORDS3 = new Set([
8356
8405
  "which",
8357
8406
  "why"
8358
8407
  ]);
8359
- var collectMetadataStrings2 = (value) => {
8408
+ var INTERNAL_RETRIEVAL_METADATA_KEYS = new Set([
8409
+ "retrievalChannels",
8410
+ "retrievalQuery",
8411
+ "retrievalQueryIndex",
8412
+ "retrievalQueryOrigin",
8413
+ "retrievalQueryOrigins"
8414
+ ]);
8415
+ var collectMetadataStrings2 = (value, key) => {
8416
+ if (typeof key === "string" && INTERNAL_RETRIEVAL_METADATA_KEYS.has(key)) {
8417
+ return [];
8418
+ }
8360
8419
  if (typeof value === "string" || typeof value === "number") {
8361
8420
  return [String(value)];
8362
8421
  }
@@ -8364,7 +8423,7 @@ var collectMetadataStrings2 = (value) => {
8364
8423
  return value.flatMap((entry) => collectMetadataStrings2(entry));
8365
8424
  }
8366
8425
  if (value && typeof value === "object") {
8367
- return Object.values(value).flatMap((entry) => collectMetadataStrings2(entry));
8426
+ return Object.entries(value).flatMap(([entryKey, entry]) => collectMetadataStrings2(entry, entryKey));
8368
8427
  }
8369
8428
  return [];
8370
8429
  };
@@ -9647,6 +9706,7 @@ var createEmailExtractor = () => ({
9647
9706
  chunking: input.chunking,
9648
9707
  contentType: attachment.contentType,
9649
9708
  data: attachment.data,
9709
+ extractorRegistry: input.extractorRegistry,
9650
9710
  format: inferFormatFromContentType(attachment.contentType ?? null) ?? inferFormatFromName(attachment.fileName),
9651
9711
  metadata: {
9652
9712
  ...messageMetadata,
@@ -9802,6 +9862,8 @@ ${slide.text}`),
9802
9862
  });
9803
9863
  var createRAGArchiveExpander = (expander) => expander;
9804
9864
  var createRAGFileExtractor = (extractor) => extractor;
9865
+ var createRAGFileExtractorRegistry = (registry) => registry;
9866
+ var createRAGChunkingRegistry = (registry) => registry;
9805
9867
  var createRAGImageOCRExtractor = (provider) => ({
9806
9868
  name: `absolute_image_ocr:${provider.name}`,
9807
9869
  supports: imageExtractorSupports,
@@ -9892,11 +9954,12 @@ var createTextFileExtractor = () => ({
9892
9954
  title: input.title
9893
9955
  })
9894
9956
  });
9895
- var expandArchiveEntry = async (entry, archiveInput, extractors) => {
9957
+ var expandArchiveEntry = async (entry, archiveInput, extractors, registry) => {
9896
9958
  const documents = await extractRAGFileDocuments({
9897
9959
  chunking: archiveInput.chunking,
9898
9960
  contentType: entry.contentType,
9899
9961
  data: entry.data,
9962
+ extractorRegistry: archiveInput.extractorRegistry,
9900
9963
  format: entry.format,
9901
9964
  metadata: {
9902
9965
  ...archiveInput.metadata ?? {},
@@ -9910,7 +9973,7 @@ var expandArchiveEntry = async (entry, archiveInput, extractors) => {
9910
9973
  name: basename(entry.path),
9911
9974
  source: archiveInput.source && !archiveInput.source.startsWith("http") ? `${archiveInput.source}#${entry.path}` : entry.path,
9912
9975
  title: basename(entry.path)
9913
- }, extractors);
9976
+ }, extractors, registry);
9914
9977
  return documents;
9915
9978
  };
9916
9979
  var createPDFFileExtractor = () => ({
@@ -10011,8 +10074,83 @@ var DEFAULT_FILE_EXTRACTORS = [
10011
10074
  createPDFFileExtractor(),
10012
10075
  createTextFileExtractor()
10013
10076
  ];
10014
- var resolveFileExtractors = (extractors) => extractors && extractors.length > 0 ? [...extractors, ...DEFAULT_FILE_EXTRACTORS] : DEFAULT_FILE_EXTRACTORS;
10015
- var applyExtractorDefaults = (document, input, extractorName) => ({
10077
+ var resolveExtractorRegistry = (registry) => {
10078
+ if (!registry) {
10079
+ return {
10080
+ defaultOrder: "registry_first",
10081
+ includeDefaults: true,
10082
+ registrations: []
10083
+ };
10084
+ }
10085
+ if (Array.isArray(registry)) {
10086
+ return {
10087
+ defaultOrder: "registry_first",
10088
+ includeDefaults: true,
10089
+ registrations: registry
10090
+ };
10091
+ }
10092
+ return {
10093
+ defaultOrder: registry.defaultOrder ?? "registry_first",
10094
+ includeDefaults: registry.includeDefaults ?? true,
10095
+ registrations: registry.registrations
10096
+ };
10097
+ };
10098
+ var createExtractorRegistryInput = (input) => ({
10099
+ ...input,
10100
+ inferredContentType: input.contentType ?? null,
10101
+ inferredExtension: inferExtensionFromInput(input) || null,
10102
+ inferredFormat: input.format ?? inferFormatFromContentType(input.contentType ?? null) ?? inferFormatFromName(input.path ?? input.source ?? input.name ?? input.title)
10103
+ });
10104
+ var registrationMatches = async (registration, input) => {
10105
+ const normalizedContentType = input.inferredContentType?.toLowerCase();
10106
+ const normalizedExtension = input.inferredExtension?.toLowerCase();
10107
+ const normalizedName = (input.path ?? input.source ?? input.name ?? input.title ?? "").toLowerCase();
10108
+ if (registration.contentTypes?.length && !registration.contentTypes.some((entry) => normalizedContentType === entry.toLowerCase())) {
10109
+ return false;
10110
+ }
10111
+ if (registration.extensions?.length && !registration.extensions.some((entry) => normalizedExtension === entry.toLowerCase())) {
10112
+ return false;
10113
+ }
10114
+ if (registration.formats?.length && (!input.inferredFormat || !registration.formats.includes(input.inferredFormat))) {
10115
+ return false;
10116
+ }
10117
+ if (registration.names?.length && !registration.names.some((entry) => normalizedName.includes(entry.toLowerCase()))) {
10118
+ return false;
10119
+ }
10120
+ if (registration.match) {
10121
+ return registration.match(input);
10122
+ }
10123
+ return true;
10124
+ };
10125
+ var dedupeResolvedExtractors = (extractors) => {
10126
+ const seen = new Set;
10127
+ const ordered = [];
10128
+ for (const entry of extractors) {
10129
+ if (seen.has(entry.extractor.name)) {
10130
+ continue;
10131
+ }
10132
+ seen.add(entry.extractor.name);
10133
+ ordered.push(entry);
10134
+ }
10135
+ return ordered;
10136
+ };
10137
+ var resolveFileExtractors = async (input, extractors, registry) => {
10138
+ const explicit = extractors ?? [];
10139
+ const resolvedRegistry = resolveExtractorRegistry(registry);
10140
+ const registryInput = createExtractorRegistryInput(input);
10141
+ const matchedRegistrations = (await Promise.all(resolvedRegistry.registrations.map(async (registration) => ({
10142
+ matches: await registrationMatches(registration, registryInput),
10143
+ priority: registration.priority ?? 0,
10144
+ registration
10145
+ })))).filter((entry) => entry.matches).sort((left, right) => right.priority - left.priority).map((entry) => ({
10146
+ extractor: entry.registration.extractor,
10147
+ registryMatchName: entry.registration.name ?? entry.registration.extractor.name
10148
+ }));
10149
+ const defaults = resolvedRegistry.includeDefaults ? DEFAULT_FILE_EXTRACTORS.map((extractor) => ({ extractor })) : [];
10150
+ const explicitResolved = explicit.map((extractor) => ({ extractor }));
10151
+ return dedupeResolvedExtractors(resolvedRegistry.defaultOrder === "defaults_first" ? [...explicitResolved, ...defaults, ...matchedRegistrations] : [...explicitResolved, ...matchedRegistrations, ...defaults]);
10152
+ };
10153
+ var applyExtractorDefaults = (document, input, extractorName, registryMatchName) => ({
10016
10154
  chunking: document.chunking ?? input.chunking,
10017
10155
  format: document.format ?? input.format ?? inferFormatFromContentType(document.contentType ?? input.contentType ?? null) ?? inferFormatFromName(document.source ?? input.source ?? input.path ?? input.name),
10018
10156
  id: document.id,
@@ -10020,20 +10158,22 @@ var applyExtractorDefaults = (document, input, extractorName) => ({
10020
10158
  ...input.metadata ?? {},
10021
10159
  ...document.metadata ?? {},
10022
10160
  contentType: document.contentType ?? input.contentType,
10023
- extractor: document.extractor ?? extractorName
10161
+ extractor: document.extractor ?? extractorName,
10162
+ ...registryMatchName ? { extractorRegistryMatch: registryMatchName } : {}
10024
10163
  },
10025
10164
  source: document.source ?? input.source ?? input.path ?? input.name,
10026
10165
  text: document.text,
10027
10166
  title: document.title ?? input.title
10028
10167
  });
10029
- var extractRAGFileDocuments = async (input, extractors) => {
10030
- for (const extractor of resolveFileExtractors(extractors)) {
10168
+ var extractRAGFileDocuments = async (input, extractors, registry) => {
10169
+ for (const resolvedExtractor of await resolveFileExtractors(input, extractors, registry)) {
10170
+ const { extractor, registryMatchName } = resolvedExtractor;
10031
10171
  if (!await extractor.supports(input)) {
10032
10172
  continue;
10033
10173
  }
10034
10174
  const extracted = await extractor.extract(input);
10035
10175
  const documents = Array.isArray(extracted) ? extracted : [extracted];
10036
- return documents.map((document) => applyExtractorDefaults(document, input, extractor.name));
10176
+ return documents.map((document) => applyExtractorDefaults(document, input, extractor.name, registryMatchName));
10037
10177
  }
10038
10178
  throw new Error(`No RAG file extractor matched ${inferNameFromInput(input)}. Register a custom extractor for this file type.`);
10039
10179
  };
@@ -10044,7 +10184,7 @@ var getFirstExtractedDocument = (documents, label) => {
10044
10184
  }
10045
10185
  return document;
10046
10186
  };
10047
- var loadExtractedDocuments = async (input, extractors) => extractRAGFileDocuments(input, extractors);
10187
+ var loadExtractedDocuments = async (input, extractors, registry) => extractRAGFileDocuments(input, extractors, registry);
10048
10188
  var sentenceUnits = (text) => {
10049
10189
  const matches = text.match(/[^.!?\n]+(?:[.!?]+|$)/g);
10050
10190
  if (!matches) {
@@ -10241,11 +10381,92 @@ var resolveChunkingUnits = (text, options) => {
10241
10381
  }
10242
10382
  return paragraphUnits(text);
10243
10383
  };
10244
- var resolveChunkingOptions = (document, defaults) => {
10245
- const maxChunkLength = document.chunking?.maxChunkLength ?? defaults?.maxChunkLength ?? DEFAULT_MAX_CHUNK_LENGTH;
10246
- const chunkOverlap = document.chunking?.chunkOverlap ?? defaults?.chunkOverlap ?? DEFAULT_CHUNK_OVERLAP;
10247
- const minChunkLength = document.chunking?.minChunkLength ?? defaults?.minChunkLength ?? DEFAULT_MIN_CHUNK_LENGTH;
10248
- const strategy = document.chunking?.strategy ?? defaults?.strategy ?? DEFAULT_STRATEGY;
10384
+ var resolveChunkingProfiles = (registry) => {
10385
+ if (!registry) {
10386
+ return [];
10387
+ }
10388
+ const profiles = Array.isArray(registry) ? registry : registry.profiles;
10389
+ return profiles.map((profile, index) => normalizeChunkingProfile(profile, index)).sort((left, right) => right.priority - left.priority).map(({ profile }) => profile);
10390
+ };
10391
+ var normalizeChunkingProfile = (profile, index) => {
10392
+ if ("resolve" in profile) {
10393
+ return {
10394
+ priority: 0,
10395
+ profile
10396
+ };
10397
+ }
10398
+ const options = normalizeChunkingProfileOptions(profile.profile);
10399
+ const sourceSet = profile.sources?.filter((value) => typeof value === "string") ?? [];
10400
+ const documentIdSet = profile.documentIds?.filter((value) => typeof value === "string") ?? [];
10401
+ const formatSet = profile.formats?.filter((value) => typeof value === "string") ?? [];
10402
+ const sourceNativeKindSet = profile.sourceNativeKinds?.filter((value) => typeof value === "string") ?? [];
10403
+ return {
10404
+ priority: profile.priority ?? 0,
10405
+ profile: {
10406
+ name: profile.name ?? `chunking_profile_${String(index + 1).padStart(2, "0")}`,
10407
+ resolve: (input) => {
10408
+ const documentId = input.document.id?.trim() || (typeof input.metadata.documentId === "string" ? input.metadata.documentId : undefined);
10409
+ const source = input.document.source?.trim();
10410
+ if (formatSet.length > 0 && !formatSet.includes(input.format)) {
10411
+ return;
10412
+ }
10413
+ if (sourceNativeKindSet.length > 0 && (!input.sourceNativeKind || !sourceNativeKindSet.includes(input.sourceNativeKind))) {
10414
+ return;
10415
+ }
10416
+ if (sourceSet.length > 0 && (!source || !sourceSet.includes(source))) {
10417
+ return;
10418
+ }
10419
+ if (documentIdSet.length > 0 && (!documentId || !documentIdSet.includes(documentId))) {
10420
+ return;
10421
+ }
10422
+ return options;
10423
+ }
10424
+ }
10425
+ };
10426
+ };
10427
+ var normalizeChunkingProfileOptions = (profile) => isChunkingProfileOptionsWrapper(profile) ? profile.options ?? {} : profile;
10428
+ var isChunkingProfileOptionsWrapper = (profile) => Object.prototype.hasOwnProperty.call(profile, "options");
10429
+ var resolveChunkingProfileOverrides = ({
10430
+ defaults,
10431
+ document,
10432
+ format,
10433
+ normalizedText,
10434
+ registry
10435
+ }) => {
10436
+ const metadata = document.metadata ?? {};
10437
+ const sourceNativeKind = typeof metadata.sourceNativeKind === "string" ? metadata.sourceNativeKind : undefined;
10438
+ for (const profile of resolveChunkingProfiles(registry)) {
10439
+ const options = profile.resolve({
10440
+ defaults,
10441
+ document,
10442
+ format,
10443
+ metadata,
10444
+ normalizedText,
10445
+ sourceNativeKind
10446
+ });
10447
+ if (options) {
10448
+ return {
10449
+ name: profile.name,
10450
+ options
10451
+ };
10452
+ }
10453
+ }
10454
+ return;
10455
+ };
10456
+ var resolveChunkingOptions = (document, defaults, registry, format, normalizedText) => {
10457
+ const resolvedFormat = format ?? inferFormat(document);
10458
+ const resolvedNormalizedText = normalizedText ?? normalizeDocumentText(document.text, resolvedFormat);
10459
+ const profileOverrides = resolveChunkingProfileOverrides({
10460
+ defaults,
10461
+ document,
10462
+ format: resolvedFormat,
10463
+ normalizedText: resolvedNormalizedText,
10464
+ registry
10465
+ });
10466
+ const maxChunkLength = document.chunking?.maxChunkLength ?? profileOverrides?.options.maxChunkLength ?? defaults?.maxChunkLength ?? DEFAULT_MAX_CHUNK_LENGTH;
10467
+ const chunkOverlap = document.chunking?.chunkOverlap ?? profileOverrides?.options.chunkOverlap ?? defaults?.chunkOverlap ?? DEFAULT_CHUNK_OVERLAP;
10468
+ const minChunkLength = document.chunking?.minChunkLength ?? profileOverrides?.options.minChunkLength ?? defaults?.minChunkLength ?? DEFAULT_MIN_CHUNK_LENGTH;
10469
+ const strategy = document.chunking?.strategy ?? profileOverrides?.options.strategy ?? defaults?.strategy ?? DEFAULT_STRATEGY;
10249
10470
  return {
10250
10471
  chunkOverlap: Math.max(0, Math.min(chunkOverlap, maxChunkLength - 1)),
10251
10472
  maxChunkLength: Math.max(RAG_MIN_CHUNK_LENGTH_FLOOR, maxChunkLength),
@@ -10263,10 +10484,17 @@ var createChunkEntries = (document, format, text, options) => {
10263
10484
  const units = resolveChunkingUnits(text, options);
10264
10485
  return chunkFromUnits(units, options.maxChunkLength, options.chunkOverlap, options.minChunkLength).map((entry) => ({ text: entry }));
10265
10486
  };
10266
- var prepareRAGDocument = (document, defaultChunking) => {
10487
+ var prepareRAGDocument = (document, defaultChunking, chunkingRegistry) => {
10267
10488
  const format = inferFormat(document);
10268
10489
  const normalizedText = normalizeDocumentText(document.text, format);
10269
- const chunking = resolveChunkingOptions(document, defaultChunking);
10490
+ const chunkingProfileOverrides = resolveChunkingProfileOverrides({
10491
+ defaults: defaultChunking,
10492
+ document,
10493
+ format,
10494
+ normalizedText,
10495
+ registry: chunkingRegistry
10496
+ });
10497
+ const chunking = resolveChunkingOptions(document, defaultChunking, chunkingRegistry, format, normalizedText);
10270
10498
  const documentId = document.id?.trim() || slugify(document.source || document.title || normalizedText.slice(0, RAG_DOCUMENT_ID_PREVIEW_LENGTH));
10271
10499
  const title = document.title?.trim() || documentId;
10272
10500
  let sourceExtension = "txt";
@@ -10276,9 +10504,12 @@ var prepareRAGDocument = (document, defaultChunking) => {
10276
10504
  sourceExtension = "html";
10277
10505
  }
10278
10506
  const source = document.source?.trim() || `${documentId}.${sourceExtension}`;
10507
+ const corpusKey = document.corpusKey?.trim() || (typeof document.metadata?.corpusKey === "string" ? document.metadata.corpusKey.trim() : undefined) || undefined;
10279
10508
  const metadata = {
10280
10509
  ...document.metadata ?? {},
10510
+ ...corpusKey ? { corpusKey } : {},
10281
10511
  documentId,
10512
+ ...chunkingProfileOverrides?.name ? { chunkingProfile: chunkingProfileOverrides.name } : {},
10282
10513
  format,
10283
10514
  source,
10284
10515
  title
@@ -10302,6 +10533,7 @@ var prepareRAGDocument = (document, defaultChunking) => {
10302
10533
  const nextChunkId = index + 1 < chunkEntries.length ? `${documentId}:${String(index + 2).padStart(RAG_CHUNK_ID_PAD_LENGTH, "0")}` : undefined;
10303
10534
  return {
10304
10535
  chunkId: `${documentId}:${String(index + 1).padStart(RAG_CHUNK_ID_PAD_LENGTH, "0")}`,
10536
+ ...corpusKey ? { corpusKey } : {},
10305
10537
  metadata: {
10306
10538
  ...metadata,
10307
10539
  chunkCount: chunkEntries.length,
@@ -10324,6 +10556,7 @@ var prepareRAGDocument = (document, defaultChunking) => {
10324
10556
  };
10325
10557
  });
10326
10558
  return {
10559
+ ...corpusKey ? { corpusKey } : {},
10327
10560
  chunks,
10328
10561
  documentId,
10329
10562
  format,
@@ -10333,7 +10566,7 @@ var prepareRAGDocument = (document, defaultChunking) => {
10333
10566
  title
10334
10567
  };
10335
10568
  };
10336
- var prepareRAGDocuments = (input) => input.documents.map((document) => prepareRAGDocument(document, input.defaultChunking));
10569
+ var prepareRAGDocuments = (input) => input.documents.map((document) => prepareRAGDocument(document, input.defaultChunking, input.chunkingRegistry));
10337
10570
  var mergeMetadata = (inputMetadata, extraMetadata, baseMetadata) => ({
10338
10571
  ...baseMetadata ?? {},
10339
10572
  ...inputMetadata ?? {},
@@ -10348,12 +10581,13 @@ var loadRAGDocumentFile = async (input) => {
10348
10581
  chunking: input.chunking,
10349
10582
  contentType: input.contentType,
10350
10583
  data,
10584
+ extractorRegistry: input.extractorRegistry,
10351
10585
  format: input.format,
10352
10586
  metadata: input.metadata,
10353
10587
  path: input.path,
10354
10588
  source: input.source,
10355
10589
  title: input.title
10356
- }, input.extractors);
10590
+ }, input.extractors, input.extractorRegistry);
10357
10591
  return getFirstExtractedDocument(documents, "for file input");
10358
10592
  };
10359
10593
  var loadRAGDocumentFromURL = async (input) => {
@@ -10370,12 +10604,13 @@ var loadRAGDocumentFromURL = async (input) => {
10370
10604
  chunking: input.chunking,
10371
10605
  contentType: input.contentType ?? response.headers.get("content-type") ?? undefined,
10372
10606
  data,
10607
+ extractorRegistry: input.extractorRegistry,
10373
10608
  format: input.format ?? inferFormatFromUrl(url),
10374
10609
  metadata: input.metadata,
10375
10610
  name: basename(new URL(url).pathname),
10376
10611
  source: input.source ?? url,
10377
10612
  title: input.title
10378
- }, input.extractors);
10613
+ }, input.extractors, input.extractorRegistry);
10379
10614
  return getFirstExtractedDocument(documents, "for URL input");
10380
10615
  };
10381
10616
  var loadRAGDocumentsFromUploads = async (input) => {
@@ -10384,12 +10619,13 @@ var loadRAGDocumentsFromUploads = async (input) => {
10384
10619
  chunking: upload.chunking,
10385
10620
  contentType: upload.contentType,
10386
10621
  data: decodeUploadContent(upload),
10622
+ extractorRegistry: input.extractorRegistry,
10387
10623
  format: upload.format,
10388
10624
  metadata: upload.metadata,
10389
10625
  name: upload.name,
10390
10626
  source: upload.source ?? upload.name,
10391
10627
  title: upload.title
10392
- }, input.extractors);
10628
+ }, input.extractors, input.extractorRegistry);
10393
10629
  return loaded.map((document) => ({
10394
10630
  ...document,
10395
10631
  metadata: mergeMetadata(document.metadata, { uploadFile: upload.name }, input.baseMetadata)
@@ -10397,6 +10633,7 @@ var loadRAGDocumentsFromUploads = async (input) => {
10397
10633
  }));
10398
10634
  return {
10399
10635
  defaultChunking: input.defaultChunking,
10636
+ chunkingRegistry: input.chunkingRegistry,
10400
10637
  documents: documents.flat()
10401
10638
  };
10402
10639
  };
@@ -10415,12 +10652,13 @@ var loadRAGDocumentsFromURLs = async (input) => {
10415
10652
  chunking: urlInput.chunking,
10416
10653
  contentType: urlInput.contentType ?? response.headers.get("content-type") ?? undefined,
10417
10654
  data,
10655
+ extractorRegistry: urlInput.extractorRegistry ?? input.extractorRegistry,
10418
10656
  format: urlInput.format ?? inferFormatFromUrl(url),
10419
10657
  metadata: urlInput.metadata,
10420
10658
  name: basename(new URL(url).pathname),
10421
10659
  source: urlInput.source ?? url,
10422
10660
  title: urlInput.title
10423
- }, urlInput.extractors ?? input.extractors);
10661
+ }, urlInput.extractors ?? input.extractors, urlInput.extractorRegistry ?? input.extractorRegistry);
10424
10662
  return loaded.map((document) => ({
10425
10663
  ...document,
10426
10664
  metadata: mergeMetadata(document.metadata, { sourceUrl: urlInput.url }, input.baseMetadata)
@@ -10428,6 +10666,7 @@ var loadRAGDocumentsFromURLs = async (input) => {
10428
10666
  }));
10429
10667
  return {
10430
10668
  defaultChunking: input.defaultChunking,
10669
+ chunkingRegistry: input.chunkingRegistry,
10431
10670
  documents: documents.flat()
10432
10671
  };
10433
10672
  };
@@ -10436,15 +10675,16 @@ var loadRAGDocumentUpload = async (input) => {
10436
10675
  chunking: input.chunking,
10437
10676
  contentType: input.contentType,
10438
10677
  data: decodeUploadContent(input),
10678
+ extractorRegistry: input.extractorRegistry,
10439
10679
  format: input.format,
10440
10680
  metadata: input.metadata,
10441
10681
  name: input.name,
10442
10682
  source: input.source ?? input.name,
10443
10683
  title: input.title
10444
- }, input.extractors);
10684
+ }, input.extractors, input.extractorRegistry);
10445
10685
  return getFirstExtractedDocument(documents, "for upload input");
10446
10686
  };
10447
- var prepareRAGDocumentFile = async (input, defaultChunking) => prepareRAGDocument(await loadRAGDocumentFile(input), defaultChunking);
10687
+ var prepareRAGDocumentFile = async (input, defaultChunking, chunkingRegistry) => prepareRAGDocument(await loadRAGDocumentFile(input), defaultChunking, chunkingRegistry);
10448
10688
  var DEFAULT_DIRECTORY_EXTENSIONS = [
10449
10689
  ".txt",
10450
10690
  ".md",
@@ -10504,7 +10744,7 @@ var buildRAGUpsertInputFromUploads = async (input) => ({
10504
10744
  });
10505
10745
  var loadRAGDocumentsFromDirectory = async (input) => {
10506
10746
  const root = resolve(input.directory);
10507
- const includeExtensions = input.includeExtensions === undefined && input.extractors?.length ? null : new Set((input.includeExtensions ?? DEFAULT_DIRECTORY_EXTENSIONS).map((entry) => entry.startsWith(".") ? entry.toLowerCase() : `.${entry.toLowerCase()}`));
10747
+ const includeExtensions = input.includeExtensions === undefined && (input.extractors?.length || input.extractorRegistry) ? null : new Set((input.includeExtensions ?? DEFAULT_DIRECTORY_EXTENSIONS).map((entry) => entry.startsWith(".") ? entry.toLowerCase() : `.${entry.toLowerCase()}`));
10508
10748
  const files = await collectDirectoryFiles(root, input.recursive !== false, includeExtensions);
10509
10749
  const documents = await Promise.all(files.map(async (path) => {
10510
10750
  const source = relative(root, path).replace(/\\/g, "/");
@@ -10512,13 +10752,14 @@ var loadRAGDocumentsFromDirectory = async (input) => {
10512
10752
  const loaded = await loadExtractedDocuments({
10513
10753
  chunking: input.defaultChunking,
10514
10754
  data,
10755
+ extractorRegistry: input.extractorRegistry,
10515
10756
  metadata: {
10516
10757
  fileName: basename(path),
10517
10758
  relativePath: source
10518
10759
  },
10519
10760
  path,
10520
10761
  source
10521
- }, input.extractors);
10762
+ }, input.extractors, input.extractorRegistry);
10522
10763
  return loaded.map((document) => ({
10523
10764
  ...document,
10524
10765
  metadata: mergeMetadata(document.metadata, undefined, input.baseMetadata)
@@ -10526,6 +10767,7 @@ var loadRAGDocumentsFromDirectory = async (input) => {
10526
10767
  }));
10527
10768
  return {
10528
10769
  defaultChunking: input.defaultChunking,
10770
+ chunkingRegistry: input.chunkingRegistry,
10529
10771
  documents: documents.flat()
10530
10772
  };
10531
10773
  };
@@ -10752,6 +10994,19 @@ var annotateRetrievalQueryOrigin = (input) => {
10752
10994
  };
10753
10995
  var shouldRunVectorRetrieval = (mode) => mode === "vector" || mode === "hybrid";
10754
10996
  var shouldRunLexicalRetrieval = (mode, store) => mode === "lexical" || mode === "hybrid" && Boolean(store.queryLexical);
10997
+ var resolveRAGRetrievalStrategy = (retrievalStrategy) => {
10998
+ if (!retrievalStrategy) {
10999
+ return null;
11000
+ }
11001
+ if (typeof retrievalStrategy === "function") {
11002
+ return {
11003
+ defaultLabel: undefined,
11004
+ providerName: undefined,
11005
+ select: retrievalStrategy
11006
+ };
11007
+ }
11008
+ return retrievalStrategy;
11009
+ };
10755
11010
  var createRAGCollection = (options) => {
10756
11011
  const defaultTopK = options.defaultTopK ?? DEFAULT_TOP_K2;
10757
11012
  const defaultCandidateMultiplier = Math.max(1, Math.floor(options.defaultCandidateMultiplier ?? 4));
@@ -10768,10 +11023,11 @@ var createRAGCollection = (options) => {
10768
11023
  const model = input.model ?? options.defaultModel;
10769
11024
  const topK = input.topK ?? defaultTopK;
10770
11025
  const hasReranker = Boolean(input.rerank ?? options.rerank);
10771
- const retrieval = resolveRAGHybridSearchOptions(input.retrieval);
11026
+ const requestedRetrieval = resolveRAGHybridSearchOptions(input.retrieval);
10772
11027
  const hasQueryTransform = Boolean(input.queryTransform ?? options.queryTransform);
10773
- const shouldExpandCandidates = hasReranker || hasQueryTransform || retrieval.mode !== "vector";
11028
+ const shouldExpandCandidates = hasReranker || hasQueryTransform || requestedRetrieval.mode !== "vector";
10774
11029
  const candidateTopK = Math.max(topK, Math.floor(input.candidateTopK ?? (shouldExpandCandidates ? topK * defaultCandidateMultiplier : topK)));
11030
+ const resolvedQueryTransform = resolveRAGQueryTransform(input.queryTransform ?? options.queryTransform);
10775
11031
  const transformed = await applyRAGQueryTransform({
10776
11032
  input: {
10777
11033
  candidateTopK,
@@ -10781,9 +11037,25 @@ var createRAGCollection = (options) => {
10781
11037
  scoreThreshold: input.scoreThreshold,
10782
11038
  topK
10783
11039
  },
10784
- queryTransform: input.queryTransform ?? options.queryTransform
11040
+ queryTransform: resolvedQueryTransform ?? undefined
10785
11041
  });
10786
11042
  const searchQueries = Array.from(new Set([transformed.query, ...transformed.variants ?? []])).filter(Boolean);
11043
+ const resolvedRetrievalStrategy = resolveRAGRetrievalStrategy(input.retrievalStrategy ?? options.retrievalStrategy);
11044
+ const retrievalDecision = resolvedRetrievalStrategy ? await Promise.resolve(resolvedRetrievalStrategy.select({
11045
+ candidateTopK,
11046
+ filter: input.filter,
11047
+ model,
11048
+ query: input.query,
11049
+ retrieval: requestedRetrieval,
11050
+ scoreThreshold: input.scoreThreshold,
11051
+ topK,
11052
+ transformedQuery: transformed.query,
11053
+ variantQueries: searchQueries.slice(1)
11054
+ })) : undefined;
11055
+ const retrieval = retrievalDecision ? {
11056
+ ...requestedRetrieval,
11057
+ ...retrievalDecision
11058
+ } : requestedRetrieval;
10787
11059
  const runVector = shouldRunVectorRetrieval(retrieval.mode);
10788
11060
  const runLexical = shouldRunLexicalRetrieval(retrieval.mode, options.store);
10789
11061
  const lexicalTopK = Math.max(topK, Math.floor(retrieval.lexicalTopK ?? candidateTopK));
@@ -10826,12 +11098,31 @@ var createRAGCollection = (options) => {
10826
11098
  steps.push({
10827
11099
  label: "Expanded query variants",
10828
11100
  metadata: {
11101
+ label: transformed.label ?? resolvedQueryTransform?.providerName ?? null,
11102
+ providerName: resolvedQueryTransform?.providerName ?? null,
11103
+ reason: transformed.reason ?? null,
10829
11104
  transformedQuery: transformed.query,
10830
- variantCount: Math.max(0, searchQueries.length - 1)
11105
+ variantCount: Math.max(0, searchQueries.length - 1),
11106
+ ...transformed.metadata ?? {}
10831
11107
  },
10832
11108
  stage: "query_transform"
10833
11109
  });
10834
11110
  }
11111
+ if (retrievalDecision || requestedRetrieval.mode !== retrieval.mode || requestedRetrieval.lexicalTopK !== retrieval.lexicalTopK) {
11112
+ steps.push({
11113
+ label: retrievalDecision?.label ?? "Selected retrieval strategy",
11114
+ metadata: {
11115
+ applied: Boolean(retrievalDecision),
11116
+ label: retrievalDecision?.label ?? resolvedRetrievalStrategy?.defaultLabel ?? null,
11117
+ providerName: resolvedRetrievalStrategy?.providerName ?? null,
11118
+ reason: retrievalDecision?.reason ?? null,
11119
+ requestedMode: requestedRetrieval.mode,
11120
+ selectedMode: retrieval.mode,
11121
+ ...retrievalDecision?.metadata ?? {}
11122
+ },
11123
+ stage: "routing"
11124
+ });
11125
+ }
10835
11126
  const resultGroups = await Promise.all(searchQueries.map(async (query, queryIndex) => {
10836
11127
  const [vectorResults2, lexicalResults2] = await Promise.all([
10837
11128
  runVector ? embed({
@@ -10990,6 +11281,9 @@ var createRAGCollection = (options) => {
10990
11281
  mode: retrieval.mode,
10991
11282
  diversityStrategy: retrieval.diversityStrategy,
10992
11283
  query: input.query,
11284
+ queryTransformLabel: transformed.label,
11285
+ queryTransformProvider: resolvedQueryTransform?.providerName,
11286
+ queryTransformReason: transformed.reason,
10993
11287
  resultCounts: {
10994
11288
  final: limited.length,
10995
11289
  fused: results.length,
@@ -10997,8 +11291,12 @@ var createRAGCollection = (options) => {
10997
11291
  reranked: diversified.length,
10998
11292
  vector: vectorResults.length
10999
11293
  },
11294
+ requestedMode: requestedRetrieval.mode,
11000
11295
  runLexical,
11001
11296
  runVector,
11297
+ routingLabel: retrievalDecision?.label ?? resolvedRetrievalStrategy?.defaultLabel,
11298
+ routingProvider: resolvedRetrievalStrategy?.providerName,
11299
+ routingReason: retrievalDecision?.reason,
11002
11300
  sourceBalanceStrategy: retrieval.sourceBalanceStrategy,
11003
11301
  steps,
11004
11302
  topK,
@@ -11039,6 +11337,9 @@ var createRAGCollection = (options) => {
11039
11337
  mode: retrieval.mode,
11040
11338
  diversityStrategy: retrieval.diversityStrategy,
11041
11339
  query: input.query,
11340
+ queryTransformLabel: transformed.label,
11341
+ queryTransformProvider: resolvedQueryTransform?.providerName,
11342
+ queryTransformReason: transformed.reason,
11042
11343
  resultCounts: {
11043
11344
  final: filtered.length,
11044
11345
  fused: results.length,
@@ -11046,8 +11347,12 @@ var createRAGCollection = (options) => {
11046
11347
  reranked: diversified.length,
11047
11348
  vector: vectorResults.length
11048
11349
  },
11350
+ requestedMode: requestedRetrieval.mode,
11049
11351
  runLexical,
11050
11352
  runVector,
11353
+ routingLabel: retrievalDecision?.label ?? resolvedRetrievalStrategy?.defaultLabel,
11354
+ routingProvider: resolvedRetrievalStrategy?.providerName,
11355
+ routingReason: retrievalDecision?.reason,
11051
11356
  scoreThreshold,
11052
11357
  sourceBalanceStrategy: retrieval.sourceBalanceStrategy,
11053
11358
  steps,
@@ -11677,6 +11982,7 @@ var isRAGDocumentUrlArray = (value) => Array.isArray(value) && value.every((entr
11677
11982
  var isRAGDocumentChunkArray = (value) => Array.isArray(value) && value.every((entry) => isRAGDocumentChunk(entry));
11678
11983
  var buildSources2 = (results) => results.map((result) => ({
11679
11984
  chunkId: result.chunkId,
11985
+ corpusKey: result.corpusKey ?? (typeof result.metadata?.corpusKey === "string" ? result.metadata.corpusKey : undefined),
11680
11986
  labels: buildRAGSourceLabels({
11681
11987
  metadata: result.metadata,
11682
11988
  source: result.source,
@@ -11827,6 +12133,8 @@ var buildRAGContextFromQuery = async (config, topK, scoreThreshold, queryText, r
11827
12133
  };
11828
12134
  var ragChat = (config) => {
11829
12135
  const path = config.path ?? DEFAULT_PATH2;
12136
+ const authorizeRAGAction = config.authorizeRAGAction;
12137
+ const resolveRAGAccessScope = config.resolveRAGAccessScope;
11830
12138
  const topK = config.topK ?? DEFAULT_TOP_K3;
11831
12139
  const { scoreThreshold } = config;
11832
12140
  const { extractors } = config;
@@ -11841,6 +12149,13 @@ var ragChat = (config) => {
11841
12149
  const adminActions = [];
11842
12150
  const adminJobs = [];
11843
12151
  const syncJobs = [];
12152
+ const { jobStateStore } = config;
12153
+ const jobHistoryRetention = {
12154
+ maxAdminActions: config.jobHistoryRetention?.maxAdminActions ?? MAX_ADMIN_ACTIONS,
12155
+ maxAdminJobs: config.jobHistoryRetention?.maxAdminJobs ?? MAX_ADMIN_JOBS,
12156
+ maxIngestJobs: config.jobHistoryRetention?.maxIngestJobs ?? MAX_INGEST_JOBS,
12157
+ maxSyncJobs: config.jobHistoryRetention?.maxSyncJobs ?? MAX_ADMIN_JOBS
12158
+ };
11844
12159
  const { searchTraceStore } = config;
11845
12160
  const { searchTraceRetention } = config;
11846
12161
  const { searchTraceRetentionSchedule } = config;
@@ -11859,6 +12174,134 @@ var ragChat = (config) => {
11859
12174
  retention: searchTraceRetention,
11860
12175
  schedule: searchTraceRetentionSchedule
11861
12176
  };
12177
+ let jobStateLoaded = false;
12178
+ let jobStateLoadPromise;
12179
+ const normalizeAuthorizationDecision = (decision) => typeof decision === "boolean" ? { allowed: decision } : decision ?? { allowed: true };
12180
+ const checkAuthorization = async (request, action, resource) => {
12181
+ if (!authorizeRAGAction) {
12182
+ return { allowed: true };
12183
+ }
12184
+ return normalizeAuthorizationDecision(await authorizeRAGAction({
12185
+ action,
12186
+ request,
12187
+ resource
12188
+ }));
12189
+ };
12190
+ const isAuthorized = async (request, action, resource) => (await checkAuthorization(request, action, resource)).allowed;
12191
+ const buildAuthorizationFailure = (decision, fallback = "Forbidden") => ({
12192
+ error: decision.reason ?? fallback,
12193
+ ok: false
12194
+ });
12195
+ const isAccessScopeError = (error) => typeof error === "string" && (error.includes("allowed RAG access scope") || error.includes("Scoped sync-all is not allowed"));
12196
+ const authorizeMutationRoute = async (request, action, input) => {
12197
+ const decision = await checkAuthorization(request, action, input.resource);
12198
+ return decision.allowed ? null : buildAuthorizationFailure(decision, input.fallback);
12199
+ };
12200
+ const normalizeAccessScope = (scope) => {
12201
+ if (!scope) {
12202
+ return;
12203
+ }
12204
+ const normalizeStringArray3 = (values) => {
12205
+ const next = (values ?? []).map((value) => value.trim()).filter((value) => value.length > 0);
12206
+ return next.length > 0 ? [...new Set(next)] : undefined;
12207
+ };
12208
+ const requiredMetadata = scope.requiredMetadata && Object.keys(scope.requiredMetadata).length > 0 ? scope.requiredMetadata : undefined;
12209
+ return {
12210
+ allowedComparisonGroupKeys: normalizeStringArray3(scope.allowedComparisonGroupKeys),
12211
+ allowedCorpusKeys: normalizeStringArray3(scope.allowedCorpusKeys),
12212
+ allowedDocumentIds: normalizeStringArray3(scope.allowedDocumentIds),
12213
+ allowedSourcePrefixes: normalizeStringArray3(scope.allowedSourcePrefixes),
12214
+ allowedSources: normalizeStringArray3(scope.allowedSources),
12215
+ allowedSyncSourceIds: normalizeStringArray3(scope.allowedSyncSourceIds),
12216
+ requiredMetadata
12217
+ };
12218
+ };
12219
+ const loadAccessScope = async (request) => request && resolveRAGAccessScope ? normalizeAccessScope(await resolveRAGAccessScope(request)) : undefined;
12220
+ const matchesRequiredMetadata = (requiredMetadata, metadata) => {
12221
+ if (!requiredMetadata) {
12222
+ return true;
12223
+ }
12224
+ return Object.entries(requiredMetadata).every(([key, value]) => metadata?.[key] === value);
12225
+ };
12226
+ const matchesAccessScope = (scope, input) => {
12227
+ if (!scope) {
12228
+ return true;
12229
+ }
12230
+ const corpusKey = input.corpusKey ?? (typeof input.metadata?.corpusKey === "string" ? input.metadata.corpusKey : undefined);
12231
+ if (scope.allowedCorpusKeys?.length && (!corpusKey || !scope.allowedCorpusKeys.includes(corpusKey))) {
12232
+ return false;
12233
+ }
12234
+ if (scope.allowedDocumentIds?.length && (!input.documentId || !scope.allowedDocumentIds.includes(input.documentId))) {
12235
+ return false;
12236
+ }
12237
+ if (scope.allowedSources?.length && (!input.source || !scope.allowedSources.includes(input.source))) {
12238
+ return false;
12239
+ }
12240
+ if (scope.allowedSourcePrefixes?.length && (!input.source || !scope.allowedSourcePrefixes.some((prefix) => (input.source ?? "").startsWith(prefix)))) {
12241
+ return false;
12242
+ }
12243
+ return matchesRequiredMetadata(scope.requiredMetadata, input.metadata);
12244
+ };
12245
+ const matchesSyncSourceScope = (scope, source) => !scope?.allowedSyncSourceIds?.length || scope.allowedSyncSourceIds.includes(source.id);
12246
+ const isAllowedComparisonGroupKey = (scope, groupKey) => !scope?.allowedComparisonGroupKeys?.length || typeof groupKey === "string" && scope.allowedComparisonGroupKeys.includes(groupKey);
12247
+ const filterByComparisonGroupKey = (scope, records) => scope?.allowedComparisonGroupKeys?.length ? records.filter((record) => isAllowedComparisonGroupKey(scope, record.groupKey)) : records;
12248
+ const persistJobStateIfConfigured = async () => {
12249
+ if (!jobStateStore) {
12250
+ return;
12251
+ }
12252
+ await jobStateStore.save({
12253
+ adminActions: adminActions.slice(0, Math.max(0, jobHistoryRetention.maxAdminActions)),
12254
+ adminJobs: adminJobs.slice(0, Math.max(0, jobHistoryRetention.maxAdminJobs)),
12255
+ ingestJobs: ingestJobs.slice(0, Math.max(0, jobHistoryRetention.maxIngestJobs)),
12256
+ syncJobs: syncJobs.slice(0, Math.max(0, jobHistoryRetention.maxSyncJobs))
12257
+ });
12258
+ };
12259
+ const recoverIngestJob = (job, recoveredAt) => job.status === "running" ? {
12260
+ ...job,
12261
+ elapsedMs: Math.max(0, recoveredAt - job.startedAt),
12262
+ error: job.error ?? "Interrupted before completion during recovery",
12263
+ finishedAt: recoveredAt,
12264
+ status: "failed"
12265
+ } : job;
12266
+ const recoverAdminJob = (job, recoveredAt) => job.status === "running" ? {
12267
+ ...job,
12268
+ elapsedMs: Math.max(0, recoveredAt - job.startedAt),
12269
+ error: job.error ?? "Interrupted before completion during recovery",
12270
+ finishedAt: recoveredAt,
12271
+ status: "failed"
12272
+ } : job;
12273
+ const ensureJobStateLoaded = async () => {
12274
+ if (jobStateLoaded) {
12275
+ return;
12276
+ }
12277
+ if (jobStateLoadPromise) {
12278
+ await jobStateLoadPromise;
12279
+ return;
12280
+ }
12281
+ jobStateLoadPromise = (async () => {
12282
+ if (!jobStateStore) {
12283
+ jobStateLoaded = true;
12284
+ return;
12285
+ }
12286
+ const loaded = await jobStateStore.load();
12287
+ const recoveredAt = Date.now();
12288
+ const nextIngestJobs = (loaded?.ingestJobs ?? []).map((job) => recoverIngestJob(job, recoveredAt)).slice(0, Math.max(0, jobHistoryRetention.maxIngestJobs));
12289
+ const nextAdminActions = (loaded?.adminActions ?? []).slice(0, Math.max(0, jobHistoryRetention.maxAdminActions));
12290
+ const nextAdminJobs = (loaded?.adminJobs ?? []).map((job) => recoverAdminJob(job, recoveredAt)).slice(0, Math.max(0, jobHistoryRetention.maxAdminJobs));
12291
+ const nextSyncJobs = (loaded?.syncJobs ?? []).map((job) => recoverAdminJob(job, recoveredAt)).slice(0, Math.max(0, jobHistoryRetention.maxSyncJobs));
12292
+ adminActions.splice(0, adminActions.length, ...nextAdminActions);
12293
+ ingestJobs.splice(0, ingestJobs.length, ...nextIngestJobs);
12294
+ adminJobs.splice(0, adminJobs.length, ...nextAdminJobs);
12295
+ syncJobs.splice(0, syncJobs.length, ...nextSyncJobs);
12296
+ jobStateLoaded = true;
12297
+ await persistJobStateIfConfigured();
12298
+ })();
12299
+ try {
12300
+ await jobStateLoadPromise;
12301
+ } finally {
12302
+ jobStateLoadPromise = undefined;
12303
+ }
12304
+ };
11862
12305
  const createIngestJob = (inputKind, requestedCount) => {
11863
12306
  const job = {
11864
12307
  id: generateId(),
@@ -11868,9 +12311,10 @@ var ragChat = (config) => {
11868
12311
  status: "running"
11869
12312
  };
11870
12313
  ingestJobs.unshift(job);
11871
- if (ingestJobs.length > MAX_INGEST_JOBS) {
11872
- ingestJobs.length = MAX_INGEST_JOBS;
12314
+ if (ingestJobs.length > jobHistoryRetention.maxIngestJobs) {
12315
+ ingestJobs.length = jobHistoryRetention.maxIngestJobs;
11873
12316
  }
12317
+ persistJobStateIfConfigured();
11874
12318
  return job;
11875
12319
  };
11876
12320
  const completeIngestJob = (job, input) => {
@@ -11881,6 +12325,7 @@ var ragChat = (config) => {
11881
12325
  job.chunkCount = input.chunkCount;
11882
12326
  job.documentCount = input.documentCount;
11883
12327
  job.extractorNames = input.extractorNames;
12328
+ persistJobStateIfConfigured();
11884
12329
  };
11885
12330
  const failIngestJob = (job, error, extractorNames) => {
11886
12331
  const finishedAt = Date.now();
@@ -11889,6 +12334,7 @@ var ragChat = (config) => {
11889
12334
  job.elapsedMs = finishedAt - job.startedAt;
11890
12335
  job.error = error;
11891
12336
  job.extractorNames = extractorNames;
12337
+ persistJobStateIfConfigured();
11892
12338
  };
11893
12339
  const createAdminAction = (action, documentId, target) => {
11894
12340
  const record = {
@@ -11900,9 +12346,10 @@ var ragChat = (config) => {
11900
12346
  target
11901
12347
  };
11902
12348
  adminActions.unshift(record);
11903
- if (adminActions.length > MAX_ADMIN_ACTIONS) {
11904
- adminActions.length = MAX_ADMIN_ACTIONS;
12349
+ if (adminActions.length > jobHistoryRetention.maxAdminActions) {
12350
+ adminActions.length = jobHistoryRetention.maxAdminActions;
11905
12351
  }
12352
+ persistJobStateIfConfigured();
11906
12353
  return record;
11907
12354
  };
11908
12355
  const createAdminJob = (action, target, bucket = adminJobs) => {
@@ -11914,9 +12361,11 @@ var ragChat = (config) => {
11914
12361
  target
11915
12362
  };
11916
12363
  bucket.unshift(job);
11917
- if (bucket.length > MAX_ADMIN_JOBS) {
11918
- bucket.length = MAX_ADMIN_JOBS;
12364
+ const maxJobs = bucket === syncJobs ? jobHistoryRetention.maxSyncJobs : jobHistoryRetention.maxAdminJobs;
12365
+ if (bucket.length > maxJobs) {
12366
+ bucket.length = maxJobs;
11919
12367
  }
12368
+ persistJobStateIfConfigured();
11920
12369
  return job;
11921
12370
  };
11922
12371
  const completeAdminAction = (record) => {
@@ -11924,6 +12373,7 @@ var ragChat = (config) => {
11924
12373
  record.status = "completed";
11925
12374
  record.finishedAt = finishedAt;
11926
12375
  record.elapsedMs = finishedAt - record.startedAt;
12376
+ persistJobStateIfConfigured();
11927
12377
  };
11928
12378
  const failAdminAction = (record, error) => {
11929
12379
  const finishedAt = Date.now();
@@ -11931,12 +12381,14 @@ var ragChat = (config) => {
11931
12381
  record.finishedAt = finishedAt;
11932
12382
  record.elapsedMs = finishedAt - record.startedAt;
11933
12383
  record.error = error;
12384
+ persistJobStateIfConfigured();
11934
12385
  };
11935
12386
  const completeAdminJob = (job) => {
11936
12387
  const finishedAt = Date.now();
11937
12388
  job.status = "completed";
11938
12389
  job.finishedAt = finishedAt;
11939
12390
  job.elapsedMs = finishedAt - job.startedAt;
12391
+ persistJobStateIfConfigured();
11940
12392
  };
11941
12393
  const failAdminJob = (job, error) => {
11942
12394
  const finishedAt = Date.now();
@@ -11944,8 +12396,10 @@ var ragChat = (config) => {
11944
12396
  job.finishedAt = finishedAt;
11945
12397
  job.elapsedMs = finishedAt - job.startedAt;
11946
12398
  job.error = error;
12399
+ persistJobStateIfConfigured();
11947
12400
  };
11948
12401
  const runSearchTracePrune = async (input, trigger = "manual") => {
12402
+ await ensureJobStateLoaded();
11949
12403
  if (!searchTraceStore) {
11950
12404
  throw new Error("RAG search trace store is not configured");
11951
12405
  }
@@ -12033,11 +12487,12 @@ var ragChat = (config) => {
12033
12487
  throw error;
12034
12488
  }
12035
12489
  };
12036
- const buildSyncSources = async () => {
12490
+ const buildSyncSources = async (scope) => {
12037
12491
  if (!indexManager?.listSyncSources) {
12038
12492
  return [];
12039
12493
  }
12040
- return await indexManager.listSyncSources();
12494
+ const sources = await indexManager.listSyncSources();
12495
+ return sources.filter((source) => matchesSyncSourceScope(scope, source));
12041
12496
  };
12042
12497
  const toHTMXResponse = (html, status, extraHeaders) => new Response(html, {
12043
12498
  headers: {
@@ -12201,6 +12656,7 @@ var ragChat = (config) => {
12201
12656
  return null;
12202
12657
  }
12203
12658
  return {
12659
+ corpusKey: getStringProperty(candidate, "corpusKey") ?? undefined,
12204
12660
  filter: caseFilter,
12205
12661
  id: getStringProperty(candidate, "id") ?? `case-${caseIndex + 1}`,
12206
12662
  retrieval: parsedCaseRetrieval === undefined || parsedCaseRetrieval === null ? undefined : parsedCaseRetrieval,
@@ -12238,7 +12694,7 @@ var ragChat = (config) => {
12238
12694
  scoreThreshold: typeof getNumberProperty(body, "scoreThreshold") === "number" ? getNumberProperty(body, "scoreThreshold") : undefined
12239
12695
  };
12240
12696
  };
12241
- const handleEvaluate = async (body) => {
12697
+ const handleEvaluate = async (body, request) => {
12242
12698
  const input = toRAGEvaluationInput(body);
12243
12699
  if (!input) {
12244
12700
  return {
@@ -12246,6 +12702,17 @@ var ragChat = (config) => {
12246
12702
  ok: false
12247
12703
  };
12248
12704
  }
12705
+ const accessScope = await loadAccessScope(request);
12706
+ for (const evaluationCase of input.cases) {
12707
+ if (evaluationCase.corpusKey && !matchesAccessScope(accessScope, {
12708
+ corpusKey: evaluationCase.corpusKey
12709
+ }) || (evaluationCase.expectedDocumentIds ?? []).some((documentId) => !matchesAccessScope(accessScope, { documentId })) || (evaluationCase.expectedSources ?? []).some((source) => !matchesAccessScope(accessScope, { source }))) {
12710
+ return {
12711
+ error: "Evaluation case is outside the allowed RAG access scope",
12712
+ ok: false
12713
+ };
12714
+ }
12715
+ }
12249
12716
  const collection = resolveCollection();
12250
12717
  if (!collection) {
12251
12718
  return {
@@ -12748,7 +13215,7 @@ var ragChat = (config) => {
12748
13215
  });
12749
13216
  return incidents.find((entry) => entry.id === incidentId);
12750
13217
  };
12751
- const handleEvaluateRetrievals = async (body) => {
13218
+ const handleEvaluateRetrievals = async (body, request) => {
12752
13219
  const input = toRAGRetrievalComparisonRequest(body);
12753
13220
  if (!input) {
12754
13221
  return {
@@ -12756,6 +13223,23 @@ var ragChat = (config) => {
12756
13223
  ok: false
12757
13224
  };
12758
13225
  }
13226
+ const accessScope = await loadAccessScope(request);
13227
+ if (!isAllowedComparisonGroupKey(accessScope, input.groupKey)) {
13228
+ return {
13229
+ error: "Retrieval comparison group is outside the allowed RAG access scope",
13230
+ ok: false
13231
+ };
13232
+ }
13233
+ for (const evaluationCase of input.cases) {
13234
+ if (evaluationCase.corpusKey && !matchesAccessScope(accessScope, {
13235
+ corpusKey: evaluationCase.corpusKey
13236
+ }) || (evaluationCase.expectedDocumentIds ?? []).some((documentId) => !matchesAccessScope(accessScope, { documentId })) || (evaluationCase.expectedSources ?? []).some((source) => !matchesAccessScope(accessScope, { source }))) {
13237
+ return {
13238
+ error: "Retrieval comparison case is outside the allowed RAG access scope",
13239
+ ok: false
13240
+ };
13241
+ }
13242
+ }
12759
13243
  const collection = resolveCollection();
12760
13244
  if (!collection) {
12761
13245
  return {
@@ -12802,6 +13286,7 @@ var ragChat = (config) => {
12802
13286
  await persistRAGRetrievalComparisonRun({
12803
13287
  run: {
12804
13288
  comparison,
13289
+ corpusKeys: comparison.corpusKeys,
12805
13290
  decisionSummary,
12806
13291
  elapsedMs: finishedAt - startedAt,
12807
13292
  finishedAt,
@@ -12825,15 +13310,23 @@ var ragChat = (config) => {
12825
13310
  ok: true
12826
13311
  };
12827
13312
  };
12828
- const handleRetrievalComparisonHistory = async (queryInput) => {
13313
+ const handleRetrievalComparisonHistory = async (queryInput, request) => {
12829
13314
  if (!retrievalComparisonHistoryStore) {
12830
13315
  return {
12831
13316
  error: "RAG retrieval comparison history store is not configured",
12832
13317
  ok: false
12833
13318
  };
12834
13319
  }
13320
+ const accessScope = await loadAccessScope(request);
13321
+ const groupKey = getStringProperty(queryInput, "groupKey");
13322
+ if (!isAllowedComparisonGroupKey(accessScope, groupKey)) {
13323
+ return {
13324
+ error: "Retrieval comparison group is outside the allowed RAG access scope",
13325
+ ok: false
13326
+ };
13327
+ }
12835
13328
  const runs = await loadRAGRetrievalComparisonHistory({
12836
- groupKey: getStringProperty(queryInput, "groupKey"),
13329
+ groupKey,
12837
13330
  label: getStringProperty(queryInput, "label"),
12838
13331
  limit: getIntegerLikeProperty(queryInput, "limit"),
12839
13332
  store: retrievalComparisonHistoryStore,
@@ -12843,25 +13336,33 @@ var ragChat = (config) => {
12843
13336
  });
12844
13337
  return {
12845
13338
  ok: true,
12846
- runs
13339
+ runs: filterByComparisonGroupKey(accessScope, runs)
12847
13340
  };
12848
13341
  };
12849
- const handleRetrievalBaselineList = async (queryInput) => {
13342
+ const handleRetrievalBaselineList = async (queryInput, request) => {
12850
13343
  if (!retrievalBaselineStore) {
12851
13344
  return {
12852
13345
  error: "RAG retrieval baseline store is not configured",
12853
13346
  ok: false
12854
13347
  };
12855
13348
  }
13349
+ const accessScope = await loadAccessScope(request);
13350
+ const groupKey = getStringProperty(queryInput, "groupKey");
13351
+ if (!isAllowedComparisonGroupKey(accessScope, groupKey)) {
13352
+ return {
13353
+ error: "Retrieval baseline group is outside the allowed RAG access scope",
13354
+ ok: false
13355
+ };
13356
+ }
12856
13357
  const baselines = await loadRAGRetrievalBaselines({
12857
- groupKey: getStringProperty(queryInput, "groupKey"),
13358
+ groupKey,
12858
13359
  limit: getIntegerLikeProperty(queryInput, "limit"),
12859
13360
  status: getStringProperty(queryInput, "status") === "active" || getStringProperty(queryInput, "status") === "superseded" ? getStringProperty(queryInput, "status") : undefined,
12860
13361
  store: retrievalBaselineStore,
12861
13362
  tag: getStringProperty(queryInput, "tag")
12862
13363
  });
12863
13364
  return {
12864
- baselines,
13365
+ baselines: filterByComparisonGroupKey(accessScope, baselines),
12865
13366
  ok: true
12866
13367
  };
12867
13368
  };
@@ -13574,7 +14075,7 @@ var ragChat = (config) => {
13574
14075
  });
13575
14076
  return baseline;
13576
14077
  };
13577
- const handlePromoteRetrievalBaseline = async (body) => {
14078
+ const handlePromoteRetrievalBaseline = async (body, request) => {
13578
14079
  if (!retrievalBaselineStore) {
13579
14080
  return {
13580
14081
  error: "RAG retrieval baseline store is not configured",
@@ -13671,7 +14172,7 @@ var ragChat = (config) => {
13671
14172
  };
13672
14173
  }
13673
14174
  };
13674
- const handlePromoteRetrievalBaselineFromRun = async (body) => {
14175
+ const handlePromoteRetrievalBaselineFromRun = async (body, request) => {
13675
14176
  if (!retrievalBaselineStore || !retrievalComparisonHistoryStore) {
13676
14177
  return {
13677
14178
  error: "RAG retrieval baseline store and retrieval comparison history store are required",
@@ -13771,7 +14272,7 @@ var ragChat = (config) => {
13771
14272
  };
13772
14273
  }
13773
14274
  };
13774
- const handleRevertRetrievalBaseline = async (body) => {
14275
+ const handleRevertRetrievalBaseline = async (body, request) => {
13775
14276
  if (!retrievalBaselineStore) {
13776
14277
  return {
13777
14278
  error: "RAG retrieval baseline store is not configured",
@@ -13846,24 +14347,32 @@ var ragChat = (config) => {
13846
14347
  };
13847
14348
  }
13848
14349
  };
13849
- const handleRetrievalReleaseDecisionList = async (queryInput) => {
14350
+ const handleRetrievalReleaseDecisionList = async (queryInput, request) => {
13850
14351
  if (!config.retrievalReleaseDecisionStore) {
13851
14352
  return {
13852
14353
  error: "RAG retrieval release decision store is not configured",
13853
14354
  ok: false
13854
14355
  };
13855
14356
  }
14357
+ const accessScope = await loadAccessScope(request);
14358
+ const groupKey = getStringProperty(queryInput, "groupKey");
14359
+ if (!isAllowedComparisonGroupKey(accessScope, groupKey)) {
14360
+ return {
14361
+ error: "Retrieval release decision group is outside the allowed RAG access scope",
14362
+ ok: false
14363
+ };
14364
+ }
13856
14365
  const kind = getStringProperty(queryInput, "kind");
13857
14366
  const targetRolloutLabel = getStringProperty(queryInput, "targetRolloutLabel");
13858
14367
  const freshnessStatusFilter = getStringProperty(queryInput, "freshnessStatus");
13859
14368
  const decisions = await loadRAGRetrievalReleaseDecisions({
13860
- groupKey: getStringProperty(queryInput, "groupKey"),
14369
+ groupKey,
13861
14370
  kind: kind === "approve" || kind === "promote" || kind === "reject" || kind === "revert" ? kind : undefined,
13862
14371
  limit: getIntegerLikeProperty(queryInput, "limit"),
13863
14372
  store: config.retrievalReleaseDecisionStore
13864
14373
  });
13865
14374
  return {
13866
- decisions: decisions.map((decision) => ({
14375
+ decisions: filterByComparisonGroupKey(accessScope, decisions).map((decision) => ({
13867
14376
  ...decision,
13868
14377
  ...getDecisionFreshness({ record: decision })
13869
14378
  })).filter((decision) => {
@@ -13881,7 +14390,7 @@ var ragChat = (config) => {
13881
14390
  ok: true
13882
14391
  };
13883
14392
  };
13884
- const handleRetrievalReleaseDecisionAction = async (body, kind) => {
14393
+ const handleRetrievalReleaseDecisionAction = async (body, kind, request) => {
13885
14394
  if (!retrievalComparisonHistoryStore || !config.retrievalReleaseDecisionStore) {
13886
14395
  return {
13887
14396
  error: "RAG retrieval comparison history store and release decision store are required",
@@ -15208,7 +15717,7 @@ var ragChat = (config) => {
15208
15717
  return { error: message, ok: false };
15209
15718
  }
15210
15719
  };
15211
- const handleSearch = async (body) => {
15720
+ const handleSearch = async (body, request) => {
15212
15721
  if (!isObjectRecord2(body)) {
15213
15722
  return { error: "Invalid payload", ok: false };
15214
15723
  }
@@ -15220,6 +15729,7 @@ var ragChat = (config) => {
15220
15729
  };
15221
15730
  }
15222
15731
  const collection = resolveCollection();
15732
+ const accessScope = await loadAccessScope(request);
15223
15733
  if (!collection) {
15224
15734
  return { error: "RAG collection is not configured", ok: false };
15225
15735
  }
@@ -15262,14 +15772,28 @@ var ragChat = (config) => {
15262
15772
  trace: result.trace
15263
15773
  });
15264
15774
  }
15775
+ const scopedResults = buildSources2(result.results).filter((entry) => matchesAccessScope(accessScope, {
15776
+ corpusKey: entry.corpusKey,
15777
+ documentId: typeof entry.metadata?.documentId === "string" ? entry.metadata.documentId : entry.chunkId.split(":")[0],
15778
+ metadata: entry.metadata,
15779
+ source: entry.source
15780
+ }));
15265
15781
  return {
15266
15782
  ok: true,
15267
- results: buildSources2(result.results),
15783
+ results: scopedResults,
15268
15784
  trace: result.trace
15269
15785
  };
15270
15786
  }
15271
15787
  const results = await collection.search(input);
15272
- return { ok: true, results: buildSources2(results) };
15788
+ return {
15789
+ ok: true,
15790
+ results: buildSources2(results).filter((entry) => matchesAccessScope(accessScope, {
15791
+ corpusKey: entry.corpusKey,
15792
+ documentId: typeof entry.metadata?.documentId === "string" ? entry.metadata.documentId : entry.chunkId.split(":")[0],
15793
+ metadata: entry.metadata,
15794
+ source: entry.source
15795
+ }))
15796
+ };
15273
15797
  };
15274
15798
  const handleTraceHistory = async (queryInput) => {
15275
15799
  if (!searchTraceStore) {
@@ -15415,7 +15939,10 @@ var ragChat = (config) => {
15415
15939
  let inspectedChunks = 0;
15416
15940
  let documentsWithSourceLabels = 0;
15417
15941
  let chunksWithSourceLabels = 0;
15942
+ const corpusKeys = new Map;
15418
15943
  const sourceNativeKinds = new Map;
15944
+ const extractorRegistryMatches = new Map;
15945
+ const chunkingProfiles = new Map;
15419
15946
  const sampleDocuments = [];
15420
15947
  const sampleChunks = [];
15421
15948
  let oldestDocumentAgeMs;
@@ -15467,11 +15994,26 @@ var ragChat = (config) => {
15467
15994
  documentsWithSourceLabels += 1;
15468
15995
  }
15469
15996
  const documentSourceNativeKind = typeof document.metadata?.sourceNativeKind === "string" ? document.metadata.sourceNativeKind : undefined;
15997
+ const documentCorpusKey = document.corpusKey ?? (typeof document.metadata?.corpusKey === "string" ? document.metadata.corpusKey : undefined);
15998
+ const documentExtractorRegistryMatch = typeof document.metadata?.extractorRegistryMatch === "string" ? document.metadata.extractorRegistryMatch : undefined;
15999
+ const documentChunkingProfile = typeof document.metadata?.chunkingProfile === "string" ? document.metadata.chunkingProfile : undefined;
15470
16000
  if (documentSourceNativeKind) {
15471
16001
  sourceNativeKinds.set(documentSourceNativeKind, (sourceNativeKinds.get(documentSourceNativeKind) ?? 0) + 1);
15472
16002
  }
15473
- if (sampleDocuments.length < 5 && (documentLabels || documentSourceNativeKind)) {
16003
+ if (documentCorpusKey) {
16004
+ corpusKeys.set(documentCorpusKey, (corpusKeys.get(documentCorpusKey) ?? 0) + 1);
16005
+ }
16006
+ if (documentExtractorRegistryMatch) {
16007
+ extractorRegistryMatches.set(documentExtractorRegistryMatch, (extractorRegistryMatches.get(documentExtractorRegistryMatch) ?? 0) + 1);
16008
+ }
16009
+ if (documentChunkingProfile) {
16010
+ chunkingProfiles.set(documentChunkingProfile, (chunkingProfiles.get(documentChunkingProfile) ?? 0) + 1);
16011
+ }
16012
+ if (sampleDocuments.length < 5 && (documentCorpusKey || documentLabels || documentSourceNativeKind || documentExtractorRegistryMatch || documentChunkingProfile)) {
15474
16013
  sampleDocuments.push({
16014
+ corpusKey: documentCorpusKey,
16015
+ chunkingProfile: documentChunkingProfile,
16016
+ extractorRegistryMatch: documentExtractorRegistryMatch,
15475
16017
  id: document.id,
15476
16018
  labels: documentLabels,
15477
16019
  source: document.source,
@@ -15497,13 +16039,28 @@ var ragChat = (config) => {
15497
16039
  chunksWithSourceLabels += 1;
15498
16040
  }
15499
16041
  const chunkSourceNativeKind = typeof chunk.metadata?.sourceNativeKind === "string" ? chunk.metadata.sourceNativeKind : undefined;
16042
+ const chunkCorpusKey = chunk.corpusKey ?? (typeof chunk.metadata?.corpusKey === "string" ? chunk.metadata.corpusKey : documentCorpusKey);
16043
+ const chunkExtractorRegistryMatch = typeof chunk.metadata?.extractorRegistryMatch === "string" ? chunk.metadata.extractorRegistryMatch : undefined;
16044
+ const chunkChunkingProfile = typeof chunk.metadata?.chunkingProfile === "string" ? chunk.metadata.chunkingProfile : undefined;
15500
16045
  if (chunkSourceNativeKind) {
15501
16046
  sourceNativeKinds.set(chunkSourceNativeKind, (sourceNativeKinds.get(chunkSourceNativeKind) ?? 0) + 1);
15502
16047
  }
15503
- if (sampleChunks.length < 8 && (chunkLabels || chunkSourceNativeKind)) {
16048
+ if (chunkCorpusKey) {
16049
+ corpusKeys.set(chunkCorpusKey, (corpusKeys.get(chunkCorpusKey) ?? 0) + 1);
16050
+ }
16051
+ if (chunkExtractorRegistryMatch) {
16052
+ extractorRegistryMatches.set(chunkExtractorRegistryMatch, (extractorRegistryMatches.get(chunkExtractorRegistryMatch) ?? 0) + 1);
16053
+ }
16054
+ if (chunkChunkingProfile) {
16055
+ chunkingProfiles.set(chunkChunkingProfile, (chunkingProfiles.get(chunkChunkingProfile) ?? 0) + 1);
16056
+ }
16057
+ if (sampleChunks.length < 8 && (chunkCorpusKey || chunkLabels || chunkSourceNativeKind || chunkExtractorRegistryMatch || chunkChunkingProfile)) {
15504
16058
  sampleChunks.push({
15505
16059
  chunkId: chunk.chunkId,
16060
+ chunkingProfile: chunkChunkingProfile,
16061
+ corpusKey: chunkCorpusKey,
15506
16062
  documentId: document.id,
16063
+ extractorRegistryMatch: chunkExtractorRegistryMatch,
15507
16064
  labels: chunkLabels,
15508
16065
  source: chunk.source ?? preview.document.source,
15509
16066
  sourceNativeKind: chunkSourceNativeKind
@@ -15566,8 +16123,11 @@ var ragChat = (config) => {
15566
16123
  inspectedChunks,
15567
16124
  inspectedDocuments,
15568
16125
  inspection: {
16126
+ chunkingProfiles: Object.fromEntries(chunkingProfiles.entries()),
16127
+ corpusKeys: Object.fromEntries(corpusKeys.entries()),
15569
16128
  chunksWithSourceLabels,
15570
16129
  documentsWithSourceLabels,
16130
+ extractorRegistryMatches: Object.fromEntries(extractorRegistryMatches.entries()),
15571
16131
  sampleChunks,
15572
16132
  sampleDocuments,
15573
16133
  sourceNativeKinds: Object.fromEntries(sourceNativeKinds.entries())
@@ -15590,28 +16150,34 @@ var ragChat = (config) => {
15590
16150
  providerName: typeof config.provider === "function" ? config.readinessProviderName : undefined,
15591
16151
  rerankerConfigured: Boolean(config.rerank ?? config.collection)
15592
16152
  });
15593
- const buildAdminCapabilities = () => ({
15594
- canClearIndex: Boolean(ragStore?.clear),
15595
- canCreateDocument: Boolean(indexManager?.createDocument),
15596
- canDeleteDocument: Boolean(indexManager?.deleteDocument),
15597
- canListSyncSources: Boolean(indexManager?.listSyncSources),
15598
- canManageRetrievalBaselines: Boolean(retrievalBaselineStore),
15599
- canPruneSearchTraces: Boolean(searchTraceStore),
15600
- canReindexDocument: Boolean(indexManager?.reindexDocument),
15601
- canReindexSource: Boolean(indexManager?.reindexSource),
15602
- canReseed: Boolean(indexManager?.reseed),
15603
- canReset: Boolean(indexManager?.reset),
15604
- canSyncAllSources: Boolean(indexManager?.syncAllSources),
15605
- canSyncSource: Boolean(indexManager?.syncSource)
16153
+ const buildAdminCapabilities = async (request) => ({
16154
+ canClearIndex: Boolean(ragStore?.clear) && (request ? await isAuthorized(request, "clear_index") : true),
16155
+ canCreateDocument: Boolean(indexManager?.createDocument) && (request ? await isAuthorized(request, "create_document") : true),
16156
+ canDeleteDocument: Boolean(indexManager?.deleteDocument) && (request ? await isAuthorized(request, "delete_document") : true),
16157
+ canListSyncSources: Boolean(indexManager?.listSyncSources) && (request ? await isAuthorized(request, "list_sync_sources") : true),
16158
+ canManageRetrievalBaselines: Boolean(retrievalBaselineStore) && (request ? await isAuthorized(request, "manage_retrieval_admin") : true),
16159
+ canPruneSearchTraces: Boolean(searchTraceStore) && (request ? await isAuthorized(request, "prune_search_traces") : true),
16160
+ canReindexDocument: Boolean(indexManager?.reindexDocument) && (request ? await isAuthorized(request, "reindex_document") : true),
16161
+ canReindexSource: Boolean(indexManager?.reindexSource) && (request ? await isAuthorized(request, "reindex_source") : true),
16162
+ canReseed: Boolean(indexManager?.reseed) && (request ? await isAuthorized(request, "reseed") : true),
16163
+ canReset: Boolean(indexManager?.reset) && (request ? await isAuthorized(request, "reset") : true),
16164
+ canSyncAllSources: Boolean(indexManager?.syncAllSources) && (request ? await isAuthorized(request, "sync_all_sources") : true),
16165
+ canSyncSource: Boolean(indexManager?.syncSource) && (request ? await isAuthorized(request, "sync_source") : true)
15606
16166
  });
15607
- const buildOperationsPayload = async () => {
16167
+ const buildOperationsPayload = async (request) => {
16168
+ const accessScope = await loadAccessScope(request);
15608
16169
  const collection = config.collection ?? (ragStore ? createRAGCollection({
15609
16170
  defaultModel: config.embeddingModel,
15610
16171
  defaultTopK: topK,
15611
16172
  embedding: config.embedding,
15612
16173
  store: ragStore
15613
16174
  }) : null);
15614
- const indexedDocuments = indexManager ? await indexManager.listDocuments({}) : [];
16175
+ const indexedDocuments = indexManager ? (await indexManager.listDocuments({})).filter((document) => matchesAccessScope(accessScope, {
16176
+ corpusKey: document.corpusKey,
16177
+ documentId: document.id,
16178
+ metadata: document.metadata,
16179
+ source: document.source
16180
+ })) : [];
15615
16181
  const traceStats = searchTraceStore ? await summarizeRAGSearchTraceStore({
15616
16182
  store: searchTraceStore
15617
16183
  }) : undefined;
@@ -16602,7 +17168,7 @@ var ragChat = (config) => {
16602
17168
  searchTraceRuntime.stats = traceStats;
16603
17169
  searchTraceRuntime.recentRuns = recentTraceRuns;
16604
17170
  return {
16605
- admin: buildAdminCapabilities(),
17171
+ admin: await buildAdminCapabilities(request),
16606
17172
  adminActions: [...adminActions],
16607
17173
  adminJobs: [...adminJobs, ...syncJobs].sort((left, right) => right.startedAt - left.startedAt),
16608
17174
  capabilities: collection?.getCapabilities?.(),
@@ -16681,27 +17247,27 @@ var ragChat = (config) => {
16681
17247
  stats: traceStats
16682
17248
  },
16683
17249
  status: collection?.getStatus?.(),
16684
- syncSources: await buildSyncSources()
17250
+ syncSources: await buildSyncSources(accessScope)
16685
17251
  };
16686
17252
  };
16687
- const handleStatus = async () => buildOperationsPayload();
16688
- const handleRetrievalReleaseStatus = async () => {
16689
- const result = await buildOperationsPayload();
17253
+ const handleStatus = async (request) => buildOperationsPayload(request);
17254
+ const handleRetrievalReleaseStatus = async (request) => {
17255
+ const result = await buildOperationsPayload(request);
16690
17256
  return {
16691
17257
  ok: true,
16692
17258
  retrievalComparisons: result.retrievalComparisons
16693
17259
  };
16694
17260
  };
16695
- const handleRetrievalReleaseDriftStatus = async () => {
16696
- const result = await buildOperationsPayload();
17261
+ const handleRetrievalReleaseDriftStatus = async (request) => {
17262
+ const result = await buildOperationsPayload(request);
16697
17263
  return {
16698
17264
  handoffDriftCountsByLane: result.retrievalComparisons?.handoffDriftCountsByLane,
16699
17265
  handoffDriftRollups: result.retrievalComparisons?.handoffDriftRollups,
16700
17266
  ok: true
16701
17267
  };
16702
17268
  };
16703
- const handleRetrievalLaneHandoffIncidentStatus = async () => {
16704
- const result = await buildOperationsPayload();
17269
+ const handleRetrievalLaneHandoffIncidentStatus = async (request) => {
17270
+ const result = await buildOperationsPayload(request);
16705
17271
  const incidents = result.retrievalComparisons?.recentLaneHandoffIncidents;
16706
17272
  return {
16707
17273
  freshnessWindows: result.retrievalComparisons?.handoffFreshnessWindows,
@@ -16711,8 +17277,8 @@ var ragChat = (config) => {
16711
17277
  ok: true
16712
17278
  };
16713
17279
  };
16714
- const handleRetrievalLaneHandoffStatus = async () => {
16715
- const result = await buildOperationsPayload();
17280
+ const handleRetrievalLaneHandoffStatus = async (request) => {
17281
+ const result = await buildOperationsPayload(request);
16716
17282
  const incidents = result.retrievalComparisons?.recentLaneHandoffIncidents;
16717
17283
  return {
16718
17284
  autoComplete: result.retrievalComparisons?.handoffAutoComplete,
@@ -16725,7 +17291,7 @@ var ragChat = (config) => {
16725
17291
  ok: true
16726
17292
  };
16727
17293
  };
16728
- const handleOps = async () => buildOperationsPayload();
17294
+ const handleOps = async (request) => buildOperationsPayload(request);
16729
17295
  if (searchTraceStore && searchTraceRetention && searchTraceRetentionSchedule) {
16730
17296
  const runScheduledSearchTracePrune = async () => {
16731
17297
  searchTraceRuntime.nextScheduledAt = Date.now() + searchTraceRetentionSchedule.intervalMs;
@@ -16743,14 +17309,20 @@ var ragChat = (config) => {
16743
17309
  }, searchTraceRetentionSchedule.intervalMs);
16744
17310
  timer.unref?.();
16745
17311
  }
16746
- const handleDocuments = async (kind) => {
17312
+ const handleDocuments = async (kind, request) => {
16747
17313
  if (!indexManager) {
16748
17314
  return {
16749
17315
  error: "RAG index document management is not configured",
16750
17316
  ok: false
16751
17317
  };
16752
17318
  }
16753
- const documents = await indexManager.listDocuments({ kind });
17319
+ const accessScope = await loadAccessScope(request);
17320
+ const documents = (await indexManager.listDocuments({ kind })).filter((document) => matchesAccessScope(accessScope, {
17321
+ corpusKey: document.corpusKey,
17322
+ documentId: document.id,
17323
+ metadata: document.metadata,
17324
+ source: document.source
17325
+ }));
16754
17326
  return {
16755
17327
  documents: documents.map((document) => ({
16756
17328
  ...document,
@@ -16763,7 +17335,7 @@ var ragChat = (config) => {
16763
17335
  ok: true
16764
17336
  };
16765
17337
  };
16766
- const handleCreateDocument = async (body) => {
17338
+ const handleCreateDocument = async (body, request) => {
16767
17339
  if (!indexManager?.createDocument) {
16768
17340
  return {
16769
17341
  error: "RAG document creation is not configured",
@@ -16782,6 +17354,18 @@ var ragChat = (config) => {
16782
17354
  ok: false
16783
17355
  };
16784
17356
  }
17357
+ const accessScope = await loadAccessScope(request);
17358
+ if (!matchesAccessScope(accessScope, {
17359
+ documentId: body.id,
17360
+ corpusKey: body.corpusKey,
17361
+ metadata: body.metadata,
17362
+ source: body.source
17363
+ })) {
17364
+ return {
17365
+ error: "Document is outside the allowed RAG access scope",
17366
+ ok: false
17367
+ };
17368
+ }
16785
17369
  const job = createAdminJob("create_document", body.id);
16786
17370
  try {
16787
17371
  const result = await indexManager.createDocument(body);
@@ -16797,7 +17381,7 @@ var ragChat = (config) => {
16797
17381
  throw caught;
16798
17382
  }
16799
17383
  };
16800
- const handleDocumentChunks = async (id) => {
17384
+ const handleDocumentChunks = async (id, request) => {
16801
17385
  if (!indexManager) {
16802
17386
  return {
16803
17387
  error: "RAG chunk preview is not configured",
@@ -16817,6 +17401,18 @@ var ragChat = (config) => {
16817
17401
  ok: false
16818
17402
  };
16819
17403
  }
17404
+ const accessScope = await loadAccessScope(request);
17405
+ if (!matchesAccessScope(accessScope, {
17406
+ documentId: preview.document.id,
17407
+ corpusKey: preview.document.corpusKey,
17408
+ metadata: preview.document.metadata,
17409
+ source: preview.document.source
17410
+ })) {
17411
+ return {
17412
+ error: "document not found",
17413
+ ok: false
17414
+ };
17415
+ }
16820
17416
  const chunks = preview.chunks.map((chunk) => ({
16821
17417
  ...chunk,
16822
17418
  labels: buildRAGSourceLabels({
@@ -16847,7 +17443,7 @@ var ragChat = (config) => {
16847
17443
  })
16848
17444
  };
16849
17445
  };
16850
- const handleDeleteDocument = async (id) => {
17446
+ const handleDeleteDocument = async (id, request) => {
16851
17447
  if (!indexManager?.deleteDocument) {
16852
17448
  return {
16853
17449
  error: "RAG document deletion is not configured",
@@ -16860,6 +17456,13 @@ var ragChat = (config) => {
16860
17456
  ok: false
16861
17457
  };
16862
17458
  }
17459
+ const accessScope = await loadAccessScope(request);
17460
+ if (accessScope && !matchesAccessScope(accessScope, { documentId: id })) {
17461
+ return {
17462
+ error: "Document is outside the allowed RAG access scope",
17463
+ ok: false
17464
+ };
17465
+ }
16863
17466
  const job = createAdminJob("delete_document", id);
16864
17467
  const deleted = await indexManager.deleteDocument(id);
16865
17468
  if (!deleted) {
@@ -16879,7 +17482,7 @@ var ragChat = (config) => {
16879
17482
  ok: true
16880
17483
  };
16881
17484
  };
16882
- const handleReindexDocument = async (id) => {
17485
+ const handleReindexDocument = async (id, request) => {
16883
17486
  if (!indexManager?.reindexDocument) {
16884
17487
  return {
16885
17488
  error: "RAG document reindex is not configured",
@@ -16892,6 +17495,13 @@ var ragChat = (config) => {
16892
17495
  ok: false
16893
17496
  };
16894
17497
  }
17498
+ const accessScope = await loadAccessScope(request);
17499
+ if (accessScope && !matchesAccessScope(accessScope, { documentId: id })) {
17500
+ return {
17501
+ error: "Document is outside the allowed RAG access scope",
17502
+ ok: false
17503
+ };
17504
+ }
16895
17505
  const job = createAdminJob("reindex_document", id);
16896
17506
  try {
16897
17507
  const result = {
@@ -16911,7 +17521,7 @@ var ragChat = (config) => {
16911
17521
  throw caught;
16912
17522
  }
16913
17523
  };
16914
- const handleReindexSource = async (source) => {
17524
+ const handleReindexSource = async (source, request) => {
16915
17525
  if (!indexManager?.reindexSource) {
16916
17526
  return {
16917
17527
  error: "RAG source reindex is not configured",
@@ -16924,6 +17534,15 @@ var ragChat = (config) => {
16924
17534
  ok: false
16925
17535
  };
16926
17536
  }
17537
+ const accessScope = await loadAccessScope(request);
17538
+ if (!matchesAccessScope(accessScope, {
17539
+ source
17540
+ })) {
17541
+ return {
17542
+ error: "Source is outside the allowed RAG access scope",
17543
+ ok: false
17544
+ };
17545
+ }
16927
17546
  const job = createAdminJob("reindex_source", source);
16928
17547
  try {
16929
17548
  const result = {
@@ -17007,25 +17626,33 @@ var ragChat = (config) => {
17007
17626
  ...normalized
17008
17627
  };
17009
17628
  };
17010
- const handleSyncSources = async () => {
17629
+ const handleSyncSources = async (request) => {
17011
17630
  if (!indexManager?.listSyncSources) {
17012
17631
  return {
17013
17632
  error: "RAG source sync is not configured",
17014
17633
  ok: false
17015
17634
  };
17016
17635
  }
17636
+ const accessScope = await loadAccessScope(request);
17017
17637
  return {
17018
17638
  ok: true,
17019
- sources: await indexManager.listSyncSources()
17639
+ sources: await buildSyncSources(accessScope)
17020
17640
  };
17021
17641
  };
17022
- const handleSyncAllSources = async (options) => {
17642
+ const handleSyncAllSources = async (request, options) => {
17023
17643
  if (!indexManager?.syncAllSources) {
17024
17644
  return {
17025
17645
  error: "RAG source sync is not configured",
17026
17646
  ok: false
17027
17647
  };
17028
17648
  }
17649
+ const accessScope = await loadAccessScope(request);
17650
+ if (accessScope?.allowedSyncSourceIds?.length) {
17651
+ return {
17652
+ error: "Scoped sync-all is not allowed; sync individual sources instead",
17653
+ ok: false
17654
+ };
17655
+ }
17029
17656
  const job = createAdminJob("sync_all_sources", undefined, syncJobs);
17030
17657
  const action = createAdminAction("sync_all_sources");
17031
17658
  try {
@@ -17051,7 +17678,7 @@ var ragChat = (config) => {
17051
17678
  completeAdminAction(action);
17052
17679
  return {
17053
17680
  ok: true,
17054
- sources: await buildSyncSources()
17681
+ sources: await buildSyncSources(accessScope)
17055
17682
  };
17056
17683
  } catch (caught) {
17057
17684
  const message = caught instanceof Error ? caught.message : String(caught);
@@ -17060,7 +17687,7 @@ var ragChat = (config) => {
17060
17687
  throw caught;
17061
17688
  }
17062
17689
  };
17063
- const handleSyncSource = async (id, options) => {
17690
+ const handleSyncSource = async (id, request, options) => {
17064
17691
  if (!indexManager?.syncSource) {
17065
17692
  return {
17066
17693
  error: "RAG source sync is not configured",
@@ -17073,6 +17700,13 @@ var ragChat = (config) => {
17073
17700
  ok: false
17074
17701
  };
17075
17702
  }
17703
+ const accessScope = await loadAccessScope(request);
17704
+ if (!matchesSyncSourceScope(accessScope, { id })) {
17705
+ return {
17706
+ error: "Sync source is outside the allowed RAG access scope",
17707
+ ok: false
17708
+ };
17709
+ }
17076
17710
  const job = createAdminJob("sync_source", id, syncJobs);
17077
17711
  const action = createAdminAction("sync_source", undefined, id);
17078
17712
  try {
@@ -17089,7 +17723,7 @@ var ragChat = (config) => {
17089
17723
  }
17090
17724
  completeAdminJob(job);
17091
17725
  completeAdminAction(action);
17092
- const source = (await buildSyncSources()).find((record) => record.id === id);
17726
+ const source = (await buildSyncSources(accessScope)).find((record) => record.id === id);
17093
17727
  return source ? { ok: true, source } : {
17094
17728
  error: "sync source not found",
17095
17729
  ok: false
@@ -17155,7 +17789,7 @@ var ragChat = (config) => {
17155
17789
  };
17156
17790
  return;
17157
17791
  }
17158
- const lastMessage = conversation.messages.findLast((msg) => msg.id === messageId && msg.role === "user");
17792
+ const lastMessage = conversation.messages.findLast((message) => message.id === messageId && message.role === "user");
17159
17793
  if (!lastMessage) {
17160
17794
  yield {
17161
17795
  data: renderers.error("Message not found"),
@@ -17199,7 +17833,7 @@ var ragChat = (config) => {
17199
17833
  const controller = new AbortController;
17200
17834
  abortControllers.set(conversationId, controller);
17201
17835
  const history = buildHistory(conversation);
17202
- const lastMessageIndex = conversation.messages.findIndex((msg) => msg.id === messageId);
17836
+ const lastMessageIndex = conversation.messages.findIndex((message) => message.id === messageId);
17203
17837
  const userHistory = lastMessageIndex >= 0 ? history.slice(0, lastMessageIndex) : history;
17204
17838
  const messageWithContext = buildUserMessage2(content, lastMessage.attachments, ragContext);
17205
17839
  const sseStream = streamAIToSSE(conversationId, assistantMessageId, {
@@ -17225,24 +17859,24 @@ var ragChat = (config) => {
17225
17859
  };
17226
17860
  return new Elysia2().ws(path, {
17227
17861
  message: async (ws, raw) => {
17228
- const msg = parseAIMessage(raw);
17229
- if (!msg) {
17862
+ const message = parseAIMessage(raw);
17863
+ if (!message) {
17230
17864
  return;
17231
17865
  }
17232
- if (msg.type === "cancel") {
17233
- handleCancel(msg.conversationId);
17866
+ if (message.type === "cancel") {
17867
+ handleCancel(message.conversationId);
17234
17868
  return;
17235
17869
  }
17236
- if (msg.type === "branch") {
17237
- await handleBranch(ws, msg.messageId, msg.conversationId);
17870
+ if (message.type === "branch") {
17871
+ await handleBranch(ws, message.messageId, message.conversationId);
17238
17872
  return;
17239
17873
  }
17240
- if (msg.type === "message") {
17241
- await handleMessage(ws, msg.content, msg.conversationId, msg.attachments);
17874
+ if (message.type === "message") {
17875
+ await handleMessage(ws, message.content, message.conversationId, message.attachments);
17242
17876
  }
17243
17877
  }
17244
17878
  }).post(`${path}/search`, async ({ body, request, set }) => {
17245
- const result = await handleSearch(body);
17879
+ const result = await handleSearch(body, request);
17246
17880
  if (!result.ok) {
17247
17881
  set.status = result.error === "Invalid payload" || result.error?.startsWith("Expected payload shape:") ? HTTP_STATUS_BAD_REQUEST : HTTP_STATUS_NOT_FOUND;
17248
17882
  }
@@ -17319,6 +17953,16 @@ var ragChat = (config) => {
17319
17953
  }
17320
17954
  return result;
17321
17955
  }).post(`${path}/traces/prune`, async ({ body, request, set }) => {
17956
+ const denied = await authorizeMutationRoute(request, "prune_search_traces", {
17957
+ fallback: "Search trace pruning is not allowed"
17958
+ });
17959
+ if (denied) {
17960
+ set.status = 403;
17961
+ if (config.htmx && isHTMXRequest(request)) {
17962
+ return toHTMXResponse(workflowRenderers.error(denied.error ?? "Search trace prune failed"), getNumericStatus(set.status));
17963
+ }
17964
+ return denied;
17965
+ }
17322
17966
  const result = await handleTracePrune(body);
17323
17967
  if (!result.ok) {
17324
17968
  set.status = HTTP_STATUS_BAD_REQUEST;
@@ -17349,7 +17993,7 @@ var ragChat = (config) => {
17349
17993
  }
17350
17994
  return result;
17351
17995
  }).post(`${path}/compare/retrieval`, async ({ body, request, set }) => {
17352
- const result = await handleEvaluateRetrievals(body);
17996
+ const result = await handleEvaluateRetrievals(body, request);
17353
17997
  if (!result.ok) {
17354
17998
  set.status = HTTP_STATUS_BAD_REQUEST;
17355
17999
  }
@@ -17364,7 +18008,7 @@ var ragChat = (config) => {
17364
18008
  }
17365
18009
  return result;
17366
18010
  }).get(`${path}/compare/retrieval/history`, async ({ query, request, set }) => {
17367
- const result = await handleRetrievalComparisonHistory(query);
18011
+ const result = await handleRetrievalComparisonHistory(query, request);
17368
18012
  if (!result.ok) {
17369
18013
  set.status = HTTP_STATUS_BAD_REQUEST;
17370
18014
  }
@@ -17379,7 +18023,7 @@ var ragChat = (config) => {
17379
18023
  }
17380
18024
  return result;
17381
18025
  }).get(`${path}/compare/retrieval/baselines`, async ({ query, request, set }) => {
17382
- const result = await handleRetrievalBaselineList(query);
18026
+ const result = await handleRetrievalBaselineList(query, request);
17383
18027
  if (!result.ok) {
17384
18028
  set.status = HTTP_STATUS_BAD_REQUEST;
17385
18029
  }
@@ -17394,7 +18038,7 @@ var ragChat = (config) => {
17394
18038
  }
17395
18039
  return result;
17396
18040
  }).get(`${path}/compare/retrieval/baselines/decisions`, async ({ query, request, set }) => {
17397
- const result = await handleRetrievalReleaseDecisionList(query);
18041
+ const result = await handleRetrievalReleaseDecisionList(query, request);
17398
18042
  if (!result.ok) {
17399
18043
  set.status = HTTP_STATUS_BAD_REQUEST;
17400
18044
  }
@@ -17499,6 +18143,16 @@ var ragChat = (config) => {
17499
18143
  }
17500
18144
  return result;
17501
18145
  }).post(`${path}/compare/retrieval/handoffs/incidents/acknowledge`, async ({ body, request, set }) => {
18146
+ const denied = await authorizeMutationRoute(request, "manage_retrieval_admin", {
18147
+ fallback: "Retrieval lane handoff incident acknowledgement is not allowed"
18148
+ });
18149
+ if (denied) {
18150
+ set.status = 403;
18151
+ if (config.htmx && isHTMXRequest(request)) {
18152
+ return toHTMXResponse(workflowRenderers.error(denied.error ?? "Retrieval lane handoff incident acknowledgement failed"), getNumericStatus(set.status));
18153
+ }
18154
+ return denied;
18155
+ }
17502
18156
  const result = await handleRetrievalLaneHandoffIncidentAcknowledge(body);
17503
18157
  if (!result.ok) {
17504
18158
  set.status = HTTP_STATUS_BAD_REQUEST;
@@ -17514,6 +18168,16 @@ var ragChat = (config) => {
17514
18168
  }
17515
18169
  return result;
17516
18170
  }).post(`${path}/compare/retrieval/handoffs/incidents/unacknowledge`, async ({ body, request, set }) => {
18171
+ const denied = await authorizeMutationRoute(request, "manage_retrieval_admin", {
18172
+ fallback: "Retrieval lane handoff incident unacknowledge is not allowed"
18173
+ });
18174
+ if (denied) {
18175
+ set.status = 403;
18176
+ if (config.htmx && isHTMXRequest(request)) {
18177
+ return toHTMXResponse(workflowRenderers.error(denied.error ?? "Retrieval lane handoff incident unacknowledge failed"), getNumericStatus(set.status));
18178
+ }
18179
+ return denied;
18180
+ }
17517
18181
  const result = await handleRetrievalLaneHandoffIncidentUnacknowledge(body);
17518
18182
  if (!result.ok) {
17519
18183
  set.status = HTTP_STATUS_BAD_REQUEST;
@@ -17529,6 +18193,16 @@ var ragChat = (config) => {
17529
18193
  }
17530
18194
  return result;
17531
18195
  }).post(`${path}/compare/retrieval/handoffs/incidents/resolve`, async ({ body, request, set }) => {
18196
+ const denied = await authorizeMutationRoute(request, "manage_retrieval_admin", {
18197
+ fallback: "Retrieval lane handoff incident resolve is not allowed"
18198
+ });
18199
+ if (denied) {
18200
+ set.status = 403;
18201
+ if (config.htmx && isHTMXRequest(request)) {
18202
+ return toHTMXResponse(workflowRenderers.error(denied.error ?? "Retrieval lane handoff incident resolve failed"), getNumericStatus(set.status));
18203
+ }
18204
+ return denied;
18205
+ }
17532
18206
  const result = await handleResolveRetrievalLaneHandoffIncident(body);
17533
18207
  if (!result.ok) {
17534
18208
  set.status = HTTP_STATUS_BAD_REQUEST;
@@ -17544,6 +18218,16 @@ var ragChat = (config) => {
17544
18218
  }
17545
18219
  return result;
17546
18220
  }).post(`${path}/compare/retrieval/handoffs/decide`, async ({ body, request, set }) => {
18221
+ const denied = await authorizeMutationRoute(request, "manage_retrieval_admin", {
18222
+ fallback: "Retrieval lane handoff decision is not allowed"
18223
+ });
18224
+ if (denied) {
18225
+ set.status = 403;
18226
+ if (config.htmx && isHTMXRequest(request)) {
18227
+ return toHTMXResponse(workflowRenderers.error(denied.error ?? "Retrieval lane handoff decision failed"), getNumericStatus(set.status));
18228
+ }
18229
+ return denied;
18230
+ }
17547
18231
  const result = await handleRetrievalLaneHandoffDecision(body);
17548
18232
  if (!result.ok) {
17549
18233
  set.status = HTTP_STATUS_BAD_REQUEST;
@@ -17604,6 +18288,16 @@ var ragChat = (config) => {
17604
18288
  }
17605
18289
  return result;
17606
18290
  }).post(`${path}/compare/retrieval/incidents/remediations`, async ({ body, request, set }) => {
18291
+ const denied = await authorizeMutationRoute(request, "manage_retrieval_admin", {
18292
+ fallback: "Retrieval incident remediation decision record is not allowed"
18293
+ });
18294
+ if (denied) {
18295
+ set.status = 403;
18296
+ if (config.htmx && isHTMXRequest(request)) {
18297
+ return toHTMXResponse(workflowRenderers.error(denied.error ?? "Retrieval incident remediation decision record failed"), getNumericStatus(set.status));
18298
+ }
18299
+ return denied;
18300
+ }
17607
18301
  const result = await handleRecordRetrievalIncidentRemediationDecision(body);
17608
18302
  if (!result.ok) {
17609
18303
  set.status = HTTP_STATUS_BAD_REQUEST;
@@ -17619,6 +18313,16 @@ var ragChat = (config) => {
17619
18313
  }
17620
18314
  return result;
17621
18315
  }).post(`${path}/compare/retrieval/incidents/remediations/execute`, async ({ body, request, set }) => {
18316
+ const denied = await authorizeMutationRoute(request, "manage_retrieval_admin", {
18317
+ fallback: "Retrieval incident remediation execution is not allowed"
18318
+ });
18319
+ if (denied) {
18320
+ set.status = 403;
18321
+ if (config.htmx && isHTMXRequest(request)) {
18322
+ return toHTMXResponse(workflowRenderers.error(denied.error ?? "Retrieval incident remediation execution failed"), getNumericStatus(set.status));
18323
+ }
18324
+ return denied;
18325
+ }
17622
18326
  const result = await handleExecuteRetrievalIncidentRemediation(body);
17623
18327
  if (!result.ok) {
17624
18328
  set.status = HTTP_STATUS_BAD_REQUEST;
@@ -17634,6 +18338,16 @@ var ragChat = (config) => {
17634
18338
  }
17635
18339
  return result;
17636
18340
  }).post(`${path}/compare/retrieval/incidents/remediations/execute/bulk`, async ({ body, request, set }) => {
18341
+ const denied = await authorizeMutationRoute(request, "manage_retrieval_admin", {
18342
+ fallback: "Bulk retrieval incident remediation execution is not allowed"
18343
+ });
18344
+ if (denied) {
18345
+ set.status = 403;
18346
+ if (config.htmx && isHTMXRequest(request)) {
18347
+ return toHTMXResponse(workflowRenderers.error(denied.error ?? "Bulk retrieval incident remediation execution failed"), getNumericStatus(set.status));
18348
+ }
18349
+ return denied;
18350
+ }
17637
18351
  const result = await handleBulkExecuteRetrievalIncidentRemediations(body);
17638
18352
  if (!result.ok) {
17639
18353
  set.status = HTTP_STATUS_BAD_REQUEST;
@@ -17649,6 +18363,16 @@ var ragChat = (config) => {
17649
18363
  }
17650
18364
  return result;
17651
18365
  }).post(`${path}/compare/retrieval/incidents/acknowledge`, async ({ body, request, set }) => {
18366
+ const denied = await authorizeMutationRoute(request, "manage_retrieval_admin", {
18367
+ fallback: "Retrieval release incident acknowledgement is not allowed"
18368
+ });
18369
+ if (denied) {
18370
+ set.status = 403;
18371
+ if (config.htmx && isHTMXRequest(request)) {
18372
+ return toHTMXResponse(workflowRenderers.error(denied.error ?? "Retrieval release incident acknowledgement failed"), getNumericStatus(set.status));
18373
+ }
18374
+ return denied;
18375
+ }
17652
18376
  const result = await handleAcknowledgeRetrievalReleaseIncident(body);
17653
18377
  if (!result.ok) {
17654
18378
  set.status = HTTP_STATUS_BAD_REQUEST;
@@ -17664,6 +18388,16 @@ var ragChat = (config) => {
17664
18388
  }
17665
18389
  return result;
17666
18390
  }).post(`${path}/compare/retrieval/incidents/unacknowledge`, async ({ body, request, set }) => {
18391
+ const denied = await authorizeMutationRoute(request, "manage_retrieval_admin", {
18392
+ fallback: "Retrieval release incident unacknowledge is not allowed"
18393
+ });
18394
+ if (denied) {
18395
+ set.status = 403;
18396
+ if (config.htmx && isHTMXRequest(request)) {
18397
+ return toHTMXResponse(workflowRenderers.error(denied.error ?? "Retrieval release incident unacknowledge failed"), getNumericStatus(set.status));
18398
+ }
18399
+ return denied;
18400
+ }
17667
18401
  const result = await handleUnacknowledgeRetrievalReleaseIncident(body);
17668
18402
  if (!result.ok) {
17669
18403
  set.status = HTTP_STATUS_BAD_REQUEST;
@@ -17679,6 +18413,16 @@ var ragChat = (config) => {
17679
18413
  }
17680
18414
  return result;
17681
18415
  }).post(`${path}/compare/retrieval/incidents/resolve`, async ({ body, request, set }) => {
18416
+ const denied = await authorizeMutationRoute(request, "manage_retrieval_admin", {
18417
+ fallback: "Retrieval release incident resolve is not allowed"
18418
+ });
18419
+ if (denied) {
18420
+ set.status = 403;
18421
+ if (config.htmx && isHTMXRequest(request)) {
18422
+ return toHTMXResponse(workflowRenderers.error(denied.error ?? "Retrieval release incident resolve failed"), getNumericStatus(set.status));
18423
+ }
18424
+ return denied;
18425
+ }
17682
18426
  const result = await handleResolveRetrievalReleaseIncident(body);
17683
18427
  if (!result.ok) {
17684
18428
  set.status = HTTP_STATUS_BAD_REQUEST;
@@ -17709,7 +18453,15 @@ var ragChat = (config) => {
17709
18453
  }
17710
18454
  return result;
17711
18455
  }).post(`${path}/compare/retrieval/baselines/approve`, async ({ body, request, set }) => {
17712
- const result = await handleRetrievalReleaseDecisionAction(body, "approve");
18456
+ const denied = await authorizeMutationRoute(request, "manage_retrieval_admin", { fallback: "Retrieval approval is not allowed" });
18457
+ if (denied) {
18458
+ set.status = 403;
18459
+ if (config.htmx && isHTMXRequest(request)) {
18460
+ return toHTMXResponse(workflowRenderers.error(denied.error ?? "Retrieval approval failed"), getNumericStatus(set.status));
18461
+ }
18462
+ return denied;
18463
+ }
18464
+ const result = await handleRetrievalReleaseDecisionAction(body, "approve", request);
17713
18465
  if (!result.ok) {
17714
18466
  set.status = HTTP_STATUS_BAD_REQUEST;
17715
18467
  }
@@ -17724,7 +18476,15 @@ var ragChat = (config) => {
17724
18476
  }
17725
18477
  return result;
17726
18478
  }).post(`${path}/compare/retrieval/baselines/reject`, async ({ body, request, set }) => {
17727
- const result = await handleRetrievalReleaseDecisionAction(body, "reject");
18479
+ const denied = await authorizeMutationRoute(request, "manage_retrieval_admin", { fallback: "Retrieval rejection is not allowed" });
18480
+ if (denied) {
18481
+ set.status = 403;
18482
+ if (config.htmx && isHTMXRequest(request)) {
18483
+ return toHTMXResponse(workflowRenderers.error(denied.error ?? "Retrieval rejection failed"), getNumericStatus(set.status));
18484
+ }
18485
+ return denied;
18486
+ }
18487
+ const result = await handleRetrievalReleaseDecisionAction(body, "reject", request);
17728
18488
  if (!result.ok) {
17729
18489
  set.status = HTTP_STATUS_BAD_REQUEST;
17730
18490
  }
@@ -17739,7 +18499,15 @@ var ragChat = (config) => {
17739
18499
  }
17740
18500
  return result;
17741
18501
  }).post(`${path}/compare/retrieval/baselines/promote`, async ({ body, request, set }) => {
17742
- const result = await handlePromoteRetrievalBaseline(body);
18502
+ const denied = await authorizeMutationRoute(request, "manage_retrieval_admin", { fallback: "Retrieval baseline promotion is not allowed" });
18503
+ if (denied) {
18504
+ set.status = 403;
18505
+ if (config.htmx && isHTMXRequest(request)) {
18506
+ return toHTMXResponse(workflowRenderers.error(denied.error ?? "Retrieval baseline promotion failed"), getNumericStatus(set.status));
18507
+ }
18508
+ return denied;
18509
+ }
18510
+ const result = await handlePromoteRetrievalBaseline(body, request);
17743
18511
  if (!result.ok) {
17744
18512
  set.status = HTTP_STATUS_BAD_REQUEST;
17745
18513
  }
@@ -17754,6 +18522,16 @@ var ragChat = (config) => {
17754
18522
  }
17755
18523
  return result;
17756
18524
  }).post(`${path}/compare/retrieval/baselines/promote-lane`, async ({ body, request, set }) => {
18525
+ const denied = await authorizeMutationRoute(request, "manage_retrieval_admin", {
18526
+ fallback: "Retrieval rollout-lane promotion is not allowed"
18527
+ });
18528
+ if (denied) {
18529
+ set.status = 403;
18530
+ if (config.htmx && isHTMXRequest(request)) {
18531
+ return toHTMXResponse(workflowRenderers.error(denied.error ?? "Retrieval rollout-lane promotion failed"), getNumericStatus(set.status));
18532
+ }
18533
+ return denied;
18534
+ }
17757
18535
  const result = await handlePromoteRetrievalBaselineToLane(body);
17758
18536
  if (!result.ok) {
17759
18537
  set.status = HTTP_STATUS_BAD_REQUEST;
@@ -17769,7 +18547,17 @@ var ragChat = (config) => {
17769
18547
  }
17770
18548
  return result;
17771
18549
  }).post(`${path}/compare/retrieval/baselines/promote-run`, async ({ body, request, set }) => {
17772
- const result = await handlePromoteRetrievalBaselineFromRun(body);
18550
+ const denied = await authorizeMutationRoute(request, "manage_retrieval_admin", {
18551
+ fallback: "Retrieval baseline promotion from run is not allowed"
18552
+ });
18553
+ if (denied) {
18554
+ set.status = 403;
18555
+ if (config.htmx && isHTMXRequest(request)) {
18556
+ return toHTMXResponse(workflowRenderers.error(denied.error ?? "Retrieval baseline promotion from run failed"), getNumericStatus(set.status));
18557
+ }
18558
+ return denied;
18559
+ }
18560
+ const result = await handlePromoteRetrievalBaselineFromRun(body, request);
17773
18561
  if (!result.ok) {
17774
18562
  set.status = HTTP_STATUS_BAD_REQUEST;
17775
18563
  }
@@ -17784,7 +18572,15 @@ var ragChat = (config) => {
17784
18572
  }
17785
18573
  return result;
17786
18574
  }).post(`${path}/compare/retrieval/baselines/revert`, async ({ body, request, set }) => {
17787
- const result = await handleRevertRetrievalBaseline(body);
18575
+ const denied = await authorizeMutationRoute(request, "manage_retrieval_admin", { fallback: "Retrieval baseline revert is not allowed" });
18576
+ if (denied) {
18577
+ set.status = 403;
18578
+ if (config.htmx && isHTMXRequest(request)) {
18579
+ return toHTMXResponse(workflowRenderers.error(denied.error ?? "Retrieval baseline revert failed"), getNumericStatus(set.status));
18580
+ }
18581
+ return denied;
18582
+ }
18583
+ const result = await handleRevertRetrievalBaseline(body, request);
17788
18584
  if (!result.ok) {
17789
18585
  set.status = HTTP_STATUS_BAD_REQUEST;
17790
18586
  }
@@ -17799,7 +18595,7 @@ var ragChat = (config) => {
17799
18595
  }
17800
18596
  return result;
17801
18597
  }).post(`${path}/evaluate`, async ({ body, request, set }) => {
17802
- const result = await handleEvaluate(body);
18598
+ const result = await handleEvaluate(body, request);
17803
18599
  if (!result.ok) {
17804
18600
  set.status = HTTP_STATUS_BAD_REQUEST;
17805
18601
  }
@@ -17814,7 +18610,7 @@ var ragChat = (config) => {
17814
18610
  }
17815
18611
  return result;
17816
18612
  }).get(`${path}/status`, async ({ request }) => {
17817
- const result = await handleStatus();
18613
+ const result = await handleStatus(request);
17818
18614
  if (config.htmx && isHTMXRequest(request)) {
17819
18615
  return toHTMXResponse(workflowRenderers.status({
17820
18616
  capabilities: result.capabilities,
@@ -17883,20 +18679,21 @@ var ragChat = (config) => {
17883
18679
  });
17884
18680
  }
17885
18681
  return result;
17886
- }).get(`${path}/status/release`, async () => {
17887
- return handleRetrievalReleaseStatus();
18682
+ }).get(`${path}/status/release`, async ({ request }) => {
18683
+ return handleRetrievalReleaseStatus(request);
17888
18684
  }).get(`${path}/status/release/incidents`, async () => {
17889
18685
  return handleRetrievalReleaseIncidentStatus();
17890
18686
  }).get(`${path}/status/release/remediations`, async () => {
17891
18687
  return handleRetrievalIncidentRemediationStatus();
17892
- }).get(`${path}/status/release/drift`, async () => {
17893
- return handleRetrievalReleaseDriftStatus();
17894
- }).get(`${path}/status/handoffs/incidents`, async () => {
17895
- return handleRetrievalLaneHandoffIncidentStatus();
17896
- }).get(`${path}/status/handoffs`, async () => {
17897
- return handleRetrievalLaneHandoffStatus();
18688
+ }).get(`${path}/status/release/drift`, async ({ request }) => {
18689
+ return handleRetrievalReleaseDriftStatus(request);
18690
+ }).get(`${path}/status/handoffs/incidents`, async ({ request }) => {
18691
+ return handleRetrievalLaneHandoffIncidentStatus(request);
18692
+ }).get(`${path}/status/handoffs`, async ({ request }) => {
18693
+ return handleRetrievalLaneHandoffStatus(request);
17898
18694
  }).get(`${path}/ops`, async ({ request }) => {
17899
- const result = await handleOps();
18695
+ await ensureJobStateLoaded();
18696
+ const result = await handleOps(request);
17900
18697
  if (config.htmx && isHTMXRequest(request)) {
17901
18698
  return toHTMXResponse(workflowRenderers.status({
17902
18699
  capabilities: result.capabilities,
@@ -17906,7 +18703,7 @@ var ragChat = (config) => {
17906
18703
  }
17907
18704
  return result;
17908
18705
  }).get(`${path}/documents`, async ({ query, request, set }) => {
17909
- const result = await handleDocuments(getStringProperty(query, "kind"));
18706
+ const result = await handleDocuments(getStringProperty(query, "kind"), request);
17910
18707
  if (!result.ok) {
17911
18708
  set.status = HTTP_STATUS_NOT_FOUND;
17912
18709
  }
@@ -17920,9 +18717,19 @@ var ragChat = (config) => {
17920
18717
  }
17921
18718
  return result;
17922
18719
  }).post(`${path}/documents`, async ({ body, request, set }) => {
17923
- const result = await handleCreateDocument(body);
18720
+ await ensureJobStateLoaded();
18721
+ const authorization = await checkAuthorization(request, "create_document");
18722
+ if (!authorization.allowed) {
18723
+ set.status = 403;
18724
+ const result2 = buildAuthorizationFailure(authorization, "Document creation is not allowed");
18725
+ if (config.htmx && isHTMXRequest(request)) {
18726
+ return toHTMXResponse(workflowRenderers.error(result2.error ?? "Failed to create document"), getNumericStatus(set.status));
18727
+ }
18728
+ return result2;
18729
+ }
18730
+ const result = await handleCreateDocument(body, request);
17924
18731
  if (!result.ok) {
17925
- const status = result.error?.includes("not configured") ? HTTP_STATUS_NOT_FOUND : HTTP_STATUS_BAD_REQUEST;
18732
+ const status = isAccessScopeError(result.error) ? 403 : result.error?.includes("not configured") ? HTTP_STATUS_NOT_FOUND : HTTP_STATUS_BAD_REQUEST;
17926
18733
  set.status = status;
17927
18734
  }
17928
18735
  if (config.htmx && isHTMXRequest(request)) {
@@ -17933,9 +18740,9 @@ var ragChat = (config) => {
17933
18740
  }
17934
18741
  return result;
17935
18742
  }).get(`${path}/documents/:id/chunks`, async ({ params, request, set }) => {
17936
- const result = await handleDocumentChunks(typeof params.id === "string" ? params.id.trim() : "");
18743
+ const result = await handleDocumentChunks(typeof params.id === "string" ? params.id.trim() : "", request);
17937
18744
  if (!result.ok) {
17938
- const status = result.error === "document id is required" ? HTTP_STATUS_BAD_REQUEST : HTTP_STATUS_NOT_FOUND;
18745
+ const status = isAccessScopeError(result.error) ? 403 : result.error === "document id is required" ? HTTP_STATUS_BAD_REQUEST : HTTP_STATUS_NOT_FOUND;
17939
18746
  set.status = status;
17940
18747
  }
17941
18748
  if (config.htmx && isHTMXRequest(request)) {
@@ -17949,10 +18756,13 @@ var ragChat = (config) => {
17949
18756
  const result = await handleBackends();
17950
18757
  if (!result.ok) {
17951
18758
  set.status = HTTP_STATUS_NOT_FOUND;
18759
+ if (isAccessScopeError(result.error)) {
18760
+ set.status = 403;
18761
+ }
17952
18762
  }
17953
18763
  return result;
17954
18764
  }).get(`${path}/sync`, async ({ request, set }) => {
17955
- const result = await handleSyncSources();
18765
+ const result = await handleSyncSources(request);
17956
18766
  if (!result.ok) {
17957
18767
  set.status = HTTP_STATUS_NOT_FOUND;
17958
18768
  }
@@ -17967,8 +18777,18 @@ var ragChat = (config) => {
17967
18777
  }
17968
18778
  return result;
17969
18779
  }).post(`${path}/sync`, async ({ body, request, set }) => {
18780
+ await ensureJobStateLoaded();
18781
+ const authorization = await checkAuthorization(request, "sync_all_sources");
18782
+ if (!authorization.allowed) {
18783
+ set.status = 403;
18784
+ const result2 = buildAuthorizationFailure(authorization, "Source sync is not allowed");
18785
+ if (config.htmx && isHTMXRequest(request)) {
18786
+ return toHTMXResponse(workflowRenderers.error(result2.error ?? "Failed to sync sources"), getNumericStatus(set.status));
18787
+ }
18788
+ return result2;
18789
+ }
17970
18790
  const background = getBooleanProperty(body, "background");
17971
- const result = await handleSyncAllSources({ background });
18791
+ const result = await handleSyncAllSources(request, { background });
17972
18792
  if (!result.ok) {
17973
18793
  set.status = HTTP_STATUS_NOT_FOUND;
17974
18794
  }
@@ -17983,10 +18803,28 @@ var ragChat = (config) => {
17983
18803
  }
17984
18804
  return result;
17985
18805
  }).post(`${path}/sync/:id`, async ({ body, params, request, set }) => {
18806
+ await ensureJobStateLoaded();
18807
+ const syncSourceId = typeof params.id === "string" ? params.id.trim() : "";
18808
+ const authorization = await checkAuthorization(request, "sync_source", {
18809
+ sourceId: syncSourceId
18810
+ });
18811
+ if (!authorization.allowed) {
18812
+ set.status = 403;
18813
+ const result2 = buildAuthorizationFailure(authorization, "Source sync is not allowed");
18814
+ if (config.htmx && isHTMXRequest(request)) {
18815
+ return toHTMXResponse(workflowRenderers.error(result2.error ?? "Failed to sync source"), getNumericStatus(set.status));
18816
+ }
18817
+ return result2;
18818
+ }
17986
18819
  const background = getBooleanProperty(body, "background");
17987
- const result = await handleSyncSource(typeof params.id === "string" ? params.id.trim() : "", { background });
18820
+ const result = await handleSyncSource(syncSourceId, request, {
18821
+ background
18822
+ });
17988
18823
  if (!result.ok) {
17989
18824
  set.status = result.error === "sync source id is required" ? HTTP_STATUS_BAD_REQUEST : HTTP_STATUS_NOT_FOUND;
18825
+ if (isAccessScopeError(result.error)) {
18826
+ set.status = 403;
18827
+ }
17990
18828
  }
17991
18829
  if (config.htmx && isHTMXRequest(request)) {
17992
18830
  const html = result.ok ? workflowRenderers.mutationResult({
@@ -17999,6 +18837,18 @@ var ragChat = (config) => {
17999
18837
  }
18000
18838
  return result;
18001
18839
  }).post(`${path}/ingest`, async ({ body, request, set }) => {
18840
+ await ensureJobStateLoaded();
18841
+ const authorization = await checkAuthorization(request, "ingest", {
18842
+ path: `${path}/ingest`
18843
+ });
18844
+ if (!authorization.allowed) {
18845
+ set.status = 403;
18846
+ const result2 = buildAuthorizationFailure(authorization, "RAG ingest is not allowed");
18847
+ if (config.htmx && isHTMXRequest(request)) {
18848
+ return toHTMXResponse(workflowRenderers.error(result2.error ?? "RAG ingest failed"), getNumericStatus(set.status));
18849
+ }
18850
+ return result2;
18851
+ }
18002
18852
  const result = await handleIngest(body);
18003
18853
  if (!result.ok) {
18004
18854
  set.status = HTTP_STATUS_BAD_REQUEST;
@@ -18010,7 +18860,13 @@ var ragChat = (config) => {
18010
18860
  return toHTMXResponse(workflowRenderers.mutationResult(result), HTTP_STATUS_OK, { "HX-Trigger": "rag:mutated" });
18011
18861
  }
18012
18862
  return result;
18013
- }).delete(`${path}/index`, async () => {
18863
+ }).delete(`${path}/index`, async ({ request, set }) => {
18864
+ await ensureJobStateLoaded();
18865
+ const authorization = await checkAuthorization(request, "clear_index");
18866
+ if (!authorization.allowed) {
18867
+ set.status = 403;
18868
+ return buildAuthorizationFailure(authorization, "Index clearing is not allowed");
18869
+ }
18014
18870
  if (!ragStore) {
18015
18871
  return { ok: false };
18016
18872
  }
@@ -18029,9 +18885,22 @@ var ragChat = (config) => {
18029
18885
  }
18030
18886
  return { ok: true };
18031
18887
  }).delete(`${path}/documents/:id`, async ({ params, request, set }) => {
18032
- const result = await handleDeleteDocument(typeof params.id === "string" ? params.id.trim() : "");
18888
+ await ensureJobStateLoaded();
18889
+ const documentId = typeof params.id === "string" ? params.id.trim() : "";
18890
+ const authorization = await checkAuthorization(request, "delete_document", {
18891
+ documentId
18892
+ });
18893
+ if (!authorization.allowed) {
18894
+ set.status = 403;
18895
+ const result2 = buildAuthorizationFailure(authorization, "Document deletion is not allowed");
18896
+ if (config.htmx && isHTMXRequest(request)) {
18897
+ return toHTMXResponse(workflowRenderers.error(result2.error ?? "Failed to delete document"), getNumericStatus(set.status), { "HX-Trigger": "rag:mutated" });
18898
+ }
18899
+ return result2;
18900
+ }
18901
+ const result = await handleDeleteDocument(documentId, request);
18033
18902
  if (!result.ok) {
18034
- const status = result.error === "document id is required" ? HTTP_STATUS_BAD_REQUEST : HTTP_STATUS_NOT_FOUND;
18903
+ const status = isAccessScopeError(result.error) ? 403 : result.error === "document id is required" ? HTTP_STATUS_BAD_REQUEST : HTTP_STATUS_NOT_FOUND;
18035
18904
  set.status = status;
18036
18905
  }
18037
18906
  if (config.htmx && isHTMXRequest(request)) {
@@ -18042,9 +18911,20 @@ var ragChat = (config) => {
18042
18911
  }
18043
18912
  return result;
18044
18913
  }).post(`${path}/reindex/documents/:id`, async ({ params, request, set }) => {
18045
- const result = await handleReindexDocument(typeof params.id === "string" ? params.id.trim() : "");
18914
+ await ensureJobStateLoaded();
18915
+ const documentId = typeof params.id === "string" ? params.id.trim() : "";
18916
+ const authorization = await checkAuthorization(request, "reindex_document", { documentId });
18917
+ if (!authorization.allowed) {
18918
+ set.status = 403;
18919
+ const result2 = buildAuthorizationFailure(authorization, "Document reindex is not allowed");
18920
+ if (config.htmx && isHTMXRequest(request)) {
18921
+ return toHTMXResponse(workflowRenderers.error(result2.error ?? "Failed to reindex document"), getNumericStatus(set.status), { "HX-Trigger": "rag:mutated" });
18922
+ }
18923
+ return result2;
18924
+ }
18925
+ const result = await handleReindexDocument(documentId, request);
18046
18926
  if (!result.ok) {
18047
- set.status = result.error === "document id is required" ? HTTP_STATUS_BAD_REQUEST : HTTP_STATUS_NOT_FOUND;
18927
+ set.status = isAccessScopeError(result.error) ? 403 : result.error === "document id is required" ? HTTP_STATUS_BAD_REQUEST : HTTP_STATUS_NOT_FOUND;
18048
18928
  }
18049
18929
  if (config.htmx && isHTMXRequest(request)) {
18050
18930
  const html = result.ok ? workflowRenderers.mutationResult(result) : workflowRenderers.error(result.error ?? "Failed to reindex document");
@@ -18054,10 +18934,22 @@ var ragChat = (config) => {
18054
18934
  }
18055
18935
  return result;
18056
18936
  }).post(`${path}/reindex/source`, async ({ body, request, set }) => {
18937
+ await ensureJobStateLoaded();
18057
18938
  const source = getStringProperty(body, "source")?.trim() ?? "";
18058
- const result = await handleReindexSource(source);
18939
+ const authorization = await checkAuthorization(request, "reindex_source", {
18940
+ source
18941
+ });
18942
+ if (!authorization.allowed) {
18943
+ set.status = 403;
18944
+ const result2 = buildAuthorizationFailure(authorization, "Source reindex is not allowed");
18945
+ if (config.htmx && isHTMXRequest(request)) {
18946
+ return toHTMXResponse(workflowRenderers.error(result2.error ?? "Failed to reindex source"), getNumericStatus(set.status), { "HX-Trigger": "rag:mutated" });
18947
+ }
18948
+ return result2;
18949
+ }
18950
+ const result = await handleReindexSource(source, request);
18059
18951
  if (!result.ok) {
18060
- set.status = result.error === "source is required" ? HTTP_STATUS_BAD_REQUEST : HTTP_STATUS_NOT_FOUND;
18952
+ set.status = isAccessScopeError(result.error) ? 403 : result.error === "source is required" ? HTTP_STATUS_BAD_REQUEST : HTTP_STATUS_NOT_FOUND;
18061
18953
  }
18062
18954
  if (config.htmx && isHTMXRequest(request)) {
18063
18955
  const html = result.ok ? workflowRenderers.mutationResult(result) : workflowRenderers.error(result.error ?? "Failed to reindex source");
@@ -18067,6 +18959,16 @@ var ragChat = (config) => {
18067
18959
  }
18068
18960
  return result;
18069
18961
  }).post(`${path}/reseed`, async ({ request, set }) => {
18962
+ await ensureJobStateLoaded();
18963
+ const authorization = await checkAuthorization(request, "reseed");
18964
+ if (!authorization.allowed) {
18965
+ set.status = 403;
18966
+ const result2 = buildAuthorizationFailure(authorization, "Index reseed is not allowed");
18967
+ if (config.htmx && isHTMXRequest(request)) {
18968
+ return toHTMXResponse(workflowRenderers.error(result2.error ?? "Failed to reseed index"), getNumericStatus(set.status), { "HX-Trigger": "rag:mutated" });
18969
+ }
18970
+ return result2;
18971
+ }
18070
18972
  const result = await handleReseed();
18071
18973
  if (!result.ok) {
18072
18974
  set.status = 404;
@@ -18079,6 +18981,16 @@ var ragChat = (config) => {
18079
18981
  }
18080
18982
  return result;
18081
18983
  }).post(`${path}/reset`, async ({ request, set }) => {
18984
+ await ensureJobStateLoaded();
18985
+ const authorization = await checkAuthorization(request, "reset");
18986
+ if (!authorization.allowed) {
18987
+ set.status = 403;
18988
+ const result2 = buildAuthorizationFailure(authorization, "Index reset is not allowed");
18989
+ if (config.htmx && isHTMXRequest(request)) {
18990
+ return toHTMXResponse(workflowRenderers.error(result2.error ?? "Failed to reset index"), getNumericStatus(set.status), { "HX-Trigger": "rag:mutated" });
18991
+ }
18992
+ return result2;
18993
+ }
18082
18994
  const result = await handleReset();
18083
18995
  if (!result.ok) {
18084
18996
  set.status = 404;
@@ -18108,6 +19020,89 @@ var ragChat = (config) => {
18108
19020
  // src/ai/rag/htmxConfig.ts
18109
19021
  var createRAGHTMXConfig = (config) => config;
18110
19022
  var createRAGHTMXWorkflowRenderConfig = (config) => config;
19023
+ // src/ai/rag/retrievalStrategies.ts
19024
+ var tokenize4 = (value) => value.toLowerCase().split(/[^a-z0-9]+/i).map((token) => token.trim()).filter((token) => token.length > 0);
19025
+ var hasAnyToken2 = (tokens, values) => values.some((value) => tokens.includes(value));
19026
+ var createHeuristicRAGRetrievalStrategy = (options = {}) => ({
19027
+ defaultLabel: options.defaultLabel ?? "Heuristic retrieval routing",
19028
+ providerName: options.providerName ?? "heuristic_retrieval_strategy",
19029
+ select: (input) => {
19030
+ const scopedSource = typeof input.filter?.source === "string" && input.filter.source.trim().length > 0;
19031
+ const scopedDocumentId = typeof input.filter?.documentId === "string" && input.filter.documentId.trim().length > 0;
19032
+ if ((scopedSource || scopedDocumentId) && input.retrieval.mode !== "vector") {
19033
+ return {
19034
+ label: "Scoped direct route",
19035
+ mode: "vector",
19036
+ reason: scopedDocumentId ? "documentId filter narrows retrieval to one target document" : "source filter narrows retrieval to one source family",
19037
+ metadata: {
19038
+ selector: "scoped_direct_route"
19039
+ }
19040
+ };
19041
+ }
19042
+ const tokens = tokenize4(input.query);
19043
+ const transformedTokens = tokenize4(input.transformedQuery);
19044
+ const combined = Array.from(new Set([...tokens, ...transformedTokens]));
19045
+ const hasVariants = input.variantQueries.length > 0;
19046
+ const supportLexical = hasAnyToken2(combined, ["faq", "policy", "password", "billing"]) && !hasVariants;
19047
+ if (supportLexical) {
19048
+ return {
19049
+ label: "Support lexical route",
19050
+ mode: "lexical",
19051
+ reason: "faq/support phrase matched",
19052
+ metadata: {
19053
+ selector: "support_lexical"
19054
+ }
19055
+ };
19056
+ }
19057
+ const sourceNativeHybrid = hasVariants || hasAnyToken2(combined, [
19058
+ "sheet",
19059
+ "worksheet",
19060
+ "workbook",
19061
+ "spreadsheet",
19062
+ "timestamp",
19063
+ "transcript",
19064
+ "attachment",
19065
+ "archive"
19066
+ ]);
19067
+ if (sourceNativeHybrid && input.retrieval.mode === "vector") {
19068
+ return {
19069
+ label: "Source-native hybrid route",
19070
+ mode: "hybrid",
19071
+ lexicalTopK: Math.max(input.topK, Math.floor(input.retrieval.lexicalTopK ?? input.candidateTopK)),
19072
+ reason: hasVariants ? "query expansion introduced source-native variants" : "source-native terminology benefits from hybrid retrieval",
19073
+ metadata: {
19074
+ selector: "source_native_hybrid"
19075
+ }
19076
+ };
19077
+ }
19078
+ return;
19079
+ }
19080
+ });
19081
+ // src/ai/rag/accessControl.ts
19082
+ var createRAGAccessControl = (options) => {
19083
+ const authorize = options.authorize;
19084
+ const contextCache = new WeakMap;
19085
+ const resolveScope = options.resolveScope;
19086
+ const loadContext = (request) => {
19087
+ const existing = contextCache.get(request);
19088
+ if (existing) {
19089
+ return existing;
19090
+ }
19091
+ const next = Promise.resolve(options.resolveContext(request));
19092
+ contextCache.set(request, next);
19093
+ return next;
19094
+ };
19095
+ return {
19096
+ authorizeRAGAction: authorize ? async (input) => authorize({
19097
+ ...input,
19098
+ context: await loadContext(input.request)
19099
+ }) : undefined,
19100
+ resolveRAGAccessScope: resolveScope ? async (request) => resolveScope({
19101
+ context: await loadContext(request),
19102
+ request
19103
+ }) : undefined
19104
+ };
19105
+ };
18111
19106
  // src/ai/rag/embeddingProviders.ts
18112
19107
  var DEFAULT_OPENAI_BASE_URL = "https://api.openai.com";
18113
19108
  var DEFAULT_GEMINI_BASE_URL = "https://generativelanguage.googleapis.com";
@@ -18965,6 +19960,8 @@ var isManagedBySyncSource = (document, sourceId) => document.metadata?.syncSourc
18965
19960
  var getDocumentSyncFingerprint = (document) => typeof document.metadata?.syncFingerprint === "string" ? document.metadata.syncFingerprint : undefined;
18966
19961
  var reconcileManagedDocuments = async (input) => {
18967
19962
  const prepared = prepareRAGDocuments({
19963
+ chunkingRegistry: input.chunkingRegistry,
19964
+ defaultChunking: input.defaultChunking,
18968
19965
  documents: input.documents
18969
19966
  });
18970
19967
  const nextDocumentIds = new Set(prepared.map((document) => document.documentId));
@@ -19009,6 +20006,19 @@ var toSourceRecord = (source, overrides) => ({
19009
20006
  target: source.target,
19010
20007
  ...overrides
19011
20008
  });
20009
+ var recoverSyncSourceRecord = (source, record, recoveredAt) => record.status === "running" ? toSourceRecord(source, {
20010
+ ...record,
20011
+ lastError: record.lastError ?? "Interrupted before completion during recovery",
20012
+ lastSyncedAt: recoveredAt,
20013
+ nextRetryAt: undefined,
20014
+ status: "failed"
20015
+ }) : toSourceRecord(source, {
20016
+ ...record,
20017
+ metadata: {
20018
+ ...source.metadata ?? {},
20019
+ ...record.metadata ?? {}
20020
+ }
20021
+ });
19012
20022
  var createRAGDirectorySyncSource = (options) => ({
19013
20023
  description: options.description,
19014
20024
  id: options.id,
@@ -19022,14 +20032,18 @@ var createRAGDirectorySyncSource = (options) => ({
19022
20032
  const loaded = await loadRAGDocumentsFromDirectory({
19023
20033
  baseMetadata: options.baseMetadata,
19024
20034
  defaultChunking: options.defaultChunking,
20035
+ chunkingRegistry: options.chunkingRegistry,
19025
20036
  directory: options.directory,
19026
20037
  extractors: options.extractors,
20038
+ extractorRegistry: options.extractorRegistry,
19027
20039
  includeExtensions: options.includeExtensions,
19028
20040
  recursive: options.recursive
19029
20041
  });
19030
20042
  const managedDocuments = loaded.documents.map((document) => toManagedSyncDocument(options.id, document, typeof document.metadata?.relativePath === "string" ? document.metadata.relativePath : document.source ?? document.title ?? ""));
19031
20043
  const reconciled = await reconcileManagedDocuments({
20044
+ chunkingRegistry: options.chunkingRegistry,
19032
20045
  collection,
20046
+ defaultChunking: options.defaultChunking,
19033
20047
  deleteDocument,
19034
20048
  documents: managedDocuments,
19035
20049
  listDocuments,
@@ -19060,12 +20074,16 @@ var createRAGUrlSyncSource = (options) => ({
19060
20074
  const loaded = await loadRAGDocumentsFromURLs({
19061
20075
  baseMetadata: options.baseMetadata,
19062
20076
  defaultChunking: options.defaultChunking,
20077
+ chunkingRegistry: options.chunkingRegistry,
19063
20078
  extractors: options.extractors,
20079
+ extractorRegistry: options.extractorRegistry,
19064
20080
  urls: options.urls
19065
20081
  });
19066
20082
  const managedDocuments = loaded.documents.map((document) => toManagedSyncDocument(options.id, document, typeof document.metadata?.sourceUrl === "string" ? document.metadata.sourceUrl : document.source ?? document.title ?? ""));
19067
20083
  const reconciled = await reconcileManagedDocuments({
20084
+ chunkingRegistry: options.chunkingRegistry,
19068
20085
  collection,
20086
+ defaultChunking: options.defaultChunking,
19069
20087
  deleteDocument,
19070
20088
  documents: managedDocuments,
19071
20089
  listDocuments,
@@ -19163,12 +20181,16 @@ var createRAGStorageSyncSource = (options) => ({
19163
20181
  const loaded = await loadRAGDocumentsFromUploads({
19164
20182
  baseMetadata: options.baseMetadata,
19165
20183
  defaultChunking: options.defaultChunking,
20184
+ chunkingRegistry: options.chunkingRegistry,
19166
20185
  extractors: options.extractors,
20186
+ extractorRegistry: options.extractorRegistry,
19167
20187
  uploads
19168
20188
  });
19169
20189
  const managedDocuments = loaded.documents.map((document) => toManagedSyncDocument(options.id, document, typeof document.metadata?.storageKey === "string" ? document.metadata.storageKey : document.source ?? document.title ?? ""));
19170
20190
  const reconciled = await reconcileManagedDocuments({
20191
+ chunkingRegistry: options.chunkingRegistry,
19171
20192
  collection,
20193
+ defaultChunking: options.defaultChunking,
19172
20194
  deleteDocument,
19173
20195
  documents: managedDocuments,
19174
20196
  listDocuments,
@@ -19249,7 +20271,9 @@ var createRAGEmailSyncSource = (options) => ({
19249
20271
  const loadedAttachments = attachmentUploads.length > 0 ? await loadRAGDocumentsFromUploads({
19250
20272
  baseMetadata: options.baseMetadata,
19251
20273
  defaultChunking: options.defaultChunking,
20274
+ chunkingRegistry: options.chunkingRegistry,
19252
20275
  extractors: options.extractors,
20276
+ extractorRegistry: options.extractorRegistry,
19253
20277
  uploads: attachmentUploads
19254
20278
  }) : { documents: [] };
19255
20279
  const managedDocuments = [
@@ -19257,7 +20281,9 @@ var createRAGEmailSyncSource = (options) => ({
19257
20281
  ...loadedAttachments.documents.map((document) => toManagedSyncDocument(options.id, document, `attachment:${String(document.metadata?.attachmentId ?? document.source ?? document.title ?? "")}`))
19258
20282
  ];
19259
20283
  const reconciled = await reconcileManagedDocuments({
20284
+ chunkingRegistry: options.chunkingRegistry,
19260
20285
  collection,
20286
+ defaultChunking: options.defaultChunking,
19261
20287
  deleteDocument,
19262
20288
  documents: managedDocuments,
19263
20289
  listDocuments,
@@ -19292,22 +20318,18 @@ var createRAGSyncManager = (options) => {
19292
20318
  }
19293
20319
  if (!hydrationPromise) {
19294
20320
  hydrationPromise = Promise.resolve(options.loadState()).then((records) => {
20321
+ const recoveredAt = Date.now();
19295
20322
  for (const record of records ?? []) {
19296
20323
  const source = sourceMap.get(record.id);
19297
20324
  if (!source) {
19298
20325
  continue;
19299
20326
  }
19300
- state.set(record.id, toSourceRecord(source, {
19301
- ...record,
19302
- metadata: {
19303
- ...source.metadata ?? {},
19304
- ...record.metadata ?? {}
19305
- }
19306
- }));
20327
+ state.set(record.id, recoverSyncSourceRecord(source, record, recoveredAt));
19307
20328
  }
19308
20329
  });
19309
20330
  }
19310
20331
  await hydrationPromise;
20332
+ await persistState();
19311
20333
  };
19312
20334
  const resolveRetryAttempts = (source) => Math.max(0, source.retryAttempts ?? options.retryAttempts ?? 0);
19313
20335
  const resolveRetryDelayMs = (source) => Math.max(0, source.retryDelayMs ?? options.retryDelayMs ?? 0);
@@ -19553,6 +20575,48 @@ var createRAGSyncScheduler = (input) => {
19553
20575
  listSchedules: () => [...input.schedules]
19554
20576
  };
19555
20577
  };
20578
+ // src/ai/rag/jobState.ts
20579
+ import { mkdir as mkdir3, readFile as readFile4, writeFile as writeFile3 } from "fs/promises";
20580
+ import { dirname as dirname3, resolve as resolve3 } from "path";
20581
+ var parseJobState = (content) => {
20582
+ try {
20583
+ const parsed = JSON.parse(content);
20584
+ return {
20585
+ adminActions: Array.isArray(parsed.adminActions) ? parsed.adminActions : [],
20586
+ adminJobs: Array.isArray(parsed.adminJobs) ? parsed.adminJobs : [],
20587
+ ingestJobs: Array.isArray(parsed.ingestJobs) ? parsed.ingestJobs : [],
20588
+ syncJobs: Array.isArray(parsed.syncJobs) ? parsed.syncJobs : []
20589
+ };
20590
+ } catch {
20591
+ return {
20592
+ adminActions: [],
20593
+ adminJobs: [],
20594
+ ingestJobs: [],
20595
+ syncJobs: []
20596
+ };
20597
+ }
20598
+ };
20599
+ var createRAGFileJobStateStore = (path) => {
20600
+ const resolvedPath = resolve3(path);
20601
+ return {
20602
+ load: async () => {
20603
+ try {
20604
+ return parseJobState(await readFile4(resolvedPath, "utf8"));
20605
+ } catch {
20606
+ return {
20607
+ adminActions: [],
20608
+ adminJobs: [],
20609
+ ingestJobs: [],
20610
+ syncJobs: []
20611
+ };
20612
+ }
20613
+ },
20614
+ save: async (state) => {
20615
+ await mkdir3(dirname3(resolvedPath), { recursive: true });
20616
+ await writeFile3(resolvedPath, JSON.stringify(state, null, 2), "utf8");
20617
+ }
20618
+ };
20619
+ };
19556
20620
  // src/ai/rag/adapters/utils.ts
19557
20621
  var vectorDimensionDefault = 24;
19558
20622
  var createRAGVector = (text, dimensions = vectorDimensionDefault) => {
@@ -19711,7 +20775,7 @@ import { existsSync as existsSync2 } from "fs";
19711
20775
  // src/ai/rag/resolveAbsoluteSQLiteVec.ts
19712
20776
  import { existsSync, readFileSync } from "fs";
19713
20777
  import { arch, platform } from "os";
19714
- import { dirname as dirname3, join as join2 } from "path";
20778
+ import { dirname as dirname4, join as join2 } from "path";
19715
20779
  var PLATFORM_PACKAGE_MAP = {
19716
20780
  "darwin-arm64": {
19717
20781
  libraryFile: "vec0.dylib",
@@ -19760,12 +20824,12 @@ var resolveAbsoluteSQLiteVec = () => {
19760
20824
  };
19761
20825
  }
19762
20826
  try {
19763
- const resolve3 = import.meta.resolve;
19764
- if (typeof resolve3 !== "function") {
20827
+ const resolve4 = import.meta.resolve;
20828
+ if (typeof resolve4 !== "function") {
19765
20829
  throw new Error("AbsoluteJS sqlite-vec package resolution requires import.meta.resolve support.");
19766
20830
  }
19767
- const packageJsonPath = new URL(resolve3(`${packageInfo.packageName}/package.json`)).pathname;
19768
- const packageRoot = dirname3(packageJsonPath);
20831
+ const packageJsonPath = new URL(resolve4(`${packageInfo.packageName}/package.json`)).pathname;
20832
+ const packageRoot = dirname4(packageJsonPath);
19769
20833
  const libraryPath = join2(packageRoot, packageInfo.libraryFile);
19770
20834
  const packageVersion = readPackageVersion(packageJsonPath);
19771
20835
  if (!existsSync(libraryPath)) {
@@ -20466,73 +21530,73 @@ var createConversationManager = () => {
20466
21530
  };
20467
21531
  };
20468
21532
  // src/ai/client/actions.ts
20469
- var serverMessageToAction = (msg) => {
20470
- switch (msg.type) {
21533
+ var serverMessageToAction = (message) => {
21534
+ switch (message.type) {
20471
21535
  case "chunk":
20472
21536
  return {
20473
- content: msg.content,
20474
- conversationId: msg.conversationId,
20475
- messageId: msg.messageId,
21537
+ content: message.content,
21538
+ conversationId: message.conversationId,
21539
+ messageId: message.messageId,
20476
21540
  type: "chunk"
20477
21541
  };
20478
21542
  case "thinking":
20479
21543
  return {
20480
- content: msg.content,
20481
- conversationId: msg.conversationId,
20482
- messageId: msg.messageId,
21544
+ content: message.content,
21545
+ conversationId: message.conversationId,
21546
+ messageId: message.messageId,
20483
21547
  type: "thinking"
20484
21548
  };
20485
21549
  case "tool_status":
20486
21550
  return {
20487
- conversationId: msg.conversationId,
20488
- input: msg.input,
20489
- messageId: msg.messageId,
20490
- name: msg.name,
20491
- result: msg.result,
20492
- status: msg.status,
21551
+ conversationId: message.conversationId,
21552
+ input: message.input,
21553
+ messageId: message.messageId,
21554
+ name: message.name,
21555
+ result: message.result,
21556
+ status: message.status,
20493
21557
  type: "tool_status"
20494
21558
  };
20495
21559
  case "image":
20496
21560
  return {
20497
- conversationId: msg.conversationId,
20498
- data: msg.data,
20499
- format: msg.format,
20500
- imageId: msg.imageId,
20501
- isPartial: msg.isPartial,
20502
- messageId: msg.messageId,
20503
- revisedPrompt: msg.revisedPrompt,
21561
+ conversationId: message.conversationId,
21562
+ data: message.data,
21563
+ format: message.format,
21564
+ imageId: message.imageId,
21565
+ isPartial: message.isPartial,
21566
+ messageId: message.messageId,
21567
+ revisedPrompt: message.revisedPrompt,
20504
21568
  type: "image"
20505
21569
  };
20506
21570
  case "complete":
20507
21571
  return {
20508
- conversationId: msg.conversationId,
20509
- durationMs: msg.durationMs,
20510
- messageId: msg.messageId,
20511
- model: msg.model,
20512
- sources: msg.sources,
21572
+ conversationId: message.conversationId,
21573
+ durationMs: message.durationMs,
21574
+ messageId: message.messageId,
21575
+ model: message.model,
21576
+ sources: message.sources,
20513
21577
  type: "complete",
20514
- usage: msg.usage
21578
+ usage: message.usage
20515
21579
  };
20516
21580
  case "rag_retrieving":
20517
21581
  return {
20518
- conversationId: msg.conversationId,
20519
- messageId: msg.messageId,
20520
- retrievalStartedAt: msg.retrievalStartedAt,
21582
+ conversationId: message.conversationId,
21583
+ messageId: message.messageId,
21584
+ retrievalStartedAt: message.retrievalStartedAt,
20521
21585
  type: "rag_retrieving"
20522
21586
  };
20523
21587
  case "rag_retrieved":
20524
21588
  return {
20525
- conversationId: msg.conversationId,
20526
- messageId: msg.messageId,
20527
- retrievalDurationMs: msg.retrievalDurationMs,
20528
- retrievalStartedAt: msg.retrievalStartedAt,
20529
- retrievedAt: msg.retrievedAt,
20530
- sources: msg.sources,
20531
- trace: msg.trace,
21589
+ conversationId: message.conversationId,
21590
+ messageId: message.messageId,
21591
+ retrievalDurationMs: message.retrievalDurationMs,
21592
+ retrievalStartedAt: message.retrievalStartedAt,
21593
+ retrievedAt: message.retrievedAt,
21594
+ sources: message.sources,
21595
+ trace: message.trace,
20532
21596
  type: "rag_retrieved"
20533
21597
  };
20534
21598
  case "error":
20535
- return { message: msg.message, type: "error" };
21599
+ return { message: message.message, type: "error" };
20536
21600
  default:
20537
21601
  return null;
20538
21602
  }
@@ -20991,8 +22055,8 @@ var createAIStream = (path, conversationId) => {
20991
22055
  listeners.forEach((listener) => listener());
20992
22056
  };
20993
22057
  const unsubscribeStore = store.subscribe(syncState);
20994
- const unsubscribeConnection = connection.subscribe((msg) => {
20995
- const action = serverMessageToAction(msg);
22058
+ const unsubscribeConnection = connection.subscribe((message) => {
22059
+ const action = serverMessageToAction(message);
20996
22060
  if (action) {
20997
22061
  store.dispatch(action);
20998
22062
  }
@@ -22351,6 +23415,8 @@ export {
22351
23415
  createRAGFileRetrievalComparisonHistoryStore,
22352
23416
  createRAGFileRetrievalBaselineStore,
22353
23417
  createRAGFileRetrievalBaselineGatePolicyHistoryStore,
23418
+ createRAGFileJobStateStore,
23419
+ createRAGFileExtractorRegistry,
22354
23420
  createRAGFileExtractor,
22355
23421
  createRAGFileEvaluationHistoryStore,
22356
23422
  createRAGFileAnswerGroundingEvaluationHistoryStore,
@@ -22361,14 +23427,17 @@ export {
22361
23427
  createRAGDirectorySyncSource,
22362
23428
  createRAGCollection,
22363
23429
  createRAGClient,
23430
+ createRAGChunkingRegistry,
22364
23431
  createRAGBunS3SyncClient,
22365
23432
  createRAGArchiveFileExtractor,
22366
23433
  createRAGArchiveExpander,
23434
+ createRAGAccessControl,
22367
23435
  createPDFFileExtractor,
22368
23436
  createOfficeDocumentExtractor,
22369
23437
  createMemoryStore,
22370
23438
  createLegacyDocumentExtractor,
22371
23439
  createInMemoryRAGStore,
23440
+ createHeuristicRAGRetrievalStrategy,
22372
23441
  createHeuristicRAGReranker,
22373
23442
  createHeuristicRAGQueryTransform,
22374
23443
  createEmailExtractor,
@@ -22382,56 +23451,21 @@ export {
22382
23451
  buildRAGUpsertInputFromURLs,
22383
23452
  buildRAGUpsertInputFromDocuments,
22384
23453
  buildRAGUpsertInputFromDirectory,
22385
- buildRAGSyncSourcePresentations,
22386
- buildRAGSyncSourcePresentation,
22387
- buildRAGSyncOverviewPresentation,
22388
- buildRAGSourceSummaries,
22389
- buildRAGSourceGroups,
22390
23454
  buildRAGSearchTraceRecord,
22391
23455
  buildRAGSearchTraceDiff,
22392
- buildRAGRetrievalTracePresentation,
22393
23456
  buildRAGRetrievalTraceHistoryTrend,
22394
23457
  buildRAGRetrievalReleaseVerdict,
22395
- buildRAGRetrievalOverviewPresentation,
22396
- buildRAGRetrievalComparisonPresentations,
22397
- buildRAGRetrievalComparisonOverviewPresentation,
22398
23458
  buildRAGRetrievalComparisonDecisionSummary,
22399
- buildRAGRerankerOverviewPresentation,
22400
- buildRAGRerankerComparisonPresentations,
22401
- buildRAGRerankerComparisonOverviewPresentation,
22402
- buildRAGReadinessPresentation,
22403
- buildRAGQualityOverviewPresentation,
22404
23459
  buildRAGLexicalHaystack,
22405
- buildRAGGroundingReferences,
22406
- buildRAGGroundingProviderPresentations,
22407
- buildRAGGroundingProviderOverviewPresentation,
22408
- buildRAGGroundingProviderCaseComparisonPresentations,
22409
- buildRAGGroundingOverviewPresentation,
22410
- buildRAGGroundedAnswer,
22411
23460
  buildRAGEvaluationRunDiff,
22412
23461
  buildRAGEvaluationResponse,
22413
23462
  buildRAGEvaluationLeaderboard,
22414
- buildRAGEvaluationHistoryRows,
22415
- buildRAGEvaluationHistoryPresentation,
22416
- buildRAGEvaluationCaseTracePresentations,
22417
- buildRAGCorpusHealthPresentation,
22418
23463
  buildRAGContext,
22419
- buildRAGComparisonTraceSummaryRows,
22420
- buildRAGComparisonTraceDiffRows,
22421
- buildRAGCitations,
22422
- buildRAGCitationReferenceMap,
22423
- buildRAGAnswerGroundingHistoryRows,
22424
- buildRAGAnswerGroundingHistoryPresentation,
22425
23464
  buildRAGAnswerGroundingEvaluationRunDiff,
22426
23465
  buildRAGAnswerGroundingEvaluationResponse,
22427
23466
  buildRAGAnswerGroundingEvaluationLeaderboard,
22428
- buildRAGAnswerGroundingCaseSnapshotPresentations,
22429
23467
  buildRAGAnswerGroundingCaseDifficultyRunDiff,
22430
23468
  buildRAGAnswerGroundingCaseDifficultyLeaderboard,
22431
- buildRAGAdminJobPresentations,
22432
- buildRAGAdminJobPresentation,
22433
- buildRAGAdminActionPresentations,
22434
- buildRAGAdminActionPresentation,
22435
23469
  applyRAGReranking,
22436
23470
  applyRAGQueryTransform,
22437
23471
  anthropicOCR,
@@ -22440,5 +23474,5 @@ export {
22440
23474
  aiChat
22441
23475
  };
22442
23476
 
22443
- //# debugId=593D26DEF70C96A364756E2164756E21
23477
+ //# debugId=01B4F90C8AC5AD4364756E2164756E21
22444
23478
  //# sourceMappingURL=index.js.map