@absolutejs/absolute 0.19.0-beta.615 → 0.19.0-beta.617

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (46) hide show
  1. package/dist/ai/client/index.js +93 -46
  2. package/dist/ai/client/index.js.map +7 -7
  3. package/dist/ai/client/ui.js +5 -1
  4. package/dist/ai/client/ui.js.map +3 -3
  5. package/dist/ai/index.js +1544 -253
  6. package/dist/ai/index.js.map +14 -12
  7. package/dist/ai/rag/quality.js +49 -6
  8. package/dist/ai/rag/quality.js.map +4 -4
  9. package/dist/ai/rag/ui.js +5 -1
  10. package/dist/ai/rag/ui.js.map +3 -3
  11. package/dist/ai-client/angular/ai/index.js +44 -40
  12. package/dist/ai-client/react/ai/index.js +46 -42
  13. package/dist/ai-client/vue/ai/index.js +44 -40
  14. package/dist/angular/ai/index.js +46 -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 +97 -50
  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/index.d.ts +4 -2
  32. package/dist/src/ai/rag/ingestion.d.ts +12 -3
  33. package/dist/src/ai/rag/jobState.d.ts +2 -0
  34. package/dist/src/ai/rag/quality.d.ts +20 -10
  35. package/dist/src/vue/ai/useRAG.d.ts +26 -0
  36. package/dist/src/vue/ai/useRAGChunkPreview.d.ts +4 -0
  37. package/dist/src/vue/ai/useRAGDocuments.d.ts +4 -0
  38. package/dist/src/vue/ai/useRAGEvaluate.d.ts +14 -0
  39. package/dist/src/vue/ai/useRAGIndexAdmin.d.ts +2 -0
  40. package/dist/src/vue/ai/useRAGSearch.d.ts +2 -0
  41. package/dist/svelte/ai/index.js +93 -46
  42. package/dist/svelte/ai/index.js.map +7 -7
  43. package/dist/types/ai.d.ts +190 -0
  44. package/dist/vue/ai/index.js +93 -46
  45. package/dist/vue/ai/index.js.map +7 -7
  46. 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,
