@remnic/core 9.3.642 → 9.3.643

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/src/access-mcp.ts CHANGED
@@ -715,6 +715,11 @@ export class EngramMcpServer {
715
715
  description:
716
716
  "Optional current user-context scopes, such as repo, work, personal, client, or private.",
717
717
  },
718
+ allowUnverifiedPreview: {
719
+ type: "boolean",
720
+ description:
721
+ "If true, the inspector may show recalled preview text when X-ray provenance is missing or unavailable. Explicitly blocked memories remain redacted.",
722
+ },
718
723
  },
719
724
  required: ["query"],
720
725
  additionalProperties: false,
@@ -2608,6 +2613,17 @@ export class EngramMcpServer {
2608
2613
  if (currentContextScopes !== undefined) {
2609
2614
  input.currentContextScopes = currentContextScopes;
2610
2615
  }
2616
+ if (
2617
+ args.allowUnverifiedPreview !== undefined &&
2618
+ args.allowUnverifiedPreview !== null
2619
+ ) {
2620
+ if (typeof args.allowUnverifiedPreview !== "boolean") {
2621
+ throw new EngramAccessInputError(
2622
+ "allowUnverifiedPreview must be a boolean",
2623
+ );
2624
+ }
2625
+ input.allowUnverifiedPreview = args.allowUnverifiedPreview;
2626
+ }
2611
2627
  const recallSessionKey = resolveChatGptInspectorRecallSessionKey(
2612
2628
  input.sessionKey,
2613
2629
  effectivePrincipal,
@@ -18,6 +18,7 @@ export interface RemnicChatGptMemoryInspectorInput {
18
18
  sessionKey?: string;
19
19
  namespace?: string;
20
20
  currentContextScopes?: string[];
21
+ allowUnverifiedPreview?: boolean;
21
22
  }
22
23
 
23
24
  export interface RemnicChatGptMemoryCard {
@@ -115,21 +116,42 @@ export function buildChatGptMemoryInspectorResult(
115
116
  actionConfidence: ActionConfidenceResult,
116
117
  ): RemnicChatGptMemoryInspectorResult {
117
118
  const xrayUnavailable = xray === null;
118
- const matchXrayResult = buildXrayResultMatcher(xray);
119
+ const allowUnverifiedPreview = input.allowUnverifiedPreview === true;
120
+ const matchXrayResult = buildXrayResultMatcher(xray, recall.results);
119
121
  const matchedXrayResults = recall.results.map(matchXrayResult);
122
+ const hasBlockedSameId = buildBlockedSameIdMatcher(xray);
120
123
 
121
124
  const memories = recall.results.slice(0, 8).map((summary) => {
122
125
  const xrayResult = matchXrayResult(summary);
123
126
  const provenance = xrayResult?.provenance;
124
- const unverified = !xrayUnavailable && provenance === undefined;
125
- const blocked = provenance?.safety === "blocked";
127
+ const blockedByAmbiguousSameId = !xrayUnavailable
128
+ && xrayResult === undefined
129
+ && hasBlockedSameId(summary.id);
130
+ const blocked = provenance?.safety === "blocked" || blockedByAmbiguousSameId;
131
+ const unverified = !xrayUnavailable && provenance === undefined && !blockedByAmbiguousSameId;
126
132
  const preview = xrayUnavailable
127
- ? "Preview withheld: X-ray provenance was unavailable for this recall."
128
- : unverified
129
- ? "Preview withheld: X-ray provenance was missing for this memory."
133
+ ? allowUnverifiedPreview
134
+ ? summary.preview
135
+ : "Preview withheld: X-ray provenance was unavailable for this recall."
130
136
  : blocked
131
137
  ? "Preview withheld: this memory is blocked in the current context."
132
- : summary.preview;
138
+ : unverified
139
+ ? allowUnverifiedPreview
140
+ ? summary.preview
141
+ : "Preview withheld: X-ray provenance was missing for this memory."
142
+ : summary.preview;
143
+ const fallbackSafety = blockedByAmbiguousSameId
144
+ ? "blocked"
145
+ : xrayUnavailable || unverified
146
+ ? allowUnverifiedPreview
147
+ ? "requires-review"
148
+ : "blocked"
149
+ : undefined;
150
+ const fallbackSafetyReason = xrayUnavailable
151
+ ? "X-ray provenance was unavailable for this recall."
152
+ : blockedByAmbiguousSameId
153
+ ? "An unmatched X-ray row with this memory id was blocked."
154
+ : "X-ray provenance was missing for this memory.";
133
155
  return {
134
156
  id: summary.id,
135
157
  path: summary.path,
@@ -144,25 +166,43 @@ export function buildChatGptMemoryInspectorResult(
144
166
  confidence: provenance?.confidence,
145
167
  stale: provenance?.stale,
146
168
  corrected: provenance?.corrected,
147
- safeToUse: provenance?.safeToUse ?? (unverified ? false : undefined),
148
- safety: provenance?.safety ?? (unverified ? "blocked" : undefined),
169
+ safeToUse: provenance?.safeToUse
170
+ ?? (xrayUnavailable || unverified || blockedByAmbiguousSameId ? false : undefined),
171
+ safety: provenance?.safety ?? fallbackSafety,
149
172
  safetyReasons: provenance?.safetyReasons
150
- ?? (unverified ? ["X-ray provenance was missing for this memory."] : []),
173
+ ?? (
174
+ xrayUnavailable || unverified || blockedByAmbiguousSameId
175
+ ? [fallbackSafetyReason]
176
+ : []
177
+ ),
151
178
  userContextScopes: provenance?.userContextScopes ?? [],
152
179
  };
153
180
  });
154
181
  const blockedCount = matchedXrayResults
155
182
  .filter((result) => result?.provenance?.safety === "blocked")
156
183
  .length;
184
+ const ambiguousBlockedCount = recall.results
185
+ .filter((summary, index) =>
186
+ matchedXrayResults[index] === undefined && hasBlockedSameId(summary.id)
187
+ )
188
+ .length;
157
189
  const missingProvenanceCount = xrayUnavailable
158
190
  ? 0
159
191
  : matchedXrayResults
160
- .filter((result) => result?.provenance === undefined)
192
+ .filter((result, index) =>
193
+ result?.provenance === undefined
194
+ && !(result === undefined && hasBlockedSameId(recall.results[index]?.id ?? ""))
195
+ )
161
196
  .length;
197
+ const totalBlockedCount = blockedCount + ambiguousBlockedCount;
162
198
  const safeRecallPreview = xrayUnavailable
163
- ? "Recall preview withheld: X-ray provenance was unavailable, so memory safety could not be verified."
164
- : blockedCount > 0 || missingProvenanceCount > 0
165
- ? formatUnsafeRecallPreview(blockedCount, missingProvenanceCount)
199
+ ? allowUnverifiedPreview
200
+ ? `Unverified recall preview: X-ray provenance was unavailable, so memory safety could not be verified.\n\n${truncate(recall.context, 1_500)}`
201
+ : "Recall preview withheld: X-ray provenance was unavailable, so memory safety could not be verified."
202
+ : totalBlockedCount > 0 || missingProvenanceCount > 0
203
+ ? totalBlockedCount > 0 || !allowUnverifiedPreview
204
+ ? formatUnsafeRecallPreview(totalBlockedCount, missingProvenanceCount)
205
+ : `Unverified recall preview: ${missingProvenanceCount} retrieved ${memoryNoun(missingProvenanceCount)} ${isAre(missingProvenanceCount)} missing X-ray provenance.\n\n${truncate(recall.context, 1_500)}`
166
206
  : truncate(recall.context, 1_500);
167
207
 
168
208
  const primaryMemoryId = memories[0]?.id ?? "<memory-id>";
@@ -372,7 +412,7 @@ function buildRecallProvenances(
372
412
  if (xray === null) {
373
413
  return recall.results.map(missingRecallProvenance);
374
414
  }
375
- const matchXrayResult = buildXrayResultMatcher(xray);
415
+ const matchXrayResult = buildXrayResultMatcher(xray, recall.results);
376
416
  return recall.results.map((summary) => {
377
417
  const result = matchXrayResult(summary);
378
418
  if (result === undefined) {
@@ -384,21 +424,43 @@ function buildRecallProvenances(
384
424
 
385
425
  function buildXrayResultMatcher(
386
426
  xray: RecallXraySnapshot | null,
427
+ recallResults: EngramAccessRecallResponse["results"],
387
428
  ): (summary: EngramAccessRecallResponse["results"][number]) => RecallXrayResult | undefined {
388
429
  const xrayById = new Map<string, RecallXrayResult>();
430
+ const xrayIdCounts = new Map<string, number>();
431
+ const recallIdCounts = new Map<string, number>();
389
432
  const xrayByPath = new Map<string, RecallXrayResult>();
433
+ for (const summary of recallResults) {
434
+ recallIdCounts.set(summary.id, (recallIdCounts.get(summary.id) ?? 0) + 1);
435
+ }
390
436
  for (const result of xray?.results ?? []) {
391
437
  xrayById.set(result.memoryId, result);
438
+ xrayIdCounts.set(result.memoryId, (xrayIdCounts.get(result.memoryId) ?? 0) + 1);
392
439
  xrayByPath.set(result.path, result);
393
440
  }
394
441
  return (summary) => {
395
442
  if (summary.path) {
396
- return xrayByPath.get(summary.path);
443
+ const pathMatch = xrayByPath.get(summary.path);
444
+ if (pathMatch) return pathMatch;
397
445
  }
398
- return xrayById.get(summary.id);
446
+ return xrayIdCounts.get(summary.id) === 1 && recallIdCounts.get(summary.id) === 1
447
+ ? xrayById.get(summary.id)
448
+ : undefined;
399
449
  };
400
450
  }
401
451
 
452
+ function buildBlockedSameIdMatcher(
453
+ xray: RecallXraySnapshot | null,
454
+ ): (memoryId: string) => boolean {
455
+ const blockedIds = new Set<string>();
456
+ for (const result of xray?.results ?? []) {
457
+ if (result.provenance?.safety === "blocked") {
458
+ blockedIds.add(result.memoryId);
459
+ }
460
+ }
461
+ return (memoryId) => blockedIds.has(memoryId);
462
+ }
463
+
402
464
  function missingProvenance(result: RecallXrayResult): RetrievedMemoryProvenance {
403
465
  return {
404
466
  source: "unknown",