@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
|
@@ -0,0 +1,319 @@
|
|
|
1
|
+
import assert from "node:assert/strict";
|
|
2
|
+
import { tmpdir } from "node:os";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import test from "node:test";
|
|
5
|
+
|
|
6
|
+
import { parseConfig } from "./config.js";
|
|
7
|
+
import { Orchestrator } from "./orchestrator.js";
|
|
8
|
+
import { buildXraySnapshot, type RecallXraySnapshot } from "./recall-xray.js";
|
|
9
|
+
|
|
10
|
+
let orchestratorSequence = 0;
|
|
11
|
+
|
|
12
|
+
function makeOrchestrator(): Orchestrator {
|
|
13
|
+
orchestratorSequence += 1;
|
|
14
|
+
const memoryDir = path.join(
|
|
15
|
+
tmpdir(),
|
|
16
|
+
`remnic-xray-capture-unit-${process.pid}-${orchestratorSequence}`,
|
|
17
|
+
);
|
|
18
|
+
return new Orchestrator(parseConfig({
|
|
19
|
+
memoryDir,
|
|
20
|
+
workspaceDir: memoryDir,
|
|
21
|
+
qmdEnabled: false,
|
|
22
|
+
embeddingFallbackEnabled: false,
|
|
23
|
+
}));
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function snapshot(query: string, snapshotId: string): RecallXraySnapshot {
|
|
27
|
+
return buildXraySnapshot({
|
|
28
|
+
query,
|
|
29
|
+
snapshotIdGenerator: () => snapshotId,
|
|
30
|
+
now: () => 1,
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function setSnapshot(orchestrator: Orchestrator, value: RecallXraySnapshot): void {
|
|
35
|
+
(orchestrator as unknown as { lastXraySnapshot: RecallXraySnapshot | null })
|
|
36
|
+
.lastXraySnapshot = value;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function stubRecall(
|
|
40
|
+
orchestrator: Orchestrator,
|
|
41
|
+
invoke: (
|
|
42
|
+
prompt: string,
|
|
43
|
+
sessionKey: string | undefined,
|
|
44
|
+
options: Record<string, unknown>,
|
|
45
|
+
) => Promise<string>,
|
|
46
|
+
): void {
|
|
47
|
+
(orchestrator as unknown as { invokeRecall: typeof invoke }).invokeRecall = invoke;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function deferred<T>(): {
|
|
51
|
+
promise: Promise<T>;
|
|
52
|
+
resolve: (value: T) => void;
|
|
53
|
+
reject: (reason?: unknown) => void;
|
|
54
|
+
} {
|
|
55
|
+
let resolve!: (value: T) => void;
|
|
56
|
+
let reject!: (reason?: unknown) => void;
|
|
57
|
+
const promise = new Promise<T>((res, rej) => {
|
|
58
|
+
resolve = res;
|
|
59
|
+
reject = rej;
|
|
60
|
+
});
|
|
61
|
+
return { promise, resolve, reject };
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
test("recallWithXrayCapture clears stale state and returns an owned nested clone", async () => {
|
|
65
|
+
const orchestrator = makeOrchestrator();
|
|
66
|
+
setSnapshot(orchestrator, snapshot("stale", "stale-id"));
|
|
67
|
+
const captured = buildXraySnapshot({
|
|
68
|
+
query: "normalized query",
|
|
69
|
+
snapshotIdGenerator: () => "fresh-id",
|
|
70
|
+
now: () => 1,
|
|
71
|
+
results: [{
|
|
72
|
+
memoryId: "memory-1",
|
|
73
|
+
path: "memories/memory-1.md",
|
|
74
|
+
servedBy: "hybrid",
|
|
75
|
+
scoreDecomposition: { final: 1 },
|
|
76
|
+
admittedBy: ["retrieval"],
|
|
77
|
+
graphPath: ["a", "b"],
|
|
78
|
+
}],
|
|
79
|
+
filters: [{ name: "retrieval", considered: 1, admitted: 1 }],
|
|
80
|
+
});
|
|
81
|
+
stubRecall(orchestrator, async (_prompt, _sessionKey, options) => {
|
|
82
|
+
assert.equal(options.xrayCapture, true);
|
|
83
|
+
assert.equal(orchestrator.getLastXraySnapshot(), null);
|
|
84
|
+
setSnapshot(orchestrator, captured);
|
|
85
|
+
return "recall-result";
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
const result = await orchestrator.recallWithXrayCapture("raw query", "session");
|
|
89
|
+
assert.equal(result.result, "recall-result");
|
|
90
|
+
assert.deepEqual(result.snapshot, captured);
|
|
91
|
+
assert.notEqual(result.snapshot, captured);
|
|
92
|
+
|
|
93
|
+
result.snapshot!.results[0]!.admittedBy.push("tampered");
|
|
94
|
+
result.snapshot!.results[0]!.graphPath!.push("tampered");
|
|
95
|
+
result.snapshot!.filters[0]!.reason = "tampered";
|
|
96
|
+
const stored = orchestrator.getLastXraySnapshot();
|
|
97
|
+
assert.deepEqual(stored?.results[0]?.admittedBy, ["retrieval"]);
|
|
98
|
+
assert.deepEqual(stored?.results[0]?.graphPath, ["a", "b"]);
|
|
99
|
+
assert.equal(stored?.filters[0]?.reason, undefined);
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
test("recallWithXrayCapture serializes every consumer of one orchestrator slot", async () => {
|
|
103
|
+
const orchestrator = makeOrchestrator();
|
|
104
|
+
const firstGate = deferred<void>();
|
|
105
|
+
const firstStarted = deferred<void>();
|
|
106
|
+
const order: string[] = [];
|
|
107
|
+
stubRecall(orchestrator, async (prompt) => {
|
|
108
|
+
order.push(`${prompt}:start`);
|
|
109
|
+
if (prompt === "first") {
|
|
110
|
+
firstStarted.resolve();
|
|
111
|
+
await firstGate.promise;
|
|
112
|
+
}
|
|
113
|
+
setSnapshot(orchestrator, snapshot(prompt, `${prompt}-id`));
|
|
114
|
+
order.push(`${prompt}:end`);
|
|
115
|
+
return prompt;
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
const first = orchestrator.recallWithXrayCapture("first");
|
|
119
|
+
const second = orchestrator.recallWithXrayCapture("second");
|
|
120
|
+
await firstStarted.promise;
|
|
121
|
+
assert.deepEqual(order, ["first:start"]);
|
|
122
|
+
firstGate.resolve();
|
|
123
|
+
|
|
124
|
+
const [firstResult, secondResult] = await Promise.all([first, second]);
|
|
125
|
+
assert.deepEqual(order, ["first:start", "first:end", "second:start", "second:end"]);
|
|
126
|
+
assert.equal(firstResult.snapshot?.snapshotId, "first-id");
|
|
127
|
+
assert.equal(secondResult.snapshot?.snapshotId, "second-id");
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
test("recallWithXrayCapture queues are independent across orchestrators", async () => {
|
|
131
|
+
const firstOrchestrator = makeOrchestrator();
|
|
132
|
+
const secondOrchestrator = makeOrchestrator();
|
|
133
|
+
const firstGate = deferred<void>();
|
|
134
|
+
let secondStarted = false;
|
|
135
|
+
stubRecall(firstOrchestrator, async () => {
|
|
136
|
+
await firstGate.promise;
|
|
137
|
+
return "first";
|
|
138
|
+
});
|
|
139
|
+
stubRecall(secondOrchestrator, async () => {
|
|
140
|
+
secondStarted = true;
|
|
141
|
+
return "second";
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
const first = firstOrchestrator.recallWithXrayCapture("first");
|
|
145
|
+
await secondOrchestrator.recallWithXrayCapture("second");
|
|
146
|
+
assert.equal(secondStarted, true);
|
|
147
|
+
firstGate.resolve();
|
|
148
|
+
await first;
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
test("recallWithXrayCapture rejects a pre-aborted signal without clearing or invoking", async () => {
|
|
152
|
+
const orchestrator = makeOrchestrator();
|
|
153
|
+
const stale = snapshot("stale", "stale-id");
|
|
154
|
+
setSnapshot(orchestrator, stale);
|
|
155
|
+
const abortController = new AbortController();
|
|
156
|
+
abortController.abort();
|
|
157
|
+
let invoked = false;
|
|
158
|
+
stubRecall(orchestrator, async () => {
|
|
159
|
+
invoked = true;
|
|
160
|
+
return "unexpected";
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
await assert.rejects(
|
|
164
|
+
orchestrator.recallWithXrayCapture("aborted", undefined, {
|
|
165
|
+
abortSignal: abortController.signal,
|
|
166
|
+
}),
|
|
167
|
+
{ name: "AbortError" },
|
|
168
|
+
);
|
|
169
|
+
assert.equal(invoked, false);
|
|
170
|
+
assert.deepEqual(orchestrator.getLastXraySnapshot(), stale);
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
test("queued abort rejects promptly while its barrier prevents a third capture overtaking", async () => {
|
|
174
|
+
const orchestrator = makeOrchestrator();
|
|
175
|
+
const firstGate = deferred<void>();
|
|
176
|
+
const abortController = new AbortController();
|
|
177
|
+
const order: string[] = [];
|
|
178
|
+
stubRecall(orchestrator, async (prompt) => {
|
|
179
|
+
order.push(`${prompt}:start`);
|
|
180
|
+
if (prompt === "first") await firstGate.promise;
|
|
181
|
+
setSnapshot(orchestrator, snapshot(prompt, `${prompt}-id`));
|
|
182
|
+
order.push(`${prompt}:end`);
|
|
183
|
+
return prompt;
|
|
184
|
+
});
|
|
185
|
+
|
|
186
|
+
const first = orchestrator.recallWithXrayCapture("first");
|
|
187
|
+
await Promise.resolve();
|
|
188
|
+
const queued = orchestrator.recallWithXrayCapture("aborted", undefined, {
|
|
189
|
+
abortSignal: abortController.signal,
|
|
190
|
+
});
|
|
191
|
+
const third = orchestrator.recallWithXrayCapture("third");
|
|
192
|
+
abortController.abort();
|
|
193
|
+
|
|
194
|
+
await assert.rejects(queued, { name: "AbortError" });
|
|
195
|
+
assert.deepEqual(order, ["first:start"]);
|
|
196
|
+
firstGate.resolve();
|
|
197
|
+
|
|
198
|
+
const [firstResult, thirdResult] = await Promise.all([first, third]);
|
|
199
|
+
assert.deepEqual(order, ["first:start", "first:end", "third:start", "third:end"]);
|
|
200
|
+
assert.equal(firstResult.snapshot?.snapshotId, "first-id");
|
|
201
|
+
assert.equal(thirdResult.snapshot?.snapshotId, "third-id");
|
|
202
|
+
});
|
|
203
|
+
|
|
204
|
+
test("started abort retains ownership until recall settles", async () => {
|
|
205
|
+
const orchestrator = makeOrchestrator();
|
|
206
|
+
const activeGate = deferred<void>();
|
|
207
|
+
const activeStarted = deferred<void>();
|
|
208
|
+
const abortController = new AbortController();
|
|
209
|
+
let nextStarted = false;
|
|
210
|
+
stubRecall(orchestrator, async (prompt) => {
|
|
211
|
+
if (prompt === "active") {
|
|
212
|
+
activeStarted.resolve();
|
|
213
|
+
await activeGate.promise;
|
|
214
|
+
setSnapshot(orchestrator, snapshot("active", "active-id"));
|
|
215
|
+
} else {
|
|
216
|
+
nextStarted = true;
|
|
217
|
+
setSnapshot(orchestrator, snapshot("next", "next-id"));
|
|
218
|
+
}
|
|
219
|
+
return prompt;
|
|
220
|
+
});
|
|
221
|
+
|
|
222
|
+
const active = orchestrator.recallWithXrayCapture("active", undefined, {
|
|
223
|
+
abortSignal: abortController.signal,
|
|
224
|
+
});
|
|
225
|
+
await activeStarted.promise;
|
|
226
|
+
abortController.abort();
|
|
227
|
+
const next = orchestrator.recallWithXrayCapture("next");
|
|
228
|
+
await Promise.resolve();
|
|
229
|
+
assert.equal(nextStarted, false);
|
|
230
|
+
|
|
231
|
+
activeGate.resolve();
|
|
232
|
+
const activeResult = await active;
|
|
233
|
+
const nextResult = await next;
|
|
234
|
+
assert.equal(activeResult.snapshot?.snapshotId, "active-id");
|
|
235
|
+
assert.equal(nextResult.snapshot?.snapshotId, "next-id");
|
|
236
|
+
});
|
|
237
|
+
|
|
238
|
+
test("failed and empty captures restore prior shared snapshot but return no stale snapshot", async () => {
|
|
239
|
+
const orchestrator = makeOrchestrator();
|
|
240
|
+
const prior = snapshot("prior", "prior-id");
|
|
241
|
+
setSnapshot(orchestrator, prior);
|
|
242
|
+
stubRecall(orchestrator, async (prompt) => {
|
|
243
|
+
if (prompt === "failure") throw new Error("recall failed");
|
|
244
|
+
return "empty";
|
|
245
|
+
});
|
|
246
|
+
|
|
247
|
+
await assert.rejects(
|
|
248
|
+
orchestrator.recallWithXrayCapture("failure"),
|
|
249
|
+
/recall failed/,
|
|
250
|
+
);
|
|
251
|
+
assert.equal(orchestrator.getLastXraySnapshot()?.snapshotId, "prior-id");
|
|
252
|
+
|
|
253
|
+
const empty = await orchestrator.recallWithXrayCapture("empty");
|
|
254
|
+
assert.equal(empty.snapshot, null);
|
|
255
|
+
assert.equal(orchestrator.getLastXraySnapshot()?.snapshotId, "prior-id");
|
|
256
|
+
});
|
|
257
|
+
|
|
258
|
+
test("legacy capturing recall shares the queue and preserves its string return", async () => {
|
|
259
|
+
const orchestrator = makeOrchestrator();
|
|
260
|
+
const firstGate = deferred<void>();
|
|
261
|
+
const order: string[] = [];
|
|
262
|
+
stubRecall(orchestrator, async (prompt) => {
|
|
263
|
+
order.push(`${prompt}:start`);
|
|
264
|
+
if (prompt === "atomic") await firstGate.promise;
|
|
265
|
+
setSnapshot(orchestrator, snapshot(prompt, `${prompt}-id`));
|
|
266
|
+
order.push(`${prompt}:end`);
|
|
267
|
+
return `${prompt}-result`;
|
|
268
|
+
});
|
|
269
|
+
|
|
270
|
+
const atomic = orchestrator.recallWithXrayCapture("atomic");
|
|
271
|
+
await Promise.resolve();
|
|
272
|
+
const legacy = orchestrator.recall("legacy", undefined, { xrayCapture: true });
|
|
273
|
+
await Promise.resolve();
|
|
274
|
+
assert.deepEqual(order, ["atomic:start"]);
|
|
275
|
+
firstGate.resolve();
|
|
276
|
+
|
|
277
|
+
const [atomicResult, legacyResult] = await Promise.all([atomic, legacy]);
|
|
278
|
+
assert.equal(atomicResult.result, "atomic-result");
|
|
279
|
+
assert.equal(legacyResult, "legacy-result");
|
|
280
|
+
assert.deepEqual(order, ["atomic:start", "atomic:end", "legacy:start", "legacy:end"]);
|
|
281
|
+
});
|
|
282
|
+
|
|
283
|
+
test("ordinary recall remains independent and does not overwrite a captured snapshot", async () => {
|
|
284
|
+
const orchestrator = makeOrchestrator();
|
|
285
|
+
const captureGate = deferred<void>();
|
|
286
|
+
const captureStarted = deferred<void>();
|
|
287
|
+
const order: string[] = [];
|
|
288
|
+
stubRecall(orchestrator, async (prompt, _sessionKey, options) => {
|
|
289
|
+
order.push(prompt);
|
|
290
|
+
if (prompt === "capture") {
|
|
291
|
+
captureStarted.resolve();
|
|
292
|
+
await captureGate.promise;
|
|
293
|
+
setSnapshot(orchestrator, snapshot("capture", "capture-id"));
|
|
294
|
+
}
|
|
295
|
+
assert.equal(options.xrayCapture === true, prompt === "capture");
|
|
296
|
+
return `${prompt}-result`;
|
|
297
|
+
});
|
|
298
|
+
|
|
299
|
+
const capture = orchestrator.recallWithXrayCapture("capture");
|
|
300
|
+
await captureStarted.promise;
|
|
301
|
+
const ordinary = await orchestrator.recall("ordinary");
|
|
302
|
+
assert.equal(ordinary, "ordinary-result");
|
|
303
|
+
assert.deepEqual(order, ["capture", "ordinary"]);
|
|
304
|
+
captureGate.resolve();
|
|
305
|
+
await capture;
|
|
306
|
+
await orchestrator.recall("ordinary-after");
|
|
307
|
+
assert.equal(orchestrator.getLastXraySnapshot()?.snapshotId, "capture-id");
|
|
308
|
+
});
|
|
309
|
+
|
|
310
|
+
test("recallWithXrayCapture accepts normalized snapshot queries without equality checks", async () => {
|
|
311
|
+
const orchestrator = makeOrchestrator();
|
|
312
|
+
stubRecall(orchestrator, async () => {
|
|
313
|
+
setSnapshot(orchestrator, snapshot("daily briefing", "normalized-id"));
|
|
314
|
+
return "context";
|
|
315
|
+
});
|
|
316
|
+
|
|
317
|
+
const result = await orchestrator.recallWithXrayCapture(":cron: daily briefing");
|
|
318
|
+
assert.equal(result.snapshot?.query, "daily briefing");
|
|
319
|
+
});
|
package/src/orchestrator.ts
CHANGED
|
@@ -121,6 +121,7 @@ import { selfDeps } from "./orchestration/self-deps.js";
|
|
|
121
121
|
import { RecallEntryCoordinator } from "./orchestration/recall-entry.js";
|
|
122
122
|
import { SessionContextCoordinator } from "./orchestration/session-context.js";
|
|
123
123
|
import { drainRecallWrites, trackRecallWrite } from "./orchestration/recall-background-writes.js";
|
|
124
|
+
import { XrayCaptureQueue } from "./orchestration/xray-capture-queue.js";
|
|
124
125
|
import {
|
|
125
126
|
abortRecallError,
|
|
126
127
|
buildCompressionGuidelinesMarkdown,
|
|
@@ -258,10 +259,6 @@ import {
|
|
|
258
259
|
type EvalShadowRecallRecord,
|
|
259
260
|
} from "./evals.js";
|
|
260
261
|
import { SessionObserverState } from "./session-observer-state.js";
|
|
261
|
-
import {
|
|
262
|
-
abortError as sharedAbortError,
|
|
263
|
-
throwIfAborted as sharedThrowIfAborted,
|
|
264
|
-
} from "./abort-error.js";
|
|
265
262
|
import { CODEX_THREAD_KEY_PREFIX } from "./thread-key.js";
|
|
266
263
|
import { isDisagreementPrompt } from "./signal.js";
|
|
267
264
|
import { lintWorkspaceFiles, rotateMarkdownFileToArchive } from "./hygiene.js";
|
|
@@ -633,6 +630,8 @@ export class Orchestrator {
|
|
|
633
630
|
* capturing caller can still read their snapshot back.
|
|
634
631
|
*/
|
|
635
632
|
private lastXraySnapshot: RecallXraySnapshot | null = null;
|
|
633
|
+
/** Per-instance ordering domain for every writer of `lastXraySnapshot`. */
|
|
634
|
+
private readonly xrayCaptureQueue = new XrayCaptureQueue();
|
|
636
635
|
readonly embeddingFallback: EmbeddingFallback;
|
|
637
636
|
private readonly conversationIndexDir: string;
|
|
638
637
|
private readonly extraction: ExtractionEngine;
|
|
@@ -2182,7 +2181,7 @@ export class Orchestrator {
|
|
|
2182
2181
|
);
|
|
2183
2182
|
}
|
|
2184
2183
|
|
|
2185
|
-
|
|
2184
|
+
private invokeRecall(
|
|
2186
2185
|
prompt: string,
|
|
2187
2186
|
sessionKey?: string,
|
|
2188
2187
|
options: RecallInvocationOptions = {},
|
|
@@ -2196,23 +2195,92 @@ export class Orchestrator {
|
|
|
2196
2195
|
);
|
|
2197
2196
|
}
|
|
2198
2197
|
|
|
2198
|
+
async recall(
|
|
2199
|
+
prompt: string,
|
|
2200
|
+
sessionKey?: string,
|
|
2201
|
+
options: RecallInvocationOptions = {},
|
|
2202
|
+
): Promise<string> {
|
|
2203
|
+
if (options.xrayCapture === true) {
|
|
2204
|
+
// Preserve the legacy string-returning surface and its soft-abort
|
|
2205
|
+
// behavior while still placing every X-ray writer in the same ordering
|
|
2206
|
+
// domain. In particular, a pre-aborted legacy recall continues to
|
|
2207
|
+
// resolve with an empty string rather than rejecting.
|
|
2208
|
+
const { result } = await this.runRecallWithXrayCapture(
|
|
2209
|
+
prompt,
|
|
2210
|
+
sessionKey,
|
|
2211
|
+
options,
|
|
2212
|
+
);
|
|
2213
|
+
return result;
|
|
2214
|
+
}
|
|
2215
|
+
return this.invokeRecall(prompt, sessionKey, options);
|
|
2216
|
+
}
|
|
2217
|
+
|
|
2199
2218
|
/**
|
|
2200
|
-
* Return the most recent X-ray snapshot captured during a
|
|
2201
|
-
* `recall()` call that passed `xrayCapture: true` (issue #570 PR 1).
|
|
2219
|
+
* Return the most recent X-ray snapshot captured during a recall.
|
|
2202
2220
|
* Returns `null` when no such capture has occurred on this
|
|
2203
2221
|
* orchestrator instance. Returned snapshot is a deep copy so
|
|
2204
2222
|
* caller mutation cannot tear the stored value.
|
|
2223
|
+
*
|
|
2224
|
+
* @deprecated Reading this after a separate `recall()` is not atomic. Use
|
|
2225
|
+
* `recallWithXrayCapture()` when the snapshot must belong to that call.
|
|
2205
2226
|
*/
|
|
2206
2227
|
getLastXraySnapshot(): RecallXraySnapshot | null {
|
|
2207
2228
|
if (!this.lastXraySnapshot) return null;
|
|
2208
2229
|
return structuredClone(this.lastXraySnapshot);
|
|
2209
2230
|
}
|
|
2210
2231
|
|
|
2211
|
-
/**
|
|
2232
|
+
/**
|
|
2233
|
+
* Clear the captured X-ray snapshot. Exposed for tests / explicit reset.
|
|
2234
|
+
*
|
|
2235
|
+
* @deprecated A separate clear → recall → get sequence is not atomic. Use
|
|
2236
|
+
* `recallWithXrayCapture()` for capture operations.
|
|
2237
|
+
*/
|
|
2212
2238
|
clearLastXraySnapshot(): void {
|
|
2213
2239
|
this.lastXraySnapshot = null;
|
|
2214
2240
|
}
|
|
2215
2241
|
|
|
2242
|
+
/** Atomically run recall and return the cloned X-ray snapshot it published. */
|
|
2243
|
+
async recallWithXrayCapture(
|
|
2244
|
+
prompt: string,
|
|
2245
|
+
sessionKey?: string,
|
|
2246
|
+
options: Omit<RecallInvocationOptions, "xrayCapture"> = {},
|
|
2247
|
+
): Promise<{
|
|
2248
|
+
result: string;
|
|
2249
|
+
snapshot: RecallXraySnapshot | null;
|
|
2250
|
+
recallStartedAt: number;
|
|
2251
|
+
}> {
|
|
2252
|
+
return this.runRecallWithXrayCapture(
|
|
2253
|
+
prompt,
|
|
2254
|
+
sessionKey,
|
|
2255
|
+
options,
|
|
2256
|
+
options.abortSignal,
|
|
2257
|
+
);
|
|
2258
|
+
}
|
|
2259
|
+
|
|
2260
|
+
private async runRecallWithXrayCapture(
|
|
2261
|
+
prompt: string,
|
|
2262
|
+
sessionKey: string | undefined,
|
|
2263
|
+
options: Omit<RecallInvocationOptions, "xrayCapture"> | RecallInvocationOptions,
|
|
2264
|
+
queueAbortSignal?: AbortSignal,
|
|
2265
|
+
): Promise<{
|
|
2266
|
+
result: string;
|
|
2267
|
+
snapshot: RecallXraySnapshot | null;
|
|
2268
|
+
recallStartedAt: number;
|
|
2269
|
+
}> {
|
|
2270
|
+
return this.xrayCaptureQueue.run(
|
|
2271
|
+
() => this.invokeRecall(prompt, sessionKey, {
|
|
2272
|
+
...options,
|
|
2273
|
+
xrayCapture: true,
|
|
2274
|
+
}),
|
|
2275
|
+
{
|
|
2276
|
+
read: () => this.getLastXraySnapshot(),
|
|
2277
|
+
clear: () => this.clearLastXraySnapshot(),
|
|
2278
|
+
restore: (snapshot) => { this.lastXraySnapshot = snapshot; },
|
|
2279
|
+
},
|
|
2280
|
+
queueAbortSignal,
|
|
2281
|
+
);
|
|
2282
|
+
}
|
|
2283
|
+
|
|
2216
2284
|
async waitForDirectAnswerObservationIdle(
|
|
2217
2285
|
timeoutMs: number = 60_000,
|
|
2218
2286
|
): Promise<boolean> {
|
|
@@ -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 { isSameLcmRow, lcmEvidenceIdentity } from "./lcm/evidence-identity.js";
|
|
3
4
|
import {
|
|
4
5
|
gatherAcrossReadSessions,
|
|
5
6
|
resolveLcmReadSessionIds,
|
|
@@ -252,19 +253,26 @@ async function collectGuidanceItems(
|
|
|
252
253
|
searchWindowTokens,
|
|
253
254
|
);
|
|
254
255
|
const candidates = expanded.length > 0
|
|
255
|
-
? expanded.map((message) =>
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
:
|
|
265
|
-
|
|
256
|
+
? expanded.map((message) => {
|
|
257
|
+
const matchesSearchHit = isSameLcmRow(
|
|
258
|
+
message,
|
|
259
|
+
result.session_id,
|
|
260
|
+
result,
|
|
261
|
+
result.session_id,
|
|
262
|
+
);
|
|
263
|
+
return {
|
|
264
|
+
...lcmEvidenceIdentity(message, result.session_id),
|
|
265
|
+
sessionId: message.session_id ?? result.session_id,
|
|
266
|
+
turnIndex: message.turn_index,
|
|
267
|
+
role: message.role,
|
|
268
|
+
content: message.content,
|
|
269
|
+
...(matchesSearchHit && typeof result.score === "number"
|
|
270
|
+
? { score: result.score }
|
|
271
|
+
: {}),
|
|
272
|
+
};
|
|
273
|
+
})
|
|
266
274
|
: [{
|
|
267
|
-
|
|
275
|
+
...lcmEvidenceIdentity(result, result.session_id),
|
|
268
276
|
sessionId: result.session_id,
|
|
269
277
|
turnIndex: result.turn_index,
|
|
270
278
|
role: result.role,
|
|
@@ -331,8 +339,8 @@ async function collectGuidanceScanItems(
|
|
|
331
339
|
for (const message of messages) {
|
|
332
340
|
if (!isGuidanceEvidence(message.content, options.query, intents, options.forceGeneric === true)) continue;
|
|
333
341
|
items.push({
|
|
334
|
-
|
|
335
|
-
sessionId: options.sessionId,
|
|
342
|
+
...lcmEvidenceIdentity(message, options.sessionId),
|
|
343
|
+
sessionId: message.session_id ?? options.sessionId,
|
|
336
344
|
turnIndex: message.turn_index,
|
|
337
345
|
role: message.role,
|
|
338
346
|
content: message.content,
|
|
@@ -8,6 +8,7 @@ import {
|
|
|
8
8
|
gatherAcrossReadSessions,
|
|
9
9
|
resolveLcmReadSessionIds,
|
|
10
10
|
} from "./lcm-fallback-read.js";
|
|
11
|
+
import { isSameLcmRow, lcmEvidenceIdentity } from "./lcm/evidence-identity.js";
|
|
11
12
|
|
|
12
13
|
import {
|
|
13
14
|
unifiedDedupeAndRank,
|
|
@@ -124,27 +125,35 @@ async function collectTargetedFactSearchItems(
|
|
|
124
125
|
result.turn_index + searchWindowAfter,
|
|
125
126
|
searchWindowTokens,
|
|
126
127
|
);
|
|
128
|
+
const searchIdentity = lcmEvidenceIdentity(result, result.session_id);
|
|
127
129
|
const searchHit: EvidencePackItem = {
|
|
128
|
-
|
|
130
|
+
...searchIdentity,
|
|
129
131
|
sessionId: result.session_id,
|
|
130
132
|
turnIndex: result.turn_index,
|
|
131
133
|
role: result.role,
|
|
132
134
|
content: result.content,
|
|
133
135
|
...(typeof result.score === "number" ? { score: result.score } : {}),
|
|
134
136
|
};
|
|
135
|
-
const candidates: EvidencePackItem[] = expanded.map((message) =>
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
:
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
137
|
+
const candidates: EvidencePackItem[] = expanded.map((message) => {
|
|
138
|
+
const matchesSearchHit = isSameLcmRow(
|
|
139
|
+
message,
|
|
140
|
+
result.session_id,
|
|
141
|
+
result,
|
|
142
|
+
result.session_id,
|
|
143
|
+
);
|
|
144
|
+
return {
|
|
145
|
+
...lcmEvidenceIdentity(message, result.session_id),
|
|
146
|
+
sessionId: message.session_id ?? result.session_id,
|
|
147
|
+
turnIndex: message.turn_index,
|
|
148
|
+
role: message.role,
|
|
149
|
+
content: message.content,
|
|
150
|
+
...(matchesSearchHit && typeof result.score === "number"
|
|
151
|
+
? { score: result.score }
|
|
152
|
+
: {}),
|
|
153
|
+
};
|
|
154
|
+
});
|
|
155
|
+
const hitIndex = expanded.findIndex((message) =>
|
|
156
|
+
isSameLcmRow(message, result.session_id, result, result.session_id)
|
|
148
157
|
);
|
|
149
158
|
if (hitIndex >= 0) {
|
|
150
159
|
candidates[hitIndex] = searchHit;
|
|
@@ -200,8 +209,8 @@ async function collectTargetedFactScanItems(
|
|
|
200
209
|
for (const message of messages) {
|
|
201
210
|
if (!isTargetedFactEvidence(message.content, options.query)) continue;
|
|
202
211
|
items.push({
|
|
203
|
-
|
|
204
|
-
sessionId: options.sessionId,
|
|
212
|
+
...lcmEvidenceIdentity(message, options.sessionId),
|
|
213
|
+
sessionId: message.session_id ?? options.sessionId,
|
|
205
214
|
turnIndex: message.turn_index,
|
|
206
215
|
role: message.role,
|
|
207
216
|
content: message.content,
|