@@ -2991,16 +2995,32 @@ var loadRAGRetrievalComparisonHistory = async ({
2991
2995
  suiteId,
2992
2996
  label,
2993
2997
  winnerId,
2998
+ corpusGroupKey,
2994
2999
  groupKey,
2995
3000
  tag
2996
- }) => normalizeRetrievalComparisonRuns(await Promise.resolve(store.listRuns({ groupKey, label, limit, suiteId, tag, winnerId })));
3001
+ }) => normalizeRetrievalComparisonRuns(await Promise.resolve(store.listRuns({
3002
+ corpusGroupKey,
3003
+ groupKey,
3004
+ label,
3005
+ limit,
3006
+ suiteId,
3007
+ tag,
3008
+ winnerId
3009
+ })));
2997
3010
  var loadRAGRetrievalBaselines = async ({
2998
3011
  store,
3012
+ corpusGroupKey,
2999
3013
  groupKey,
3000
3014
  tag,
3001
3015
  limit,
3002
3016
  status
3003
- }) => normalizeRetrievalBaselineRecords(await Promise.resolve(store.listBaselines({ groupKey, limit, status, tag })));
3017
+ }) => normalizeRetrievalBaselineRecords(await Promise.resolve(store.listBaselines({
3018
+ corpusGroupKey,
3019
+ groupKey,
3020
+ limit,
3021
+ status,
3022
+ tag
3023
+ })));
3004
3024
  var persistRAGRetrievalBaseline = async ({
3005
3025
  store,
3006
3026
  record
@@ -3010,18 +3030,21 @@ var persistRAGRetrievalBaseline = async ({
3010
3030
  };
3011
3031
  var loadRAGRetrievalReleaseDecisions = async ({
3012
3032
  store,
3033
+ corpusGroupKey,
3013
3034
  groupKey,
3014
3035
  limit,
3015
3036
  kind
3016
- }) => normalizeRetrievalReleaseDecisionRecords(await Promise.resolve(store.listDecisions({ groupKey, kind, limit })));
3037
+ }) => normalizeRetrievalReleaseDecisionRecords(await Promise.resolve(store.listDecisions({ corpusGroupKey, groupKey, kind, limit })));
3017
3038
  var loadRAGRetrievalLaneHandoffDecisions = async ({
3018
3039
  store,
3040
+ corpusGroupKey,
3019
3041
  groupKey,
3020
3042
  limit,
3021
3043
  kind,
3022
3044
  sourceRolloutLabel,
3023
3045
  targetRolloutLabel
3024
3046
  }) => normalizeRetrievalLaneHandoffDecisionRecords(await Promise.resolve(store.listDecisions({
3047
+ corpusGroupKey,
3025
3048
  groupKey,
3026
3049
  kind,
3027
3050
  limit,
@@ -3044,12 +3067,14 @@ var loadRAGRetrievalReleaseIncidents = async ({
3044
3067
  })));
3045
3068
  var loadRAGRetrievalLaneHandoffIncidents = async ({
3046
3069
  store,
3070
+ corpusGroupKey,
3047
3071
  groupKey,
3048
3072
  limit,
3049
3073
  targetRolloutLabel,
3050
3074
  status,
3051
3075
  severity
3052
3076
  }) => normalizeRetrievalReleaseIncidentRecords(await Promise.resolve(store.listIncidents({
3077
+ corpusGroupKey,
3053
3078
  groupKey,
3054
3079
  limit,
3055
3080
  severity,
@@ -3058,12 +3083,14 @@ var loadRAGRetrievalLaneHandoffIncidents = async ({
3058
3083
  })));
3059
3084
  var loadRAGRetrievalLaneHandoffIncidentHistory = async ({
3060
3085
  store,
3086
+ corpusGroupKey,
3061
3087
  action,
3062
3088
  groupKey,
3063
3089
  incidentId,
3064
3090
  limit,
3065
3091
  targetRolloutLabel
3066
3092
  }) => await Promise.resolve(store.listRecords({
3093
+ corpusGroupKey,
3067
3094
  action,
3068
3095
  groupKey,
3069
3096
  incidentId,
@@ -3108,21 +3135,25 @@ var loadRAGRetrievalIncidentRemediationExecutionHistory = async ({
3108
3135
  })));
3109
3136
  var loadRAGRetrievalLaneHandoffAutoCompletePolicyHistory = async ({
3110
3137
  store,
3138
+ corpusGroupKey,
3111
3139
  groupKey,
3112
3140
  limit,
3113
3141
  targetRolloutLabel
3114
3142
  }) => await Promise.resolve(store.listRecords({
3143
+ corpusGroupKey,
3115
3144
  groupKey,
3116
3145
  limit,
3117
3146
  targetRolloutLabel
3118
3147
  }));
3119
3148
  var loadRAGRetrievalReleaseLanePolicyHistory = async ({
3120
3149
  store,
3150
+ corpusGroupKey,
3121
3151
  groupKey,
3122
3152
  limit,
3123
3153
  rolloutLabel,
3124
3154
  scope
3125
3155
  }) => await Promise.resolve(store.listRecords({
3156
+ corpusGroupKey,
3126
3157
  groupKey,
3127
3158
  limit,
3128
3159
  rolloutLabel,
@@ -3130,11 +3161,13 @@ var loadRAGRetrievalReleaseLanePolicyHistory = async ({
3130
3161
  }));
3131
3162
  var loadRAGRetrievalBaselineGatePolicyHistory = async ({
3132
3163
  store,
3164
+ corpusGroupKey,
3133
3165
  groupKey,
3134
3166
  limit,
3135
3167
  rolloutLabel,
3136
3168
  scope
3137
3169
  }) => await Promise.resolve(store.listRecords({
3170
+ corpusGroupKey,
3138
3171
  groupKey,
3139
3172
  limit,
3140
3173
  rolloutLabel,
@@ -3142,6 +3175,7 @@ var loadRAGRetrievalBaselineGatePolicyHistory = async ({
3142
3175
  }));
3143
3176
  var loadRAGRetrievalReleaseLaneEscalationPolicyHistory = async ({
3144
3177
  store,
3178
+ corpusGroupKey,
3145
3179
  groupKey,
3146
3180
  limit,
3147
3181
  targetRolloutLabel
@@ -3229,11 +3263,15 @@ var persistRAGRetrievalReleaseLaneEscalationPolicyHistory = async ({
3229
3263
  };
3230
3264
  var buildRAGEvaluationResponse = (cases) => {
3231
3265
  const totalCases = cases.length;
3266
+ const corpusKeys = [
3267
+ ...new Set(cases.flatMap((entry) => entry.corpusKey ?? []))
3268
+ ];
3232
3269
  const passedCases = cases.filter((entry) => entry.status === "pass").length;
3233
3270
  const partialCases = cases.filter((entry) => entry.status === "partial").length;
3234
3271
  const failedCases = cases.filter((entry) => entry.status === "fail").length;
3235
3272
  return {
3236
3273
  cases,
3274
+ ...corpusKeys.length > 0 ? { corpusKeys } : {},
3237
3275
  elapsedMs: cases.reduce((sum, result) => sum + result.elapsedMs, 0),
3238
3276
  ok: true,
3239
3277
  passingRate: totalCases > 0 ? passedCases / totalCases * 100 : 0,
@@ -3459,6 +3497,9 @@ var compareRAGRetrievalStrategies = async ({
3459
3497
  traceSummary: entry.traceSummary
3460
3498
  })));
3461
3499
  return {
3500
+ corpusKeys: [
3501
+ ...new Set(entries.flatMap((entry) => entry.response.corpusKeys ?? []))
3502
+ ],
3462
3503
  entries,
3463
3504
  leaderboard,
3464
3505
  summary: summarizeRAGRetrievalComparison(entries),
@@ -3491,6 +3532,7 @@ var compareRAGRetrievalTraceSummaries = (current, previous) => ({
3491
3532
  });
3492
3533
  var buildSearchTraceResultSnapshots = (results) => results.map((result) => ({
3493
3534
  chunkId: result.chunkId,
3535
+ corpusKey: result.corpusKey ?? (typeof result.metadata?.corpusKey === "string" ? result.metadata.corpusKey : undefined),
3494
3536
  documentId: typeof result.metadata?.documentId === "string" ? result.metadata.documentId : undefined,
3495
3537
  score: result.score,
3496
3538
  source: result.source,
@@ -3690,6 +3732,7 @@ var summarizeRAGEvaluationCase = ({
3690
3732
  const status = expectedCount === 0 ? "partial" : matchedCount === expectedCount ? "pass" : matchedCount > 0 ? "partial" : "fail";
3691
3733
  return {
3692
3734
  caseId: caseInput.id ?? `case-${caseIndex + 1}`,
3735
+ corpusKey: caseInput.corpusKey,
3693
3736
  elapsedMs,
3694
3737
  expectedCount,
3695
3738
  expectedIds,
@@ -4289,10 +4332,14 @@ var buildProvenanceLabel2 = (metadata) => {
4289
4332
  const transcriptSource = getContextString2(metadata.transcriptSource);
4290
4333
  const pdfTextMode = getContextString2(metadata.pdfTextMode);
4291
4334
  const ocrEngine = getContextString2(metadata.ocrEngine);
4335
+ const extractorRegistryMatch = getContextString2(metadata.extractorRegistryMatch);
4336
+ const chunkingProfile = getContextString2(metadata.chunkingProfile);
4292
4337
  const ocrConfidence = getContextNumber2(metadata.ocrRegionConfidence) ?? getContextNumber2(metadata.ocrConfidence);
4293
4338
  const labels = [
4294
4339
  pdfTextMode ? `PDF ${pdfTextMode}` : "",
4295
4340
  ocrEngine ? `OCR ${ocrEngine}` : "",
4341
+ extractorRegistryMatch ? `Extractor ${extractorRegistryMatch}` : "",
4342
+ chunkingProfile ? `Chunking ${chunkingProfile}` : "",
4296
4343
  typeof ocrConfidence === "number" ? `Confidence ${ocrConfidence.toFixed(2)}` : "",
4297
4344
  mediaKind ? `Media ${mediaKind}` : "",
4298
4345
  transcriptSource ? `Transcript ${transcriptSource}` : "",
@@ -6741,12 +6788,12 @@ var checkBackpressure = async (socket) => {
6741
6788
  await delay(BACKPRESSURE_DELAY);
6742
6789
  }
6743
6790
  };
6744
- var sendMessage = async (socket, msg) => {
6791
+ var sendMessage = async (socket, message) => {
6745
6792
  if (socket.readyState !== WS_OPEN) {
6746
6793
  return false;
6747
6794
  }
6748
6795
  await checkBackpressure(socket);
6749
- socket.send(serializeAIMessage(msg));
6796
+ socket.send(serializeAIMessage(message));
6750
6797
  return true;
6751
6798
  };
6752
6799
  var buildToolDefinitions = (tools) => Object.entries(tools).map(([name, def]) => ({
@@ -9689,6 +9736,7 @@ var createEmailExtractor = () => ({
9689
9736
  chunking: input.chunking,
9690
9737
  contentType: attachment.contentType,
9691
9738
  data: attachment.data,
9739
+ extractorRegistry: input.extractorRegistry,
9692
9740
  format: inferFormatFromContentType(attachment.contentType ?? null) ?? inferFormatFromName(attachment.fileName),
9693
9741
  metadata: {
9694
9742
  ...messageMetadata,
@@ -9844,6 +9892,8 @@ ${slide.text}`),
9844
9892
  });
9845
9893
  var createRAGArchiveExpander = (expander) => expander;
9846
9894
  var createRAGFileExtractor = (extractor) => extractor;
9895
+ var createRAGFileExtractorRegistry = (registry) => registry;
9896
+ var createRAGChunkingRegistry = (registry) => registry;
9847
9897
  var createRAGImageOCRExtractor = (provider) => ({
9848
9898
  name: `absolute_image_ocr:${provider.name}`,
9849
9899
  supports: imageExtractorSupports,
@@ -9934,11 +9984,12 @@ var createTextFileExtractor = () => ({
9934
9984
  title: input.title
9935
9985
  })
9936
9986
  });
9937
- var expandArchiveEntry = async (entry, archiveInput, extractors) => {
9987
+ var expandArchiveEntry = async (entry, archiveInput, extractors, registry) => {
9938
9988
  const documents = await extractRAGFileDocuments({
9939
9989
  chunking: archiveInput.chunking,
9940
9990
  contentType: entry.contentType,
9941
9991
  data: entry.data,
9992
+ extractorRegistry: archiveInput.extractorRegistry,
9942
9993
  format: entry.format,
9943
9994
  metadata: {
9944
9995
  ...archiveInput.metadata ?? {},
@@ -9952,7 +10003,7 @@ var expandArchiveEntry = async (entry, archiveInput, extractors) => {
9952
10003
  name: basename(entry.path),
9953
10004
  source: archiveInput.source && !archiveInput.source.startsWith("http") ? `${archiveInput.source}#${entry.path}` : entry.path,
9954
10005
  title: basename(entry.path)
9955
- }, extractors);
10006
+ }, extractors, registry);
9956
10007
  return documents;
9957
10008
  };
9958
10009
  var createPDFFileExtractor = () => ({
@@ -10053,8 +10104,83 @@ var DEFAULT_FILE_EXTRACTORS = [
10053
10104
  createPDFFileExtractor(),
10054
10105
  createTextFileExtractor()
10055
10106
  ];
10056
- var resolveFileExtractors = (extractors) => extractors && extractors.length > 0 ? [...extractors, ...DEFAULT_FILE_EXTRACTORS] : DEFAULT_FILE_EXTRACTORS;
10057
- var applyExtractorDefaults = (document, input, extractorName) => ({
10107
+ var resolveExtractorRegistry = (registry) => {
10108
+ if (!registry) {
10109
+ return {
10110
+ defaultOrder: "registry_first",
10111
+ includeDefaults: true,
10112
+ registrations: []
10113
+ };
10114
+ }
10115
+ if (Array.isArray(registry)) {
10116
+ return {
10117
+ defaultOrder: "registry_first",
10118
+ includeDefaults: true,
10119
+ registrations: registry
10120
+ };
10121
+ }
10122
+ return {
10123
+ defaultOrder: registry.defaultOrder ?? "registry_first",
10124
+ includeDefaults: registry.includeDefaults ?? true,
10125
+ registrations: registry.registrations
10126
+ };
10127
+ };
10128
+ var createExtractorRegistryInput = (input) => ({
10129
+ ...input,
10130
+ inferredContentType: input.contentType ?? null,
10131
+ inferredExtension: inferExtensionFromInput(input) || null,
10132
+ inferredFormat: input.format ?? inferFormatFromContentType(input.contentType ?? null) ?? inferFormatFromName(input.path ?? input.source ?? input.name ?? input.title)
10133
+ });
10134
+ var registrationMatches = async (registration, input) => {
10135
+ const normalizedContentType = input.inferredContentType?.toLowerCase();
10136
+ const normalizedExtension = input.inferredExtension?.toLowerCase();
10137
+ const normalizedName = (input.path ?? input.source ?? input.name ?? input.title ?? "").toLowerCase();
10138
+ if (registration.contentTypes?.length && !registration.contentTypes.some((entry) => normalizedContentType === entry.toLowerCase())) {
10139
+ return false;
10140
+ }
10141
+ if (registration.extensions?.length && !registration.extensions.some((entry) => normalizedExtension === entry.toLowerCase())) {
10142
+ return false;
10143
+ }
10144
+ if (registration.formats?.length && (!input.inferredFormat || !registration.formats.includes(input.inferredFormat))) {
10145
+ return false;
10146
+ }
10147
+ if (registration.names?.length && !registration.names.some((entry) => normalizedName.includes(entry.toLowerCase()))) {
10148
+ return false;
10149
+ }
10150
+ if (registration.match) {
10151
+ return registration.match(input);
10152
+ }
10153
+ return true;
10154
+ };
10155
+ var dedupeResolvedExtractors = (extractors) => {
10156
+ const seen = new Set;
10157
+ const ordered = [];
10158
+ for (const entry of extractors) {
10159
+ if (seen.has(entry.extractor.name)) {
10160
+ continue;
10161
+ }
10162
+ seen.add(entry.extractor.name);
10163
+ ordered.push(entry);
10164
+ }
10165
+ return ordered;
10166
+ };
10167
+ var resolveFileExtractors = async (input, extractors, registry) => {
10168
+ const explicit = extractors ?? [];
10169
+ const resolvedRegistry = resolveExtractorRegistry(registry);
10170
+ const registryInput = createExtractorRegistryInput(input);
10171
+ const matchedRegistrations = (await Promise.all(resolvedRegistry.registrations.map(async (registration) => ({
10172
+ matches: await registrationMatches(registration, registryInput),
10173
+ priority: registration.priority ?? 0,
10174
+ registration
10175
+ })))).filter((entry) => entry.matches).sort((left, right) => right.priority - left.priority).map((entry) => ({
10176
+ extractor: entry.registration.extractor,
10177
+ registryMatchName: entry.registration.name ?? entry.registration.extractor.name
10178
+ }));
10179
+ const defaults = resolvedRegistry.includeDefaults ? DEFAULT_FILE_EXTRACTORS.map((extractor) => ({ extractor })) : [];
10180
+ const explicitResolved = explicit.map((extractor) => ({ extractor }));
10181
+ return dedupeResolvedExtractors(resolvedRegistry.defaultOrder === "defaults_first" ? [...explicitResolved, ...defaults, ...matchedRegistrations] : [...explicitResolved, ...matchedRegistrations, ...defaults]);
10182
+ };
10183
+ var applyExtractorDefaults = (document, input, extractorName, registryMatchName) => ({
10058
10184
  chunking: document.chunking ?? input.chunking,
10059
10185
  format: document.format ?? input.format ?? inferFormatFromContentType(document.contentType ?? input.contentType ?? null) ?? inferFormatFromName(document.source ?? input.source ?? input.path ?? input.name),
10060
10186
  id: document.id,
@@ -10062,20 +10188,22 @@ var applyExtractorDefaults = (document, input, extractorName) => ({
10062
10188
  ...input.metadata ?? {},
10063
10189
  ...document.metadata ?? {},
10064
10190
  contentType: document.contentType ?? input.contentType,
10065
- extractor: document.extractor ?? extractorName
10191
+ extractor: document.extractor ?? extractorName,
10192
+ ...registryMatchName ? { extractorRegistryMatch: registryMatchName } : {}
10066
10193
  },
10067
10194
  source: document.source ?? input.source ?? input.path ?? input.name,
10068
10195
  text: document.text,
10069
10196
  title: document.title ?? input.title
10070
10197
  });
10071
- var extractRAGFileDocuments = async (input, extractors) => {
10072
- for (const extractor of resolveFileExtractors(extractors)) {
10198
+ var extractRAGFileDocuments = async (input, extractors, registry) => {
10199
+ for (const resolvedExtractor of await resolveFileExtractors(input, extractors, registry)) {
10200
+ const { extractor, registryMatchName } = resolvedExtractor;
10073
10201
  if (!await extractor.supports(input)) {
10074
10202
  continue;
10075
10203
  }
10076
10204
  const extracted = await extractor.extract(input);
10077
10205
  const documents = Array.isArray(extracted) ? extracted : [extracted];
10078
- return documents.map((document) => applyExtractorDefaults(document, input, extractor.name));
10206
+ return documents.map((document) => applyExtractorDefaults(document, input, extractor.name, registryMatchName));
10079
10207
  }
10080
10208
  throw new Error(`No RAG file extractor matched ${inferNameFromInput(input)}. Register a custom extractor for this file type.`);
10081
10209
  };
@@ -10086,7 +10214,7 @@ var getFirstExtractedDocument = (documents, label) => {
10086
10214
  }
10087
10215
  return document;
10088
10216
  };
10089
- var loadExtractedDocuments = async (input, extractors) => extractRAGFileDocuments(input, extractors);
10217
+ var loadExtractedDocuments = async (input, extractors, registry) => extractRAGFileDocuments(input, extractors, registry);
10090
10218
  var sentenceUnits = (text) => {
10091
10219
  const matches = text.match(/[^.!?\n]+(?:[.!?]+|$)/g);
10092
10220
  if (!matches) {
@@ -10283,11 +10411,92 @@ var resolveChunkingUnits = (text, options) => {
10283
10411
  }
10284
10412
  return paragraphUnits(text);
10285
10413
  };
10286
- var resolveChunkingOptions = (document, defaults) => {
10287
- const maxChunkLength = document.chunking?.maxChunkLength ?? defaults?.maxChunkLength ?? DEFAULT_MAX_CHUNK_LENGTH;
10288
- const chunkOverlap = document.chunking?.chunkOverlap ?? defaults?.chunkOverlap ?? DEFAULT_CHUNK_OVERLAP;
10289
- const minChunkLength = document.chunking?.minChunkLength ?? defaults?.minChunkLength ?? DEFAULT_MIN_CHUNK_LENGTH;
10290
- const strategy = document.chunking?.strategy ?? defaults?.strategy ?? DEFAULT_STRATEGY;
10414
+ var resolveChunkingProfiles = (registry) => {
10415
+ if (!registry) {
10416
+ return [];
10417
+ }
10418
+ const profiles = Array.isArray(registry) ? registry : registry.profiles;
10419
+ return profiles.map((profile, index) => normalizeChunkingProfile(profile, index)).sort((left, right) => right.priority - left.priority).map(({ profile }) => profile);
10420
+ };
10421
+ var normalizeChunkingProfile = (profile, index) => {
10422
+ if ("resolve" in profile) {
10423
+ return {
10424
+ priority: 0,
10425
+ profile
10426
+ };
10427
+ }
10428
+ const options = normalizeChunkingProfileOptions(profile.profile);
10429
+ const sourceSet = profile.sources?.filter((value) => typeof value === "string") ?? [];
10430
+ const documentIdSet = profile.documentIds?.filter((value) => typeof value === "string") ?? [];
10431
+ const formatSet = profile.formats?.filter((value) => typeof value === "string") ?? [];
10432
+ const sourceNativeKindSet = profile.sourceNativeKinds?.filter((value) => typeof value === "string") ?? [];
10433
+ return {
10434
+ priority: profile.priority ?? 0,
10435
+ profile: {
10436
+ name: profile.name ?? `chunking_profile_${String(index + 1).padStart(2, "0")}`,
10437
+ resolve: (input) => {
10438
+ const documentId = input.document.id?.trim() || (typeof input.metadata.documentId === "string" ? input.metadata.documentId : undefined);
10439
+ const source = input.document.source?.trim();
10440
+ if (formatSet.length > 0 && !formatSet.includes(input.format)) {
10441
+ return;
10442
+ }
10443
+ if (sourceNativeKindSet.length > 0 && (!input.sourceNativeKind || !sourceNativeKindSet.includes(input.sourceNativeKind))) {
10444
+ return;
10445
+ }
10446
+ if (sourceSet.length > 0 && (!source || !sourceSet.includes(source))) {
10447
+ return;
10448
+ }
10449
+ if (documentIdSet.length > 0 && (!documentId || !documentIdSet.includes(documentId))) {
10450
+ return;
10451
+ }
10452
+ return options;
10453
+ }
10454
+ }
10455
+ };
10456
+ };
10457
+ var normalizeChunkingProfileOptions = (profile) => isChunkingProfileOptionsWrapper(profile) ? profile.options ?? {} : profile;
10458
+ var isChunkingProfileOptionsWrapper = (profile) => Object.prototype.hasOwnProperty.call(profile, "options");
10459
+ var resolveChunkingProfileOverrides = ({
10460
+ defaults,
10461
+ document,
10462
+ format,
10463
+ normalizedText,
10464
+ registry
10465
+ }) => {
10466
+ const metadata = document.metadata ?? {};
10467
+ const sourceNativeKind = typeof metadata.sourceNativeKind === "string" ? metadata.sourceNativeKind : undefined;
10468
+ for (const profile of resolveChunkingProfiles(registry)) {
10469
+ const options = profile.resolve({
10470
+ defaults,
10471
+ document,
10472
+ format,
10473
+ metadata,
10474
+ normalizedText,
10475
+ sourceNativeKind
10476
+ });
10477
+ if (options) {
10478
+ return {
10479
+ name: profile.name,
10480
+ options
10481
+ };
10482
+ }
10483
+ }
10484
+ return;
10485
+ };
10486
+ var resolveChunkingOptions = (document, defaults, registry, format, normalizedText) => {
10487
+ const resolvedFormat = format ?? inferFormat(document);
10488
+ const resolvedNormalizedText = normalizedText ?? normalizeDocumentText(document.text, resolvedFormat);
10489
+ const profileOverrides = resolveChunkingProfileOverrides({
10490
+ defaults,
10491
+ document,
10492
+ format: resolvedFormat,
10493
+ normalizedText: resolvedNormalizedText,
10494
+ registry
10495
+ });
10496
+ const maxChunkLength = document.chunking?.maxChunkLength ?? profileOverrides?.options.maxChunkLength ?? defaults?.maxChunkLength ?? DEFAULT_MAX_CHUNK_LENGTH;
10497
+ const chunkOverlap = document.chunking?.chunkOverlap ?? profileOverrides?.options.chunkOverlap ?? defaults?.chunkOverlap ?? DEFAULT_CHUNK_OVERLAP;
10498
+ const minChunkLength = document.chunking?.minChunkLength ?? profileOverrides?.options.minChunkLength ?? defaults?.minChunkLength ?? DEFAULT_MIN_CHUNK_LENGTH;
10499
+ const strategy = document.chunking?.strategy ?? profileOverrides?.options.strategy ?? defaults?.strategy ?? DEFAULT_STRATEGY;
10291
10500
  return {
10292
10501
  chunkOverlap: Math.max(0, Math.min(chunkOverlap, maxChunkLength - 1)),
10293
10502
  maxChunkLength: Math.max(RAG_MIN_CHUNK_LENGTH_FLOOR, maxChunkLength),
@@ -10305,10 +10514,17 @@ var createChunkEntries = (document, format, text, options) => {
10305
10514
  const units = resolveChunkingUnits(text, options);
10306
10515
  return chunkFromUnits(units, options.maxChunkLength, options.chunkOverlap, options.minChunkLength).map((entry) => ({ text: entry }));
10307
10516
  };
10308
- var prepareRAGDocument = (document, defaultChunking) => {
10517
+ var prepareRAGDocument = (document, defaultChunking, chunkingRegistry) => {
10309
10518
  const format = inferFormat(document);
10310
10519
  const normalizedText = normalizeDocumentText(document.text, format);
10311
- const chunking = resolveChunkingOptions(document, defaultChunking);
10520
+ const chunkingProfileOverrides = resolveChunkingProfileOverrides({
10521
+ defaults: defaultChunking,
10522
+ document,
10523
+ format,
10524
+ normalizedText,
10525
+ registry: chunkingRegistry
10526
+ });
10527
+ const chunking = resolveChunkingOptions(document, defaultChunking, chunkingRegistry, format, normalizedText);
10312
10528
  const documentId = document.id?.trim() || slugify(document.source || document.title || normalizedText.slice(0, RAG_DOCUMENT_ID_PREVIEW_LENGTH));
10313
10529
  const title = document.title?.trim() || documentId;
10314
10530
  let sourceExtension = "txt";
@@ -10318,9 +10534,12 @@ var prepareRAGDocument = (document, defaultChunking) => {
10318
10534
  sourceExtension = "html";
10319
10535
  }
10320
10536
  const source = document.source?.trim() || `${documentId}.${sourceExtension}`;
10537
+ const corpusKey = document.corpusKey?.trim() || (typeof document.metadata?.corpusKey === "string" ? document.metadata.corpusKey.trim() : undefined) || undefined;
10321
10538
  const metadata = {
10322
10539
  ...document.metadata ?? {},
10540
+ ...corpusKey ? { corpusKey } : {},
10323
10541
  documentId,
10542
+ ...chunkingProfileOverrides?.name ? { chunkingProfile: chunkingProfileOverrides.name } : {},
10324
10543
  format,
10325
10544
  source,
10326
10545
  title
@@ -10344,6 +10563,7 @@ var prepareRAGDocument = (document, defaultChunking) => {
10344
10563
  const nextChunkId = index + 1 < chunkEntries.length ? `${documentId}:${String(index + 2).padStart(RAG_CHUNK_ID_PAD_LENGTH, "0")}` : undefined;
10345
10564
  return {
10346
10565
  chunkId: `${documentId}:${String(index + 1).padStart(RAG_CHUNK_ID_PAD_LENGTH, "0")}`,
10566
+ ...corpusKey ? { corpusKey } : {},
10347
10567
  metadata: {
10348
10568
  ...metadata,
10349
10569
  chunkCount: chunkEntries.length,
@@ -10366,6 +10586,7 @@ var prepareRAGDocument = (document, defaultChunking) => {
10366
10586
  };
10367
10587
  });
10368
10588
  return {
10589
+ ...corpusKey ? { corpusKey } : {},
10369
10590
  chunks,
10370
10591
  documentId,
10371
10592
  format,
@@ -10375,7 +10596,7 @@ var prepareRAGDocument = (document, defaultChunking) => {
10375
10596
  title
10376
10597
  };
10377
10598
  };
10378
- var prepareRAGDocuments = (input) => input.documents.map((document) => prepareRAGDocument(document, input.defaultChunking));
10599
+ var prepareRAGDocuments = (input) => input.documents.map((document) => prepareRAGDocument(document, input.defaultChunking, input.chunkingRegistry));
10379
10600
  var mergeMetadata = (inputMetadata, extraMetadata, baseMetadata) => ({
10380
10601
  ...baseMetadata ?? {},
10381
10602
  ...inputMetadata ?? {},
@@ -10390,12 +10611,13 @@ var loadRAGDocumentFile = async (input) => {
10390
10611
  chunking: input.chunking,
10391
10612
  contentType: input.contentType,
10392
10613
  data,
10614
+ extractorRegistry: input.extractorRegistry,
10393
10615
  format: input.format,
10394
10616
  metadata: input.metadata,
10395
10617
  path: input.path,
10396
10618
  source: input.source,
10397
10619
  title: input.title
10398
- }, input.extractors);
10620
+ }, input.extractors, input.extractorRegistry);
10399
10621
  return getFirstExtractedDocument(documents, "for file input");
10400
10622
  };
10401
10623
  var loadRAGDocumentFromURL = async (input) => {
@@ -10412,12 +10634,13 @@ var loadRAGDocumentFromURL = async (input) => {
10412
10634
  chunking: input.chunking,
10413
10635
  contentType: input.contentType ?? response.headers.get("content-type") ?? undefined,
10414
10636
  data,
10637
+ extractorRegistry: input.extractorRegistry,
10415
10638
  format: input.format ?? inferFormatFromUrl(url),
10416
10639
  metadata: input.metadata,
10417
10640
  name: basename(new URL(url).pathname),
10418
10641
  source: input.source ?? url,
10419
10642
  title: input.title
10420
- }, input.extractors);
10643
+ }, input.extractors, input.extractorRegistry);
10421
10644
  return getFirstExtractedDocument(documents, "for URL input");
10422
10645
  };
10423
10646
  var loadRAGDocumentsFromUploads = async (input) => {
@@ -10426,12 +10649,13 @@ var loadRAGDocumentsFromUploads = async (input) => {
10426
10649
  chunking: upload.chunking,
10427
10650
  contentType: upload.contentType,
10428
10651
  data: decodeUploadContent(upload),
10652
+ extractorRegistry: input.extractorRegistry,
10429
10653
  format: upload.format,
10430
10654
  metadata: upload.metadata,
10431
10655
  name: upload.name,
10432
10656
  source: upload.source ?? upload.name,
10433
10657
  title: upload.title
10434
- }, input.extractors);
10658
+ }, input.extractors, input.extractorRegistry);
10435
10659
  return loaded.map((document) => ({
10436
10660
  ...document,
10437
10661
  metadata: mergeMetadata(document.metadata, { uploadFile: upload.name }, input.baseMetadata)
@@ -10439,6 +10663,7 @@ var loadRAGDocumentsFromUploads = async (input) => {
10439
10663
  }));
10440
10664
  return {
10441
10665
  defaultChunking: input.defaultChunking,
10666
+ chunkingRegistry: input.chunkingRegistry,
10442
10667
  documents: documents.flat()
10443
10668
  };
10444
10669
  };
@@ -10457,12 +10682,13 @@ var loadRAGDocumentsFromURLs = async (input) => {
10457
10682
  chunking: urlInput.chunking,
10458
10683
  contentType: urlInput.contentType ?? response.headers.get("content-type") ?? undefined,
10459
10684
  data,
10685
+ extractorRegistry: urlInput.extractorRegistry ?? input.extractorRegistry,
10460
10686
  format: urlInput.format ?? inferFormatFromUrl(url),
10461
10687
  metadata: urlInput.metadata,
10462
10688
  name: basename(new URL(url).pathname),
10463
10689
  source: urlInput.source ?? url,
10464
10690
  title: urlInput.title
10465
- }, urlInput.extractors ?? input.extractors);
10691
+ }, urlInput.extractors ?? input.extractors, urlInput.extractorRegistry ?? input.extractorRegistry);
10466
10692
  return loaded.map((document) => ({
10467
10693
  ...document,
10468
10694
  metadata: mergeMetadata(document.metadata, { sourceUrl: urlInput.url }, input.baseMetadata)
@@ -10470,6 +10696,7 @@ var loadRAGDocumentsFromURLs = async (input) => {
10470
10696
  }));
10471
10697
  return {
10472
10698
  defaultChunking: input.defaultChunking,
10699
+ chunkingRegistry: input.chunkingRegistry,
10473
10700
  documents: documents.flat()
10474
10701
  };
10475
10702
  };
@@ -10478,15 +10705,16 @@ var loadRAGDocumentUpload = async (input) => {
10478
10705
  chunking: input.chunking,
10479
10706
  contentType: input.contentType,
10480
10707
  data: decodeUploadContent(input),
10708
+ extractorRegistry: input.extractorRegistry,
10481
10709
  format: input.format,
10482
10710
  metadata: input.metadata,
10483
10711
  name: input.name,
10484
10712
  source: input.source ?? input.name,
10485
10713
  title: input.title
10486
- }, input.extractors);
10714
+ }, input.extractors, input.extractorRegistry);
10487
10715
  return getFirstExtractedDocument(documents, "for upload input");
10488
10716
  };
10489
- var prepareRAGDocumentFile = async (input, defaultChunking) => prepareRAGDocument(await loadRAGDocumentFile(input), defaultChunking);
10717
+ var prepareRAGDocumentFile = async (input, defaultChunking, chunkingRegistry) => prepareRAGDocument(await loadRAGDocumentFile(input), defaultChunking, chunkingRegistry);
10490
10718
  var DEFAULT_DIRECTORY_EXTENSIONS = [
10491
10719
  ".txt",
10492
10720
  ".md",
@@ -10546,7 +10774,7 @@ var buildRAGUpsertInputFromUploads = async (input) => ({
10546
10774
  });
10547
10775
  var loadRAGDocumentsFromDirectory = async (input) => {
10548
10776
  const root = resolve(input.directory);
10549
- const includeExtensions = input.includeExtensions === undefined && input.extractors?.length ? null : new Set((input.includeExtensions ?? DEFAULT_DIRECTORY_EXTENSIONS).map((entry) => entry.startsWith(".") ? entry.toLowerCase() : `.${entry.toLowerCase()}`));
10777
+ 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()}`));
10550
10778
  const files = await collectDirectoryFiles(root, input.recursive !== false, includeExtensions);
10551
10779
  const documents = await Promise.all(files.map(async (path) => {
10552
10780
  const source = relative(root, path).replace(/\\/g, "/");
@@ -10554,13 +10782,14 @@ var loadRAGDocumentsFromDirectory = async (input) => {
10554
10782
  const loaded = await loadExtractedDocuments({
10555
10783
  chunking: input.defaultChunking,
10556
10784
  data,
10785
+ extractorRegistry: input.extractorRegistry,
10557
10786
  metadata: {
10558
10787
  fileName: basename(path),
10559
10788
  relativePath: source
10560
10789
  },
10561
10790
  path,
10562
10791
  source
10563
- }, input.extractors);
10792
+ }, input.extractors, input.extractorRegistry);
10564
10793
  return loaded.map((document) => ({
10565
10794
  ...document,
10566
10795
  metadata: mergeMetadata(document.metadata, undefined, input.baseMetadata)
@@ -10568,6 +10797,7 @@ var loadRAGDocumentsFromDirectory = async (input) => {
10568
10797
  }));
10569
10798
  return {
10570
10799
  defaultChunking: input.defaultChunking,
10800
+ chunkingRegistry: input.chunkingRegistry,
10571
10801
  documents: documents.flat()
10572
10802
  };
10573
10803
  };
@@ -11782,6 +12012,7 @@ var isRAGDocumentUrlArray = (value) => Array.isArray(value) && value.every((entr
11782
12012
  var isRAGDocumentChunkArray = (value) => Array.isArray(value) && value.every((entry) => isRAGDocumentChunk(entry));
11783
12013
  var buildSources2 = (results) => results.map((result) => ({
11784
12014
  chunkId: result.chunkId,
12015
+ corpusKey: result.corpusKey ?? (typeof result.metadata?.corpusKey === "string" ? result.metadata.corpusKey : undefined),
11785
12016
  labels: buildRAGSourceLabels({
11786
12017
  metadata: result.metadata,
11787
12018
  source: result.source,
@@ -11932,6 +12163,8 @@ var buildRAGContextFromQuery = async (config, topK, scoreThreshold, queryText, r
11932
12163
  };
11933
12164
  var ragChat = (config) => {
11934
12165
  const path = config.path ?? DEFAULT_PATH2;
12166
+ const authorizeRAGAction = config.authorizeRAGAction;
12167
+ const resolveRAGAccessScope = config.resolveRAGAccessScope;
11935
12168
  const topK = config.topK ?? DEFAULT_TOP_K3;
11936
12169
  const { scoreThreshold } = config;
11937
12170
  const { extractors } = config;
@@ -11946,6 +12179,13 @@ var ragChat = (config) => {
11946
12179
  const adminActions = [];
11947
12180
  const adminJobs = [];
11948
12181
  const syncJobs = [];
12182
+ const { jobStateStore } = config;
12183
+ const jobHistoryRetention = {
12184
+ maxAdminActions: config.jobHistoryRetention?.maxAdminActions ?? MAX_ADMIN_ACTIONS,
12185
+ maxAdminJobs: config.jobHistoryRetention?.maxAdminJobs ?? MAX_ADMIN_JOBS,
12186
+ maxIngestJobs: config.jobHistoryRetention?.maxIngestJobs ?? MAX_INGEST_JOBS,
12187
+ maxSyncJobs: config.jobHistoryRetention?.maxSyncJobs ?? MAX_ADMIN_JOBS
12188
+ };
11949
12189
  const { searchTraceStore } = config;
11950
12190
  const { searchTraceRetention } = config;
11951
12191
  const { searchTraceRetentionSchedule } = config;
@@ -11964,6 +12204,153 @@ var ragChat = (config) => {
11964
12204
  retention: searchTraceRetention,
11965
12205
  schedule: searchTraceRetentionSchedule
11966
12206
  };
12207
+ let jobStateLoaded = false;
12208
+ let jobStateLoadPromise;
12209
+ const normalizeAuthorizationDecision = (decision) => typeof decision === "boolean" ? { allowed: decision } : decision ?? { allowed: true };
12210
+ const checkAuthorization = async (request, action, resource) => {
12211
+ if (!authorizeRAGAction) {
12212
+ return { allowed: true };
12213
+ }
12214
+ return normalizeAuthorizationDecision(await authorizeRAGAction({
12215
+ action,
12216
+ request,
12217
+ resource
12218
+ }));
12219
+ };
12220
+ const isAuthorized = async (request, action, resource) => (await checkAuthorization(request, action, resource)).allowed;
12221
+ const buildAuthorizationFailure = (decision, fallback = "Forbidden") => ({
12222
+ error: decision.reason ?? fallback,
12223
+ ok: false
12224
+ });
12225
+ const isAccessScopeError = (error) => typeof error === "string" && (error.includes("allowed RAG access scope") || error.includes("Scoped sync-all is not allowed"));
12226
+ const authorizeMutationRoute = async (request, action, input) => {
12227
+ const decision = await checkAuthorization(request, action, input.resource);
12228
+ return decision.allowed ? null : buildAuthorizationFailure(decision, input.fallback);
12229
+ };
12230
+ const normalizeAccessScope = (scope) => {
12231
+ if (!scope) {
12232
+ return;
12233
+ }
12234
+ const normalizeStringArray3 = (values) => {
12235
+ const next = (values ?? []).map((value) => value.trim()).filter((value) => value.length > 0);
12236
+ return next.length > 0 ? [...new Set(next)] : undefined;
12237
+ };
12238
+ const requiredMetadata = scope.requiredMetadata && Object.keys(scope.requiredMetadata).length > 0 ? scope.requiredMetadata : undefined;
12239
+ return {
12240
+ allowedComparisonGroupKeys: normalizeStringArray3(scope.allowedComparisonGroupKeys),
12241
+ allowedCorpusGroupKeys: normalizeStringArray3(scope.allowedCorpusGroupKeys),
12242
+ allowedCorpusKeys: normalizeStringArray3(scope.allowedCorpusKeys),
12243
+ allowedDocumentIds: normalizeStringArray3(scope.allowedDocumentIds),
12244
+ allowedSourcePrefixes: normalizeStringArray3(scope.allowedSourcePrefixes),
12245
+ allowedSources: normalizeStringArray3(scope.allowedSources),
12246
+ allowedSyncSourceIds: normalizeStringArray3(scope.allowedSyncSourceIds),
12247
+ requiredMetadata
12248
+ };
12249
+ };
12250
+ const loadAccessScope = async (request) => request && resolveRAGAccessScope ? normalizeAccessScope(await resolveRAGAccessScope(request)) : undefined;
12251
+ const matchesRequiredMetadata = (requiredMetadata, metadata) => {
12252
+ if (!requiredMetadata) {
12253
+ return true;
12254
+ }
12255
+ return Object.entries(requiredMetadata).every(([key, value]) => metadata?.[key] === value);
12256
+ };
12257
+ const matchesAccessScope = (scope, input) => {
12258
+ if (!scope) {
12259
+ return true;
12260
+ }
12261
+ const corpusKey = input.corpusKey ?? (typeof input.metadata?.corpusKey === "string" ? input.metadata.corpusKey : undefined);
12262
+ if (scope.allowedCorpusKeys?.length && (!corpusKey || !scope.allowedCorpusKeys.includes(corpusKey))) {
12263
+ return false;
12264
+ }
12265
+ if (scope.allowedDocumentIds?.length && (!input.documentId || !scope.allowedDocumentIds.includes(input.documentId))) {
12266
+ return false;
12267
+ }
12268
+ if (scope.allowedSources?.length && (!input.source || !scope.allowedSources.includes(input.source))) {
12269
+ return false;
12270
+ }
12271
+ if (scope.allowedSourcePrefixes?.length && (!input.source || !scope.allowedSourcePrefixes.some((prefix) => (input.source ?? "").startsWith(prefix)))) {
12272
+ return false;
12273
+ }
12274
+ return matchesRequiredMetadata(scope.requiredMetadata, input.metadata);
12275
+ };
12276
+ const matchesSyncSourceScope = (scope, source) => !scope?.allowedSyncSourceIds?.length || scope.allowedSyncSourceIds.includes(source.id);
12277
+ const deriveCorpusGroupKey = (input) => {
12278
+ const explicitCorpusGroupKey = input.corpusGroupKey?.trim();
12279
+ if (explicitCorpusGroupKey) {
12280
+ return explicitCorpusGroupKey;
12281
+ }
12282
+ const normalizedCorpusKeys = [
12283
+ ...new Set((input.corpusKeys ?? []).map((value) => value.trim()).filter((value) => value.length > 0))
12284
+ ].sort((left, right) => left.localeCompare(right));
12285
+ if (normalizedCorpusKeys.length === 0) {
12286
+ return;
12287
+ }
12288
+ if (normalizedCorpusKeys.length === 1) {
12289
+ return normalizedCorpusKeys[0];
12290
+ }
12291
+ return normalizedCorpusKeys.join("+");
12292
+ };
12293
+ const isAllowedCorpusGroupKey = (scope, corpusGroupKey) => !scope?.allowedCorpusGroupKeys?.length || typeof corpusGroupKey === "string" && scope.allowedCorpusGroupKeys.includes(corpusGroupKey);
12294
+ const isAllowedComparisonGroupKey = (scope, groupKey) => !scope?.allowedComparisonGroupKeys?.length || typeof groupKey === "string" && scope.allowedComparisonGroupKeys.includes(groupKey);
12295
+ const filterByComparisonGroupKey = (scope, records) => scope?.allowedComparisonGroupKeys?.length ? records.filter((record) => isAllowedComparisonGroupKey(scope, record.groupKey)) : records;
12296
+ const filterByCorpusGroupKey = (scope, records) => scope?.allowedCorpusGroupKeys?.length ? records.filter((record) => isAllowedCorpusGroupKey(scope, record.corpusGroupKey)) : records;
12297
+ const persistJobStateIfConfigured = async () => {
12298
+ if (!jobStateStore) {
12299
+ return;
12300
+ }
12301
+ await jobStateStore.save({
12302
+ adminActions: adminActions.slice(0, Math.max(0, jobHistoryRetention.maxAdminActions)),
12303
+ adminJobs: adminJobs.slice(0, Math.max(0, jobHistoryRetention.maxAdminJobs)),
12304
+ ingestJobs: ingestJobs.slice(0, Math.max(0, jobHistoryRetention.maxIngestJobs)),
12305
+ syncJobs: syncJobs.slice(0, Math.max(0, jobHistoryRetention.maxSyncJobs))
12306
+ });
12307
+ };
12308
+ const recoverIngestJob = (job, recoveredAt) => job.status === "running" ? {
12309
+ ...job,
12310
+ elapsedMs: Math.max(0, recoveredAt - job.startedAt),
12311
+ error: job.error ?? "Interrupted before completion during recovery",
12312
+ finishedAt: recoveredAt,
12313
+ status: "failed"
12314
+ } : job;
12315
+ const recoverAdminJob = (job, recoveredAt) => job.status === "running" ? {
12316
+ ...job,
12317
+ elapsedMs: Math.max(0, recoveredAt - job.startedAt),
12318
+ error: job.error ?? "Interrupted before completion during recovery",
12319
+ finishedAt: recoveredAt,
12320
+ status: "failed"
12321
+ } : job;
12322
+ const ensureJobStateLoaded = async () => {
12323
+ if (jobStateLoaded) {
12324
+ return;
12325
+ }
12326
+ if (jobStateLoadPromise) {
12327
+ await jobStateLoadPromise;
12328
+ return;
12329
+ }
12330
+ jobStateLoadPromise = (async () => {
12331
+ if (!jobStateStore) {
12332
+ jobStateLoaded = true;
12333
+ return;
12334
+ }
12335
+ const loaded = await jobStateStore.load();
12336
+ const recoveredAt = Date.now();
12337
+ const nextIngestJobs = (loaded?.ingestJobs ?? []).map((job) => recoverIngestJob(job, recoveredAt)).slice(0, Math.max(0, jobHistoryRetention.maxIngestJobs));
12338
+ const nextAdminActions = (loaded?.adminActions ?? []).slice(0, Math.max(0, jobHistoryRetention.maxAdminActions));
12339
+ const nextAdminJobs = (loaded?.adminJobs ?? []).map((job) => recoverAdminJob(job, recoveredAt)).slice(0, Math.max(0, jobHistoryRetention.maxAdminJobs));
12340
+ const nextSyncJobs = (loaded?.syncJobs ?? []).map((job) => recoverAdminJob(job, recoveredAt)).slice(0, Math.max(0, jobHistoryRetention.maxSyncJobs));
12341
+ adminActions.splice(0, adminActions.length, ...nextAdminActions);
12342
+ ingestJobs.splice(0, ingestJobs.length, ...nextIngestJobs);
12343
+ adminJobs.splice(0, adminJobs.length, ...nextAdminJobs);
12344
+ syncJobs.splice(0, syncJobs.length, ...nextSyncJobs);
12345
+ jobStateLoaded = true;
12346
+ await persistJobStateIfConfigured();
12347
+ })();
12348
+ try {
12349
+ await jobStateLoadPromise;
12350
+ } finally {
12351
+ jobStateLoadPromise = undefined;
12352
+ }
12353
+ };
11967
12354
  const createIngestJob = (inputKind, requestedCount) => {
11968
12355
  const job = {
11969
12356
  id: generateId(),
@@ -11973,9 +12360,10 @@ var ragChat = (config) => {
11973
12360
  status: "running"
11974
12361
  };
11975
12362
  ingestJobs.unshift(job);
11976
- if (ingestJobs.length > MAX_INGEST_JOBS) {
11977
- ingestJobs.length = MAX_INGEST_JOBS;
12363
+ if (ingestJobs.length > jobHistoryRetention.maxIngestJobs) {
12364
+ ingestJobs.length = jobHistoryRetention.maxIngestJobs;
11978
12365
  }
12366
+ persistJobStateIfConfigured();
11979
12367
  return job;
11980
12368
  };
11981
12369
  const completeIngestJob = (job, input) => {
@@ -11986,6 +12374,7 @@ var ragChat = (config) => {
11986
12374
  job.chunkCount = input.chunkCount;
11987
12375
  job.documentCount = input.documentCount;
11988
12376
  job.extractorNames = input.extractorNames;
12377
+ persistJobStateIfConfigured();
11989
12378
  };
11990
12379
  const failIngestJob = (job, error, extractorNames) => {
11991
12380
  const finishedAt = Date.now();
@@ -11994,6 +12383,7 @@ var ragChat = (config) => {
11994
12383
  job.elapsedMs = finishedAt - job.startedAt;
11995
12384
  job.error = error;
11996
12385
  job.extractorNames = extractorNames;
12386
+ persistJobStateIfConfigured();
11997
12387
  };
11998
12388
  const createAdminAction = (action, documentId, target) => {
11999
12389
  const record = {
@@ -12005,9 +12395,10 @@ var ragChat = (config) => {
12005
12395
  target
12006
12396
  };
12007
12397
  adminActions.unshift(record);
12008
- if (adminActions.length > MAX_ADMIN_ACTIONS) {
12009
- adminActions.length = MAX_ADMIN_ACTIONS;
12398
+ if (adminActions.length > jobHistoryRetention.maxAdminActions) {
12399
+ adminActions.length = jobHistoryRetention.maxAdminActions;
12010
12400
  }
12401
+ persistJobStateIfConfigured();
12011
12402
  return record;
12012
12403
  };
12013
12404
  const createAdminJob = (action, target, bucket = adminJobs) => {
@@ -12019,9 +12410,11 @@ var ragChat = (config) => {
12019
12410
  target
12020
12411
  };
12021
12412
  bucket.unshift(job);
12022
- if (bucket.length > MAX_ADMIN_JOBS) {
12023
- bucket.length = MAX_ADMIN_JOBS;
12413
+ const maxJobs = bucket === syncJobs ? jobHistoryRetention.maxSyncJobs : jobHistoryRetention.maxAdminJobs;
12414
+ if (bucket.length > maxJobs) {
12415
+ bucket.length = maxJobs;
12024
12416
  }
12417
+ persistJobStateIfConfigured();
12025
12418
  return job;
12026
12419
  };
12027
12420
  const completeAdminAction = (record) => {
@@ -12029,6 +12422,7 @@ var ragChat = (config) => {
12029
12422
  record.status = "completed";
12030
12423
  record.finishedAt = finishedAt;
12031
12424
  record.elapsedMs = finishedAt - record.startedAt;
12425
+ persistJobStateIfConfigured();
12032
12426
  };
12033
12427
  const failAdminAction = (record, error) => {
12034
12428
  const finishedAt = Date.now();
@@ -12036,12 +12430,14 @@ var ragChat = (config) => {
12036
12430
  record.finishedAt = finishedAt;
12037
12431
  record.elapsedMs = finishedAt - record.startedAt;
12038
12432
  record.error = error;
12433
+ persistJobStateIfConfigured();
12039
12434
  };
12040
12435
  const completeAdminJob = (job) => {
12041
12436
  const finishedAt = Date.now();
12042
12437
  job.status = "completed";
12043
12438
  job.finishedAt = finishedAt;
12044
12439
  job.elapsedMs = finishedAt - job.startedAt;
12440
+ persistJobStateIfConfigured();
12045
12441
  };
12046
12442
  const failAdminJob = (job, error) => {
12047
12443
  const finishedAt = Date.now();
@@ -12049,8 +12445,10 @@ var ragChat = (config) => {
12049
12445
  job.finishedAt = finishedAt;
12050
12446
  job.elapsedMs = finishedAt - job.startedAt;
12051
12447
  job.error = error;
12448
+ persistJobStateIfConfigured();
12052
12449
  };
12053
12450
  const runSearchTracePrune = async (input, trigger = "manual") => {
12451
+ await ensureJobStateLoaded();
12054
12452
  if (!searchTraceStore) {
12055
12453
  throw new Error("RAG search trace store is not configured");
12056
12454
  }
@@ -12138,11 +12536,12 @@ var ragChat = (config) => {
12138
12536
  throw error;
12139
12537
  }
12140
12538
  };
12141
- const buildSyncSources = async () => {
12539
+ const buildSyncSources = async (scope) => {
12142
12540
  if (!indexManager?.listSyncSources) {
12143
12541
  return [];
12144
12542
  }
12145
- return await indexManager.listSyncSources();
12543
+ const sources = await indexManager.listSyncSources();
12544
+ return sources.filter((source) => matchesSyncSourceScope(scope, source));
12146
12545
  };
12147
12546
  const toHTMXResponse = (html, status, extraHeaders) => new Response(html, {
12148
12547
  headers: {
@@ -12306,6 +12705,7 @@ var ragChat = (config) => {
12306
12705
  return null;
12307
12706
  }
12308
12707
  return {
12708
+ corpusKey: getStringProperty(candidate, "corpusKey") ?? undefined,
12309
12709
  filter: caseFilter,
12310
12710
  id: getStringProperty(candidate, "id") ?? `case-${caseIndex + 1}`,
12311
12711
  retrieval: parsedCaseRetrieval === undefined || parsedCaseRetrieval === null ? undefined : parsedCaseRetrieval,
@@ -12343,7 +12743,7 @@ var ragChat = (config) => {
12343
12743
  scoreThreshold: typeof getNumberProperty(body, "scoreThreshold") === "number" ? getNumberProperty(body, "scoreThreshold") : undefined
12344
12744
  };
12345
12745
  };
12346
- const handleEvaluate = async (body) => {
12746
+ const handleEvaluate = async (body, request) => {
12347
12747
  const input = toRAGEvaluationInput(body);
12348
12748
  if (!input) {
12349
12749
  return {
@@ -12351,6 +12751,17 @@ var ragChat = (config) => {
12351
12751
  ok: false
12352
12752
  };
12353
12753
  }
12754
+ const accessScope = await loadAccessScope(request);
12755
+ for (const evaluationCase of input.cases) {
12756
+ if (evaluationCase.corpusKey && !matchesAccessScope(accessScope, {
12757
+ corpusKey: evaluationCase.corpusKey
12758
+ }) || (evaluationCase.expectedDocumentIds ?? []).some((documentId) => !matchesAccessScope(accessScope, { documentId })) || (evaluationCase.expectedSources ?? []).some((source) => !matchesAccessScope(accessScope, { source }))) {
12759
+ return {
12760
+ error: "Evaluation case is outside the allowed RAG access scope",
12761
+ ok: false
12762
+ };
12763
+ }
12764
+ }
12354
12765
  const collection = resolveCollection();
12355
12766
  if (!collection) {
12356
12767
  return {
@@ -12393,6 +12804,7 @@ var ragChat = (config) => {
12393
12804
  ...input,
12394
12805
  baselineRetrievalId: getStringProperty(body, "baselineRetrievalId"),
12395
12806
  candidateRetrievalId: getStringProperty(body, "candidateRetrievalId"),
12807
+ corpusGroupKey: getStringProperty(body, "corpusGroupKey"),
12396
12808
  groupKey: getStringProperty(body, "groupKey"),
12397
12809
  label: getStringProperty(body, "label"),
12398
12810
  persistRun: getBooleanProperty(body, "persistRun") === true,
@@ -12410,6 +12822,7 @@ var ragChat = (config) => {
12410
12822
  return null;
12411
12823
  }
12412
12824
  return {
12825
+ corpusGroupKey: getStringProperty(body, "corpusGroupKey"),
12413
12826
  groupKey,
12414
12827
  approvedAt: getIntegerLikeProperty(body, "approvedAt"),
12415
12828
  approvedBy: getStringProperty(body, "approvedBy"),
@@ -12438,6 +12851,7 @@ var ragChat = (config) => {
12438
12851
  return null;
12439
12852
  }
12440
12853
  return {
12854
+ corpusGroupKey: getStringProperty(body, "corpusGroupKey"),
12441
12855
  groupKey,
12442
12856
  overrideGate: getBooleanProperty(body, "overrideGate") === true,
12443
12857
  overrideReason: getStringProperty(body, "overrideReason"),
@@ -12468,6 +12882,7 @@ var ragChat = (config) => {
12468
12882
  return null;
12469
12883
  }
12470
12884
  return {
12885
+ corpusGroupKey: getStringProperty(body, "corpusGroupKey"),
12471
12886
  groupKey,
12472
12887
  approvedAt: getIntegerLikeProperty(body, "approvedAt"),
12473
12888
  approvedBy: getStringProperty(body, "approvedBy"),
@@ -12514,6 +12929,7 @@ var ragChat = (config) => {
12514
12929
  }
12515
12930
  return {
12516
12931
  candidateRetrievalId: getStringProperty(body, "candidateRetrievalId"),
12932
+ corpusGroupKey: getStringProperty(body, "corpusGroupKey"),
12517
12933
  decidedAt: getIntegerLikeProperty(body, "decidedAt"),
12518
12934
  decidedBy: getStringProperty(body, "decidedBy"),
12519
12935
  executePromotion: getBooleanProperty(body, "executePromotion") === true,
@@ -12853,7 +13269,7 @@ var ragChat = (config) => {
12853
13269
  });
12854
13270
  return incidents.find((entry) => entry.id === incidentId);
12855
13271
  };
12856
- const handleEvaluateRetrievals = async (body) => {
13272
+ const handleEvaluateRetrievals = async (body, request) => {
12857
13273
  const input = toRAGRetrievalComparisonRequest(body);
12858
13274
  if (!input) {
12859
13275
  return {
@@ -12861,6 +13277,29 @@ var ragChat = (config) => {
12861
13277
  ok: false
12862
13278
  };
12863
13279
  }
13280
+ const accessScope = await loadAccessScope(request);
13281
+ if (!isAllowedComparisonGroupKey(accessScope, input.groupKey)) {
13282
+ return {
13283
+ error: "Retrieval comparison group is outside the allowed RAG access scope",
13284
+ ok: false
13285
+ };
13286
+ }
13287
+ if (!isAllowedCorpusGroupKey(accessScope, input.corpusGroupKey)) {
13288
+ return {
13289
+ error: "Retrieval comparison corpus group is outside the allowed RAG access scope",
13290
+ ok: false
13291
+ };
13292
+ }
13293
+ for (const evaluationCase of input.cases) {
13294
+ if (evaluationCase.corpusKey && !matchesAccessScope(accessScope, {
13295
+ corpusKey: evaluationCase.corpusKey
13296
+ }) || (evaluationCase.expectedDocumentIds ?? []).some((documentId) => !matchesAccessScope(accessScope, { documentId })) || (evaluationCase.expectedSources ?? []).some((source) => !matchesAccessScope(accessScope, { source }))) {
13297
+ return {
13298
+ error: "Retrieval comparison case is outside the allowed RAG access scope",
13299
+ ok: false
13300
+ };
13301
+ }
13302
+ }
12864
13303
  const collection = resolveCollection();
12865
13304
  if (!collection) {
12866
13305
  return {
@@ -12896,6 +13335,19 @@ var ragChat = (config) => {
12896
13335
  label: suiteLabel
12897
13336
  }
12898
13337
  });
13338
+ const corpusGroupKey = deriveCorpusGroupKey({
13339
+ corpusGroupKey: input.corpusGroupKey,
13340
+ corpusKeys: comparison.corpusKeys
13341
+ });
13342
+ if (!isAllowedCorpusGroupKey(accessScope, corpusGroupKey)) {
13343
+ return {
13344
+ error: "Retrieval comparison corpus group is outside the allowed RAG access scope",
13345
+ ok: false
13346
+ };
13347
+ }
13348
+ if (corpusGroupKey) {
13349
+ comparison.corpusGroupKey = corpusGroupKey;
13350
+ }
12899
13351
  if (input.persistRun && retrievalComparisonHistoryStore) {
12900
13352
  const finishedAt = Date.now();
12901
13353
  const decisionSummary = buildRAGRetrievalComparisonDecisionSummary({
@@ -12907,6 +13359,8 @@ var ragChat = (config) => {
12907
13359
  await persistRAGRetrievalComparisonRun({
12908
13360
  run: {
12909
13361
  comparison,
13362
+ corpusGroupKey,
13363
+ corpusKeys: comparison.corpusKeys,
12910
13364
  decisionSummary,
12911
13365
  elapsedMs: finishedAt - startedAt,
12912
13366
  finishedAt,
@@ -12930,15 +13384,31 @@ var ragChat = (config) => {
12930
13384
  ok: true
12931
13385
  };
12932
13386
  };
12933
- const handleRetrievalComparisonHistory = async (queryInput) => {
13387
+ const handleRetrievalComparisonHistory = async (queryInput, request) => {
12934
13388
  if (!retrievalComparisonHistoryStore) {
12935
13389
  return {
12936
13390
  error: "RAG retrieval comparison history store is not configured",
12937
13391
  ok: false
12938
13392
  };
12939
13393
  }
13394
+ const accessScope = await loadAccessScope(request);
13395
+ const groupKey = getStringProperty(queryInput, "groupKey");
13396
+ const corpusGroupKey = getStringProperty(queryInput, "corpusGroupKey");
13397
+ if (!isAllowedComparisonGroupKey(accessScope, groupKey)) {
13398
+ return {
13399
+ error: "Retrieval comparison group is outside the allowed RAG access scope",
13400
+ ok: false
13401
+ };
13402
+ }
13403
+ if (!isAllowedCorpusGroupKey(accessScope, corpusGroupKey)) {
13404
+ return {
13405
+ error: "Retrieval comparison corpus group is outside the allowed RAG access scope",
13406
+ ok: false
13407
+ };
13408
+ }
12940
13409
  const runs = await loadRAGRetrievalComparisonHistory({
12941
- groupKey: getStringProperty(queryInput, "groupKey"),
13410
+ corpusGroupKey,
13411
+ groupKey,
12942
13412
  label: getStringProperty(queryInput, "label"),
12943
13413
  limit: getIntegerLikeProperty(queryInput, "limit"),
12944
13414
  store: retrievalComparisonHistoryStore,
@@ -12948,25 +13418,41 @@ var ragChat = (config) => {
12948
13418
  });
12949
13419
  return {
12950
13420
  ok: true,
12951
- runs
13421
+ runs: filterByCorpusGroupKey(accessScope, filterByComparisonGroupKey(accessScope, runs))
12952
13422
  };
12953
13423
  };
12954
- const handleRetrievalBaselineList = async (queryInput) => {
13424
+ const handleRetrievalBaselineList = async (queryInput, request) => {
12955
13425
  if (!retrievalBaselineStore) {
12956
13426
  return {
12957
13427
  error: "RAG retrieval baseline store is not configured",
12958
13428
  ok: false
12959
13429
  };
12960
13430
  }
13431
+ const accessScope = await loadAccessScope(request);
13432
+ const groupKey = getStringProperty(queryInput, "groupKey");
13433
+ const corpusGroupKey = getStringProperty(queryInput, "corpusGroupKey");
13434
+ if (!isAllowedComparisonGroupKey(accessScope, groupKey)) {
13435
+ return {
13436
+ error: "Retrieval baseline group is outside the allowed RAG access scope",
13437
+ ok: false
13438
+ };
13439
+ }
13440
+ if (!isAllowedCorpusGroupKey(accessScope, corpusGroupKey)) {
13441
+ return {
13442
+ error: "Retrieval baseline corpus group is outside the allowed RAG access scope",
13443
+ ok: false
13444
+ };
13445
+ }
12961
13446
  const baselines = await loadRAGRetrievalBaselines({
12962
- groupKey: getStringProperty(queryInput, "groupKey"),
13447
+ corpusGroupKey,
13448
+ groupKey,
12963
13449
  limit: getIntegerLikeProperty(queryInput, "limit"),
12964
13450
  status: getStringProperty(queryInput, "status") === "active" || getStringProperty(queryInput, "status") === "superseded" ? getStringProperty(queryInput, "status") : undefined,
12965
13451
  store: retrievalBaselineStore,
12966
13452
  tag: getStringProperty(queryInput, "tag")
12967
13453
  });
12968
13454
  return {
12969
- baselines,
13455
+ baselines: filterByCorpusGroupKey(accessScope, filterByComparisonGroupKey(accessScope, baselines)),
12970
13456
  ok: true
12971
13457
  };
12972
13458
  };
@@ -12977,6 +13463,7 @@ var ragChat = (config) => {
12977
13463
  await persistRAGRetrievalReleaseDecision({
12978
13464
  record: {
12979
13465
  baselineId: input.baseline?.id,
13466
+ corpusGroupKey: input.baseline?.corpusGroupKey ?? input.corpusGroupKey,
12980
13467
  decidedAt: input.baseline?.promotedAt ?? input.decidedAt ?? Date.now(),
12981
13468
  decidedBy: input.decidedBy,
12982
13469
  groupKey: input.baseline?.groupKey ?? input.groupKey,
@@ -13093,6 +13580,7 @@ var ragChat = (config) => {
13093
13580
  await persistRAGRetrievalLaneHandoffIncidentHistory({
13094
13581
  record: {
13095
13582
  action: input.action,
13583
+ corpusGroupKey: input.incident.corpusGroupKey,
13096
13584
  groupKey: input.incident.groupKey,
13097
13585
  id: generateId(),
13098
13586
  incidentId: input.incident.id,
@@ -13115,6 +13603,7 @@ var ragChat = (config) => {
13115
13603
  await persistRAGRetrievalLaneHandoffAutoCompletePolicyHistory({
13116
13604
  record: {
13117
13605
  changeKind: input.changeKind,
13606
+ corpusGroupKey: input.corpusGroupKey,
13118
13607
  enabled: input.enabled,
13119
13608
  groupKey: input.groupKey,
13120
13609
  id: generateId(),
@@ -13135,6 +13624,7 @@ var ragChat = (config) => {
13135
13624
  record: {
13136
13625
  approvalMaxAgeMs: input.approvalMaxAgeMs,
13137
13626
  changeKind: input.changeKind,
13627
+ corpusGroupKey: input.corpusGroupKey,
13138
13628
  groupKey: input.groupKey,
13139
13629
  id: generateId(),
13140
13630
  previousApprovalMaxAgeMs: input.previousApprovalMaxAgeMs,
@@ -13154,6 +13644,7 @@ var ragChat = (config) => {
13154
13644
  await persistRAGRetrievalBaselineGatePolicyHistory({
13155
13645
  record: {
13156
13646
  changeKind: input.changeKind,
13647
+ corpusGroupKey: input.corpusGroupKey,
13157
13648
  groupKey: input.groupKey,
13158
13649
  id: generateId(),
13159
13650
  policy: input.policy,
@@ -13173,6 +13664,7 @@ var ragChat = (config) => {
13173
13664
  record: {
13174
13665
  approvalExpiredSeverity: input.approvalExpiredSeverity,
13175
13666
  changeKind: input.changeKind,
13667
+ corpusGroupKey: input.corpusGroupKey,
13176
13668
  gateFailureSeverity: input.gateFailureSeverity,
13177
13669
  groupKey: input.groupKey,
13178
13670
  id: generateId(),
@@ -13261,6 +13753,7 @@ var ragChat = (config) => {
13261
13753
  const matchingHandoff = (input.handoffs ?? []).find((entry) => entry.groupKey === incident.groupKey && entry.targetRolloutLabel === incident.targetRolloutLabel);
13262
13754
  const laneIncident = {
13263
13755
  ...incident,
13756
+ corpusGroupKey: matchingHandoff?.corpusGroupKey,
13264
13757
  kind: "handoff_stale",
13265
13758
  sourceRolloutLabel: matchingHandoff?.sourceRolloutLabel
13266
13759
  };
@@ -13660,6 +14153,7 @@ var ragChat = (config) => {
13660
14153
  approvedAt: input.approvedAt,
13661
14154
  approvedBy: input.approvedBy,
13662
14155
  approvalNotes: input.approvalNotes,
14156
+ corpusGroupKey: input.corpusGroupKey,
13663
14157
  groupKey: input.groupKey,
13664
14158
  id: generateId(),
13665
14159
  label: input.label ?? input.retrievalId,
@@ -13679,7 +14173,7 @@ var ragChat = (config) => {
13679
14173
  });
13680
14174
  return baseline;
13681
14175
  };
13682
- const handlePromoteRetrievalBaseline = async (body) => {
14176
+ const handlePromoteRetrievalBaseline = async (body, request) => {
13683
14177
  if (!retrievalBaselineStore) {
13684
14178
  return {
13685
14179
  error: "RAG retrieval baseline store is not configured",
@@ -13693,6 +14187,19 @@ var ragChat = (config) => {
13693
14187
  ok: false
13694
14188
  };
13695
14189
  }
14190
+ const accessScope = await loadAccessScope(request);
14191
+ if (!isAllowedComparisonGroupKey(accessScope, input.groupKey)) {
14192
+ return {
14193
+ error: "Retrieval baseline group is outside the allowed RAG access scope",
14194
+ ok: false
14195
+ };
14196
+ }
14197
+ if (!isAllowedCorpusGroupKey(accessScope, input.corpusGroupKey)) {
14198
+ return {
14199
+ error: "Retrieval baseline corpus group is outside the allowed RAG access scope",
14200
+ ok: false
14201
+ };
14202
+ }
13696
14203
  const job = createAdminJob("promote_retrieval_baseline", input.groupKey);
13697
14204
  const action = createAdminAction("promote_retrieval_baseline", undefined, input.groupKey);
13698
14205
  try {
@@ -13735,6 +14242,7 @@ var ragChat = (config) => {
13735
14242
  approvedAt: input.approvedAt,
13736
14243
  approvedBy: input.approvedBy,
13737
14244
  approvalNotes: input.approvalNotes,
14245
+ corpusGroupKey: input.corpusGroupKey,
13738
14246
  groupKey: input.groupKey,
13739
14247
  label: input.label,
13740
14248
  metadata: input.metadata,
@@ -13748,6 +14256,7 @@ var ragChat = (config) => {
13748
14256
  });
13749
14257
  await persistRetrievalReleaseDecisionIfConfigured({
13750
14258
  baseline,
14259
+ corpusGroupKey: baseline.corpusGroupKey,
13751
14260
  decidedBy: input.approvedBy,
13752
14261
  kind: "promote",
13753
14262
  notes: input.approvalNotes,
@@ -13776,7 +14285,7 @@ var ragChat = (config) => {
13776
14285
  };
13777
14286
  }
13778
14287
  };
13779
- const handlePromoteRetrievalBaselineFromRun = async (body) => {
14288
+ const handlePromoteRetrievalBaselineFromRun = async (body, request) => {
13780
14289
  if (!retrievalBaselineStore || !retrievalComparisonHistoryStore) {
13781
14290
  return {
13782
14291
  error: "RAG retrieval baseline store and retrieval comparison history store are required",
@@ -13790,6 +14299,19 @@ var ragChat = (config) => {
13790
14299
  ok: false
13791
14300
  };
13792
14301
  }
14302
+ const accessScope = await loadAccessScope(request);
14303
+ if (!isAllowedComparisonGroupKey(accessScope, input.groupKey)) {
14304
+ return {
14305
+ error: "Retrieval baseline group is outside the allowed RAG access scope",
14306
+ ok: false
14307
+ };
14308
+ }
14309
+ if (!isAllowedCorpusGroupKey(accessScope, input.corpusGroupKey)) {
14310
+ return {
14311
+ error: "Retrieval baseline corpus group is outside the allowed RAG access scope",
14312
+ ok: false
14313
+ };
14314
+ }
13793
14315
  const job = createAdminJob("promote_retrieval_baseline", input.groupKey);
13794
14316
  const action = createAdminAction("promote_retrieval_baseline", undefined, input.groupKey);
13795
14317
  try {
@@ -13801,6 +14323,13 @@ var ragChat = (config) => {
13801
14323
  if (!sourceRun) {
13802
14324
  throw new Error("Retrieval comparison run was not found");
13803
14325
  }
14326
+ const corpusGroupKey = deriveCorpusGroupKey({
14327
+ corpusGroupKey: input.corpusGroupKey ?? sourceRun.corpusGroupKey ?? sourceRun.comparison.corpusGroupKey,
14328
+ corpusKeys: sourceRun.corpusKeys ?? sourceRun.comparison.corpusKeys
14329
+ });
14330
+ if (!isAllowedCorpusGroupKey(accessScope, corpusGroupKey)) {
14331
+ throw new Error("Retrieval baseline corpus group is outside the allowed RAG access scope");
14332
+ }
13804
14333
  const retrievalId = input.retrievalId ?? sourceRun.decisionSummary?.candidateRetrievalId ?? sourceRun.comparison.summary.bestByPassingRate ?? sourceRun.comparison.entries[0]?.retrievalId;
13805
14334
  if (!retrievalId) {
13806
14335
  throw new Error("Unable to resolve retrieval candidate from comparison run");
@@ -13832,6 +14361,7 @@ var ragChat = (config) => {
13832
14361
  approvedAt: input.approvedAt,
13833
14362
  approvedBy: input.approvedBy,
13834
14363
  approvalNotes: input.approvalNotes,
14364
+ corpusGroupKey,
13835
14365
  groupKey: input.groupKey,
13836
14366
  label: entry?.label ?? retrievalId,
13837
14367
  metadata: input.metadata,
@@ -13845,6 +14375,7 @@ var ragChat = (config) => {
13845
14375
  });
13846
14376
  await persistRetrievalReleaseDecisionIfConfigured({
13847
14377
  baseline,
14378
+ corpusGroupKey,
13848
14379
  decidedBy: input.approvedBy,
13849
14380
  gateStatus: gate?.status,
13850
14381
  kind: "promote",
@@ -13876,7 +14407,7 @@ var ragChat = (config) => {
13876
14407
  };
13877
14408
  }
13878
14409
  };
13879
- const handleRevertRetrievalBaseline = async (body) => {
14410
+ const handleRevertRetrievalBaseline = async (body, request) => {
13880
14411
  if (!retrievalBaselineStore) {
13881
14412
  return {
13882
14413
  error: "RAG retrieval baseline store is not configured",
@@ -13890,6 +14421,19 @@ var ragChat = (config) => {
13890
14421
  ok: false
13891
14422
  };
13892
14423
  }
14424
+ const accessScope = await loadAccessScope(request);
14425
+ if (!isAllowedComparisonGroupKey(accessScope, input.groupKey)) {
14426
+ return {
14427
+ error: "Retrieval baseline group is outside the allowed RAG access scope",
14428
+ ok: false
14429
+ };
14430
+ }
14431
+ if (!isAllowedCorpusGroupKey(accessScope, input.corpusGroupKey)) {
14432
+ return {
14433
+ error: "Retrieval baseline corpus group is outside the allowed RAG access scope",
14434
+ ok: false
14435
+ };
14436
+ }
13893
14437
  const job = createAdminJob("revert_retrieval_baseline", input.groupKey);
13894
14438
  const action = createAdminAction("revert_retrieval_baseline", undefined, input.groupKey);
13895
14439
  try {
@@ -13901,10 +14445,14 @@ var ragChat = (config) => {
13901
14445
  if (!targetBaseline) {
13902
14446
  throw new Error("Retrieval baseline version was not found");
13903
14447
  }
14448
+ if (!isAllowedCorpusGroupKey(accessScope, targetBaseline.corpusGroupKey)) {
14449
+ throw new Error("Retrieval baseline corpus group is outside the allowed RAG access scope");
14450
+ }
13904
14451
  const baseline = await promoteRetrievalBaselineRecord({
13905
14452
  approvedAt: input.approvedAt,
13906
14453
  approvedBy: input.approvedBy,
13907
14454
  approvalNotes: input.approvalNotes,
14455
+ corpusGroupKey: targetBaseline.corpusGroupKey,
13908
14456
  groupKey: input.groupKey,
13909
14457
  label: targetBaseline.label,
13910
14458
  metadata: {
@@ -13921,6 +14469,7 @@ var ragChat = (config) => {
13921
14469
  });
13922
14470
  await persistRetrievalReleaseDecisionIfConfigured({
13923
14471
  baseline,
14472
+ corpusGroupKey: baseline.corpusGroupKey,
13924
14473
  decidedBy: input.approvedBy,
13925
14474
  kind: "revert",
13926
14475
  notes: input.approvalNotes,
@@ -13951,24 +14500,40 @@ var ragChat = (config) => {
13951
14500
  };
13952
14501
  }
13953
14502
  };
13954
- const handleRetrievalReleaseDecisionList = async (queryInput) => {
14503
+ const handleRetrievalReleaseDecisionList = async (queryInput, request) => {
13955
14504
  if (!config.retrievalReleaseDecisionStore) {
13956
14505
  return {
13957
14506
  error: "RAG retrieval release decision store is not configured",
13958
14507
  ok: false
13959
14508
  };
13960
14509
  }
14510
+ const accessScope = await loadAccessScope(request);
14511
+ const groupKey = getStringProperty(queryInput, "groupKey");
14512
+ const corpusGroupKey = getStringProperty(queryInput, "corpusGroupKey");
14513
+ if (!isAllowedComparisonGroupKey(accessScope, groupKey)) {
14514
+ return {
14515
+ error: "Retrieval release decision group is outside the allowed RAG access scope",
14516
+ ok: false
14517
+ };
14518
+ }
14519
+ if (!isAllowedCorpusGroupKey(accessScope, corpusGroupKey)) {
14520
+ return {
14521
+ error: "Retrieval release decision corpus group is outside the allowed RAG access scope",
14522
+ ok: false
14523
+ };
14524
+ }
13961
14525
  const kind = getStringProperty(queryInput, "kind");
13962
14526
  const targetRolloutLabel = getStringProperty(queryInput, "targetRolloutLabel");
13963
14527
  const freshnessStatusFilter = getStringProperty(queryInput, "freshnessStatus");
13964
14528
  const decisions = await loadRAGRetrievalReleaseDecisions({
13965
- groupKey: getStringProperty(queryInput, "groupKey"),
14529
+ corpusGroupKey,
14530
+ groupKey,
13966
14531
  kind: kind === "approve" || kind === "promote" || kind === "reject" || kind === "revert" ? kind : undefined,
13967
14532
  limit: getIntegerLikeProperty(queryInput, "limit"),
13968
14533
  store: config.retrievalReleaseDecisionStore
13969
14534
  });
13970
14535
  return {
13971
- decisions: decisions.map((decision) => ({
14536
+ decisions: filterByCorpusGroupKey(accessScope, filterByComparisonGroupKey(accessScope, decisions)).map((decision) => ({
13972
14537
  ...decision,
13973
14538
  ...getDecisionFreshness({ record: decision })
13974
14539
  })).filter((decision) => {
@@ -13986,7 +14551,7 @@ var ragChat = (config) => {
13986
14551
  ok: true
13987
14552
  };
13988
14553
  };
13989
- const handleRetrievalReleaseDecisionAction = async (body, kind) => {
14554
+ const handleRetrievalReleaseDecisionAction = async (body, kind, request) => {
13990
14555
  if (!retrievalComparisonHistoryStore || !config.retrievalReleaseDecisionStore) {
13991
14556
  return {
13992
14557
  error: "RAG retrieval comparison history store and release decision store are required",
@@ -14000,6 +14565,13 @@ var ragChat = (config) => {
14000
14565
  ok: false
14001
14566
  };
14002
14567
  }
14568
+ const accessScope = await loadAccessScope(request);
14569
+ if (!isAllowedComparisonGroupKey(accessScope, input.groupKey)) {
14570
+ return {
14571
+ error: "Retrieval release decision group is outside the allowed RAG access scope",
14572
+ ok: false
14573
+ };
14574
+ }
14003
14575
  const runs = await loadRAGRetrievalComparisonHistory({
14004
14576
  groupKey: input.groupKey,
14005
14577
  store: retrievalComparisonHistoryStore
@@ -14011,6 +14583,16 @@ var ragChat = (config) => {
14011
14583
  ok: false
14012
14584
  };
14013
14585
  }
14586
+ const corpusGroupKey = deriveCorpusGroupKey({
14587
+ corpusGroupKey: sourceRun.corpusGroupKey ?? sourceRun.comparison.corpusGroupKey,
14588
+ corpusKeys: sourceRun.corpusKeys ?? sourceRun.comparison.corpusKeys
14589
+ });
14590
+ if (!isAllowedCorpusGroupKey(accessScope, corpusGroupKey)) {
14591
+ return {
14592
+ error: "Retrieval release decision corpus group is outside the allowed RAG access scope",
14593
+ ok: false
14594
+ };
14595
+ }
14014
14596
  const retrievalId = input.retrievalId ?? sourceRun.decisionSummary?.candidateRetrievalId ?? sourceRun.comparison.summary.bestByPassingRate ?? sourceRun.comparison.entries[0]?.retrievalId;
14015
14597
  if (!retrievalId) {
14016
14598
  return {
@@ -14040,6 +14622,7 @@ var ragChat = (config) => {
14040
14622
  const decidedAt = Date.now();
14041
14623
  await persistRetrievalReleaseDecisionIfConfigured({
14042
14624
  baseline: undefined,
14625
+ corpusGroupKey,
14043
14626
  decidedAt: input.decidedAt,
14044
14627
  decidedBy: input.decidedBy,
14045
14628
  gateStatus: gate?.status,
@@ -14053,17 +14636,19 @@ var ragChat = (config) => {
14053
14636
  targetRolloutLabel: input.targetRolloutLabel
14054
14637
  });
14055
14638
  const decisions = await loadRAGRetrievalReleaseDecisions({
14639
+ corpusGroupKey,
14056
14640
  groupKey: input.groupKey,
14057
14641
  limit: 10,
14058
14642
  store: config.retrievalReleaseDecisionStore
14059
14643
  });
14060
14644
  return {
14061
- decisions,
14645
+ decisions: filterByCorpusGroupKey(accessScope, decisions),
14062
14646
  ok: true
14063
14647
  };
14064
14648
  };
14065
- const handleRetrievalReleaseGroupHistory = async (queryInput) => {
14649
+ const handleRetrievalReleaseGroupHistory = async (queryInput, request) => {
14066
14650
  const groupKey = getStringProperty(queryInput, "groupKey");
14651
+ const corpusGroupKey = getStringProperty(queryInput, "corpusGroupKey");
14067
14652
  const targetRolloutLabel = getStringProperty(queryInput, "targetRolloutLabel");
14068
14653
  if (!groupKey) {
14069
14654
  return {
@@ -14071,7 +14656,21 @@ var ragChat = (config) => {
14071
14656
  ok: false
14072
14657
  };
14073
14658
  }
14659
+ const accessScope = await loadAccessScope(request);
14660
+ if (!isAllowedComparisonGroupKey(accessScope, groupKey)) {
14661
+ return {
14662
+ error: "Retrieval release decision group is outside the allowed RAG access scope",
14663
+ ok: false
14664
+ };
14665
+ }
14666
+ if (!isAllowedCorpusGroupKey(accessScope, corpusGroupKey)) {
14667
+ return {
14668
+ error: "Retrieval release corpus group is outside the allowed RAG access scope",
14669
+ ok: false
14670
+ };
14671
+ }
14074
14672
  const decisions = config.retrievalReleaseDecisionStore ? (await loadRAGRetrievalReleaseDecisions({
14673
+ corpusGroupKey,
14075
14674
  groupKey,
14076
14675
  limit: getIntegerLikeProperty(queryInput, "decisionLimit") ?? 20,
14077
14676
  store: config.retrievalReleaseDecisionStore
@@ -14080,11 +14679,13 @@ var ragChat = (config) => {
14080
14679
  ...getDecisionFreshness({ record: decision })
14081
14680
  })).filter((decision) => targetRolloutLabel === "canary" || targetRolloutLabel === "stable" || targetRolloutLabel === "rollback_target" ? decision.targetRolloutLabel === targetRolloutLabel : true) : undefined;
14082
14681
  const baselines = retrievalBaselineStore ? await loadRAGRetrievalBaselines({
14682
+ corpusGroupKey,
14083
14683
  groupKey,
14084
14684
  limit: getIntegerLikeProperty(queryInput, "baselineLimit") ?? 20,
14085
14685
  store: retrievalBaselineStore
14086
14686
  }).then((entries) => targetRolloutLabel === "canary" || targetRolloutLabel === "stable" || targetRolloutLabel === "rollback_target" ? entries.filter((entry) => entry.rolloutLabel === targetRolloutLabel) : entries) : undefined;
14087
14687
  const runs = retrievalComparisonHistoryStore ? await loadRAGRetrievalComparisonHistory({
14688
+ corpusGroupKey,
14088
14689
  groupKey,
14089
14690
  limit: getIntegerLikeProperty(queryInput, "runLimit") ?? 20,
14090
14691
  store: retrievalComparisonHistoryStore
@@ -14092,11 +14693,13 @@ var ragChat = (config) => {
14092
14693
  const latest = decisions?.[0];
14093
14694
  return {
14094
14695
  baselines,
14696
+ corpusGroupKey: corpusGroupKey ?? decisions?.[0]?.corpusGroupKey ?? baselines?.[0]?.corpusGroupKey ?? runs?.[0]?.corpusGroupKey,
14095
14697
  decisions,
14096
14698
  groupKey,
14097
14699
  ok: true,
14098
14700
  runs,
14099
14701
  timeline: {
14702
+ corpusGroupKey: corpusGroupKey ?? decisions?.[0]?.corpusGroupKey ?? baselines?.[0]?.corpusGroupKey ?? runs?.[0]?.corpusGroupKey,
14100
14703
  groupKey,
14101
14704
  lastApprovedAt: decisions?.find((entry) => entry.kind === "approve")?.decidedAt,
14102
14705
  lastPromotedAt: decisions?.find((entry) => entry.kind === "promote")?.decidedAt,
@@ -14108,29 +14711,53 @@ var ragChat = (config) => {
14108
14711
  }
14109
14712
  };
14110
14713
  };
14111
- const handleRetrievalLaneHandoffList = async (queryInput) => {
14714
+ const handleRetrievalLaneHandoffList = async (queryInput, request) => {
14112
14715
  const result = await buildOperationsPayload();
14716
+ const accessScope = await loadAccessScope(request);
14717
+ const corpusGroupKey = getStringProperty(queryInput, "corpusGroupKey");
14718
+ if (!isAllowedCorpusGroupKey(accessScope, corpusGroupKey)) {
14719
+ return {
14720
+ error: "Retrieval lane handoff corpus group is outside the allowed RAG access scope",
14721
+ ok: false
14722
+ };
14723
+ }
14113
14724
  const sourceRolloutLabel = getStringProperty(queryInput, "sourceRolloutLabel");
14114
14725
  const targetRolloutLabel = getStringProperty(queryInput, "targetRolloutLabel");
14115
14726
  const limit = getIntegerLikeProperty(queryInput, "limit");
14116
- const handoffs = (result.retrievalComparisons?.releaseLaneHandoffs ?? []).filter((entry) => (!getStringProperty(queryInput, "groupKey") || entry.groupKey === getStringProperty(queryInput, "groupKey")) && (sourceRolloutLabel !== "canary" && sourceRolloutLabel !== "stable" && sourceRolloutLabel !== "rollback_target" || entry.sourceRolloutLabel === sourceRolloutLabel) && (targetRolloutLabel !== "canary" && targetRolloutLabel !== "stable" && targetRolloutLabel !== "rollback_target" || entry.targetRolloutLabel === targetRolloutLabel));
14727
+ const handoffs = (result.retrievalComparisons?.releaseLaneHandoffs ?? []).filter((entry) => (!corpusGroupKey || entry.corpusGroupKey === corpusGroupKey) && (!getStringProperty(queryInput, "groupKey") || entry.groupKey === getStringProperty(queryInput, "groupKey")) && (sourceRolloutLabel !== "canary" && sourceRolloutLabel !== "stable" && sourceRolloutLabel !== "rollback_target" || entry.sourceRolloutLabel === sourceRolloutLabel) && (targetRolloutLabel !== "canary" && targetRolloutLabel !== "stable" && targetRolloutLabel !== "rollback_target" || entry.targetRolloutLabel === targetRolloutLabel));
14117
14728
  return {
14118
14729
  handoffs: typeof limit === "number" ? handoffs.slice(0, limit) : handoffs,
14119
14730
  ok: true
14120
14731
  };
14121
14732
  };
14122
- const handleRetrievalLaneHandoffDecisionList = async (queryInput) => {
14733
+ const handleRetrievalLaneHandoffDecisionList = async (queryInput, request) => {
14123
14734
  if (!config.retrievalLaneHandoffDecisionStore) {
14124
14735
  return {
14125
14736
  error: "RAG retrieval lane handoff decision store is not configured",
14126
14737
  ok: false
14127
14738
  };
14128
14739
  }
14740
+ const accessScope = await loadAccessScope(request);
14741
+ const groupKey = getStringProperty(queryInput, "groupKey");
14742
+ const corpusGroupKey = getStringProperty(queryInput, "corpusGroupKey");
14743
+ if (!isAllowedComparisonGroupKey(accessScope, groupKey)) {
14744
+ return {
14745
+ error: "Retrieval lane handoff decision group is outside the allowed RAG access scope",
14746
+ ok: false
14747
+ };
14748
+ }
14749
+ if (!isAllowedCorpusGroupKey(accessScope, corpusGroupKey)) {
14750
+ return {
14751
+ error: "Retrieval lane handoff decision corpus group is outside the allowed RAG access scope",
14752
+ ok: false
14753
+ };
14754
+ }
14129
14755
  const sourceRolloutLabel = getStringProperty(queryInput, "sourceRolloutLabel");
14130
14756
  const targetRolloutLabel = getStringProperty(queryInput, "targetRolloutLabel");
14131
14757
  const kind = getStringProperty(queryInput, "kind");
14132
14758
  const decisions = await loadRAGRetrievalLaneHandoffDecisions({
14133
- groupKey: getStringProperty(queryInput, "groupKey"),
14759
+ corpusGroupKey,
14760
+ groupKey,
14134
14761
  kind: kind === "approve" || kind === "reject" || kind === "complete" ? kind : undefined,
14135
14762
  limit: getIntegerLikeProperty(queryInput, "limit"),
14136
14763
  sourceRolloutLabel: sourceRolloutLabel === "canary" || sourceRolloutLabel === "stable" || sourceRolloutLabel === "rollback_target" ? sourceRolloutLabel : undefined,
@@ -14138,116 +14765,230 @@ var ragChat = (config) => {
14138
14765
  targetRolloutLabel: targetRolloutLabel === "canary" || targetRolloutLabel === "stable" || targetRolloutLabel === "rollback_target" ? targetRolloutLabel : undefined
14139
14766
  });
14140
14767
  return {
14141
- decisions,
14768
+ decisions: filterByCorpusGroupKey(accessScope, filterByComparisonGroupKey(accessScope, decisions)),
14142
14769
  ok: true
14143
14770
  };
14144
14771
  };
14145
- const handleRetrievalLaneHandoffIncidentList = async (queryInput) => {
14772
+ const handleRetrievalLaneHandoffIncidentList = async (queryInput, request) => {
14146
14773
  if (!config.retrievalLaneHandoffIncidentStore) {
14147
14774
  return {
14148
14775
  error: "RAG retrieval lane handoff incident store is not configured",
14149
14776
  ok: false
14150
14777
  };
14151
14778
  }
14779
+ const accessScope = await loadAccessScope(request);
14780
+ const groupKey = getStringProperty(queryInput, "groupKey");
14781
+ const corpusGroupKey = getStringProperty(queryInput, "corpusGroupKey");
14782
+ if (!isAllowedComparisonGroupKey(accessScope, groupKey)) {
14783
+ return {
14784
+ error: "Retrieval lane handoff incident group is outside the allowed RAG access scope",
14785
+ ok: false
14786
+ };
14787
+ }
14788
+ if (!isAllowedCorpusGroupKey(accessScope, corpusGroupKey)) {
14789
+ return {
14790
+ error: "Retrieval lane handoff incident corpus group is outside the allowed RAG access scope",
14791
+ ok: false
14792
+ };
14793
+ }
14152
14794
  const targetRolloutLabel = getStringProperty(queryInput, "targetRolloutLabel");
14153
14795
  const status = getStringProperty(queryInput, "status");
14154
14796
  const severity = getStringProperty(queryInput, "severity");
14155
14797
  const incidents = await loadRAGRetrievalLaneHandoffIncidents({
14156
- groupKey: getStringProperty(queryInput, "groupKey"),
14798
+ corpusGroupKey,
14799
+ groupKey,
14157
14800
  limit: getIntegerLikeProperty(queryInput, "limit"),
14158
14801
  severity: severity === "warning" || severity === "critical" ? severity : undefined,
14159
14802
  status: status === "open" || status === "resolved" ? status : undefined,
14160
14803
  store: config.retrievalLaneHandoffIncidentStore,
14161
14804
  targetRolloutLabel: targetRolloutLabel === "canary" || targetRolloutLabel === "stable" || targetRolloutLabel === "rollback_target" ? targetRolloutLabel : undefined
14162
14805
  });
14163
- return { incidents, ok: true };
14806
+ return {
14807
+ incidents: filterByCorpusGroupKey(accessScope, filterByComparisonGroupKey(accessScope, incidents)),
14808
+ ok: true
14809
+ };
14164
14810
  };
14165
- const handleRetrievalLaneHandoffIncidentHistoryList = async (queryInput) => {
14811
+ const handleRetrievalLaneHandoffIncidentHistoryList = async (queryInput, request) => {
14166
14812
  if (!config.retrievalLaneHandoffIncidentHistoryStore) {
14167
14813
  return {
14168
14814
  error: "RAG retrieval lane handoff incident history store is not configured",
14169
14815
  ok: false
14170
14816
  };
14171
14817
  }
14818
+ const accessScope = await loadAccessScope(request);
14819
+ const groupKey = getStringProperty(queryInput, "groupKey");
14820
+ const corpusGroupKey = getStringProperty(queryInput, "corpusGroupKey");
14821
+ if (!isAllowedComparisonGroupKey(accessScope, groupKey)) {
14822
+ return {
14823
+ error: "Retrieval lane handoff incident history group is outside the allowed RAG access scope",
14824
+ ok: false
14825
+ };
14826
+ }
14827
+ if (!isAllowedCorpusGroupKey(accessScope, corpusGroupKey)) {
14828
+ return {
14829
+ error: "Retrieval lane handoff incident history corpus group is outside the allowed RAG access scope",
14830
+ ok: false
14831
+ };
14832
+ }
14172
14833
  const targetRolloutLabel = getStringProperty(queryInput, "targetRolloutLabel");
14173
14834
  const action = getStringProperty(queryInput, "action");
14174
14835
  const records = await loadRAGRetrievalLaneHandoffIncidentHistory({
14836
+ corpusGroupKey,
14175
14837
  action: action === "opened" || action === "acknowledged" || action === "unacknowledged" || action === "resolved" ? action : undefined,
14176
- groupKey: getStringProperty(queryInput, "groupKey"),
14838
+ groupKey,
14177
14839
  incidentId: getStringProperty(queryInput, "incidentId"),
14178
14840
  limit: getIntegerLikeProperty(queryInput, "limit"),
14179
14841
  store: config.retrievalLaneHandoffIncidentHistoryStore,
14180
14842
  targetRolloutLabel: targetRolloutLabel === "canary" || targetRolloutLabel === "stable" || targetRolloutLabel === "rollback_target" ? targetRolloutLabel : undefined
14181
14843
  });
14182
- return { ok: true, records };
14844
+ return {
14845
+ ok: true,
14846
+ records: filterByCorpusGroupKey(accessScope, filterByComparisonGroupKey(accessScope, records))
14847
+ };
14183
14848
  };
14184
- const handleRetrievalLaneHandoffAutoCompletePolicyHistoryList = async (queryInput) => {
14849
+ const handleRetrievalLaneHandoffAutoCompletePolicyHistoryList = async (queryInput, request) => {
14185
14850
  if (!config.retrievalLaneHandoffAutoCompletePolicyHistoryStore) {
14186
14851
  return {
14187
14852
  error: "RAG retrieval lane handoff auto-complete policy history store is not configured",
14188
14853
  ok: false
14189
14854
  };
14190
14855
  }
14856
+ const accessScope = await loadAccessScope(request);
14857
+ const groupKey = getStringProperty(queryInput, "groupKey");
14858
+ const corpusGroupKey = getStringProperty(queryInput, "corpusGroupKey");
14859
+ if (!isAllowedComparisonGroupKey(accessScope, groupKey)) {
14860
+ return {
14861
+ error: "Retrieval lane handoff auto-complete policy group is outside the allowed RAG access scope",
14862
+ ok: false
14863
+ };
14864
+ }
14865
+ if (!isAllowedCorpusGroupKey(accessScope, corpusGroupKey)) {
14866
+ return {
14867
+ error: "Retrieval lane handoff auto-complete policy corpus group is outside the allowed RAG access scope",
14868
+ ok: false
14869
+ };
14870
+ }
14191
14871
  const targetRolloutLabel = getStringProperty(queryInput, "targetRolloutLabel");
14192
14872
  const records = await loadRAGRetrievalLaneHandoffAutoCompletePolicyHistory({
14193
- groupKey: getStringProperty(queryInput, "groupKey"),
14873
+ corpusGroupKey,
14874
+ groupKey,
14194
14875
  limit: getIntegerLikeProperty(queryInput, "limit"),
14195
14876
  store: config.retrievalLaneHandoffAutoCompletePolicyHistoryStore,
14196
14877
  targetRolloutLabel: targetRolloutLabel === "canary" || targetRolloutLabel === "stable" || targetRolloutLabel === "rollback_target" ? targetRolloutLabel : undefined
14197
14878
  });
14198
- return { ok: true, records };
14879
+ return {
14880
+ ok: true,
14881
+ records: filterByCorpusGroupKey(accessScope, filterByComparisonGroupKey(accessScope, records))
14882
+ };
14199
14883
  };
14200
- const handleRetrievalReleaseLanePolicyHistoryList = async (queryInput) => {
14884
+ const handleRetrievalReleaseLanePolicyHistoryList = async (queryInput, request) => {
14201
14885
  if (!config.retrievalReleaseLanePolicyHistoryStore) {
14202
14886
  return {
14203
14887
  error: "RAG retrieval release lane policy history store is not configured",
14204
14888
  ok: false
14205
14889
  };
14206
14890
  }
14891
+ const accessScope = await loadAccessScope(request);
14892
+ const groupKey = getStringProperty(queryInput, "groupKey");
14893
+ const corpusGroupKey = getStringProperty(queryInput, "corpusGroupKey");
14894
+ if (!isAllowedComparisonGroupKey(accessScope, groupKey)) {
14895
+ return {
14896
+ error: "Retrieval release lane policy group is outside the allowed RAG access scope",
14897
+ ok: false
14898
+ };
14899
+ }
14900
+ if (!isAllowedCorpusGroupKey(accessScope, corpusGroupKey)) {
14901
+ return {
14902
+ error: "Retrieval release lane policy corpus group is outside the allowed RAG access scope",
14903
+ ok: false
14904
+ };
14905
+ }
14207
14906
  const rolloutLabel = getStringProperty(queryInput, "rolloutLabel");
14208
14907
  const scope = getStringProperty(queryInput, "scope");
14209
14908
  const records = await loadRAGRetrievalReleaseLanePolicyHistory({
14210
- groupKey: getStringProperty(queryInput, "groupKey"),
14909
+ corpusGroupKey,
14910
+ groupKey,
14211
14911
  limit: getIntegerLikeProperty(queryInput, "limit"),
14212
14912
  rolloutLabel: rolloutLabel === "canary" || rolloutLabel === "stable" || rolloutLabel === "rollback_target" ? rolloutLabel : undefined,
14213
14913
  scope: scope === "rollout_label" || scope === "group_rollout_label" ? scope : undefined,
14214
14914
  store: config.retrievalReleaseLanePolicyHistoryStore
14215
14915
  });
14216
- return { ok: true, records };
14916
+ return {
14917
+ ok: true,
14918
+ records: filterByCorpusGroupKey(accessScope, filterByComparisonGroupKey(accessScope, records))
14919
+ };
14217
14920
  };
14218
- const handleRetrievalBaselineGatePolicyHistoryList = async (queryInput) => {
14921
+ const handleRetrievalBaselineGatePolicyHistoryList = async (queryInput, request) => {
14219
14922
  if (!config.retrievalBaselineGatePolicyHistoryStore) {
14220
14923
  return {
14221
14924
  error: "RAG retrieval baseline gate policy history store is not configured",
14222
14925
  ok: false
14223
14926
  };
14224
14927
  }
14928
+ const accessScope = await loadAccessScope(request);
14929
+ const groupKey = getStringProperty(queryInput, "groupKey");
14930
+ const corpusGroupKey = getStringProperty(queryInput, "corpusGroupKey");
14931
+ if (!isAllowedComparisonGroupKey(accessScope, groupKey)) {
14932
+ return {
14933
+ error: "Retrieval baseline gate policy group is outside the allowed RAG access scope",
14934
+ ok: false
14935
+ };
14936
+ }
14937
+ if (!isAllowedCorpusGroupKey(accessScope, corpusGroupKey)) {
14938
+ return {
14939
+ error: "Retrieval baseline gate policy corpus group is outside the allowed RAG access scope",
14940
+ ok: false
14941
+ };
14942
+ }
14225
14943
  const rolloutLabel = getStringProperty(queryInput, "rolloutLabel");
14226
14944
  const scope = getStringProperty(queryInput, "scope");
14227
14945
  const records = await loadRAGRetrievalBaselineGatePolicyHistory({
14228
- groupKey: getStringProperty(queryInput, "groupKey"),
14946
+ corpusGroupKey,
14947
+ groupKey,
14229
14948
  limit: getIntegerLikeProperty(queryInput, "limit"),
14230
14949
  rolloutLabel: rolloutLabel === "canary" || rolloutLabel === "stable" || rolloutLabel === "rollback_target" ? rolloutLabel : undefined,
14231
14950
  scope: scope === "rollout_label" || scope === "group_rollout_label" ? scope : undefined,
14232
14951
  store: config.retrievalBaselineGatePolicyHistoryStore
14233
14952
  });
14234
- return { ok: true, records };
14953
+ return {
14954
+ ok: true,
14955
+ records: filterByCorpusGroupKey(accessScope, filterByComparisonGroupKey(accessScope, records))
14956
+ };
14235
14957
  };
14236
- const handleRetrievalReleaseLaneEscalationPolicyHistoryList = async (queryInput) => {
14958
+ const handleRetrievalReleaseLaneEscalationPolicyHistoryList = async (queryInput, request) => {
14237
14959
  if (!config.retrievalReleaseLaneEscalationPolicyHistoryStore) {
14238
14960
  return {
14239
14961
  error: "RAG retrieval release lane escalation policy history store is not configured",
14240
14962
  ok: false
14241
14963
  };
14242
14964
  }
14965
+ const accessScope = await loadAccessScope(request);
14966
+ const groupKey = getStringProperty(queryInput, "groupKey");
14967
+ const corpusGroupKey = getStringProperty(queryInput, "corpusGroupKey");
14968
+ if (!isAllowedComparisonGroupKey(accessScope, groupKey)) {
14969
+ return {
14970
+ error: "Retrieval release lane escalation policy group is outside the allowed RAG access scope",
14971
+ ok: false
14972
+ };
14973
+ }
14974
+ if (!isAllowedCorpusGroupKey(accessScope, corpusGroupKey)) {
14975
+ return {
14976
+ error: "Retrieval release lane escalation policy corpus group is outside the allowed RAG access scope",
14977
+ ok: false
14978
+ };
14979
+ }
14243
14980
  const targetRolloutLabel = getStringProperty(queryInput, "targetRolloutLabel");
14244
14981
  const records = await loadRAGRetrievalReleaseLaneEscalationPolicyHistory({
14245
- groupKey: getStringProperty(queryInput, "groupKey"),
14982
+ corpusGroupKey,
14983
+ groupKey,
14246
14984
  limit: getIntegerLikeProperty(queryInput, "limit"),
14247
14985
  store: config.retrievalReleaseLaneEscalationPolicyHistoryStore,
14248
14986
  targetRolloutLabel: targetRolloutLabel === "canary" || targetRolloutLabel === "stable" || targetRolloutLabel === "rollback_target" ? targetRolloutLabel : undefined
14249
14987
  });
14250
- return { ok: true, records };
14988
+ return {
14989
+ ok: true,
14990
+ records: filterByCorpusGroupKey(accessScope, filterByComparisonGroupKey(accessScope, records))
14991
+ };
14251
14992
  };
14252
14993
  const handleRetrievalLaneHandoffDecision = async (body) => {
14253
14994
  if (!config.retrievalLaneHandoffDecisionStore) {
@@ -14263,6 +15004,19 @@ var ragChat = (config) => {
14263
15004
  ok: false
14264
15005
  };
14265
15006
  }
15007
+ const accessScope = await loadAccessScope();
15008
+ if (!isAllowedComparisonGroupKey(accessScope, input.groupKey)) {
15009
+ return {
15010
+ error: "Retrieval lane handoff group is outside the allowed RAG access scope",
15011
+ ok: false
15012
+ };
15013
+ }
15014
+ if (!isAllowedCorpusGroupKey(accessScope, input.corpusGroupKey)) {
15015
+ return {
15016
+ error: "Retrieval lane handoff corpus group is outside the allowed RAG access scope",
15017
+ ok: false
15018
+ };
15019
+ }
14266
15020
  const ops = await buildOperationsPayload();
14267
15021
  const handoff = ops.retrievalComparisons?.releaseLaneHandoffs?.find((entry) => entry.groupKey === input.groupKey && entry.sourceRolloutLabel === input.sourceRolloutLabel && entry.targetRolloutLabel === input.targetRolloutLabel);
14268
15022
  if (!handoff) {
@@ -14271,6 +15025,13 @@ var ragChat = (config) => {
14271
15025
  ok: false
14272
15026
  };
14273
15027
  }
15028
+ const corpusGroupKey = input.corpusGroupKey ?? handoff.corpusGroupKey;
15029
+ if (!isAllowedCorpusGroupKey(accessScope, corpusGroupKey)) {
15030
+ return {
15031
+ error: "Retrieval lane handoff corpus group is outside the allowed RAG access scope",
15032
+ ok: false
15033
+ };
15034
+ }
14274
15035
  if (input.kind === "approve" && handoff.readyForHandoff !== true) {
14275
15036
  return {
14276
15037
  error: `handoff is not ready: ${handoff.reasons.join("; ")}`,
@@ -14295,6 +15056,7 @@ var ragChat = (config) => {
14295
15056
  approvalNotes: input.notes,
14296
15057
  approvedAt: input.decidedAt,
14297
15058
  approvedBy: input.decidedBy,
15059
+ corpusGroupKey,
14298
15060
  groupKey: input.groupKey,
14299
15061
  retrievalId: input.candidateRetrievalId ?? handoff.candidateRetrievalId,
14300
15062
  rolloutLabel: input.targetRolloutLabel,
@@ -14310,6 +15072,7 @@ var ragChat = (config) => {
14310
15072
  const decision = await persistRAGRetrievalLaneHandoffDecision({
14311
15073
  record: {
14312
15074
  candidateRetrievalId: input.candidateRetrievalId ?? handoff.candidateRetrievalId,
15075
+ corpusGroupKey,
14313
15076
  decidedAt: input.decidedAt ?? Date.now(),
14314
15077
  decidedBy: input.decidedBy,
14315
15078
  groupKey: input.groupKey,
@@ -14337,6 +15100,7 @@ var ragChat = (config) => {
14337
15100
  approvalNotes: input.notes,
14338
15101
  approvedAt,
14339
15102
  approvedBy: input.decidedBy,
15103
+ corpusGroupKey,
14340
15104
  groupKey: input.groupKey,
14341
15105
  retrievalId: input.candidateRetrievalId ?? handoff.candidateRetrievalId,
14342
15106
  rolloutLabel: input.targetRolloutLabel,
@@ -14351,6 +15115,7 @@ var ragChat = (config) => {
14351
15115
  await persistRAGRetrievalLaneHandoffDecision({
14352
15116
  record: {
14353
15117
  candidateRetrievalId: input.candidateRetrievalId ?? handoff.candidateRetrievalId,
15118
+ corpusGroupKey,
14354
15119
  decidedAt: Date.now(),
14355
15120
  decidedBy: input.decidedBy,
14356
15121
  groupKey: input.groupKey,
@@ -15313,7 +16078,7 @@ var ragChat = (config) => {
15313
16078
  return { error: message, ok: false };
15314
16079
  }
15315
16080
  };
15316
- const handleSearch = async (body) => {
16081
+ const handleSearch = async (body, request) => {
15317
16082
  if (!isObjectRecord2(body)) {
15318
16083
  return { error: "Invalid payload", ok: false };
15319
16084
  }
@@ -15325,6 +16090,7 @@ var ragChat = (config) => {
15325
16090
  };
15326
16091
  }
15327
16092
  const collection = resolveCollection();
16093
+ const accessScope = await loadAccessScope(request);
15328
16094
  if (!collection) {
15329
16095
  return { error: "RAG collection is not configured", ok: false };
15330
16096
  }
@@ -15367,14 +16133,28 @@ var ragChat = (config) => {
15367
16133
  trace: result.trace
15368
16134
  });
15369
16135
  }
16136
+ const scopedResults = buildSources2(result.results).filter((entry) => matchesAccessScope(accessScope, {
16137
+ corpusKey: entry.corpusKey,
16138
+ documentId: typeof entry.metadata?.documentId === "string" ? entry.metadata.documentId : entry.chunkId.split(":")[0],
16139
+ metadata: entry.metadata,
16140
+ source: entry.source
16141
+ }));
15370
16142
  return {
15371
16143
  ok: true,
15372
- results: buildSources2(result.results),
16144
+ results: scopedResults,
15373
16145
  trace: result.trace
15374
16146
  };
15375
16147
  }
15376
16148
  const results = await collection.search(input);
15377
- return { ok: true, results: buildSources2(results) };
16149
+ return {
16150
+ ok: true,
16151
+ results: buildSources2(results).filter((entry) => matchesAccessScope(accessScope, {
16152
+ corpusKey: entry.corpusKey,
16153
+ documentId: typeof entry.metadata?.documentId === "string" ? entry.metadata.documentId : entry.chunkId.split(":")[0],
16154
+ metadata: entry.metadata,
16155
+ source: entry.source
16156
+ }))
16157
+ };
15378
16158
  };
15379
16159
  const handleTraceHistory = async (queryInput) => {
15380
16160
  if (!searchTraceStore) {
@@ -15520,7 +16300,10 @@ var ragChat = (config) => {
15520
16300
  let inspectedChunks = 0;
15521
16301
  let documentsWithSourceLabels = 0;
15522
16302
  let chunksWithSourceLabels = 0;
16303
+ const corpusKeys = new Map;
15523
16304
  const sourceNativeKinds = new Map;
16305
+ const extractorRegistryMatches = new Map;
16306
+ const chunkingProfiles = new Map;
15524
16307
  const sampleDocuments = [];
15525
16308
  const sampleChunks = [];
15526
16309
  let oldestDocumentAgeMs;
@@ -15572,11 +16355,26 @@ var ragChat = (config) => {
15572
16355
  documentsWithSourceLabels += 1;
15573
16356
  }
15574
16357
  const documentSourceNativeKind = typeof document.metadata?.sourceNativeKind === "string" ? document.metadata.sourceNativeKind : undefined;
16358
+ const documentCorpusKey = document.corpusKey ?? (typeof document.metadata?.corpusKey === "string" ? document.metadata.corpusKey : undefined);
16359
+ const documentExtractorRegistryMatch = typeof document.metadata?.extractorRegistryMatch === "string" ? document.metadata.extractorRegistryMatch : undefined;
16360
+ const documentChunkingProfile = typeof document.metadata?.chunkingProfile === "string" ? document.metadata.chunkingProfile : undefined;
15575
16361
  if (documentSourceNativeKind) {
15576
16362
  sourceNativeKinds.set(documentSourceNativeKind, (sourceNativeKinds.get(documentSourceNativeKind) ?? 0) + 1);
15577
16363
  }
15578
- if (sampleDocuments.length < 5 && (documentLabels || documentSourceNativeKind)) {
16364
+ if (documentCorpusKey) {
16365
+ corpusKeys.set(documentCorpusKey, (corpusKeys.get(documentCorpusKey) ?? 0) + 1);
16366
+ }
16367
+ if (documentExtractorRegistryMatch) {
16368
+ extractorRegistryMatches.set(documentExtractorRegistryMatch, (extractorRegistryMatches.get(documentExtractorRegistryMatch) ?? 0) + 1);
16369
+ }
16370
+ if (documentChunkingProfile) {
16371
+ chunkingProfiles.set(documentChunkingProfile, (chunkingProfiles.get(documentChunkingProfile) ?? 0) + 1);
16372
+ }
16373
+ if (sampleDocuments.length < 5 && (documentCorpusKey || documentLabels || documentSourceNativeKind || documentExtractorRegistryMatch || documentChunkingProfile)) {
15579
16374
  sampleDocuments.push({
16375
+ corpusKey: documentCorpusKey,
16376
+ chunkingProfile: documentChunkingProfile,
16377
+ extractorRegistryMatch: documentExtractorRegistryMatch,
15580
16378
  id: document.id,
15581
16379
  labels: documentLabels,
15582
16380
  source: document.source,
@@ -15602,13 +16400,28 @@ var ragChat = (config) => {
15602
16400
  chunksWithSourceLabels += 1;
15603
16401
  }
15604
16402
  const chunkSourceNativeKind = typeof chunk.metadata?.sourceNativeKind === "string" ? chunk.metadata.sourceNativeKind : undefined;
16403
+ const chunkCorpusKey = chunk.corpusKey ?? (typeof chunk.metadata?.corpusKey === "string" ? chunk.metadata.corpusKey : documentCorpusKey);
16404
+ const chunkExtractorRegistryMatch = typeof chunk.metadata?.extractorRegistryMatch === "string" ? chunk.metadata.extractorRegistryMatch : undefined;
16405
+ const chunkChunkingProfile = typeof chunk.metadata?.chunkingProfile === "string" ? chunk.metadata.chunkingProfile : undefined;
15605
16406
  if (chunkSourceNativeKind) {
15606
16407
  sourceNativeKinds.set(chunkSourceNativeKind, (sourceNativeKinds.get(chunkSourceNativeKind) ?? 0) + 1);
15607
16408
  }
15608
- if (sampleChunks.length < 8 && (chunkLabels || chunkSourceNativeKind)) {
16409
+ if (chunkCorpusKey) {
16410
+ corpusKeys.set(chunkCorpusKey, (corpusKeys.get(chunkCorpusKey) ?? 0) + 1);
16411
+ }
16412
+ if (chunkExtractorRegistryMatch) {
16413
+ extractorRegistryMatches.set(chunkExtractorRegistryMatch, (extractorRegistryMatches.get(chunkExtractorRegistryMatch) ?? 0) + 1);
16414
+ }
16415
+ if (chunkChunkingProfile) {
16416
+ chunkingProfiles.set(chunkChunkingProfile, (chunkingProfiles.get(chunkChunkingProfile) ?? 0) + 1);
16417
+ }
16418
+ if (sampleChunks.length < 8 && (chunkCorpusKey || chunkLabels || chunkSourceNativeKind || chunkExtractorRegistryMatch || chunkChunkingProfile)) {
15609
16419
  sampleChunks.push({
15610
16420
  chunkId: chunk.chunkId,
16421
+ chunkingProfile: chunkChunkingProfile,
16422
+ corpusKey: chunkCorpusKey,
15611
16423
  documentId: document.id,
16424
+ extractorRegistryMatch: chunkExtractorRegistryMatch,
15612
16425
  labels: chunkLabels,
15613
16426
  source: chunk.source ?? preview.document.source,
15614
16427
  sourceNativeKind: chunkSourceNativeKind
@@ -15671,8 +16484,11 @@ var ragChat = (config) => {
15671
16484
  inspectedChunks,
15672
16485
  inspectedDocuments,
15673
16486
  inspection: {
16487
+ chunkingProfiles: Object.fromEntries(chunkingProfiles.entries()),
16488
+ corpusKeys: Object.fromEntries(corpusKeys.entries()),
15674
16489
  chunksWithSourceLabels,
15675
16490
  documentsWithSourceLabels,
16491
+ extractorRegistryMatches: Object.fromEntries(extractorRegistryMatches.entries()),
15676
16492
  sampleChunks,
15677
16493
  sampleDocuments,
15678
16494
  sourceNativeKinds: Object.fromEntries(sourceNativeKinds.entries())
@@ -15695,28 +16511,35 @@ var ragChat = (config) => {
15695
16511
  providerName: typeof config.provider === "function" ? config.readinessProviderName : undefined,
15696
16512
  rerankerConfigured: Boolean(config.rerank ?? config.collection)
15697
16513
  });
15698
- const buildAdminCapabilities = () => ({
15699
- canClearIndex: Boolean(ragStore?.clear),
15700
- canCreateDocument: Boolean(indexManager?.createDocument),
15701
- canDeleteDocument: Boolean(indexManager?.deleteDocument),
15702
- canListSyncSources: Boolean(indexManager?.listSyncSources),
15703
- canManageRetrievalBaselines: Boolean(retrievalBaselineStore),
15704
- canPruneSearchTraces: Boolean(searchTraceStore),
15705
- canReindexDocument: Boolean(indexManager?.reindexDocument),
15706
- canReindexSource: Boolean(indexManager?.reindexSource),
15707
- canReseed: Boolean(indexManager?.reseed),
15708
- canReset: Boolean(indexManager?.reset),
15709
- canSyncAllSources: Boolean(indexManager?.syncAllSources),
15710
- canSyncSource: Boolean(indexManager?.syncSource)
16514
+ const buildAdminCapabilities = async (request) => ({
16515
+ canClearIndex: Boolean(ragStore?.clear) && (request ? await isAuthorized(request, "clear_index") : true),
16516
+ canCreateDocument: Boolean(indexManager?.createDocument) && (request ? await isAuthorized(request, "create_document") : true),
16517
+ canDeleteDocument: Boolean(indexManager?.deleteDocument) && (request ? await isAuthorized(request, "delete_document") : true),
16518
+ canListSyncSources: Boolean(indexManager?.listSyncSources) && (request ? await isAuthorized(request, "list_sync_sources") : true),
16519
+ canManageRetrievalBaselines: Boolean(retrievalBaselineStore) && (request ? await isAuthorized(request, "manage_retrieval_admin") : true),
16520
+ canPruneSearchTraces: Boolean(searchTraceStore) && (request ? await isAuthorized(request, "prune_search_traces") : true),
16521
+ canReindexDocument: Boolean(indexManager?.reindexDocument) && (request ? await isAuthorized(request, "reindex_document") : true),
16522
+ canReindexSource: Boolean(indexManager?.reindexSource) && (request ? await isAuthorized(request, "reindex_source") : true),
16523
+ canReseed: Boolean(indexManager?.reseed) && (request ? await isAuthorized(request, "reseed") : true),
16524
+ canReset: Boolean(indexManager?.reset) && (request ? await isAuthorized(request, "reset") : true),
16525
+ canSyncAllSources: Boolean(indexManager?.syncAllSources) && (request ? await isAuthorized(request, "sync_all_sources") : true),
16526
+ canSyncSource: Boolean(indexManager?.syncSource) && (request ? await isAuthorized(request, "sync_source") : true)
15711
16527
  });
15712
- const buildOperationsPayload = async () => {
16528
+ const buildOperationsPayload = async (request) => {
16529
+ const accessScope = await loadAccessScope(request);
16530
+ const filterScopedGovernanceEntries = (entries) => entries ? filterByCorpusGroupKey(accessScope, filterByComparisonGroupKey(accessScope, entries)) : undefined;
15713
16531
  const collection = config.collection ?? (ragStore ? createRAGCollection({
15714
16532
  defaultModel: config.embeddingModel,
15715
16533
  defaultTopK: topK,
15716
16534
  embedding: config.embedding,
15717
16535
  store: ragStore
15718
16536
  }) : null);
15719
- const indexedDocuments = indexManager ? await indexManager.listDocuments({}) : [];
16537
+ const indexedDocuments = indexManager ? (await indexManager.listDocuments({})).filter((document) => matchesAccessScope(accessScope, {
16538
+ corpusKey: document.corpusKey,
16539
+ documentId: document.id,
16540
+ metadata: document.metadata,
16541
+ source: document.source
16542
+ })) : [];
15720
16543
  const traceStats = searchTraceStore ? await summarizeRAGSearchTraceStore({
15721
16544
  store: searchTraceStore
15722
16545
  }) : undefined;
@@ -15724,51 +16547,51 @@ var ragChat = (config) => {
15724
16547
  limit: 5,
15725
16548
  store: searchTracePruneHistoryStore
15726
16549
  }) : undefined;
15727
- const recentRetrievalComparisonRuns = retrievalComparisonHistoryStore ? await loadRAGRetrievalComparisonHistory({
16550
+ const recentRetrievalComparisonRuns = filterScopedGovernanceEntries(retrievalComparisonHistoryStore ? await loadRAGRetrievalComparisonHistory({
15728
16551
  limit: 5,
15729
16552
  store: retrievalComparisonHistoryStore
15730
- }) : undefined;
15731
- const activeRetrievalBaselines = retrievalBaselineStore ? await loadRAGRetrievalBaselines({
16553
+ }) : undefined);
16554
+ const activeRetrievalBaselines = filterScopedGovernanceEntries(retrievalBaselineStore ? await loadRAGRetrievalBaselines({
15732
16555
  limit: 5,
15733
16556
  status: "active",
15734
16557
  store: retrievalBaselineStore
15735
- }) : undefined;
15736
- const retrievalBaselineHistory = retrievalBaselineStore ? await loadRAGRetrievalBaselines({
16558
+ }) : undefined);
16559
+ const retrievalBaselineHistory = filterScopedGovernanceEntries(retrievalBaselineStore ? await loadRAGRetrievalBaselines({
15737
16560
  limit: 10,
15738
16561
  store: retrievalBaselineStore
15739
- }) : undefined;
15740
- const recentRetrievalReleaseDecisions = config.retrievalReleaseDecisionStore ? await loadRAGRetrievalReleaseDecisions({
16562
+ }) : undefined);
16563
+ const recentRetrievalReleaseDecisions = filterScopedGovernanceEntries(config.retrievalReleaseDecisionStore ? await loadRAGRetrievalReleaseDecisions({
15741
16564
  limit: 10,
15742
16565
  store: config.retrievalReleaseDecisionStore
15743
- }) : undefined;
15744
- const recentRetrievalLaneHandoffDecisions = config.retrievalLaneHandoffDecisionStore ? await loadRAGRetrievalLaneHandoffDecisions({
16566
+ }) : undefined);
16567
+ const recentRetrievalLaneHandoffDecisions = filterScopedGovernanceEntries(config.retrievalLaneHandoffDecisionStore ? await loadRAGRetrievalLaneHandoffDecisions({
15745
16568
  limit: 10,
15746
16569
  store: config.retrievalLaneHandoffDecisionStore
15747
- }) : undefined;
15748
- const recentRetrievalLaneHandoffIncidents = config.retrievalLaneHandoffIncidentStore ? await loadRAGRetrievalLaneHandoffIncidents({
16570
+ }) : undefined);
16571
+ const recentRetrievalLaneHandoffIncidents = filterScopedGovernanceEntries(config.retrievalLaneHandoffIncidentStore ? await loadRAGRetrievalLaneHandoffIncidents({
15749
16572
  limit: 10,
15750
16573
  store: config.retrievalLaneHandoffIncidentStore
15751
- }) : undefined;
15752
- const recentRetrievalLaneHandoffIncidentHistory = config.retrievalLaneHandoffIncidentHistoryStore ? await loadRAGRetrievalLaneHandoffIncidentHistory({
16574
+ }) : undefined);
16575
+ const recentRetrievalLaneHandoffIncidentHistory = filterScopedGovernanceEntries(config.retrievalLaneHandoffIncidentHistoryStore ? await loadRAGRetrievalLaneHandoffIncidentHistory({
15753
16576
  limit: 10,
15754
16577
  store: config.retrievalLaneHandoffIncidentHistoryStore
15755
- }) : undefined;
15756
- const recentHandoffAutoCompletePolicyHistory = config.retrievalLaneHandoffAutoCompletePolicyHistoryStore ? await loadRAGRetrievalLaneHandoffAutoCompletePolicyHistory({
16578
+ }) : undefined);
16579
+ const recentHandoffAutoCompletePolicyHistory = filterScopedGovernanceEntries(config.retrievalLaneHandoffAutoCompletePolicyHistoryStore ? await loadRAGRetrievalLaneHandoffAutoCompletePolicyHistory({
15757
16580
  limit: 10,
15758
16581
  store: config.retrievalLaneHandoffAutoCompletePolicyHistoryStore
15759
- }) : undefined;
15760
- const recentReleaseLanePolicyHistory = config.retrievalReleaseLanePolicyHistoryStore ? await loadRAGRetrievalReleaseLanePolicyHistory({
16582
+ }) : undefined);
16583
+ const recentReleaseLanePolicyHistory = filterScopedGovernanceEntries(config.retrievalReleaseLanePolicyHistoryStore ? await loadRAGRetrievalReleaseLanePolicyHistory({
15761
16584
  limit: 10,
15762
16585
  store: config.retrievalReleaseLanePolicyHistoryStore
15763
- }) : undefined;
15764
- const recentBaselineGatePolicyHistory = config.retrievalBaselineGatePolicyHistoryStore ? await loadRAGRetrievalBaselineGatePolicyHistory({
16586
+ }) : undefined);
16587
+ const recentBaselineGatePolicyHistory = filterScopedGovernanceEntries(config.retrievalBaselineGatePolicyHistoryStore ? await loadRAGRetrievalBaselineGatePolicyHistory({
15765
16588
  limit: 10,
15766
16589
  store: config.retrievalBaselineGatePolicyHistoryStore
15767
- }) : undefined;
15768
- const recentReleaseLaneEscalationPolicyHistory = config.retrievalReleaseLaneEscalationPolicyHistoryStore ? await loadRAGRetrievalReleaseLaneEscalationPolicyHistory({
16590
+ }) : undefined);
16591
+ const recentReleaseLaneEscalationPolicyHistory = filterScopedGovernanceEntries(config.retrievalReleaseLaneEscalationPolicyHistoryStore ? await loadRAGRetrievalReleaseLaneEscalationPolicyHistory({
15769
16592
  limit: 10,
15770
16593
  store: config.retrievalReleaseLaneEscalationPolicyHistoryStore
15771
- }) : undefined;
16594
+ }) : undefined);
15772
16595
  const recentIncidentRemediationDecisions = config.retrievalIncidentRemediationDecisionStore ? await loadRAGRetrievalIncidentRemediationDecisions({
15773
16596
  limit: 10,
15774
16597
  store: config.retrievalIncidentRemediationDecisionStore
@@ -15833,6 +16656,12 @@ var ragChat = (config) => {
15833
16656
  targetRolloutLabel
15834
16657
  };
15835
16658
  }) : undefined;
16659
+ const getComparisonCorpusGroupKey = (groupKey) => {
16660
+ if (!groupKey) {
16661
+ return;
16662
+ }
16663
+ return (recentRetrievalComparisonRuns ?? []).find((entry) => entry.groupKey === groupKey && typeof entry.corpusGroupKey === "string")?.corpusGroupKey ?? (activeRetrievalBaselines ?? []).find((entry) => entry.groupKey === groupKey && typeof entry.corpusGroupKey === "string")?.corpusGroupKey ?? (recentRetrievalReleaseDecisions ?? []).find((entry) => entry.groupKey === groupKey && typeof entry.corpusGroupKey === "string")?.corpusGroupKey;
16664
+ };
15836
16665
  const baseReleaseGroups = (() => {
15837
16666
  const groups = new Set;
15838
16667
  for (const run of recentRetrievalComparisonRuns ?? []) {
@@ -15894,6 +16723,7 @@ var ragChat = (config) => {
15894
16723
  approvalMaxAgeMs: getRetrievalReleasePolicy(groupKey).approvalMaxAgeMs,
15895
16724
  approvalRequired: getRetrievalReleasePolicy(groupKey).requireApprovalBeforePromotion === true,
15896
16725
  blockedReasons,
16726
+ corpusGroupKey: getComparisonCorpusGroupKey(groupKey),
15897
16727
  escalationSeverity,
15898
16728
  groupKey,
15899
16729
  latestDecisionAt: latestDecision?.decidedAt,
@@ -15943,6 +16773,7 @@ var ragChat = (config) => {
15943
16773
  const groupDecisions = (enrichedRecentRetrievalReleaseDecisions ?? []).filter((entry) => entry.groupKey === groupKey);
15944
16774
  const latest = groupDecisions[0];
15945
16775
  return {
16776
+ corpusGroupKey: getComparisonCorpusGroupKey(groupKey),
15946
16777
  groupKey,
15947
16778
  lastApprovedAt: groupDecisions.find((entry) => entry.kind === "approve")?.decidedAt,
15948
16779
  lastPromotedAt: groupDecisions.find((entry) => entry.kind === "promote")?.decidedAt,
@@ -15974,6 +16805,7 @@ var ragChat = (config) => {
15974
16805
  const laneDecisions = (enrichedRecentRetrievalReleaseDecisions ?? []).filter((entry) => entry.groupKey === groupKey && (entry.targetRolloutLabel ?? undefined) === targetRolloutLabel);
15975
16806
  const latest = laneDecisions[0];
15976
16807
  summaries.push({
16808
+ corpusGroupKey: getComparisonCorpusGroupKey(groupKey),
15977
16809
  groupKey,
15978
16810
  lastApprovedAt: laneDecisions.find((entry) => entry.kind === "approve")?.decidedAt,
15979
16811
  lastPromotedAt: laneDecisions.find((entry) => entry.kind === "promote")?.decidedAt,
@@ -16009,6 +16841,7 @@ var ragChat = (config) => {
16009
16841
  const latest = laneDecisions[0];
16010
16842
  summaries.push({
16011
16843
  approvalCount: laneDecisions.filter((entry) => entry.kind === "approve").length,
16844
+ corpusGroupKey: getComparisonCorpusGroupKey(groupKey),
16012
16845
  decisionCount: laneDecisions.length,
16013
16846
  groupKey,
16014
16847
  latestDecisionAt: latest?.decidedAt,
@@ -16302,6 +17135,7 @@ var ragChat = (config) => {
16302
17135
  if (!latest) {
16303
17136
  await persistLaneHandoffAutoCompletePolicyHistoryRecord({
16304
17137
  changeKind: "snapshot",
17138
+ corpusGroupKey: getComparisonCorpusGroupKey(policy.groupKey),
16305
17139
  enabled: policy.enabled,
16306
17140
  groupKey: policy.groupKey,
16307
17141
  maxApprovedDecisionAgeMs: policy.maxApprovedDecisionAgeMs,
@@ -16312,6 +17146,7 @@ var ragChat = (config) => {
16312
17146
  if (latest.enabled !== policy.enabled || latest.maxApprovedDecisionAgeMs !== policy.maxApprovedDecisionAgeMs) {
16313
17147
  await persistLaneHandoffAutoCompletePolicyHistoryRecord({
16314
17148
  changeKind: "changed",
17149
+ corpusGroupKey: getComparisonCorpusGroupKey(policy.groupKey),
16315
17150
  enabled: policy.enabled,
16316
17151
  groupKey: policy.groupKey,
16317
17152
  maxApprovedDecisionAgeMs: policy.maxApprovedDecisionAgeMs,
@@ -16349,6 +17184,7 @@ var ragChat = (config) => {
16349
17184
  const latest = laneIncidents[0];
16350
17185
  summaries.push({
16351
17186
  acknowledgedOpenCount,
17187
+ corpusGroupKey: getComparisonCorpusGroupKey(groupKey),
16352
17188
  groupKey,
16353
17189
  highestSeverity: laneIncidents.some((entry) => entry.severity === "critical") ? "critical" : laneIncidents.some((entry) => entry.severity === "warning") ? "warning" : undefined,
16354
17190
  latestKind: latest?.kind,
@@ -16404,6 +17240,7 @@ var ragChat = (config) => {
16404
17240
  await persistReleaseLanePolicyHistoryRecord({
16405
17241
  approvalMaxAgeMs: policy.approvalMaxAgeMs,
16406
17242
  changeKind: "snapshot",
17243
+ corpusGroupKey: getComparisonCorpusGroupKey(groupKey),
16407
17244
  groupKey,
16408
17245
  requireApprovalBeforePromotion: policy.requireApprovalBeforePromotion,
16409
17246
  rolloutLabel: policy.rolloutLabel,
@@ -16415,6 +17252,7 @@ var ragChat = (config) => {
16415
17252
  await persistReleaseLanePolicyHistoryRecord({
16416
17253
  approvalMaxAgeMs: policy.approvalMaxAgeMs,
16417
17254
  changeKind: "changed",
17255
+ corpusGroupKey: getComparisonCorpusGroupKey(groupKey),
16418
17256
  groupKey,
16419
17257
  previousApprovalMaxAgeMs: latest.approvalMaxAgeMs,
16420
17258
  previousRequireApprovalBeforePromotion: latest.requireApprovalBeforePromotion,
@@ -16434,6 +17272,7 @@ var ragChat = (config) => {
16434
17272
  if (!latest) {
16435
17273
  await persistBaselineGatePolicyHistoryRecord({
16436
17274
  changeKind: "snapshot",
17275
+ corpusGroupKey: getComparisonCorpusGroupKey(groupKey),
16437
17276
  groupKey,
16438
17277
  policy: policy.policy,
16439
17278
  rolloutLabel: policy.rolloutLabel,
@@ -16444,6 +17283,7 @@ var ragChat = (config) => {
16444
17283
  if (previousPolicy !== currentPolicy) {
16445
17284
  await persistBaselineGatePolicyHistoryRecord({
16446
17285
  changeKind: "changed",
17286
+ corpusGroupKey: getComparisonCorpusGroupKey(groupKey),
16447
17287
  groupKey,
16448
17288
  policy: policy.policy,
16449
17289
  previousPolicy: latest.policy,
@@ -16498,6 +17338,7 @@ var ragChat = (config) => {
16498
17338
  await persistReleaseLaneEscalationPolicyHistoryRecord({
16499
17339
  approvalExpiredSeverity: policy.approvalExpiredSeverity,
16500
17340
  changeKind: "snapshot",
17341
+ corpusGroupKey: getComparisonCorpusGroupKey(policy.groupKey),
16501
17342
  gateFailureSeverity: policy.gateFailureSeverity,
16502
17343
  groupKey: policy.groupKey,
16503
17344
  openIncidentSeverity: policy.openIncidentSeverity,
@@ -16510,6 +17351,7 @@ var ragChat = (config) => {
16510
17351
  await persistReleaseLaneEscalationPolicyHistoryRecord({
16511
17352
  approvalExpiredSeverity: policy.approvalExpiredSeverity,
16512
17353
  changeKind: "changed",
17354
+ corpusGroupKey: getComparisonCorpusGroupKey(policy.groupKey),
16513
17355
  gateFailureSeverity: policy.gateFailureSeverity,
16514
17356
  groupKey: policy.groupKey,
16515
17357
  openIncidentSeverity: policy.openIncidentSeverity,
@@ -16564,6 +17406,7 @@ var ragChat = (config) => {
16564
17406
  const latestWinner = latestRetrievalComparisonRun.comparison.summary.bestByPassingRate;
16565
17407
  if (latestWinner && stableWinnerByPassingRate?.retrievalId && stableWinnerByPassingRate.retrievalId !== latestWinner) {
16566
17408
  alerts.push({
17409
+ corpusGroupKey: latestRetrievalComparisonRun.corpusGroupKey,
16567
17410
  groupKey: latestRetrievalComparisonRun.groupKey,
16568
17411
  kind: "stable_winner_changed",
16569
17412
  latestRunId: latestRetrievalComparisonRun.id,
@@ -16580,6 +17423,7 @@ var ragChat = (config) => {
16580
17423
  baselineRetrievalId: latestRetrievalComparisonRun.decisionSummary?.baselineRetrievalId,
16581
17424
  candidateRetrievalId: latestRetrievalComparisonRun.decisionSummary?.candidateRetrievalId,
16582
17425
  delta,
17426
+ corpusGroupKey: latestRetrievalComparisonRun.corpusGroupKey,
16583
17427
  groupKey: latestRetrievalComparisonRun.groupKey,
16584
17428
  kind: "baseline_regression",
16585
17429
  latestRunId: latestRetrievalComparisonRun.id,
@@ -16594,6 +17438,7 @@ var ragChat = (config) => {
16594
17438
  candidateRetrievalId: latestRetrievalComparisonRun.decisionSummary?.candidateRetrievalId,
16595
17439
  delta,
16596
17440
  gate,
17441
+ corpusGroupKey: latestRetrievalComparisonRun.corpusGroupKey,
16597
17442
  groupKey: latestRetrievalComparisonRun.groupKey,
16598
17443
  kind: "baseline_gate_failed",
16599
17444
  latestRunId: latestRetrievalComparisonRun.id,
@@ -16611,6 +17456,7 @@ var ragChat = (config) => {
16611
17456
  const kind = reasonSet.has("no active canary baseline exists for this group") ? "handoff_auto_complete_source_lane_missing" : reasonSet.has("latest approved handoff decision is older than the auto-complete policy allows") ? "handoff_auto_complete_stale_approval" : reasonSet.has("approved handoff decision is required before auto-complete") ? "handoff_auto_complete_approval_missing" : reasonText.includes("gate") || reasonText.includes("passing rate delta") || reasonText.includes("average") || reasonText.includes("candidate does not match") ? "handoff_auto_complete_gate_blocked" : "handoff_auto_complete_policy_drift";
16612
17457
  alerts.push({
16613
17458
  candidateRetrievalId: entry.candidateRetrievalId,
17459
+ corpusGroupKey: getComparisonCorpusGroupKey(entry.groupKey),
16614
17460
  groupKey: entry.groupKey,
16615
17461
  kind,
16616
17462
  latestRunId: entry.sourceRunId ?? latestRetrievalComparisonRun.id,
@@ -16707,7 +17553,7 @@ var ragChat = (config) => {
16707
17553
  searchTraceRuntime.stats = traceStats;
16708
17554
  searchTraceRuntime.recentRuns = recentTraceRuns;
16709
17555
  return {
16710
- admin: buildAdminCapabilities(),
17556
+ admin: await buildAdminCapabilities(request),
16711
17557
  adminActions: [...adminActions],
16712
17558
  adminJobs: [...adminJobs, ...syncJobs].sort((left, right) => right.startedAt - left.startedAt),
16713
17559
  capabilities: collection?.getCapabilities?.(),
@@ -16721,6 +17567,7 @@ var ragChat = (config) => {
16721
17567
  latest: latestRetrievalComparisonRun ? {
16722
17568
  bestByAverageF1: latestRetrievalComparisonRun.comparison.summary.bestByAverageF1,
16723
17569
  bestByPassingRate: latestRetrievalComparisonRun.comparison.summary.bestByPassingRate,
17570
+ corpusGroupKey: latestRetrievalComparisonRun.corpusGroupKey,
16724
17571
  elapsedMs: latestRetrievalComparisonRun.elapsedMs,
16725
17572
  fastest: latestRetrievalComparisonRun.comparison.summary.fastest,
16726
17573
  finishedAt: latestRetrievalComparisonRun.finishedAt,
@@ -16786,27 +17633,27 @@ var ragChat = (config) => {
16786
17633
  stats: traceStats
16787
17634
  },
16788
17635
  status: collection?.getStatus?.(),
16789
- syncSources: await buildSyncSources()
17636
+ syncSources: await buildSyncSources(accessScope)
16790
17637
  };
16791
17638
  };
16792
- const handleStatus = async () => buildOperationsPayload();
16793
- const handleRetrievalReleaseStatus = async () => {
16794
- const result = await buildOperationsPayload();
17639
+ const handleStatus = async (request) => buildOperationsPayload(request);
17640
+ const handleRetrievalReleaseStatus = async (request) => {
17641
+ const result = await buildOperationsPayload(request);
16795
17642
  return {
16796
17643
  ok: true,
16797
17644
  retrievalComparisons: result.retrievalComparisons
16798
17645
  };
16799
17646
  };
16800
- const handleRetrievalReleaseDriftStatus = async () => {
16801
- const result = await buildOperationsPayload();
17647
+ const handleRetrievalReleaseDriftStatus = async (request) => {
17648
+ const result = await buildOperationsPayload(request);
16802
17649
  return {
16803
17650
  handoffDriftCountsByLane: result.retrievalComparisons?.handoffDriftCountsByLane,
16804
17651
  handoffDriftRollups: result.retrievalComparisons?.handoffDriftRollups,
16805
17652
  ok: true
16806
17653
  };
16807
17654
  };
16808
- const handleRetrievalLaneHandoffIncidentStatus = async () => {
16809
- const result = await buildOperationsPayload();
17655
+ const handleRetrievalLaneHandoffIncidentStatus = async (request) => {
17656
+ const result = await buildOperationsPayload(request);
16810
17657
  const incidents = result.retrievalComparisons?.recentLaneHandoffIncidents;
16811
17658
  return {
16812
17659
  freshnessWindows: result.retrievalComparisons?.handoffFreshnessWindows,
@@ -16816,8 +17663,8 @@ var ragChat = (config) => {
16816
17663
  ok: true
16817
17664
  };
16818
17665
  };
16819
- const handleRetrievalLaneHandoffStatus = async () => {
16820
- const result = await buildOperationsPayload();
17666
+ const handleRetrievalLaneHandoffStatus = async (request) => {
17667
+ const result = await buildOperationsPayload(request);
16821
17668
  const incidents = result.retrievalComparisons?.recentLaneHandoffIncidents;
16822
17669
  return {
16823
17670
  autoComplete: result.retrievalComparisons?.handoffAutoComplete,
@@ -16830,7 +17677,7 @@ var ragChat = (config) => {
16830
17677
  ok: true
16831
17678
  };
16832
17679
  };
16833
- const handleOps = async () => buildOperationsPayload();
17680
+ const handleOps = async (request) => buildOperationsPayload(request);
16834
17681
  if (searchTraceStore && searchTraceRetention && searchTraceRetentionSchedule) {
16835
17682
  const runScheduledSearchTracePrune = async () => {
16836
17683
  searchTraceRuntime.nextScheduledAt = Date.now() + searchTraceRetentionSchedule.intervalMs;
@@ -16848,14 +17695,20 @@ var ragChat = (config) => {
16848
17695
  }, searchTraceRetentionSchedule.intervalMs);
16849
17696
  timer.unref?.();
16850
17697
  }
16851
- const handleDocuments = async (kind) => {
17698
+ const handleDocuments = async (kind, request) => {
16852
17699
  if (!indexManager) {
16853
17700
  return {
16854
17701
  error: "RAG index document management is not configured",
16855
17702
  ok: false
16856
17703
  };
16857
17704
  }
16858
- const documents = await indexManager.listDocuments({ kind });
17705
+ const accessScope = await loadAccessScope(request);
17706
+ const documents = (await indexManager.listDocuments({ kind })).filter((document) => matchesAccessScope(accessScope, {
17707
+ corpusKey: document.corpusKey,
17708
+ documentId: document.id,
17709
+ metadata: document.metadata,
17710
+ source: document.source
17711
+ }));
16859
17712
  return {
16860
17713
  documents: documents.map((document) => ({
16861
17714
  ...document,
@@ -16868,7 +17721,7 @@ var ragChat = (config) => {
16868
17721
  ok: true
16869
17722
  };
16870
17723
  };
16871
- const handleCreateDocument = async (body) => {
17724
+ const handleCreateDocument = async (body, request) => {
16872
17725
  if (!indexManager?.createDocument) {
16873
17726
  return {
16874
17727
  error: "RAG document creation is not configured",
@@ -16887,6 +17740,18 @@ var ragChat = (config) => {
16887
17740
  ok: false
16888
17741
  };
16889
17742
  }
17743
+ const accessScope = await loadAccessScope(request);
17744
+ if (!matchesAccessScope(accessScope, {
17745
+ documentId: body.id,
17746
+ corpusKey: body.corpusKey,
17747
+ metadata: body.metadata,
17748
+ source: body.source
17749
+ })) {
17750
+ return {
17751
+ error: "Document is outside the allowed RAG access scope",
17752
+ ok: false
17753
+ };
17754
+ }
16890
17755
  const job = createAdminJob("create_document", body.id);
16891
17756
  try {
16892
17757
  const result = await indexManager.createDocument(body);
@@ -16902,7 +17767,7 @@ var ragChat = (config) => {
16902
17767
  throw caught;
16903
17768
  }
16904
17769
  };
16905
- const handleDocumentChunks = async (id) => {
17770
+ const handleDocumentChunks = async (id, request) => {
16906
17771
  if (!indexManager) {
16907
17772
  return {
16908
17773
  error: "RAG chunk preview is not configured",
@@ -16922,6 +17787,18 @@ var ragChat = (config) => {
16922
17787
  ok: false
16923
17788
  };
16924
17789
  }
17790
+ const accessScope = await loadAccessScope(request);
17791
+ if (!matchesAccessScope(accessScope, {
17792
+ documentId: preview.document.id,
17793
+ corpusKey: preview.document.corpusKey,
17794
+ metadata: preview.document.metadata,
17795
+ source: preview.document.source
17796
+ })) {
17797
+ return {
17798
+ error: "document not found",
17799
+ ok: false
17800
+ };
17801
+ }
16925
17802
  const chunks = preview.chunks.map((chunk) => ({
16926
17803
  ...chunk,
16927
17804
  labels: buildRAGSourceLabels({
@@ -16952,7 +17829,7 @@ var ragChat = (config) => {
16952
17829
  })
16953
17830
  };
16954
17831
  };
16955
- const handleDeleteDocument = async (id) => {
17832
+ const handleDeleteDocument = async (id, request) => {
16956
17833
  if (!indexManager?.deleteDocument) {
16957
17834
  return {
16958
17835
  error: "RAG document deletion is not configured",
@@ -16965,6 +17842,13 @@ var ragChat = (config) => {
16965
17842
  ok: false
16966
17843
  };
16967
17844
  }
17845
+ const accessScope = await loadAccessScope(request);
17846
+ if (accessScope && !matchesAccessScope(accessScope, { documentId: id })) {
17847
+ return {
17848
+ error: "Document is outside the allowed RAG access scope",
17849
+ ok: false
17850
+ };
17851
+ }
16968
17852
  const job = createAdminJob("delete_document", id);
16969
17853
  const deleted = await indexManager.deleteDocument(id);
16970
17854
  if (!deleted) {
@@ -16984,7 +17868,7 @@ var ragChat = (config) => {
16984
17868
  ok: true
16985
17869
  };
16986
17870
  };
16987
- const handleReindexDocument = async (id) => {
17871
+ const handleReindexDocument = async (id, request) => {
16988
17872
  if (!indexManager?.reindexDocument) {
16989
17873
  return {
16990
17874
  error: "RAG document reindex is not configured",
@@ -16997,6 +17881,13 @@ var ragChat = (config) => {
16997
17881
  ok: false
16998
17882
  };
16999
17883
  }
17884
+ const accessScope = await loadAccessScope(request);
17885
+ if (accessScope && !matchesAccessScope(accessScope, { documentId: id })) {
17886
+ return {
17887
+ error: "Document is outside the allowed RAG access scope",
17888
+ ok: false
17889
+ };
17890
+ }
17000
17891
  const job = createAdminJob("reindex_document", id);
17001
17892
  try {
17002
17893
  const result = {
@@ -17016,7 +17907,7 @@ var ragChat = (config) => {
17016
17907
  throw caught;
17017
17908
  }
17018
17909
  };
17019
- const handleReindexSource = async (source) => {
17910
+ const handleReindexSource = async (source, request) => {
17020
17911
  if (!indexManager?.reindexSource) {
17021
17912
  return {
17022
17913
  error: "RAG source reindex is not configured",
@@ -17029,6 +17920,15 @@ var ragChat = (config) => {
17029
17920
  ok: false
17030
17921
  };
17031
17922
  }
17923
+ const accessScope = await loadAccessScope(request);
17924
+ if (!matchesAccessScope(accessScope, {
17925
+ source
17926
+ })) {
17927
+ return {
17928
+ error: "Source is outside the allowed RAG access scope",
17929
+ ok: false
17930
+ };
17931
+ }
17032
17932
  const job = createAdminJob("reindex_source", source);
17033
17933
  try {
17034
17934
  const result = {
@@ -17112,25 +18012,33 @@ var ragChat = (config) => {
17112
18012
  ...normalized
17113
18013
  };
17114
18014
  };
17115
- const handleSyncSources = async () => {
18015
+ const handleSyncSources = async (request) => {
17116
18016
  if (!indexManager?.listSyncSources) {
17117
18017
  return {
17118
18018
  error: "RAG source sync is not configured",
17119
18019
  ok: false
17120
18020
  };
17121
18021
  }
18022
+ const accessScope = await loadAccessScope(request);
17122
18023
  return {
17123
18024
  ok: true,
17124
- sources: await indexManager.listSyncSources()
18025
+ sources: await buildSyncSources(accessScope)
17125
18026
  };
17126
18027
  };
17127
- const handleSyncAllSources = async (options) => {
18028
+ const handleSyncAllSources = async (request, options) => {
17128
18029
  if (!indexManager?.syncAllSources) {
17129
18030
  return {
17130
18031
  error: "RAG source sync is not configured",
17131
18032
  ok: false
17132
18033
  };
17133
18034
  }
18035
+ const accessScope = await loadAccessScope(request);
18036
+ if (accessScope?.allowedSyncSourceIds?.length) {
18037
+ return {
18038
+ error: "Scoped sync-all is not allowed; sync individual sources instead",
18039
+ ok: false
18040
+ };
18041
+ }
17134
18042
  const job = createAdminJob("sync_all_sources", undefined, syncJobs);
17135
18043
  const action = createAdminAction("sync_all_sources");
17136
18044
  try {
@@ -17156,7 +18064,7 @@ var ragChat = (config) => {
17156
18064
  completeAdminAction(action);
17157
18065
  return {
17158
18066
  ok: true,
17159
- sources: await buildSyncSources()
18067
+ sources: await buildSyncSources(accessScope)
17160
18068
  };
17161
18069
  } catch (caught) {
17162
18070
  const message = caught instanceof Error ? caught.message : String(caught);
@@ -17165,7 +18073,7 @@ var ragChat = (config) => {
17165
18073
  throw caught;
17166
18074
  }
17167
18075
  };
17168
- const handleSyncSource = async (id, options) => {
18076
+ const handleSyncSource = async (id, request, options) => {
17169
18077
  if (!indexManager?.syncSource) {
17170
18078
  return {
17171
18079
  error: "RAG source sync is not configured",
@@ -17178,6 +18086,13 @@ var ragChat = (config) => {
17178
18086
  ok: false
17179
18087
  };
17180
18088
  }
18089
+ const accessScope = await loadAccessScope(request);
18090
+ if (!matchesSyncSourceScope(accessScope, { id })) {
18091
+ return {
18092
+ error: "Sync source is outside the allowed RAG access scope",
18093
+ ok: false
18094
+ };
18095
+ }
17181
18096
  const job = createAdminJob("sync_source", id, syncJobs);
17182
18097
  const action = createAdminAction("sync_source", undefined, id);
17183
18098
  try {
@@ -17194,7 +18109,7 @@ var ragChat = (config) => {
17194
18109
  }
17195
18110
  completeAdminJob(job);
17196
18111
  completeAdminAction(action);
17197
- const source = (await buildSyncSources()).find((record) => record.id === id);
18112
+ const source = (await buildSyncSources(accessScope)).find((record) => record.id === id);
17198
18113
  return source ? { ok: true, source } : {
17199
18114
  error: "sync source not found",
17200
18115
  ok: false
@@ -17260,7 +18175,7 @@ var ragChat = (config) => {
17260
18175
  };
17261
18176
  return;
17262
18177
  }
17263
- const lastMessage = conversation.messages.findLast((msg) => msg.id === messageId && msg.role === "user");
18178
+ const lastMessage = conversation.messages.findLast((message) => message.id === messageId && message.role === "user");
17264
18179
  if (!lastMessage) {
17265
18180
  yield {
17266
18181
  data: renderers.error("Message not found"),
@@ -17304,7 +18219,7 @@ var ragChat = (config) => {
17304
18219
  const controller = new AbortController;
17305
18220
  abortControllers.set(conversationId, controller);
17306
18221
  const history = buildHistory(conversation);
17307
- const lastMessageIndex = conversation.messages.findIndex((msg) => msg.id === messageId);
18222
+ const lastMessageIndex = conversation.messages.findIndex((message) => message.id === messageId);
17308
18223
  const userHistory = lastMessageIndex >= 0 ? history.slice(0, lastMessageIndex) : history;
17309
18224
  const messageWithContext = buildUserMessage2(content, lastMessage.attachments, ragContext);
17310
18225
  const sseStream = streamAIToSSE(conversationId, assistantMessageId, {
@@ -17330,24 +18245,24 @@ var ragChat = (config) => {
17330
18245
  };
17331
18246
  return new Elysia2().ws(path, {
17332
18247
  message: async (ws, raw) => {
17333
- const msg = parseAIMessage(raw);
17334
- if (!msg) {
18248
+ const message = parseAIMessage(raw);
18249
+ if (!message) {
17335
18250
  return;
17336
18251
  }
17337
- if (msg.type === "cancel") {
17338
- handleCancel(msg.conversationId);
18252
+ if (message.type === "cancel") {
18253
+ handleCancel(message.conversationId);
17339
18254
  return;
17340
18255
  }
17341
- if (msg.type === "branch") {
17342
- await handleBranch(ws, msg.messageId, msg.conversationId);
18256
+ if (message.type === "branch") {
18257
+ await handleBranch(ws, message.messageId, message.conversationId);
17343
18258
  return;
17344
18259
  }
17345
- if (msg.type === "message") {
17346
- await handleMessage(ws, msg.content, msg.conversationId, msg.attachments);
18260
+ if (message.type === "message") {
18261
+ await handleMessage(ws, message.content, message.conversationId, message.attachments);
17347
18262
  }
17348
18263
  }
17349
18264
  }).post(`${path}/search`, async ({ body, request, set }) => {
17350
- const result = await handleSearch(body);
18265
+ const result = await handleSearch(body, request);
17351
18266
  if (!result.ok) {
17352
18267
  set.status = result.error === "Invalid payload" || result.error?.startsWith("Expected payload shape:") ? HTTP_STATUS_BAD_REQUEST : HTTP_STATUS_NOT_FOUND;
17353
18268
  }
@@ -17424,6 +18339,16 @@ var ragChat = (config) => {
17424
18339
  }
17425
18340
  return result;
17426
18341
  }).post(`${path}/traces/prune`, async ({ body, request, set }) => {
18342
+ const denied = await authorizeMutationRoute(request, "prune_search_traces", {
18343
+ fallback: "Search trace pruning is not allowed"
18344
+ });
18345
+ if (denied) {
18346
+ set.status = 403;
18347
+ if (config.htmx && isHTMXRequest(request)) {
18348
+ return toHTMXResponse(workflowRenderers.error(denied.error ?? "Search trace prune failed"), getNumericStatus(set.status));
18349
+ }
18350
+ return denied;
18351
+ }
17427
18352
  const result = await handleTracePrune(body);
17428
18353
  if (!result.ok) {
17429
18354
  set.status = HTTP_STATUS_BAD_REQUEST;
@@ -17454,7 +18379,7 @@ var ragChat = (config) => {
17454
18379
  }
17455
18380
  return result;
17456
18381
  }).post(`${path}/compare/retrieval`, async ({ body, request, set }) => {
17457
- const result = await handleEvaluateRetrievals(body);
18382
+ const result = await handleEvaluateRetrievals(body, request);
17458
18383
  if (!result.ok) {
17459
18384
  set.status = HTTP_STATUS_BAD_REQUEST;
17460
18385
  }
@@ -17469,7 +18394,7 @@ var ragChat = (config) => {
17469
18394
  }
17470
18395
  return result;
17471
18396
  }).get(`${path}/compare/retrieval/history`, async ({ query, request, set }) => {
17472
- const result = await handleRetrievalComparisonHistory(query);
18397
+ const result = await handleRetrievalComparisonHistory(query, request);
17473
18398
  if (!result.ok) {
17474
18399
  set.status = HTTP_STATUS_BAD_REQUEST;
17475
18400
  }
@@ -17484,7 +18409,7 @@ var ragChat = (config) => {
17484
18409
  }
17485
18410
  return result;
17486
18411
  }).get(`${path}/compare/retrieval/baselines`, async ({ query, request, set }) => {
17487
- const result = await handleRetrievalBaselineList(query);
18412
+ const result = await handleRetrievalBaselineList(query, request);
17488
18413
  if (!result.ok) {
17489
18414
  set.status = HTTP_STATUS_BAD_REQUEST;
17490
18415
  }
@@ -17499,7 +18424,7 @@ var ragChat = (config) => {
17499
18424
  }
17500
18425
  return result;
17501
18426
  }).get(`${path}/compare/retrieval/baselines/decisions`, async ({ query, request, set }) => {
17502
- const result = await handleRetrievalReleaseDecisionList(query);
18427
+ const result = await handleRetrievalReleaseDecisionList(query, request);
17503
18428
  if (!result.ok) {
17504
18429
  set.status = HTTP_STATUS_BAD_REQUEST;
17505
18430
  }
@@ -17514,7 +18439,7 @@ var ragChat = (config) => {
17514
18439
  }
17515
18440
  return result;
17516
18441
  }).get(`${path}/compare/retrieval/release-history`, async ({ query, request, set }) => {
17517
- const result = await handleRetrievalReleaseGroupHistory(query);
18442
+ const result = await handleRetrievalReleaseGroupHistory(query, request);
17518
18443
  if (!result.ok) {
17519
18444
  set.status = HTTP_STATUS_BAD_REQUEST;
17520
18445
  }
@@ -17529,7 +18454,7 @@ var ragChat = (config) => {
17529
18454
  }
17530
18455
  return result;
17531
18456
  }).get(`${path}/compare/retrieval/handoffs`, async ({ query, request, set }) => {
17532
- const result = await handleRetrievalLaneHandoffList(query);
18457
+ const result = await handleRetrievalLaneHandoffList(query, request);
17533
18458
  if (!result.ok) {
17534
18459
  set.status = HTTP_STATUS_BAD_REQUEST;
17535
18460
  }
@@ -17544,7 +18469,7 @@ var ragChat = (config) => {
17544
18469
  }
17545
18470
  return result;
17546
18471
  }).get(`${path}/compare/retrieval/handoffs/decisions`, async ({ query, request, set }) => {
17547
- const result = await handleRetrievalLaneHandoffDecisionList(query);
18472
+ const result = await handleRetrievalLaneHandoffDecisionList(query, request);
17548
18473
  if (!result.ok) {
17549
18474
  set.status = HTTP_STATUS_BAD_REQUEST;
17550
18475
  }
@@ -17559,7 +18484,7 @@ var ragChat = (config) => {
17559
18484
  }
17560
18485
  return result;
17561
18486
  }).get(`${path}/compare/retrieval/handoffs/incidents`, async ({ query, request, set }) => {
17562
- const result = await handleRetrievalLaneHandoffIncidentList(query);
18487
+ const result = await handleRetrievalLaneHandoffIncidentList(query, request);
17563
18488
  if (!result.ok) {
17564
18489
  set.status = HTTP_STATUS_BAD_REQUEST;
17565
18490
  }
@@ -17574,7 +18499,7 @@ var ragChat = (config) => {
17574
18499
  }
17575
18500
  return result;
17576
18501
  }).get(`${path}/compare/retrieval/handoffs/incidents/history`, async ({ query, request, set }) => {
17577
- const result = await handleRetrievalLaneHandoffIncidentHistoryList(query);
18502
+ const result = await handleRetrievalLaneHandoffIncidentHistoryList(query, request);
17578
18503
  if (!result.ok) {
17579
18504
  set.status = HTTP_STATUS_BAD_REQUEST;
17580
18505
  }
@@ -17604,6 +18529,16 @@ var ragChat = (config) => {
17604
18529
  }
17605
18530
  return result;
17606
18531
  }).post(`${path}/compare/retrieval/handoffs/incidents/acknowledge`, async ({ body, request, set }) => {
18532
+ const denied = await authorizeMutationRoute(request, "manage_retrieval_admin", {
18533
+ fallback: "Retrieval lane handoff incident acknowledgement is not allowed"
18534
+ });
18535
+ if (denied) {
18536
+ set.status = 403;
18537
+ if (config.htmx && isHTMXRequest(request)) {
18538
+ return toHTMXResponse(workflowRenderers.error(denied.error ?? "Retrieval lane handoff incident acknowledgement failed"), getNumericStatus(set.status));
18539
+ }
18540
+ return denied;
18541
+ }
17607
18542
  const result = await handleRetrievalLaneHandoffIncidentAcknowledge(body);
17608
18543
  if (!result.ok) {
17609
18544
  set.status = HTTP_STATUS_BAD_REQUEST;
@@ -17619,6 +18554,16 @@ var ragChat = (config) => {
17619
18554
  }
17620
18555
  return result;
17621
18556
  }).post(`${path}/compare/retrieval/handoffs/incidents/unacknowledge`, async ({ body, request, set }) => {
18557
+ const denied = await authorizeMutationRoute(request, "manage_retrieval_admin", {
18558
+ fallback: "Retrieval lane handoff incident unacknowledge is not allowed"
18559
+ });
18560
+ if (denied) {
18561
+ set.status = 403;
18562
+ if (config.htmx && isHTMXRequest(request)) {
18563
+ return toHTMXResponse(workflowRenderers.error(denied.error ?? "Retrieval lane handoff incident unacknowledge failed"), getNumericStatus(set.status));
18564
+ }
18565
+ return denied;
18566
+ }
17622
18567
  const result = await handleRetrievalLaneHandoffIncidentUnacknowledge(body);
17623
18568
  if (!result.ok) {
17624
18569
  set.status = HTTP_STATUS_BAD_REQUEST;
@@ -17634,6 +18579,16 @@ var ragChat = (config) => {
17634
18579
  }
17635
18580
  return result;
17636
18581
  }).post(`${path}/compare/retrieval/handoffs/incidents/resolve`, async ({ body, request, set }) => {
18582
+ const denied = await authorizeMutationRoute(request, "manage_retrieval_admin", {
18583
+ fallback: "Retrieval lane handoff incident resolve is not allowed"
18584
+ });
18585
+ if (denied) {
18586
+ set.status = 403;
18587
+ if (config.htmx && isHTMXRequest(request)) {
18588
+ return toHTMXResponse(workflowRenderers.error(denied.error ?? "Retrieval lane handoff incident resolve failed"), getNumericStatus(set.status));
18589
+ }
18590
+ return denied;
18591
+ }
17637
18592
  const result = await handleResolveRetrievalLaneHandoffIncident(body);
17638
18593
  if (!result.ok) {
17639
18594
  set.status = HTTP_STATUS_BAD_REQUEST;
@@ -17649,6 +18604,16 @@ var ragChat = (config) => {
17649
18604
  }
17650
18605
  return result;
17651
18606
  }).post(`${path}/compare/retrieval/handoffs/decide`, async ({ body, request, set }) => {
18607
+ const denied = await authorizeMutationRoute(request, "manage_retrieval_admin", {
18608
+ fallback: "Retrieval lane handoff decision is not allowed"
18609
+ });
18610
+ if (denied) {
18611
+ set.status = 403;
18612
+ if (config.htmx && isHTMXRequest(request)) {
18613
+ return toHTMXResponse(workflowRenderers.error(denied.error ?? "Retrieval lane handoff decision failed"), getNumericStatus(set.status));
18614
+ }
18615
+ return denied;
18616
+ }
17652
18617
  const result = await handleRetrievalLaneHandoffDecision(body);
17653
18618
  if (!result.ok) {
17654
18619
  set.status = HTTP_STATUS_BAD_REQUEST;
@@ -17709,6 +18674,16 @@ var ragChat = (config) => {
17709
18674
  }
17710
18675
  return result;
17711
18676
  }).post(`${path}/compare/retrieval/incidents/remediations`, async ({ body, request, set }) => {
18677
+ const denied = await authorizeMutationRoute(request, "manage_retrieval_admin", {
18678
+ fallback: "Retrieval incident remediation decision record is not allowed"
18679
+ });
18680
+ if (denied) {
18681
+ set.status = 403;
18682
+ if (config.htmx && isHTMXRequest(request)) {
18683
+ return toHTMXResponse(workflowRenderers.error(denied.error ?? "Retrieval incident remediation decision record failed"), getNumericStatus(set.status));
18684
+ }
18685
+ return denied;
18686
+ }
17712
18687
  const result = await handleRecordRetrievalIncidentRemediationDecision(body);
17713
18688
  if (!result.ok) {
17714
18689
  set.status = HTTP_STATUS_BAD_REQUEST;
@@ -17724,6 +18699,16 @@ var ragChat = (config) => {
17724
18699
  }
17725
18700
  return result;
17726
18701
  }).post(`${path}/compare/retrieval/incidents/remediations/execute`, async ({ body, request, set }) => {
18702
+ const denied = await authorizeMutationRoute(request, "manage_retrieval_admin", {
18703
+ fallback: "Retrieval incident remediation execution is not allowed"
18704
+ });
18705
+ if (denied) {
18706
+ set.status = 403;
18707
+ if (config.htmx && isHTMXRequest(request)) {
18708
+ return toHTMXResponse(workflowRenderers.error(denied.error ?? "Retrieval incident remediation execution failed"), getNumericStatus(set.status));
18709
+ }
18710
+ return denied;
18711
+ }
17727
18712
  const result = await handleExecuteRetrievalIncidentRemediation(body);
17728
18713
  if (!result.ok) {
17729
18714
  set.status = HTTP_STATUS_BAD_REQUEST;
@@ -17739,6 +18724,16 @@ var ragChat = (config) => {
17739
18724
  }
17740
18725
  return result;
17741
18726
  }).post(`${path}/compare/retrieval/incidents/remediations/execute/bulk`, async ({ body, request, set }) => {
18727
+ const denied = await authorizeMutationRoute(request, "manage_retrieval_admin", {
18728
+ fallback: "Bulk retrieval incident remediation execution is not allowed"
18729
+ });
18730
+ if (denied) {
18731
+ set.status = 403;
18732
+ if (config.htmx && isHTMXRequest(request)) {
18733
+ return toHTMXResponse(workflowRenderers.error(denied.error ?? "Bulk retrieval incident remediation execution failed"), getNumericStatus(set.status));
18734
+ }
18735
+ return denied;
18736
+ }
17742
18737
  const result = await handleBulkExecuteRetrievalIncidentRemediations(body);
17743
18738
  if (!result.ok) {
17744
18739
  set.status = HTTP_STATUS_BAD_REQUEST;
@@ -17754,6 +18749,16 @@ var ragChat = (config) => {
17754
18749
  }
17755
18750
  return result;
17756
18751
  }).post(`${path}/compare/retrieval/incidents/acknowledge`, async ({ body, request, set }) => {
18752
+ const denied = await authorizeMutationRoute(request, "manage_retrieval_admin", {
18753
+ fallback: "Retrieval release incident acknowledgement is not allowed"
18754
+ });
18755
+ if (denied) {
18756
+ set.status = 403;
18757
+ if (config.htmx && isHTMXRequest(request)) {
18758
+ return toHTMXResponse(workflowRenderers.error(denied.error ?? "Retrieval release incident acknowledgement failed"), getNumericStatus(set.status));
18759
+ }
18760
+ return denied;
18761
+ }
17757
18762
  const result = await handleAcknowledgeRetrievalReleaseIncident(body);
17758
18763
  if (!result.ok) {
17759
18764
  set.status = HTTP_STATUS_BAD_REQUEST;
@@ -17769,6 +18774,16 @@ var ragChat = (config) => {
17769
18774
  }
17770
18775
  return result;
17771
18776
  }).post(`${path}/compare/retrieval/incidents/unacknowledge`, async ({ body, request, set }) => {
18777
+ const denied = await authorizeMutationRoute(request, "manage_retrieval_admin", {
18778
+ fallback: "Retrieval release incident unacknowledge is not allowed"
18779
+ });
18780
+ if (denied) {
18781
+ set.status = 403;
18782
+ if (config.htmx && isHTMXRequest(request)) {
18783
+ return toHTMXResponse(workflowRenderers.error(denied.error ?? "Retrieval release incident unacknowledge failed"), getNumericStatus(set.status));
18784
+ }
18785
+ return denied;
18786
+ }
17772
18787
  const result = await handleUnacknowledgeRetrievalReleaseIncident(body);
17773
18788
  if (!result.ok) {
17774
18789
  set.status = HTTP_STATUS_BAD_REQUEST;
@@ -17784,6 +18799,16 @@ var ragChat = (config) => {
17784
18799
  }
17785
18800
  return result;
17786
18801
  }).post(`${path}/compare/retrieval/incidents/resolve`, async ({ body, request, set }) => {
18802
+ const denied = await authorizeMutationRoute(request, "manage_retrieval_admin", {
18803
+ fallback: "Retrieval release incident resolve is not allowed"
18804
+ });
18805
+ if (denied) {
18806
+ set.status = 403;
18807
+ if (config.htmx && isHTMXRequest(request)) {
18808
+ return toHTMXResponse(workflowRenderers.error(denied.error ?? "Retrieval release incident resolve failed"), getNumericStatus(set.status));
18809
+ }
18810
+ return denied;
18811
+ }
17787
18812
  const result = await handleResolveRetrievalReleaseIncident(body);
17788
18813
  if (!result.ok) {
17789
18814
  set.status = HTTP_STATUS_BAD_REQUEST;
@@ -17814,7 +18839,15 @@ var ragChat = (config) => {
17814
18839
  }
17815
18840
  return result;
17816
18841
  }).post(`${path}/compare/retrieval/baselines/approve`, async ({ body, request, set }) => {
17817
- const result = await handleRetrievalReleaseDecisionAction(body, "approve");
18842
+ const denied = await authorizeMutationRoute(request, "manage_retrieval_admin", { fallback: "Retrieval approval is not allowed" });
18843
+ if (denied) {
18844
+ set.status = 403;
18845
+ if (config.htmx && isHTMXRequest(request)) {
18846
+ return toHTMXResponse(workflowRenderers.error(denied.error ?? "Retrieval approval failed"), getNumericStatus(set.status));
18847
+ }
18848
+ return denied;
18849
+ }
18850
+ const result = await handleRetrievalReleaseDecisionAction(body, "approve", request);
17818
18851
  if (!result.ok) {
17819
18852
  set.status = HTTP_STATUS_BAD_REQUEST;
17820
18853
  }
@@ -17829,7 +18862,15 @@ var ragChat = (config) => {
17829
18862
  }
17830
18863
  return result;
17831
18864
  }).post(`${path}/compare/retrieval/baselines/reject`, async ({ body, request, set }) => {
17832
- const result = await handleRetrievalReleaseDecisionAction(body, "reject");
18865
+ const denied = await authorizeMutationRoute(request, "manage_retrieval_admin", { fallback: "Retrieval rejection is not allowed" });
18866
+ if (denied) {
18867
+ set.status = 403;
18868
+ if (config.htmx && isHTMXRequest(request)) {
18869
+ return toHTMXResponse(workflowRenderers.error(denied.error ?? "Retrieval rejection failed"), getNumericStatus(set.status));
18870
+ }
18871
+ return denied;
18872
+ }
18873
+ const result = await handleRetrievalReleaseDecisionAction(body, "reject", request);
17833
18874
  if (!result.ok) {
17834
18875
  set.status = HTTP_STATUS_BAD_REQUEST;
17835
18876
  }
@@ -17844,7 +18885,15 @@ var ragChat = (config) => {
17844
18885
  }
17845
18886
  return result;
17846
18887
  }).post(`${path}/compare/retrieval/baselines/promote`, async ({ body, request, set }) => {
17847
- const result = await handlePromoteRetrievalBaseline(body);
18888
+ const denied = await authorizeMutationRoute(request, "manage_retrieval_admin", { fallback: "Retrieval baseline promotion is not allowed" });
18889
+ if (denied) {
18890
+ set.status = 403;
18891
+ if (config.htmx && isHTMXRequest(request)) {
18892
+ return toHTMXResponse(workflowRenderers.error(denied.error ?? "Retrieval baseline promotion failed"), getNumericStatus(set.status));
18893
+ }
18894
+ return denied;
18895
+ }
18896
+ const result = await handlePromoteRetrievalBaseline(body, request);
17848
18897
  if (!result.ok) {
17849
18898
  set.status = HTTP_STATUS_BAD_REQUEST;
17850
18899
  }
@@ -17859,6 +18908,16 @@ var ragChat = (config) => {
17859
18908
  }
17860
18909
  return result;
17861
18910
  }).post(`${path}/compare/retrieval/baselines/promote-lane`, async ({ body, request, set }) => {
18911
+ const denied = await authorizeMutationRoute(request, "manage_retrieval_admin", {
18912
+ fallback: "Retrieval rollout-lane promotion is not allowed"
18913
+ });
18914
+ if (denied) {
18915
+ set.status = 403;
18916
+ if (config.htmx && isHTMXRequest(request)) {
18917
+ return toHTMXResponse(workflowRenderers.error(denied.error ?? "Retrieval rollout-lane promotion failed"), getNumericStatus(set.status));
18918
+ }
18919
+ return denied;
18920
+ }
17862
18921
  const result = await handlePromoteRetrievalBaselineToLane(body);
17863
18922
  if (!result.ok) {
17864
18923
  set.status = HTTP_STATUS_BAD_REQUEST;
@@ -17874,7 +18933,17 @@ var ragChat = (config) => {
17874
18933
  }
17875
18934
  return result;
17876
18935
  }).post(`${path}/compare/retrieval/baselines/promote-run`, async ({ body, request, set }) => {
17877
- const result = await handlePromoteRetrievalBaselineFromRun(body);
18936
+ const denied = await authorizeMutationRoute(request, "manage_retrieval_admin", {
18937
+ fallback: "Retrieval baseline promotion from run is not allowed"
18938
+ });
18939
+ if (denied) {
18940
+ set.status = 403;
18941
+ if (config.htmx && isHTMXRequest(request)) {
18942
+ return toHTMXResponse(workflowRenderers.error(denied.error ?? "Retrieval baseline promotion from run failed"), getNumericStatus(set.status));
18943
+ }
18944
+ return denied;
18945
+ }
18946
+ const result = await handlePromoteRetrievalBaselineFromRun(body, request);
17878
18947
  if (!result.ok) {
17879
18948
  set.status = HTTP_STATUS_BAD_REQUEST;
17880
18949
  }
@@ -17889,7 +18958,15 @@ var ragChat = (config) => {
17889
18958
  }
17890
18959
  return result;
17891
18960
  }).post(`${path}/compare/retrieval/baselines/revert`, async ({ body, request, set }) => {
17892
- const result = await handleRevertRetrievalBaseline(body);
18961
+ const denied = await authorizeMutationRoute(request, "manage_retrieval_admin", { fallback: "Retrieval baseline revert is not allowed" });
18962
+ if (denied) {
18963
+ set.status = 403;
18964
+ if (config.htmx && isHTMXRequest(request)) {
18965
+ return toHTMXResponse(workflowRenderers.error(denied.error ?? "Retrieval baseline revert failed"), getNumericStatus(set.status));
18966
+ }
18967
+ return denied;
18968
+ }
18969
+ const result = await handleRevertRetrievalBaseline(body, request);
17893
18970
  if (!result.ok) {
17894
18971
  set.status = HTTP_STATUS_BAD_REQUEST;
17895
18972
  }
@@ -17904,7 +18981,7 @@ var ragChat = (config) => {
17904
18981
  }
17905
18982
  return result;
17906
18983
  }).post(`${path}/evaluate`, async ({ body, request, set }) => {
17907
- const result = await handleEvaluate(body);
18984
+ const result = await handleEvaluate(body, request);
17908
18985
  if (!result.ok) {
17909
18986
  set.status = HTTP_STATUS_BAD_REQUEST;
17910
18987
  }
@@ -17919,7 +18996,7 @@ var ragChat = (config) => {
17919
18996
  }
17920
18997
  return result;
17921
18998
  }).get(`${path}/status`, async ({ request }) => {
17922
- const result = await handleStatus();
18999
+ const result = await handleStatus(request);
17923
19000
  if (config.htmx && isHTMXRequest(request)) {
17924
19001
  return toHTMXResponse(workflowRenderers.status({
17925
19002
  capabilities: result.capabilities,
@@ -17959,7 +19036,7 @@ var ragChat = (config) => {
17959
19036
  }
17960
19037
  return result;
17961
19038
  }).get(`${path}/compare/retrieval/escalation-policies/history`, async ({ query, request, set }) => {
17962
- const result = await handleRetrievalReleaseLaneEscalationPolicyHistoryList(query);
19039
+ const result = await handleRetrievalReleaseLaneEscalationPolicyHistoryList(query, request);
17963
19040
  if (!result.ok) {
17964
19041
  set.status = HTTP_STATUS_BAD_REQUEST;
17965
19042
  }
@@ -17974,7 +19051,7 @@ var ragChat = (config) => {
17974
19051
  }
17975
19052
  return result;
17976
19053
  }).get(`${path}/compare/retrieval/incident-policies/history`, async ({ query, request, set }) => {
17977
- const result = await handleRetrievalReleaseLaneEscalationPolicyHistoryList(query);
19054
+ const result = await handleRetrievalReleaseLaneEscalationPolicyHistoryList(query, request);
17978
19055
  if (!result.ok) {
17979
19056
  set.status = HTTP_STATUS_BAD_REQUEST;
17980
19057
  }
@@ -17988,20 +19065,21 @@ var ragChat = (config) => {
17988
19065
  });
17989
19066
  }
17990
19067
  return result;
17991
- }).get(`${path}/status/release`, async () => {
17992
- return handleRetrievalReleaseStatus();
19068
+ }).get(`${path}/status/release`, async ({ request }) => {
19069
+ return handleRetrievalReleaseStatus(request);
17993
19070
  }).get(`${path}/status/release/incidents`, async () => {
17994
19071
  return handleRetrievalReleaseIncidentStatus();
17995
19072
  }).get(`${path}/status/release/remediations`, async () => {
17996
19073
  return handleRetrievalIncidentRemediationStatus();
17997
- }).get(`${path}/status/release/drift`, async () => {
17998
- return handleRetrievalReleaseDriftStatus();
17999
- }).get(`${path}/status/handoffs/incidents`, async () => {
18000
- return handleRetrievalLaneHandoffIncidentStatus();
18001
- }).get(`${path}/status/handoffs`, async () => {
18002
- return handleRetrievalLaneHandoffStatus();
19074
+ }).get(`${path}/status/release/drift`, async ({ request }) => {
19075
+ return handleRetrievalReleaseDriftStatus(request);
19076
+ }).get(`${path}/status/handoffs/incidents`, async ({ request }) => {
19077
+ return handleRetrievalLaneHandoffIncidentStatus(request);
19078
+ }).get(`${path}/status/handoffs`, async ({ request }) => {
19079
+ return handleRetrievalLaneHandoffStatus(request);
18003
19080
  }).get(`${path}/ops`, async ({ request }) => {
18004
- const result = await handleOps();
19081
+ await ensureJobStateLoaded();
19082
+ const result = await handleOps(request);
18005
19083
  if (config.htmx && isHTMXRequest(request)) {
18006
19084
  return toHTMXResponse(workflowRenderers.status({
18007
19085
  capabilities: result.capabilities,
@@ -18011,7 +19089,7 @@ var ragChat = (config) => {
18011
19089
  }
18012
19090
  return result;
18013
19091
  }).get(`${path}/documents`, async ({ query, request, set }) => {
18014
- const result = await handleDocuments(getStringProperty(query, "kind"));
19092
+ const result = await handleDocuments(getStringProperty(query, "kind"), request);
18015
19093
  if (!result.ok) {
18016
19094
  set.status = HTTP_STATUS_NOT_FOUND;
18017
19095
  }
@@ -18025,9 +19103,19 @@ var ragChat = (config) => {
18025
19103
  }
18026
19104
  return result;
18027
19105
  }).post(`${path}/documents`, async ({ body, request, set }) => {
18028
- const result = await handleCreateDocument(body);
19106
+ await ensureJobStateLoaded();
19107
+ const authorization = await checkAuthorization(request, "create_document");
19108
+ if (!authorization.allowed) {
19109
+ set.status = 403;
19110
+ const result2 = buildAuthorizationFailure(authorization, "Document creation is not allowed");
19111
+ if (config.htmx && isHTMXRequest(request)) {
19112
+ return toHTMXResponse(workflowRenderers.error(result2.error ?? "Failed to create document"), getNumericStatus(set.status));
19113
+ }
19114
+ return result2;
19115
+ }
19116
+ const result = await handleCreateDocument(body, request);
18029
19117
  if (!result.ok) {
18030
- const status = result.error?.includes("not configured") ? HTTP_STATUS_NOT_FOUND : HTTP_STATUS_BAD_REQUEST;
19118
+ const status = isAccessScopeError(result.error) ? 403 : result.error?.includes("not configured") ? HTTP_STATUS_NOT_FOUND : HTTP_STATUS_BAD_REQUEST;
18031
19119
  set.status = status;
18032
19120
  }
18033
19121
  if (config.htmx && isHTMXRequest(request)) {
@@ -18038,9 +19126,9 @@ var ragChat = (config) => {
18038
19126
  }
18039
19127
  return result;
18040
19128
  }).get(`${path}/documents/:id/chunks`, async ({ params, request, set }) => {
18041
- const result = await handleDocumentChunks(typeof params.id === "string" ? params.id.trim() : "");
19129
+ const result = await handleDocumentChunks(typeof params.id === "string" ? params.id.trim() : "", request);
18042
19130
  if (!result.ok) {
18043
- const status = result.error === "document id is required" ? HTTP_STATUS_BAD_REQUEST : HTTP_STATUS_NOT_FOUND;
19131
+ const status = isAccessScopeError(result.error) ? 403 : result.error === "document id is required" ? HTTP_STATUS_BAD_REQUEST : HTTP_STATUS_NOT_FOUND;
18044
19132
  set.status = status;
18045
19133
  }
18046
19134
  if (config.htmx && isHTMXRequest(request)) {
@@ -18054,10 +19142,13 @@ var ragChat = (config) => {
18054
19142
  const result = await handleBackends();
18055
19143
  if (!result.ok) {
18056
19144
  set.status = HTTP_STATUS_NOT_FOUND;
19145
+ if (isAccessScopeError(result.error)) {
19146
+ set.status = 403;
19147
+ }
18057
19148
  }
18058
19149
  return result;
18059
19150
  }).get(`${path}/sync`, async ({ request, set }) => {
18060
- const result = await handleSyncSources();
19151
+ const result = await handleSyncSources(request);
18061
19152
  if (!result.ok) {
18062
19153
  set.status = HTTP_STATUS_NOT_FOUND;
18063
19154
  }
@@ -18072,8 +19163,18 @@ var ragChat = (config) => {
18072
19163
  }
18073
19164
  return result;
18074
19165
  }).post(`${path}/sync`, async ({ body, request, set }) => {
19166
+ await ensureJobStateLoaded();
19167
+ const authorization = await checkAuthorization(request, "sync_all_sources");
19168
+ if (!authorization.allowed) {
19169
+ set.status = 403;
19170
+ const result2 = buildAuthorizationFailure(authorization, "Source sync is not allowed");
19171
+ if (config.htmx && isHTMXRequest(request)) {
19172
+ return toHTMXResponse(workflowRenderers.error(result2.error ?? "Failed to sync sources"), getNumericStatus(set.status));
19173
+ }
19174
+ return result2;
19175
+ }
18075
19176
  const background = getBooleanProperty(body, "background");
18076
- const result = await handleSyncAllSources({ background });
19177
+ const result = await handleSyncAllSources(request, { background });
18077
19178
  if (!result.ok) {
18078
19179
  set.status = HTTP_STATUS_NOT_FOUND;
18079
19180
  }
@@ -18088,10 +19189,28 @@ var ragChat = (config) => {
18088
19189
  }
18089
19190
  return result;
18090
19191
  }).post(`${path}/sync/:id`, async ({ body, params, request, set }) => {
19192
+ await ensureJobStateLoaded();
19193
+ const syncSourceId = typeof params.id === "string" ? params.id.trim() : "";
19194
+ const authorization = await checkAuthorization(request, "sync_source", {
19195
+ sourceId: syncSourceId
19196
+ });
19197
+ if (!authorization.allowed) {
19198
+ set.status = 403;
19199
+ const result2 = buildAuthorizationFailure(authorization, "Source sync is not allowed");
19200
+ if (config.htmx && isHTMXRequest(request)) {
19201
+ return toHTMXResponse(workflowRenderers.error(result2.error ?? "Failed to sync source"), getNumericStatus(set.status));
19202
+ }
19203
+ return result2;
19204
+ }
18091
19205
  const background = getBooleanProperty(body, "background");
18092
- const result = await handleSyncSource(typeof params.id === "string" ? params.id.trim() : "", { background });
19206
+ const result = await handleSyncSource(syncSourceId, request, {
19207
+ background
19208
+ });
18093
19209
  if (!result.ok) {
18094
19210
  set.status = result.error === "sync source id is required" ? HTTP_STATUS_BAD_REQUEST : HTTP_STATUS_NOT_FOUND;
19211
+ if (isAccessScopeError(result.error)) {
19212
+ set.status = 403;
19213
+ }
18095
19214
  }
18096
19215
  if (config.htmx && isHTMXRequest(request)) {
18097
19216
  const html = result.ok ? workflowRenderers.mutationResult({
@@ -18104,6 +19223,18 @@ var ragChat = (config) => {
18104
19223
  }
18105
19224
  return result;
18106
19225
  }).post(`${path}/ingest`, async ({ body, request, set }) => {
19226
+ await ensureJobStateLoaded();
19227
+ const authorization = await checkAuthorization(request, "ingest", {
19228
+ path: `${path}/ingest`
19229
+ });
19230
+ if (!authorization.allowed) {
19231
+ set.status = 403;
19232
+ const result2 = buildAuthorizationFailure(authorization, "RAG ingest is not allowed");
19233
+ if (config.htmx && isHTMXRequest(request)) {
19234
+ return toHTMXResponse(workflowRenderers.error(result2.error ?? "RAG ingest failed"), getNumericStatus(set.status));
19235
+ }
19236
+ return result2;
19237
+ }
18107
19238
  const result = await handleIngest(body);
18108
19239
  if (!result.ok) {
18109
19240
  set.status = HTTP_STATUS_BAD_REQUEST;
@@ -18115,7 +19246,13 @@ var ragChat = (config) => {
18115
19246
  return toHTMXResponse(workflowRenderers.mutationResult(result), HTTP_STATUS_OK, { "HX-Trigger": "rag:mutated" });
18116
19247
  }
18117
19248
  return result;
18118
- }).delete(`${path}/index`, async () => {
19249
+ }).delete(`${path}/index`, async ({ request, set }) => {
19250
+ await ensureJobStateLoaded();
19251
+ const authorization = await checkAuthorization(request, "clear_index");
19252
+ if (!authorization.allowed) {
19253
+ set.status = 403;
19254
+ return buildAuthorizationFailure(authorization, "Index clearing is not allowed");
19255
+ }
18119
19256
  if (!ragStore) {
18120
19257
  return { ok: false };
18121
19258
  }
@@ -18134,9 +19271,22 @@ var ragChat = (config) => {
18134
19271
  }
18135
19272
  return { ok: true };
18136
19273
  }).delete(`${path}/documents/:id`, async ({ params, request, set }) => {
18137
- const result = await handleDeleteDocument(typeof params.id === "string" ? params.id.trim() : "");
19274
+ await ensureJobStateLoaded();
19275
+ const documentId = typeof params.id === "string" ? params.id.trim() : "";
19276
+ const authorization = await checkAuthorization(request, "delete_document", {
19277
+ documentId
19278
+ });
19279
+ if (!authorization.allowed) {
19280
+ set.status = 403;
19281
+ const result2 = buildAuthorizationFailure(authorization, "Document deletion is not allowed");
19282
+ if (config.htmx && isHTMXRequest(request)) {
19283
+ return toHTMXResponse(workflowRenderers.error(result2.error ?? "Failed to delete document"), getNumericStatus(set.status), { "HX-Trigger": "rag:mutated" });
19284
+ }
19285
+ return result2;
19286
+ }
19287
+ const result = await handleDeleteDocument(documentId, request);
18138
19288
  if (!result.ok) {
18139
- const status = result.error === "document id is required" ? HTTP_STATUS_BAD_REQUEST : HTTP_STATUS_NOT_FOUND;
19289
+ const status = isAccessScopeError(result.error) ? 403 : result.error === "document id is required" ? HTTP_STATUS_BAD_REQUEST : HTTP_STATUS_NOT_FOUND;
18140
19290
  set.status = status;
18141
19291
  }
18142
19292
  if (config.htmx && isHTMXRequest(request)) {
@@ -18147,9 +19297,20 @@ var ragChat = (config) => {
18147
19297
  }
18148
19298
  return result;
18149
19299
  }).post(`${path}/reindex/documents/:id`, async ({ params, request, set }) => {
18150
- const result = await handleReindexDocument(typeof params.id === "string" ? params.id.trim() : "");
19300
+ await ensureJobStateLoaded();
19301
+ const documentId = typeof params.id === "string" ? params.id.trim() : "";
19302
+ const authorization = await checkAuthorization(request, "reindex_document", { documentId });
19303
+ if (!authorization.allowed) {
19304
+ set.status = 403;
19305
+ const result2 = buildAuthorizationFailure(authorization, "Document reindex is not allowed");
19306
+ if (config.htmx && isHTMXRequest(request)) {
19307
+ return toHTMXResponse(workflowRenderers.error(result2.error ?? "Failed to reindex document"), getNumericStatus(set.status), { "HX-Trigger": "rag:mutated" });
19308
+ }
19309
+ return result2;
19310
+ }
19311
+ const result = await handleReindexDocument(documentId, request);
18151
19312
  if (!result.ok) {
18152
- set.status = result.error === "document id is required" ? HTTP_STATUS_BAD_REQUEST : HTTP_STATUS_NOT_FOUND;
19313
+ set.status = isAccessScopeError(result.error) ? 403 : result.error === "document id is required" ? HTTP_STATUS_BAD_REQUEST : HTTP_STATUS_NOT_FOUND;
18153
19314
  }
18154
19315
  if (config.htmx && isHTMXRequest(request)) {
18155
19316
  const html = result.ok ? workflowRenderers.mutationResult(result) : workflowRenderers.error(result.error ?? "Failed to reindex document");
@@ -18159,10 +19320,22 @@ var ragChat = (config) => {
18159
19320
  }
18160
19321
  return result;
18161
19322
  }).post(`${path}/reindex/source`, async ({ body, request, set }) => {
19323
+ await ensureJobStateLoaded();
18162
19324
  const source = getStringProperty(body, "source")?.trim() ?? "";
18163
- const result = await handleReindexSource(source);
19325
+ const authorization = await checkAuthorization(request, "reindex_source", {
19326
+ source
19327
+ });
19328
+ if (!authorization.allowed) {
19329
+ set.status = 403;
19330
+ const result2 = buildAuthorizationFailure(authorization, "Source reindex is not allowed");
19331
+ if (config.htmx && isHTMXRequest(request)) {
19332
+ return toHTMXResponse(workflowRenderers.error(result2.error ?? "Failed to reindex source"), getNumericStatus(set.status), { "HX-Trigger": "rag:mutated" });
19333
+ }
19334
+ return result2;
19335
+ }
19336
+ const result = await handleReindexSource(source, request);
18164
19337
  if (!result.ok) {
18165
- set.status = result.error === "source is required" ? HTTP_STATUS_BAD_REQUEST : HTTP_STATUS_NOT_FOUND;
19338
+ set.status = isAccessScopeError(result.error) ? 403 : result.error === "source is required" ? HTTP_STATUS_BAD_REQUEST : HTTP_STATUS_NOT_FOUND;
18166
19339
  }
18167
19340
  if (config.htmx && isHTMXRequest(request)) {
18168
19341
  const html = result.ok ? workflowRenderers.mutationResult(result) : workflowRenderers.error(result.error ?? "Failed to reindex source");
@@ -18172,6 +19345,16 @@ var ragChat = (config) => {
18172
19345
  }
18173
19346
  return result;
18174
19347
  }).post(`${path}/reseed`, async ({ request, set }) => {
19348
+ await ensureJobStateLoaded();
19349
+ const authorization = await checkAuthorization(request, "reseed");
19350
+ if (!authorization.allowed) {
19351
+ set.status = 403;
19352
+ const result2 = buildAuthorizationFailure(authorization, "Index reseed is not allowed");
19353
+ if (config.htmx && isHTMXRequest(request)) {
19354
+ return toHTMXResponse(workflowRenderers.error(result2.error ?? "Failed to reseed index"), getNumericStatus(set.status), { "HX-Trigger": "rag:mutated" });
19355
+ }
19356
+ return result2;
19357
+ }
18175
19358
  const result = await handleReseed();
18176
19359
  if (!result.ok) {
18177
19360
  set.status = 404;
@@ -18184,6 +19367,16 @@ var ragChat = (config) => {
18184
19367
  }
18185
19368
  return result;
18186
19369
  }).post(`${path}/reset`, async ({ request, set }) => {
19370
+ await ensureJobStateLoaded();
19371
+ const authorization = await checkAuthorization(request, "reset");
19372
+ if (!authorization.allowed) {
19373
+ set.status = 403;
19374
+ const result2 = buildAuthorizationFailure(authorization, "Index reset is not allowed");
19375
+ if (config.htmx && isHTMXRequest(request)) {
19376
+ return toHTMXResponse(workflowRenderers.error(result2.error ?? "Failed to reset index"), getNumericStatus(set.status), { "HX-Trigger": "rag:mutated" });
19377
+ }
19378
+ return result2;
19379
+ }
18187
19380
  const result = await handleReset();
18188
19381
  if (!result.ok) {
18189
19382
  set.status = 404;
@@ -18271,6 +19464,31 @@ var createHeuristicRAGRetrievalStrategy = (options = {}) => ({
18271
19464
  return;
18272
19465
  }
18273
19466
  });
19467
+ // src/ai/rag/accessControl.ts
19468
+ var createRAGAccessControl = (options) => {
19469
+ const authorize = options.authorize;
19470
+ const contextCache = new WeakMap;
19471
+ const resolveScope = options.resolveScope;
19472
+ const loadContext = (request) => {
19473
+ const existing = contextCache.get(request);
19474
+ if (existing) {
19475
+ return existing;
19476
+ }
19477
+ const next = Promise.resolve(options.resolveContext(request));
19478
+ contextCache.set(request, next);
19479
+ return next;
19480
+ };
19481
+ return {
19482
+ authorizeRAGAction: authorize ? async (input) => authorize({
19483
+ ...input,
19484
+ context: await loadContext(input.request)
19485
+ }) : undefined,
19486
+ resolveRAGAccessScope: resolveScope ? async (request) => resolveScope({
19487
+ context: await loadContext(request),
19488
+ request
19489
+ }) : undefined
19490
+ };
19491
+ };
18274
19492
  // src/ai/rag/embeddingProviders.ts
18275
19493
  var DEFAULT_OPENAI_BASE_URL = "https://api.openai.com";
18276
19494
  var DEFAULT_GEMINI_BASE_URL = "https://generativelanguage.googleapis.com";
@@ -19128,6 +20346,8 @@ var isManagedBySyncSource = (document, sourceId) => document.metadata?.syncSourc
19128
20346
  var getDocumentSyncFingerprint = (document) => typeof document.metadata?.syncFingerprint === "string" ? document.metadata.syncFingerprint : undefined;
19129
20347
  var reconcileManagedDocuments = async (input) => {
19130
20348
  const prepared = prepareRAGDocuments({
20349
+ chunkingRegistry: input.chunkingRegistry,
20350
+ defaultChunking: input.defaultChunking,
19131
20351
  documents: input.documents
19132
20352
  });
19133
20353
  const nextDocumentIds = new Set(prepared.map((document) => document.documentId));
@@ -19172,6 +20392,19 @@ var toSourceRecord = (source, overrides) => ({
19172
20392
  target: source.target,
19173
20393
  ...overrides
19174
20394
  });
20395
+ var recoverSyncSourceRecord = (source, record, recoveredAt) => record.status === "running" ? toSourceRecord(source, {
20396
+ ...record,
20397
+ lastError: record.lastError ?? "Interrupted before completion during recovery",
20398
+ lastSyncedAt: recoveredAt,
20399
+ nextRetryAt: undefined,
20400
+ status: "failed"
20401
+ }) : toSourceRecord(source, {
20402
+ ...record,
20403
+ metadata: {
20404
+ ...source.metadata ?? {},
20405
+ ...record.metadata ?? {}
20406
+ }
20407
+ });
19175
20408
  var createRAGDirectorySyncSource = (options) => ({
19176
20409
  description: options.description,
19177
20410
  id: options.id,
@@ -19185,14 +20418,18 @@ var createRAGDirectorySyncSource = (options) => ({
19185
20418
  const loaded = await loadRAGDocumentsFromDirectory({
19186
20419
  baseMetadata: options.baseMetadata,
19187
20420
  defaultChunking: options.defaultChunking,
20421
+ chunkingRegistry: options.chunkingRegistry,
19188
20422
  directory: options.directory,
19189
20423
  extractors: options.extractors,
20424
+ extractorRegistry: options.extractorRegistry,
19190
20425
  includeExtensions: options.includeExtensions,
19191
20426
  recursive: options.recursive
19192
20427
  });
19193
20428
  const managedDocuments = loaded.documents.map((document) => toManagedSyncDocument(options.id, document, typeof document.metadata?.relativePath === "string" ? document.metadata.relativePath : document.source ?? document.title ?? ""));
19194
20429
  const reconciled = await reconcileManagedDocuments({
20430
+ chunkingRegistry: options.chunkingRegistry,
19195
20431
  collection,
20432
+ defaultChunking: options.defaultChunking,
19196
20433
  deleteDocument,
19197
20434
  documents: managedDocuments,
19198
20435
  listDocuments,
@@ -19223,12 +20460,16 @@ var createRAGUrlSyncSource = (options) => ({
19223
20460
  const loaded = await loadRAGDocumentsFromURLs({
19224
20461
  baseMetadata: options.baseMetadata,
19225
20462
  defaultChunking: options.defaultChunking,
20463
+ chunkingRegistry: options.chunkingRegistry,
19226
20464
  extractors: options.extractors,
20465
+ extractorRegistry: options.extractorRegistry,
19227
20466
  urls: options.urls
19228
20467
  });
19229
20468
  const managedDocuments = loaded.documents.map((document) => toManagedSyncDocument(options.id, document, typeof document.metadata?.sourceUrl === "string" ? document.metadata.sourceUrl : document.source ?? document.title ?? ""));
19230
20469
  const reconciled = await reconcileManagedDocuments({
20470
+ chunkingRegistry: options.chunkingRegistry,
19231
20471
  collection,
20472
+ defaultChunking: options.defaultChunking,
19232
20473
  deleteDocument,
19233
20474
  documents: managedDocuments,
19234
20475
  listDocuments,
@@ -19326,12 +20567,16 @@ var createRAGStorageSyncSource = (options) => ({
19326
20567
  const loaded = await loadRAGDocumentsFromUploads({
19327
20568
  baseMetadata: options.baseMetadata,
19328
20569
  defaultChunking: options.defaultChunking,
20570
+ chunkingRegistry: options.chunkingRegistry,
19329
20571
  extractors: options.extractors,
20572
+ extractorRegistry: options.extractorRegistry,
19330
20573
  uploads
19331
20574
  });
19332
20575
  const managedDocuments = loaded.documents.map((document) => toManagedSyncDocument(options.id, document, typeof document.metadata?.storageKey === "string" ? document.metadata.storageKey : document.source ?? document.title ?? ""));
19333
20576
  const reconciled = await reconcileManagedDocuments({
20577
+ chunkingRegistry: options.chunkingRegistry,
19334
20578
  collection,
20579
+ defaultChunking: options.defaultChunking,
19335
20580
  deleteDocument,
19336
20581
  documents: managedDocuments,
19337
20582
  listDocuments,
@@ -19412,7 +20657,9 @@ var createRAGEmailSyncSource = (options) => ({
19412
20657
  const loadedAttachments = attachmentUploads.length > 0 ? await loadRAGDocumentsFromUploads({
19413
20658
  baseMetadata: options.baseMetadata,
19414
20659
  defaultChunking: options.defaultChunking,
20660
+ chunkingRegistry: options.chunkingRegistry,
19415
20661
  extractors: options.extractors,
20662
+ extractorRegistry: options.extractorRegistry,
19416
20663
  uploads: attachmentUploads
19417
20664
  }) : { documents: [] };
19418
20665
  const managedDocuments = [
@@ -19420,7 +20667,9 @@ var createRAGEmailSyncSource = (options) => ({
19420
20667
  ...loadedAttachments.documents.map((document) => toManagedSyncDocument(options.id, document, `attachment:${String(document.metadata?.attachmentId ?? document.source ?? document.title ?? "")}`))
19421
20668
  ];
19422
20669
  const reconciled = await reconcileManagedDocuments({
20670
+ chunkingRegistry: options.chunkingRegistry,
19423
20671
  collection,
20672
+ defaultChunking: options.defaultChunking,
19424
20673
  deleteDocument,
19425
20674
  documents: managedDocuments,
19426
20675
  listDocuments,
@@ -19455,22 +20704,18 @@ var createRAGSyncManager = (options) => {
19455
20704
  }
19456
20705
  if (!hydrationPromise) {
19457
20706
  hydrationPromise = Promise.resolve(options.loadState()).then((records) => {
20707
+ const recoveredAt = Date.now();
19458
20708
  for (const record of records ?? []) {
19459
20709
  const source = sourceMap.get(record.id);
19460
20710
  if (!source) {
19461
20711
  continue;
19462
20712
  }
19463
- state.set(record.id, toSourceRecord(source, {
19464
- ...record,
19465
- metadata: {
19466
- ...source.metadata ?? {},
19467
- ...record.metadata ?? {}
19468
- }
19469
- }));
20713
+ state.set(record.id, recoverSyncSourceRecord(source, record, recoveredAt));
19470
20714
  }
19471
20715
  });
19472
20716
  }
19473
20717
  await hydrationPromise;
20718
+ await persistState();
19474
20719
  };
19475
20720
  const resolveRetryAttempts = (source) => Math.max(0, source.retryAttempts ?? options.retryAttempts ?? 0);
19476
20721
  const resolveRetryDelayMs = (source) => Math.max(0, source.retryDelayMs ?? options.retryDelayMs ?? 0);
@@ -19716,6 +20961,48 @@ var createRAGSyncScheduler = (input) => {
19716
20961
  listSchedules: () => [...input.schedules]
19717
20962
  };
19718
20963
  };
20964
+ // src/ai/rag/jobState.ts
20965
+ import { mkdir as mkdir3, readFile as readFile4, writeFile as writeFile3 } from "fs/promises";
20966
+ import { dirname as dirname3, resolve as resolve3 } from "path";
20967
+ var parseJobState = (content) => {
20968
+ try {
20969
+ const parsed = JSON.parse(content);
20970
+ return {
20971
+ adminActions: Array.isArray(parsed.adminActions) ? parsed.adminActions : [],
20972
+ adminJobs: Array.isArray(parsed.adminJobs) ? parsed.adminJobs : [],
20973
+ ingestJobs: Array.isArray(parsed.ingestJobs) ? parsed.ingestJobs : [],
20974
+ syncJobs: Array.isArray(parsed.syncJobs) ? parsed.syncJobs : []
20975
+ };
20976
+ } catch {
20977
+ return {
20978
+ adminActions: [],
20979
+ adminJobs: [],
20980
+ ingestJobs: [],
20981
+ syncJobs: []
20982
+ };
20983
+ }
20984
+ };
20985
+ var createRAGFileJobStateStore = (path) => {
20986
+ const resolvedPath = resolve3(path);
20987
+ return {
20988
+ load: async () => {
20989
+ try {
20990
+ return parseJobState(await readFile4(resolvedPath, "utf8"));
20991
+ } catch {
20992
+ return {
20993
+ adminActions: [],
20994
+ adminJobs: [],
20995
+ ingestJobs: [],
20996
+ syncJobs: []
20997
+ };
20998
+ }
20999
+ },
21000
+ save: async (state) => {
21001
+ await mkdir3(dirname3(resolvedPath), { recursive: true });
21002
+ await writeFile3(resolvedPath, JSON.stringify(state, null, 2), "utf8");
21003
+ }
21004
+ };
21005
+ };
19719
21006
  // src/ai/rag/adapters/utils.ts
19720
21007
  var vectorDimensionDefault = 24;
19721
21008
  var createRAGVector = (text, dimensions = vectorDimensionDefault) => {
@@ -19874,7 +21161,7 @@ import { existsSync as existsSync2 } from "fs";
19874
21161
  // src/ai/rag/resolveAbsoluteSQLiteVec.ts
19875
21162
  import { existsSync, readFileSync } from "fs";
19876
21163
  import { arch, platform } from "os";
19877
- import { dirname as dirname3, join as join2 } from "path";
21164
+ import { dirname as dirname4, join as join2 } from "path";
19878
21165
  var PLATFORM_PACKAGE_MAP = {
19879
21166
  "darwin-arm64": {
19880
21167
  libraryFile: "vec0.dylib",
@@ -19923,12 +21210,12 @@ var resolveAbsoluteSQLiteVec = () => {
19923
21210
  };
19924
21211
  }
19925
21212
  try {
19926
- const resolve3 = import.meta.resolve;
19927
- if (typeof resolve3 !== "function") {
21213
+ const resolve4 = import.meta.resolve;
21214
+ if (typeof resolve4 !== "function") {
19928
21215
  throw new Error("AbsoluteJS sqlite-vec package resolution requires import.meta.resolve support.");
19929
21216
  }
19930
- const packageJsonPath = new URL(resolve3(`${packageInfo.packageName}/package.json`)).pathname;
19931
- const packageRoot = dirname3(packageJsonPath);
21217
+ const packageJsonPath = new URL(resolve4(`${packageInfo.packageName}/package.json`)).pathname;
21218
+ const packageRoot = dirname4(packageJsonPath);
19932
21219
  const libraryPath = join2(packageRoot, packageInfo.libraryFile);
19933
21220
  const packageVersion = readPackageVersion(packageJsonPath);
19934
21221
  if (!existsSync(libraryPath)) {
@@ -20629,73 +21916,73 @@ var createConversationManager = () => {
20629
21916
  };
20630
21917
  };
20631
21918
  // src/ai/client/actions.ts
20632
- var serverMessageToAction = (msg) => {
20633
- switch (msg.type) {
21919
+ var serverMessageToAction = (message) => {
21920
+ switch (message.type) {
20634
21921
  case "chunk":
20635
21922
  return {
20636
- content: msg.content,
20637
- conversationId: msg.conversationId,
20638
- messageId: msg.messageId,
21923
+ content: message.content,
21924
+ conversationId: message.conversationId,
21925
+ messageId: message.messageId,
20639
21926
  type: "chunk"
20640
21927
  };
20641
21928
  case "thinking":
20642
21929
  return {
20643
- content: msg.content,
20644
- conversationId: msg.conversationId,
20645
- messageId: msg.messageId,
21930
+ content: message.content,
21931
+ conversationId: message.conversationId,
21932
+ messageId: message.messageId,
20646
21933
  type: "thinking"
20647
21934
  };
20648
21935
  case "tool_status":
20649
21936
  return {
20650
- conversationId: msg.conversationId,
20651
- input: msg.input,
20652
- messageId: msg.messageId,
20653
- name: msg.name,
20654
- result: msg.result,
20655
- status: msg.status,
21937
+ conversationId: message.conversationId,
21938
+ input: message.input,
21939
+ messageId: message.messageId,
21940
+ name: message.name,
21941
+ result: message.result,
21942
+ status: message.status,
20656
21943
  type: "tool_status"
20657
21944
  };
20658
21945
  case "image":
20659
21946
  return {
20660
- conversationId: msg.conversationId,
20661
- data: msg.data,
20662
- format: msg.format,
20663
- imageId: msg.imageId,
20664
- isPartial: msg.isPartial,
20665
- messageId: msg.messageId,
20666
- revisedPrompt: msg.revisedPrompt,
21947
+ conversationId: message.conversationId,
21948
+ data: message.data,
21949
+ format: message.format,
21950
+ imageId: message.imageId,
21951
+ isPartial: message.isPartial,
21952
+ messageId: message.messageId,
21953
+ revisedPrompt: message.revisedPrompt,
20667
21954
  type: "image"
20668
21955
  };
20669
21956
  case "complete":
20670
21957
  return {
20671
- conversationId: msg.conversationId,
20672
- durationMs: msg.durationMs,
20673
- messageId: msg.messageId,
20674
- model: msg.model,
20675
- sources: msg.sources,
21958
+ conversationId: message.conversationId,
21959
+ durationMs: message.durationMs,
21960
+ messageId: message.messageId,
21961
+ model: message.model,
21962
+ sources: message.sources,
20676
21963
  type: "complete",
20677
- usage: msg.usage
21964
+ usage: message.usage
20678
21965
  };
20679
21966
  case "rag_retrieving":
20680
21967
  return {
20681
- conversationId: msg.conversationId,
20682
- messageId: msg.messageId,
20683
- retrievalStartedAt: msg.retrievalStartedAt,
21968
+ conversationId: message.conversationId,
21969
+ messageId: message.messageId,
21970
+ retrievalStartedAt: message.retrievalStartedAt,
20684
21971
  type: "rag_retrieving"
20685
21972
  };
20686
21973
  case "rag_retrieved":
20687
21974
  return {
20688
- conversationId: msg.conversationId,
20689
- messageId: msg.messageId,
20690
- retrievalDurationMs: msg.retrievalDurationMs,
20691
- retrievalStartedAt: msg.retrievalStartedAt,
20692
- retrievedAt: msg.retrievedAt,
20693
- sources: msg.sources,
20694
- trace: msg.trace,
21975
+ conversationId: message.conversationId,
21976
+ messageId: message.messageId,
21977
+ retrievalDurationMs: message.retrievalDurationMs,
21978
+ retrievalStartedAt: message.retrievalStartedAt,
21979
+ retrievedAt: message.retrievedAt,
21980
+ sources: message.sources,
21981
+ trace: message.trace,
20695
21982
  type: "rag_retrieved"
20696
21983
  };
20697
21984
  case "error":
20698
- return { message: msg.message, type: "error" };
21985
+ return { message: message.message, type: "error" };
20699
21986
  default:
20700
21987
  return null;
20701
21988
  }
@@ -21154,8 +22441,8 @@ var createAIStream = (path, conversationId) => {
21154
22441
  listeners.forEach((listener) => listener());
21155
22442
  };
21156
22443
  const unsubscribeStore = store.subscribe(syncState);
21157
- const unsubscribeConnection = connection.subscribe((msg) => {
21158
- const action = serverMessageToAction(msg);
22444
+ const unsubscribeConnection = connection.subscribe((message) => {
22445
+ const action = serverMessageToAction(message);
21159
22446
  if (action) {
21160
22447
  store.dispatch(action);
21161
22448
  }
@@ -22514,6 +23801,8 @@ export {
22514
23801
  createRAGFileRetrievalComparisonHistoryStore,
22515
23802
  createRAGFileRetrievalBaselineStore,
22516
23803
  createRAGFileRetrievalBaselineGatePolicyHistoryStore,
23804
+ createRAGFileJobStateStore,
23805
+ createRAGFileExtractorRegistry,
22517
23806
  createRAGFileExtractor,
22518
23807
  createRAGFileEvaluationHistoryStore,
22519
23808
  createRAGFileAnswerGroundingEvaluationHistoryStore,
@@ -22524,9 +23813,11 @@ export {
22524
23813
  createRAGDirectorySyncSource,
22525
23814
  createRAGCollection,
22526
23815
  createRAGClient,
23816
+ createRAGChunkingRegistry,
22527
23817
  createRAGBunS3SyncClient,
22528
23818
  createRAGArchiveFileExtractor,
22529
23819
  createRAGArchiveExpander,
23820
+ createRAGAccessControl,
22530
23821
  createPDFFileExtractor,
22531
23822
  createOfficeDocumentExtractor,
22532
23823
  createMemoryStore,
@@ -22569,5 +23860,5 @@ export {
22569
23860
  aiChat
22570
23861
  };
22571
23862
 
22572
- //# debugId=2EBD38302AA26EE564756E2164756E21
23863
+ //# debugId=98118EA892F30E7564756E2164756E21
22573
23864
  //# sourceMappingURL=index.js.map