@remnic/core 9.6.28 → 9.6.30
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/access-admin-ops-surface.d.ts +2 -2
- package/dist/access-admin-ops-surface.js +2 -1
- package/dist/access-boundary.d.ts +2 -2
- package/dist/access-boundary.js +3 -2
- package/dist/access-cli.js +16 -15
- package/dist/access-cli.js.map +1 -1
- package/dist/access-http.d.ts +2 -2
- package/dist/access-http.js +7 -6
- package/dist/access-identity-continuity-surface.d.ts +1 -1
- package/dist/access-identity-continuity-surface.js +2 -1
- package/dist/access-lcm-surface.d.ts +2 -2
- package/dist/access-lcm-surface.js +2 -1
- package/dist/access-mcp.d.ts +2 -2
- package/dist/access-mcp.js +6 -5
- package/dist/access-observe-write-surface.d.ts +2 -2
- package/dist/access-observe-write-surface.js +2 -1
- package/dist/access-operations-batch.js +4 -3
- package/dist/access-operations.d.ts +2 -2
- package/dist/access-operations.js +5 -4
- package/dist/access-recall-surface.d.ts +4 -3
- package/dist/access-recall-surface.js +2 -1
- package/dist/{access-service-DK7s3wMj.d.ts → access-service-BofDzaPT.d.ts} +3 -2
- package/dist/access-service.d.ts +2 -2
- package/dist/access-service.js +2 -1
- package/dist/access-surface-catalog.d.ts +2 -2
- package/dist/bootstrap.d.ts +1 -1
- package/dist/{chunk-4273YO6I.js → chunk-3VSTTB74.js} +111 -11
- package/dist/chunk-3VSTTB74.js.map +1 -0
- package/dist/{chunk-4463LUIE.js → chunk-77UPM7P3.js} +22 -11
- package/dist/chunk-77UPM7P3.js.map +1 -0
- package/dist/{chunk-FV5FVO2J.js → chunk-7O2JL5RY.js} +9 -5
- package/dist/chunk-7O2JL5RY.js.map +1 -0
- package/dist/{chunk-LZTFCAKE.js → chunk-BFU7H32D.js} +28 -15
- package/dist/chunk-BFU7H32D.js.map +1 -0
- package/dist/chunk-C37ZRXX7.js +36 -0
- package/dist/chunk-C37ZRXX7.js.map +1 -0
- package/dist/{chunk-ARDRBJHD.js → chunk-C6TKSI35.js} +3 -3
- package/dist/{chunk-N6VIXDT7.js → chunk-DAAJUA6Q.js} +4 -4
- package/dist/{chunk-MRQN5Q3I.js → chunk-GQURC4B7.js} +2 -2
- package/dist/{chunk-4SBW47WK.js → chunk-IRMLYBMU.js} +28 -15
- package/dist/chunk-IRMLYBMU.js.map +1 -0
- package/dist/{chunk-QLYLTKUG.js → chunk-MXAGJHTA.js} +3 -3
- package/dist/{chunk-YDF4HL3W.js → chunk-OXVKR5OU.js} +35 -39
- package/dist/chunk-OXVKR5OU.js.map +1 -0
- package/dist/{chunk-GXLFDLX2.js → chunk-PUQZIS6A.js} +3 -3
- package/dist/{chunk-PYPOFEMK.js → chunk-R26WO46Z.js} +1 -1
- package/dist/chunk-R26WO46Z.js.map +1 -0
- package/dist/chunk-VDC6RZI4.js +1 -0
- package/dist/{chunk-43VNWTBB.js → chunk-VJRJTRYF.js} +4 -4
- package/dist/chunk-VJRJTRYF.js.map +1 -0
- package/dist/{chunk-L6W77GWW.js → chunk-XGSOXFD4.js} +25 -13
- package/dist/chunk-XGSOXFD4.js.map +1 -0
- package/dist/{cli-CLSdmOVU.d.ts → cli-3_x5sJTL.d.ts} +2 -2
- package/dist/cli.d.ts +3 -3
- package/dist/cli.js +8 -7
- package/dist/event-order-recall.js +3 -2
- package/dist/evidence-pack.d.ts +2 -0
- package/dist/evidence-pack.js +1 -1
- package/dist/explicit-capture.d.ts +1 -1
- package/dist/explicit-cue-recall.d.ts +3 -0
- package/dist/explicit-cue-recall.js +3 -2
- package/dist/focused-list-recall.js +3 -2
- package/dist/index.d.ts +4 -4
- package/dist/index.js +16 -15
- package/dist/lcm/index.d.ts +22 -0
- package/dist/lcm/index.js +9 -1
- package/dist/mcp-memory-inspector-app.d.ts +2 -2
- package/dist/{orchestrator-BzXGNHco.d.ts → orchestrator-CAH5-PKN.d.ts} +20 -3
- package/dist/orchestrator.d.ts +1 -1
- package/dist/orchestrator.js +16 -15
- package/dist/response-guidance-recall.js +3 -2
- package/dist/targeted-fact-recall.js +3 -2
- package/package.json +2 -2
- package/src/access-lcm-surface.ts +20 -5
- package/src/access-recall-surface.ts +48 -50
- package/src/access-service.ts +2 -7
- package/src/cli.ts +4 -10
- package/src/event-order-recall.ts +5 -3
- package/src/evidence-pack.ts +2 -0
- package/src/explicit-cue-recall.ts +67 -66
- package/src/focused-list-recall.ts +25 -16
- package/src/lcm/evidence-identity.ts +59 -0
- package/src/lcm/index.ts +7 -0
- package/src/lcm-retrieval-lineage.test.ts +236 -0
- package/src/orchestration/xray-capture-queue.test.ts +42 -0
- package/src/orchestration/xray-capture-queue.ts +78 -0
- package/src/orchestrator-xray-capture.test.ts +319 -0
- package/src/orchestrator.ts +76 -8
- package/src/response-guidance-recall.ts +22 -14
- package/src/targeted-fact-recall.ts +25 -16
- package/dist/chunk-4273YO6I.js.map +0 -1
- package/dist/chunk-43VNWTBB.js.map +0 -1
- package/dist/chunk-4463LUIE.js.map +0 -1
- package/dist/chunk-4SBW47WK.js.map +0 -1
- package/dist/chunk-FV5FVO2J.js.map +0 -1
- package/dist/chunk-HHLLAQGZ.js +0 -1
- package/dist/chunk-L6W77GWW.js.map +0 -1
- package/dist/chunk-LZTFCAKE.js.map +0 -1
- package/dist/chunk-PYPOFEMK.js.map +0 -1
- package/dist/chunk-YDF4HL3W.js.map +0 -1
- /package/dist/{chunk-ARDRBJHD.js.map → chunk-C6TKSI35.js.map} +0 -0
- /package/dist/{chunk-N6VIXDT7.js.map → chunk-DAAJUA6Q.js.map} +0 -0
- /package/dist/{chunk-MRQN5Q3I.js.map → chunk-GQURC4B7.js.map} +0 -0
- /package/dist/{chunk-QLYLTKUG.js.map → chunk-MXAGJHTA.js.map} +0 -0
- /package/dist/{chunk-GXLFDLX2.js.map → chunk-PUQZIS6A.js.map} +0 -0
- /package/dist/{chunk-HHLLAQGZ.js.map → chunk-VDC6RZI4.js.map} +0 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@remnic/core",
|
|
3
|
-
"version": "9.6.
|
|
3
|
+
"version": "9.6.30",
|
|
4
4
|
"description": "Framework-agnostic Remnic memory engine — orchestrator, storage, extraction, search, trust zones",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -3000,7 +3000,7 @@
|
|
|
3000
3000
|
"core"
|
|
3001
3001
|
],
|
|
3002
3002
|
"peerDependencies": {
|
|
3003
|
-
"@remnic/coding-graph": "^9.6.
|
|
3003
|
+
"@remnic/coding-graph": "^9.6.30"
|
|
3004
3004
|
},
|
|
3005
3005
|
"peerDependenciesMeta": {
|
|
3006
3006
|
"@remnic/coding-graph": {
|
|
@@ -11,6 +11,7 @@
|
|
|
11
11
|
import { resolveNamespaceCapabilities } from "./capabilities.js";
|
|
12
12
|
import { combineNamespaces, lcmSessionKeyForNamespace, resolveCodingNamespaceOverlay } from "./coding/coding-namespace.js";
|
|
13
13
|
import { log } from "./logger.js";
|
|
14
|
+
import { lcmEvidenceIdentity } from "./lcm/evidence-identity.js";
|
|
14
15
|
import { canWriteNamespace, defaultNamespaceForPrincipal, recallNamespacesForPrincipal } from "./namespaces/principal.js";
|
|
15
16
|
import type { Orchestrator } from "./orchestrator.js";
|
|
16
17
|
import {
|
|
@@ -207,8 +208,8 @@ export class AccessLcmSurface {
|
|
|
207
208
|
lcmEnabled: true,
|
|
208
209
|
};
|
|
209
210
|
}
|
|
210
|
-
// Query each LCM read key in order, merging + deduping
|
|
211
|
-
//
|
|
211
|
+
// Query each LCM read key in order, merging + deduping by stable archive-row
|
|
212
|
+
// identity and preserving first-seen order, capped at `limit`.
|
|
212
213
|
// Use allSettled so one corrupt/failed namespace key cannot discard sibling
|
|
213
214
|
// results from other authorized profile namespaces.
|
|
214
215
|
const seenRows = new Set<string>();
|
|
@@ -216,7 +217,14 @@ export class AccessLcmSurface {
|
|
|
216
217
|
const lcmSearches: Array<{
|
|
217
218
|
key: string | undefined;
|
|
218
219
|
prefix: string | undefined;
|
|
219
|
-
promise: Promise<
|
|
220
|
+
promise: Promise<
|
|
221
|
+
Array<{
|
|
222
|
+
id?: number;
|
|
223
|
+
session_id: string;
|
|
224
|
+
content: string;
|
|
225
|
+
turn_index: number;
|
|
226
|
+
}>
|
|
227
|
+
>;
|
|
220
228
|
}> = [];
|
|
221
229
|
for (const lcmSessionKey of lcmSessionKeyIds) {
|
|
222
230
|
for (const lcmSessionPrefix of lcmSessionPrefixes) {
|
|
@@ -228,7 +236,14 @@ export class AccessLcmSurface {
|
|
|
228
236
|
limit,
|
|
229
237
|
lcmSessionKey,
|
|
230
238
|
lcmSessionPrefix,
|
|
231
|
-
) as Promise<
|
|
239
|
+
) as Promise<
|
|
240
|
+
Array<{
|
|
241
|
+
id?: number;
|
|
242
|
+
session_id: string;
|
|
243
|
+
content: string;
|
|
244
|
+
turn_index: number;
|
|
245
|
+
}>
|
|
246
|
+
>,
|
|
232
247
|
});
|
|
233
248
|
}
|
|
234
249
|
}
|
|
@@ -246,7 +261,7 @@ export class AccessLcmSurface {
|
|
|
246
261
|
continue;
|
|
247
262
|
}
|
|
248
263
|
for (const r of settled.value) {
|
|
249
|
-
const dedupeKey =
|
|
264
|
+
const dedupeKey = lcmEvidenceIdentity(r, r.session_id).id;
|
|
250
265
|
if (seenRows.has(dedupeKey)) continue;
|
|
251
266
|
seenRows.add(dedupeKey);
|
|
252
267
|
results.push({
|
|
@@ -14,9 +14,11 @@
|
|
|
14
14
|
import { createHash } from "node:crypto";
|
|
15
15
|
import * as nodePath from "node:path";
|
|
16
16
|
import { AccessAuditAdapter, type AccessAuditResult } from "./access-audit.js";
|
|
17
|
+
import { throwIfAborted } from "./abort-error.js";
|
|
17
18
|
import { resolveNamespaceCapabilities } from "./capabilities.js";
|
|
18
19
|
import { resolveCodingNamespaceOverlay } from "./coding/coding-namespace.js";
|
|
19
20
|
import { type BudgetDecision, CrossNamespaceBudget } from "./cross-namespace-budget.js";
|
|
21
|
+
import { lcmEvidenceIdentity } from "./lcm/evidence-identity.js";
|
|
20
22
|
import { normalizeProjectionTags } from "./memory-projection-format.js";
|
|
21
23
|
import { namespaceIdentityFromToken } from "./namespaces/identity.js";
|
|
22
24
|
import { canReadNamespace, defaultNamespaceForPrincipal, recallNamespacesForPrincipal, resolvePrincipal } from "./namespaces/principal.js";
|
|
@@ -173,7 +175,6 @@ export interface AccessRecallSurfaceDeps {
|
|
|
173
175
|
primaryNamespace: string,
|
|
174
176
|
recallNamespaces?: readonly string[],
|
|
175
177
|
): Promise<{ storage: StorageManager; dir: string } | null>;
|
|
176
|
-
xrayQueue: Promise<void>;
|
|
177
178
|
}
|
|
178
179
|
|
|
179
180
|
export class AccessRecallSurface {
|
|
@@ -593,7 +594,7 @@ export class AccessRecallSurface {
|
|
|
593
594
|
if (excerpts.length >= limit) break;
|
|
594
595
|
if (result.status !== "fulfilled") continue;
|
|
595
596
|
for (const r of result.value) {
|
|
596
|
-
const dedupeKey =
|
|
597
|
+
const dedupeKey = lcmEvidenceIdentity(r, r.session_id).id;
|
|
597
598
|
if (seenRows.has(dedupeKey)) continue;
|
|
598
599
|
seenRows.add(dedupeKey);
|
|
599
600
|
excerpts.push({
|
|
@@ -1117,6 +1118,8 @@ export class AccessRecallSurface {
|
|
|
1117
1118
|
* regular X-ray API/CLI/MCP surfaces keep their existing payload shape.
|
|
1118
1119
|
*/
|
|
1119
1120
|
includeRecall?: boolean;
|
|
1121
|
+
/** Cancel the capture before it starts and propagate cancellation to recall. */
|
|
1122
|
+
abortSignal?: AbortSignal;
|
|
1120
1123
|
}): Promise<{
|
|
1121
1124
|
snapshotFound: boolean;
|
|
1122
1125
|
snapshot?: RecallXraySnapshot;
|
|
@@ -1195,21 +1198,9 @@ export class AccessRecallSurface {
|
|
|
1195
1198
|
const mode = this.deps.normalizeRecallMode(request.mode);
|
|
1196
1199
|
const disclosure = request.disclosure ?? DEFAULT_RECALL_DISCLOSURE;
|
|
1197
1200
|
|
|
1198
|
-
//
|
|
1199
|
-
//
|
|
1200
|
-
|
|
1201
|
-
// Budget and principal are now threaded through
|
|
1202
|
-
// `RecallInvocationOptions`, so global config mutation is gone
|
|
1203
|
-
// (CLAUDE.md rule 47: no shared mutable state across async
|
|
1204
|
-
// boundaries). The mutex stays only for the snapshot-slot
|
|
1205
|
-
// ordering guarantee.
|
|
1206
|
-
const previousQueue = this.deps.xrayQueue;
|
|
1207
|
-
let release: () => void = () => {};
|
|
1208
|
-
this.deps.xrayQueue = new Promise<void>((resolve) => {
|
|
1209
|
-
release = resolve;
|
|
1210
|
-
});
|
|
1211
|
-
await previousQueue;
|
|
1212
|
-
const recallStartedAt = Date.now();
|
|
1201
|
+
// Reset when the orchestrator-owned snapshot lock is actually acquired so
|
|
1202
|
+
// queue wait is not misreported as recall latency.
|
|
1203
|
+
let recallStartedAt = Date.now();
|
|
1213
1204
|
|
|
1214
1205
|
const recallSessionKey = request.sessionKey?.trim() || undefined;
|
|
1215
1206
|
let xrayResponse: {
|
|
@@ -1217,37 +1208,46 @@ export class AccessRecallSurface {
|
|
|
1217
1208
|
snapshot?: RecallXraySnapshot;
|
|
1218
1209
|
} = { snapshotFound: false };
|
|
1219
1210
|
|
|
1220
|
-
|
|
1221
|
-
//
|
|
1222
|
-
//
|
|
1223
|
-
//
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1211
|
+
{
|
|
1212
|
+
// Capture through the orchestrator-owned critical section so every
|
|
1213
|
+
// consumer of its mutable snapshot slot shares the same queue. The
|
|
1214
|
+
// returned clone remains owned by this call after the lock is released.
|
|
1215
|
+
const {
|
|
1216
|
+
snapshot: rawSnapshot,
|
|
1217
|
+
recallStartedAt: capturedRecallStartedAt,
|
|
1218
|
+
} = await this.deps.orchestrator.recallWithXrayCapture(
|
|
1219
|
+
query,
|
|
1220
|
+
recallSessionKey,
|
|
1221
|
+
{
|
|
1222
|
+
...(requestedNamespace ? { namespace: requestedNamespace } : {}),
|
|
1223
|
+
...(budgetOverride !== undefined
|
|
1224
|
+
? { budgetCharsOverride: budgetOverride }
|
|
1225
|
+
: {}),
|
|
1226
|
+
...(mode !== undefined ? { mode } : {}),
|
|
1227
|
+
// When the caller supplies an authenticated principal, forward
|
|
1228
|
+
// it via the dedicated override channel so orchestrator-side
|
|
1229
|
+
// ACL decisions use the SAME principal the access-surface
|
|
1230
|
+
// pre-check above authorized. Threading an
|
|
1231
|
+
// `authenticatedPrincipal` through `sessionKey` would be wrong:
|
|
1232
|
+
// `resolvePrincipal(sessionKey)` only maps configured raw
|
|
1233
|
+
// session keys and otherwise collapses to `"default"`, which
|
|
1234
|
+
// in namespace-enabled deployments produces false denials /
|
|
1235
|
+
// wrong-scope serving despite the pre-check passing
|
|
1236
|
+
// (CLAUDE.md rule 42).
|
|
1237
|
+
...(authenticatedPrincipal
|
|
1238
|
+
? { principalOverride: authenticatedPrincipal }
|
|
1239
|
+
: {}),
|
|
1240
|
+
...(request.currentContextScopes !== undefined
|
|
1241
|
+
? { currentContextScopes: request.currentContextScopes }
|
|
1242
|
+
: {}),
|
|
1243
|
+
...(request.abortSignal ? { abortSignal: request.abortSignal } : {}),
|
|
1244
|
+
},
|
|
1245
|
+
);
|
|
1246
|
+
recallStartedAt = capturedRecallStartedAt;
|
|
1247
|
+
// Cancellation covers queueing + recall/capture. Post-capture shaping is
|
|
1248
|
+
// intentionally not wired for mid-I/O abort in this change, but do not
|
|
1249
|
+
// begin that work after the caller has already canceled.
|
|
1250
|
+
throwIfAborted(request.abortSignal, "recall X-ray aborted before postprocessing");
|
|
1251
1251
|
// Re-check namespace after capture: the recall may have served
|
|
1252
1252
|
// from a different namespace than the caller requested. Drop
|
|
1253
1253
|
// the snapshot rather than leak cross-tenant data (CLAUDE.md
|
|
@@ -1483,8 +1483,6 @@ export class AccessRecallSurface {
|
|
|
1483
1483
|
};
|
|
1484
1484
|
}
|
|
1485
1485
|
}
|
|
1486
|
-
} finally {
|
|
1487
|
-
release();
|
|
1488
1486
|
}
|
|
1489
1487
|
|
|
1490
1488
|
if (
|
package/src/access-service.ts
CHANGED
|
@@ -2917,6 +2917,8 @@ export class EngramAccessService {
|
|
|
2917
2917
|
* regular X-ray API/CLI/MCP surfaces keep their existing payload shape.
|
|
2918
2918
|
*/
|
|
2919
2919
|
includeRecall?: boolean;
|
|
2920
|
+
/** Cancel the capture before it starts and propagate cancellation to recall. */
|
|
2921
|
+
abortSignal?: AbortSignal;
|
|
2920
2922
|
}): Promise<{
|
|
2921
2923
|
snapshotFound: boolean;
|
|
2922
2924
|
snapshot?: RecallXraySnapshot;
|
|
@@ -2926,12 +2928,6 @@ export class EngramAccessService {
|
|
|
2926
2928
|
request,
|
|
2927
2929
|
);
|
|
2928
2930
|
}
|
|
2929
|
-
// Sequence lock for `recallXray` — see comment inside the method.
|
|
2930
|
-
// Lives on the instance so every x-ray call on the same service
|
|
2931
|
-
// shares it, and so separate services in the same process (e.g.
|
|
2932
|
-
// per-tenant) do not block each other.
|
|
2933
|
-
private xrayQueue: Promise<void> = Promise.resolve();
|
|
2934
|
-
|
|
2935
2931
|
async memoryStore(
|
|
2936
2932
|
request: EngramAccessMemoryStoreRequest,
|
|
2937
2933
|
hooks?: { enforceWriteQuota?: () => void | Promise<void> },
|
|
@@ -5906,4 +5902,3 @@ export class EngramAccessService {
|
|
|
5906
5902
|
);
|
|
5907
5903
|
}
|
|
5908
5904
|
}
|
|
5909
|
-
|
package/src/cli.ts
CHANGED
|
@@ -5756,16 +5756,10 @@ export function registerCli(
|
|
|
5756
5756
|
args[0],
|
|
5757
5757
|
(args[1] ?? {}) as Record<string, unknown>,
|
|
5758
5758
|
);
|
|
5759
|
-
//
|
|
5760
|
-
//
|
|
5761
|
-
//
|
|
5762
|
-
//
|
|
5763
|
-
// sequence races with concurrent callers (e.g., a gateway
|
|
5764
|
-
// agent hitting the same orchestrator) and could swap in
|
|
5765
|
-
// their snapshot mid-flight, or our capture could overwrite
|
|
5766
|
-
// theirs (cursor Medium + codex P1 review on #597). The
|
|
5767
|
-
// service enforces CLAUDE.md rules 40 (serialized state) and
|
|
5768
|
-
// 47 (no shared mutable state across async boundaries).
|
|
5759
|
+
// Delegate through `EngramAccessService` to the orchestrator's atomic
|
|
5760
|
+
// capture API, so CLI, HTTP, and MCP callers sharing an orchestrator
|
|
5761
|
+
// also share one X-ray ordering domain and receive the snapshot owned
|
|
5762
|
+
// by their invocation.
|
|
5769
5763
|
const xrayService = new EngramAccessService(orchestrator);
|
|
5770
5764
|
const response = await xrayService.recallXray({
|
|
5771
5765
|
query: parsed.query,
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { buildEvidencePack, type EvidencePackItem } from "./evidence-pack.js";
|
|
2
2
|
import type { ExplicitCueRecallEngine } from "./explicit-cue-recall.js";
|
|
3
|
+
import { lcmEvidenceIdentity } from "./lcm/evidence-identity.js";
|
|
3
4
|
|
|
4
5
|
import {
|
|
5
6
|
unifiedDedupeAndRank,
|
|
@@ -205,13 +206,14 @@ async function collectEventOrderItems(
|
|
|
205
206
|
windowTokens,
|
|
206
207
|
);
|
|
207
208
|
for (const message of messages) {
|
|
208
|
-
const
|
|
209
|
+
const identity = lcmEvidenceIdentity(message, options.sessionId);
|
|
210
|
+
const id = identity.id;
|
|
209
211
|
if (seen.has(id)) continue;
|
|
210
212
|
if (!isEventOrderCandidate(message.content, message.role, options.query)) continue;
|
|
211
213
|
seen.add(id);
|
|
212
214
|
items.push({
|
|
213
|
-
|
|
214
|
-
sessionId: options.sessionId,
|
|
215
|
+
...identity,
|
|
216
|
+
sessionId: message.session_id ?? options.sessionId,
|
|
215
217
|
turnIndex: message.turn_index,
|
|
216
218
|
role: message.role,
|
|
217
219
|
content: appendChronologicalCues(message.content, options.query),
|
package/src/evidence-pack.ts
CHANGED
|
@@ -4,6 +4,10 @@ import {
|
|
|
4
4
|
resolveLcmReadSessionIds,
|
|
5
5
|
} from "./lcm-fallback-read.js";
|
|
6
6
|
import { unifiedDedupeAndRank } from "./recall-pipeline-stages.js";
|
|
7
|
+
import {
|
|
8
|
+
lcmArchiveRowId,
|
|
9
|
+
lcmEvidenceIdentity,
|
|
10
|
+
} from "./lcm/evidence-identity.js";
|
|
7
11
|
|
|
8
12
|
export interface ExplicitCueRecallEngine {
|
|
9
13
|
expandContext(
|
|
@@ -11,7 +15,15 @@ export interface ExplicitCueRecallEngine {
|
|
|
11
15
|
fromTurn: number,
|
|
12
16
|
toTurn: number,
|
|
13
17
|
maxTokens: number,
|
|
14
|
-
): Promise<
|
|
18
|
+
): Promise<
|
|
19
|
+
Array<{
|
|
20
|
+
id?: number;
|
|
21
|
+
session_id?: string;
|
|
22
|
+
turn_index: number;
|
|
23
|
+
role: string;
|
|
24
|
+
content: string;
|
|
25
|
+
}>
|
|
26
|
+
>;
|
|
15
27
|
getStats?(sessionId?: string): Promise<{
|
|
16
28
|
totalMessages: number;
|
|
17
29
|
maxTurnIndex?: number;
|
|
@@ -22,6 +34,7 @@ export interface ExplicitCueRecallEngine {
|
|
|
22
34
|
sessionId?: string,
|
|
23
35
|
): Promise<
|
|
24
36
|
Array<{
|
|
37
|
+
id?: number;
|
|
25
38
|
turn_index: number;
|
|
26
39
|
role: string;
|
|
27
40
|
content: string;
|
|
@@ -62,6 +75,16 @@ export type ExplicitTurnReference = {
|
|
|
62
75
|
includeDirectTurn: boolean;
|
|
63
76
|
};
|
|
64
77
|
|
|
78
|
+
interface ExplicitCueEvidenceItem {
|
|
79
|
+
id: string;
|
|
80
|
+
archiveRowId?: number;
|
|
81
|
+
sessionId: string;
|
|
82
|
+
turnIndex: number;
|
|
83
|
+
role: string;
|
|
84
|
+
content: string;
|
|
85
|
+
score?: number;
|
|
86
|
+
}
|
|
87
|
+
|
|
65
88
|
const DEFAULT_MAX_CHARS = 2_400;
|
|
66
89
|
const DEFAULT_MAX_ITEM_CHARS = 1_200;
|
|
67
90
|
const DEFAULT_MAX_REFERENCES = 24;
|
|
@@ -326,23 +349,17 @@ export async function buildExplicitCueRecallSection(
|
|
|
326
349
|
return "";
|
|
327
350
|
}
|
|
328
351
|
|
|
329
|
-
const evidenceItems:
|
|
330
|
-
id: string;
|
|
331
|
-
sessionId: string;
|
|
332
|
-
turnIndex: number;
|
|
333
|
-
role: string;
|
|
334
|
-
content: string;
|
|
335
|
-
score?: number;
|
|
336
|
-
}> = [];
|
|
352
|
+
const evidenceItems: ExplicitCueEvidenceItem[] = [];
|
|
337
353
|
const seenTurns = new Set<string>();
|
|
338
354
|
|
|
339
355
|
// #1505 codex P2: gather cue evidence across the ordered LCM read key set
|
|
340
356
|
// (primary overlay → project/root fallbacks) into ONE shared accumulator
|
|
341
357
|
// (`evidenceItems` / `seenTurns`), so a branch-scoped session's project/root
|
|
342
358
|
// fallback cues are RECOVERED instead of being skipped by the old
|
|
343
|
-
// first-non-empty short-circuit. `seenTurns`
|
|
344
|
-
//
|
|
345
|
-
// `
|
|
359
|
+
// first-non-empty short-circuit. `seenTurns` is the historical local name;
|
|
360
|
+
// its keys use archive-row identity when available and fall back to
|
|
361
|
+
// `session_id`+`turn_index` for legacy engines. The budget is applied exactly
|
|
362
|
+
// once in the `buildEvidencePack` call below. `gatherAcrossReadSessions` isolates a
|
|
346
363
|
// per-key read failure (a corrupt/locked fallback index must not discard the
|
|
347
364
|
// other keys' cues); single-key recall runs each collector directly, so a
|
|
348
365
|
// failure propagates exactly as before.
|
|
@@ -510,13 +527,7 @@ async function collectTurnReferenceEvidence(options: {
|
|
|
510
527
|
sessionId?: string;
|
|
511
528
|
query: string;
|
|
512
529
|
maxReferences: number;
|
|
513
|
-
evidenceItems:
|
|
514
|
-
id: string;
|
|
515
|
-
sessionId: string;
|
|
516
|
-
turnIndex: number;
|
|
517
|
-
role: string;
|
|
518
|
-
content: string;
|
|
519
|
-
}>;
|
|
530
|
+
evidenceItems: ExplicitCueEvidenceItem[];
|
|
520
531
|
seenTurns: Set<string>;
|
|
521
532
|
}): Promise<void> {
|
|
522
533
|
if (!options.sessionId) {
|
|
@@ -575,13 +586,7 @@ async function collectFocusedTranscriptCueEvidence(options: {
|
|
|
575
586
|
engine: ExplicitCueRecallEngine;
|
|
576
587
|
sessionId?: string;
|
|
577
588
|
query: string;
|
|
578
|
-
evidenceItems:
|
|
579
|
-
id: string;
|
|
580
|
-
sessionId: string;
|
|
581
|
-
turnIndex: number;
|
|
582
|
-
role: string;
|
|
583
|
-
content: string;
|
|
584
|
-
}>;
|
|
589
|
+
evidenceItems: ExplicitCueEvidenceItem[];
|
|
585
590
|
seenTurns: Set<string>;
|
|
586
591
|
}): Promise<void> {
|
|
587
592
|
if (!options.sessionId || !options.engine.getStats) {
|
|
@@ -611,8 +616,8 @@ async function collectFocusedTranscriptCueEvidence(options: {
|
|
|
611
616
|
continue;
|
|
612
617
|
}
|
|
613
618
|
appendEvidenceItem(options.evidenceItems, options.seenTurns, {
|
|
614
|
-
|
|
615
|
-
sessionId: options.sessionId,
|
|
619
|
+
...lcmEvidenceIdentity(message, options.sessionId),
|
|
620
|
+
sessionId: message.session_id ?? options.sessionId,
|
|
616
621
|
turnIndex: message.turn_index,
|
|
617
622
|
role: message.role,
|
|
618
623
|
content: message.content,
|
|
@@ -772,13 +777,7 @@ async function collectContentLabelReferenceEvidence(options: {
|
|
|
772
777
|
sessionId?: string;
|
|
773
778
|
query: string;
|
|
774
779
|
references: ExplicitTurnReference[];
|
|
775
|
-
evidenceItems:
|
|
776
|
-
id: string;
|
|
777
|
-
sessionId: string;
|
|
778
|
-
turnIndex: number;
|
|
779
|
-
role: string;
|
|
780
|
-
content: string;
|
|
781
|
-
}>;
|
|
780
|
+
evidenceItems: ExplicitCueEvidenceItem[];
|
|
782
781
|
seenTurns: Set<string>;
|
|
783
782
|
}): Promise<Set<number>> {
|
|
784
783
|
const resolved = new Set<number>();
|
|
@@ -818,7 +817,7 @@ async function collectContentLabelReferenceEvidence(options: {
|
|
|
818
817
|
}
|
|
819
818
|
if (expanded.length === 0) {
|
|
820
819
|
appendEvidenceItem(options.evidenceItems, options.seenTurns, {
|
|
821
|
-
|
|
820
|
+
...lcmEvidenceIdentity(hit, hit.session_id),
|
|
822
821
|
sessionId: hit.session_id,
|
|
823
822
|
turnIndex: hit.turn_index,
|
|
824
823
|
role: hit.role,
|
|
@@ -845,6 +844,7 @@ async function searchReferenceContentLabels(
|
|
|
845
844
|
sessionId?: string,
|
|
846
845
|
): Promise<
|
|
847
846
|
Array<{
|
|
847
|
+
id?: number;
|
|
848
848
|
turn_index: number;
|
|
849
849
|
role: string;
|
|
850
850
|
content: string;
|
|
@@ -855,6 +855,7 @@ async function searchReferenceContentLabels(
|
|
|
855
855
|
const hits = new Map<
|
|
856
856
|
string,
|
|
857
857
|
{
|
|
858
|
+
id?: number;
|
|
858
859
|
turn_index: number;
|
|
859
860
|
role: string;
|
|
860
861
|
content: string;
|
|
@@ -878,7 +879,8 @@ async function searchReferenceContentLabels(
|
|
|
878
879
|
) {
|
|
879
880
|
continue;
|
|
880
881
|
}
|
|
881
|
-
hits.set(`${result.session_id
|
|
882
|
+
hits.set(`${lcmEvidenceIdentity(result, result.session_id).id}:${labelKind}`, {
|
|
883
|
+
...(typeof result.id === "number" ? { id: result.id } : {}),
|
|
882
884
|
turn_index: result.turn_index,
|
|
883
885
|
role: result.role,
|
|
884
886
|
content: result.content,
|
|
@@ -1054,14 +1056,7 @@ async function collectLexicalCueEvidence(options: {
|
|
|
1054
1056
|
includeBenchmarkAnchorCues?: boolean;
|
|
1055
1057
|
includeContentLexicalCues?: boolean;
|
|
1056
1058
|
includeStructuredPlanCues?: boolean;
|
|
1057
|
-
evidenceItems:
|
|
1058
|
-
id: string;
|
|
1059
|
-
sessionId: string;
|
|
1060
|
-
turnIndex: number;
|
|
1061
|
-
role: string;
|
|
1062
|
-
content: string;
|
|
1063
|
-
score?: number;
|
|
1064
|
-
}>;
|
|
1059
|
+
evidenceItems: ExplicitCueEvidenceItem[];
|
|
1065
1060
|
seenTurns: Set<string>;
|
|
1066
1061
|
}): Promise<void> {
|
|
1067
1062
|
const cues = prioritizeLexicalCueSearchCues(
|
|
@@ -1096,7 +1091,7 @@ async function collectLexicalCueEvidence(options: {
|
|
|
1096
1091
|
);
|
|
1097
1092
|
if (expanded.length === 0) {
|
|
1098
1093
|
appendEvidenceItem(options.evidenceItems, options.seenTurns, {
|
|
1099
|
-
|
|
1094
|
+
...lcmEvidenceIdentity(result, result.session_id),
|
|
1100
1095
|
sessionId: result.session_id,
|
|
1101
1096
|
turnIndex: result.turn_index,
|
|
1102
1097
|
role: result.role,
|
|
@@ -1107,7 +1102,7 @@ async function collectLexicalCueEvidence(options: {
|
|
|
1107
1102
|
}
|
|
1108
1103
|
if (exactHitFirst) {
|
|
1109
1104
|
appendEvidenceItem(options.evidenceItems, options.seenTurns, {
|
|
1110
|
-
|
|
1105
|
+
...lcmEvidenceIdentity(result, result.session_id),
|
|
1111
1106
|
sessionId: result.session_id,
|
|
1112
1107
|
turnIndex: result.turn_index,
|
|
1113
1108
|
role: result.role,
|
|
@@ -1131,6 +1126,8 @@ interface LabeledTrajectoryStep {
|
|
|
1131
1126
|
observation?: string;
|
|
1132
1127
|
actionTurnIndex?: number;
|
|
1133
1128
|
observationTurnIndex?: number;
|
|
1129
|
+
actionArchiveRowId?: number;
|
|
1130
|
+
observationArchiveRowId?: number;
|
|
1134
1131
|
}
|
|
1135
1132
|
|
|
1136
1133
|
interface TrajectoryBounds {
|
|
@@ -1187,7 +1184,12 @@ function hasTrajectoryAnalysisIntent(query: string): boolean {
|
|
|
1187
1184
|
}
|
|
1188
1185
|
|
|
1189
1186
|
function parseLabeledTrajectory(
|
|
1190
|
-
messages: Array<{
|
|
1187
|
+
messages: Array<{
|
|
1188
|
+
id?: number;
|
|
1189
|
+
turn_index: number;
|
|
1190
|
+
role: string;
|
|
1191
|
+
content: string;
|
|
1192
|
+
}>,
|
|
1191
1193
|
): LabeledTrajectoryStep[] {
|
|
1192
1194
|
const byStep = new Map<number, LabeledTrajectoryStep>();
|
|
1193
1195
|
|
|
@@ -1197,6 +1199,8 @@ function parseLabeledTrajectory(
|
|
|
1197
1199
|
const step = getOrCreateTrajectoryStep(byStep, action.step);
|
|
1198
1200
|
step.action = action.value;
|
|
1199
1201
|
step.actionTurnIndex = message.turn_index;
|
|
1202
|
+
const archiveRowId = lcmArchiveRowId(message);
|
|
1203
|
+
if (archiveRowId !== undefined) step.actionArchiveRowId = archiveRowId;
|
|
1200
1204
|
continue;
|
|
1201
1205
|
}
|
|
1202
1206
|
|
|
@@ -1205,6 +1209,10 @@ function parseLabeledTrajectory(
|
|
|
1205
1209
|
const step = getOrCreateTrajectoryStep(byStep, observation.step);
|
|
1206
1210
|
step.observation = observation.value;
|
|
1207
1211
|
step.observationTurnIndex = message.turn_index;
|
|
1212
|
+
const archiveRowId = lcmArchiveRowId(message);
|
|
1213
|
+
if (archiveRowId !== undefined) {
|
|
1214
|
+
step.observationArchiveRowId = archiveRowId;
|
|
1215
|
+
}
|
|
1208
1216
|
}
|
|
1209
1217
|
}
|
|
1210
1218
|
|
|
@@ -4538,21 +4546,21 @@ function normalizeTrajectoryQuery(value: string): string {
|
|
|
4538
4546
|
}
|
|
4539
4547
|
|
|
4540
4548
|
function appendExpandedEvidence(
|
|
4541
|
-
evidenceItems:
|
|
4542
|
-
|
|
4543
|
-
|
|
4544
|
-
|
|
4549
|
+
evidenceItems: ExplicitCueEvidenceItem[],
|
|
4550
|
+
seenTurns: Set<string>,
|
|
4551
|
+
sessionId: string,
|
|
4552
|
+
expanded: Array<{
|
|
4553
|
+
id?: number;
|
|
4554
|
+
session_id?: string;
|
|
4555
|
+
turn_index: number;
|
|
4545
4556
|
role: string;
|
|
4546
4557
|
content: string;
|
|
4547
4558
|
}>,
|
|
4548
|
-
seenTurns: Set<string>,
|
|
4549
|
-
sessionId: string,
|
|
4550
|
-
expanded: Array<{ turn_index: number; role: string; content: string }>,
|
|
4551
4559
|
): void {
|
|
4552
4560
|
for (const message of expanded) {
|
|
4553
4561
|
appendEvidenceItem(evidenceItems, seenTurns, {
|
|
4554
|
-
|
|
4555
|
-
sessionId,
|
|
4562
|
+
...lcmEvidenceIdentity(message, sessionId),
|
|
4563
|
+
sessionId: message.session_id ?? sessionId,
|
|
4556
4564
|
turnIndex: message.turn_index,
|
|
4557
4565
|
role: message.role,
|
|
4558
4566
|
content: message.content,
|
|
@@ -4689,14 +4697,7 @@ async function collectNamedMeetingFactEvidence(options: {
|
|
|
4689
4697
|
sessionId?: string;
|
|
4690
4698
|
query: string;
|
|
4691
4699
|
maxReferences: number;
|
|
4692
|
-
evidenceItems:
|
|
4693
|
-
id: string;
|
|
4694
|
-
sessionId: string;
|
|
4695
|
-
turnIndex: number;
|
|
4696
|
-
role: string;
|
|
4697
|
-
content: string;
|
|
4698
|
-
score?: number;
|
|
4699
|
-
}>;
|
|
4700
|
+
evidenceItems: ExplicitCueEvidenceItem[];
|
|
4700
4701
|
seenTurns: Set<string>;
|
|
4701
4702
|
}): Promise<void> {
|
|
4702
4703
|
const cues = collectMeetingFactCues(options.query)
|
|
@@ -4712,7 +4713,7 @@ async function collectNamedMeetingFactEvidence(options: {
|
|
|
4712
4713
|
for (const result of results) {
|
|
4713
4714
|
if (!isNamedMeetingFactEvidence(result.content, options.query)) continue;
|
|
4714
4715
|
appendEvidenceItem(options.evidenceItems, options.seenTurns, {
|
|
4715
|
-
|
|
4716
|
+
...lcmEvidenceIdentity(result, result.session_id),
|
|
4716
4717
|
sessionId: result.session_id,
|
|
4717
4718
|
turnIndex: result.turn_index,
|
|
4718
4719
|
role: result.role,
|