@remnic/core 9.3.650 → 9.3.651
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-cli.js +35 -34
- package/dist/access-cli.js.map +1 -1
- package/dist/access-http.js +15 -15
- package/dist/access-mcp.js +14 -14
- package/dist/access-schema.js +3 -3
- package/dist/access-service.js +12 -12
- package/dist/{auto-sync-54QQHOG5.js → auto-sync-5CJBJMPZ.js} +5 -5
- package/dist/briefing.js +3 -3
- package/dist/calibration.js +2 -2
- package/dist/{capsule-crypto-GWVG7LGC.js → capsule-crypto-7FJQINUR.js} +2 -2
- package/dist/causal-consolidation.js +6 -6
- package/dist/{chunk-OWHERGF2.js → chunk-2NLLXCJG.js} +2 -2
- package/dist/{chunk-OAZ5MFUB.js → chunk-3XGWCZ63.js} +45 -28
- package/dist/chunk-3XGWCZ63.js.map +1 -0
- package/dist/{chunk-QKE4LHNR.js → chunk-4HYSMH7D.js} +2 -2
- package/dist/{chunk-NMIOW7XG.js → chunk-4PTKFBST.js} +2 -2
- package/dist/{chunk-DDRNDPX4.js → chunk-4SKKVWLQ.js} +2 -2
- package/dist/chunk-5FOCXX5E.js +34 -0
- package/dist/chunk-5FOCXX5E.js.map +1 -0
- package/dist/{chunk-23RYLGYA.js → chunk-5WSDHTBO.js} +100 -111
- package/dist/chunk-5WSDHTBO.js.map +1 -0
- package/dist/{chunk-WPCCNSWO.js → chunk-6UKL6IXM.js} +4 -4
- package/dist/{chunk-DB5A3NHS.js → chunk-7LWRCOP7.js} +9 -2
- package/dist/chunk-7LWRCOP7.js.map +1 -0
- package/dist/{chunk-APJQ6UEA.js → chunk-AGNBY3VG.js} +4 -4
- package/dist/{chunk-4BISW7RX.js → chunk-AJE7FJVE.js} +2 -2
- package/dist/{chunk-ZXWAQFDE.js → chunk-CFOCZPIQ.js} +2 -2
- package/dist/{chunk-NT5TINK5.js → chunk-DHGSZ3UD.js} +2 -2
- package/dist/{chunk-OTC2KOZ2.js → chunk-EHQLDFSH.js} +2 -2
- package/dist/{chunk-AMACWKM4.js → chunk-IJHLC5CH.js} +2 -2
- package/dist/{chunk-OR7R6M5Z.js → chunk-IVYSVAC6.js} +2 -2
- package/dist/{chunk-UMKPSD35.js → chunk-JF7SFXTG.js} +2 -2
- package/dist/{chunk-MCYT2RNT.js → chunk-KJDKZVF3.js} +3 -3
- package/dist/{chunk-BUKK5SWA.js → chunk-KQAFEZQX.js} +2 -2
- package/dist/{chunk-PQFUUXWK.js → chunk-KWM33SPU.js} +2 -2
- package/dist/{chunk-A3BS64GV.js → chunk-LCC5EZTT.js} +4 -4
- package/dist/{chunk-ZT6R3WR3.js → chunk-LFTLXOFX.js} +4 -4
- package/dist/{chunk-3IJEQWQX.js → chunk-MF32AL7N.js} +4 -4
- package/dist/{chunk-D6WVJIS3.js → chunk-ORGWWNJG.js} +2 -2
- package/dist/{chunk-Z3PZRDLW.js → chunk-PRQXUSQV.js} +2 -2
- package/dist/{chunk-VWT3F4IV.js → chunk-PS3SYNHP.js} +12 -4
- package/dist/chunk-PS3SYNHP.js.map +1 -0
- package/dist/{chunk-IMWFHBG2.js → chunk-QWRC7GIO.js} +2 -2
- package/dist/{chunk-TUMH6EDV.js → chunk-RKN5J4RO.js} +26 -26
- package/dist/{chunk-TVOPSKOK.js → chunk-RSS2KWN6.js} +4 -4
- package/dist/{chunk-U3GQ33JC.js → chunk-SLTKP5WJ.js} +2 -2
- package/dist/{chunk-YAFSTKTH.js → chunk-SLYD3AH4.js} +10 -10
- package/dist/{chunk-6NKAQ74D.js → chunk-UU6MVCJ6.js} +1 -1
- package/dist/chunk-UU6MVCJ6.js.map +1 -0
- package/dist/{chunk-WEPMT6SC.js → chunk-V25ZAOSB.js} +5 -5
- package/dist/{chunk-UMTG2BN2.js → chunk-V4UDXYGG.js} +2 -2
- package/dist/{chunk-RRRCNIPK.js → chunk-WJK75OCH.js} +4 -4
- package/dist/{chunk-UVYI6VIX.js → chunk-X7Y7WX73.js} +1 -1
- package/dist/{chunk-OZKZ2TRP.js → chunk-XBIACVCO.js} +9 -2
- package/dist/chunk-XBIACVCO.js.map +1 -0
- package/dist/{chunk-ALUZN7BE.js → chunk-XMN6MMTU.js} +2 -2
- package/dist/{chunk-A4BTPHIN.js → chunk-Y7NWBBHV.js} +6 -6
- package/dist/{chunk-M75TBFKQ.js → chunk-Z2OXSMZK.js} +2 -2
- package/dist/cli.js +30 -30
- package/dist/compounding/engine.js +3 -3
- package/dist/connectors/codex-materialize-runner.js +3 -3
- package/dist/connectors/index.js +3 -3
- package/dist/entity-retrieval.js +3 -3
- package/dist/event-order-recall.js +1 -1
- package/dist/explicit-cue-recall.d.ts +7 -0
- package/dist/explicit-cue-recall.js +2 -1
- package/dist/extraction-judge.js +3 -3
- package/dist/extraction.js +3 -3
- package/dist/fallback-llm.js +2 -2
- package/dist/focused-list-recall.d.ts +6 -0
- package/dist/focused-list-recall.js +2 -1
- package/dist/index.js +83 -82
- package/dist/index.js.map +1 -1
- package/dist/lcm/engine.js +2 -2
- package/dist/lcm/index.js +5 -5
- package/dist/lcm-fallback-read.d.ts +71 -0
- package/dist/lcm-fallback-read.js +10 -0
- package/dist/lcm-fallback-read.js.map +1 -0
- package/dist/maintenance/memory-governance.js +3 -3
- package/dist/maintenance/rebuild-memory-lifecycle-ledger.js +3 -3
- package/dist/maintenance/rebuild-memory-projection.js +4 -4
- package/dist/namespaces/migrate.js +7 -7
- package/dist/namespaces/search.js +3 -3
- package/dist/namespaces/storage.js +3 -3
- package/dist/operator-toolkit.js +9 -9
- package/dist/orchestrator.js +29 -28
- package/dist/recall-planner-llm.js +2 -2
- package/dist/response-guidance-recall.d.ts +6 -0
- package/dist/response-guidance-recall.js +2 -1
- package/dist/schemas.d.ts +22 -22
- package/dist/search/factory.js +2 -2
- package/dist/search/index.js +4 -4
- package/dist/semantic-consolidation.js +4 -4
- package/dist/semantic-rule-promotion.js +3 -3
- package/dist/semantic-rule-verifier.js +3 -3
- package/dist/storage.js +2 -2
- package/dist/summarizer.js +3 -3
- package/dist/targeted-fact-recall.d.ts +6 -0
- package/dist/targeted-fact-recall.js +2 -1
- package/dist/transfer/backup.js +2 -2
- package/dist/transfer/capsule-export.js +2 -2
- package/dist/transfer/capsule-import.js +2 -2
- package/dist/transfer/import-sqlite.js +2 -2
- package/dist/transfer/types.d.ts +12 -12
- package/dist/verified-recall.js +3 -3
- package/package.json +1 -1
- package/src/event-order-recall.ts +8 -0
- package/src/explicit-cue-recall.ts +70 -29
- package/src/focused-list-recall.ts +23 -1
- package/src/lcm-fallback-read.ts +113 -0
- package/src/orchestrator.ts +168 -121
- package/src/response-guidance-recall.ts +21 -1
- package/src/targeted-fact-recall.ts +24 -3
- package/dist/chunk-23RYLGYA.js.map +0 -1
- package/dist/chunk-6NKAQ74D.js.map +0 -1
- package/dist/chunk-DB5A3NHS.js.map +0 -1
- package/dist/chunk-OAZ5MFUB.js.map +0 -1
- package/dist/chunk-OZKZ2TRP.js.map +0 -1
- package/dist/chunk-VWT3F4IV.js.map +0 -1
- /package/dist/{auto-sync-54QQHOG5.js.map → auto-sync-5CJBJMPZ.js.map} +0 -0
- /package/dist/{capsule-crypto-GWVG7LGC.js.map → capsule-crypto-7FJQINUR.js.map} +0 -0
- /package/dist/{chunk-OWHERGF2.js.map → chunk-2NLLXCJG.js.map} +0 -0
- /package/dist/{chunk-QKE4LHNR.js.map → chunk-4HYSMH7D.js.map} +0 -0
- /package/dist/{chunk-NMIOW7XG.js.map → chunk-4PTKFBST.js.map} +0 -0
- /package/dist/{chunk-DDRNDPX4.js.map → chunk-4SKKVWLQ.js.map} +0 -0
- /package/dist/{chunk-WPCCNSWO.js.map → chunk-6UKL6IXM.js.map} +0 -0
- /package/dist/{chunk-APJQ6UEA.js.map → chunk-AGNBY3VG.js.map} +0 -0
- /package/dist/{chunk-4BISW7RX.js.map → chunk-AJE7FJVE.js.map} +0 -0
- /package/dist/{chunk-ZXWAQFDE.js.map → chunk-CFOCZPIQ.js.map} +0 -0
- /package/dist/{chunk-NT5TINK5.js.map → chunk-DHGSZ3UD.js.map} +0 -0
- /package/dist/{chunk-OTC2KOZ2.js.map → chunk-EHQLDFSH.js.map} +0 -0
- /package/dist/{chunk-AMACWKM4.js.map → chunk-IJHLC5CH.js.map} +0 -0
- /package/dist/{chunk-OR7R6M5Z.js.map → chunk-IVYSVAC6.js.map} +0 -0
- /package/dist/{chunk-UMKPSD35.js.map → chunk-JF7SFXTG.js.map} +0 -0
- /package/dist/{chunk-MCYT2RNT.js.map → chunk-KJDKZVF3.js.map} +0 -0
- /package/dist/{chunk-BUKK5SWA.js.map → chunk-KQAFEZQX.js.map} +0 -0
- /package/dist/{chunk-PQFUUXWK.js.map → chunk-KWM33SPU.js.map} +0 -0
- /package/dist/{chunk-A3BS64GV.js.map → chunk-LCC5EZTT.js.map} +0 -0
- /package/dist/{chunk-ZT6R3WR3.js.map → chunk-LFTLXOFX.js.map} +0 -0
- /package/dist/{chunk-3IJEQWQX.js.map → chunk-MF32AL7N.js.map} +0 -0
- /package/dist/{chunk-D6WVJIS3.js.map → chunk-ORGWWNJG.js.map} +0 -0
- /package/dist/{chunk-Z3PZRDLW.js.map → chunk-PRQXUSQV.js.map} +0 -0
- /package/dist/{chunk-IMWFHBG2.js.map → chunk-QWRC7GIO.js.map} +0 -0
- /package/dist/{chunk-TUMH6EDV.js.map → chunk-RKN5J4RO.js.map} +0 -0
- /package/dist/{chunk-TVOPSKOK.js.map → chunk-RSS2KWN6.js.map} +0 -0
- /package/dist/{chunk-U3GQ33JC.js.map → chunk-SLTKP5WJ.js.map} +0 -0
- /package/dist/{chunk-YAFSTKTH.js.map → chunk-SLYD3AH4.js.map} +0 -0
- /package/dist/{chunk-WEPMT6SC.js.map → chunk-V25ZAOSB.js.map} +0 -0
- /package/dist/{chunk-UMTG2BN2.js.map → chunk-V4UDXYGG.js.map} +0 -0
- /package/dist/{chunk-RRRCNIPK.js.map → chunk-WJK75OCH.js.map} +0 -0
- /package/dist/{chunk-UVYI6VIX.js.map → chunk-X7Y7WX73.js.map} +0 -0
- /package/dist/{chunk-ALUZN7BE.js.map → chunk-XMN6MMTU.js.map} +0 -0
- /package/dist/{chunk-A4BTPHIN.js.map → chunk-Y7NWBBHV.js.map} +0 -0
- /package/dist/{chunk-M75TBFKQ.js.map → chunk-Z2OXSMZK.js.map} +0 -0
|
@@ -1,4 +1,8 @@
|
|
|
1
1
|
import { buildEvidencePack } from "./evidence-pack.js";
|
|
2
|
+
import {
|
|
3
|
+
gatherAcrossReadSessions,
|
|
4
|
+
resolveLcmReadSessionIds,
|
|
5
|
+
} from "./lcm-fallback-read.js";
|
|
2
6
|
|
|
3
7
|
export interface ExplicitCueRecallEngine {
|
|
4
8
|
expandContext(
|
|
@@ -29,6 +33,13 @@ export interface ExplicitCueRecallEngine {
|
|
|
29
33
|
export interface ExplicitCueRecallOptions {
|
|
30
34
|
engine: ExplicitCueRecallEngine | null | undefined;
|
|
31
35
|
sessionId?: string;
|
|
36
|
+
/**
|
|
37
|
+
* Ordered, read-authorized LCM read key set (primary overlay → project/root
|
|
38
|
+
* fallbacks). When present, cue evidence is gathered across EVERY key into one
|
|
39
|
+
* shared accumulator and merged under this section's budget (#1505 codex P2).
|
|
40
|
+
* Falls back to `sessionId`.
|
|
41
|
+
*/
|
|
42
|
+
sessionIds?: readonly (string | undefined)[];
|
|
32
43
|
query: string;
|
|
33
44
|
maxChars: number;
|
|
34
45
|
maxItemChars?: number;
|
|
@@ -324,45 +335,75 @@ export async function buildExplicitCueRecallSection(
|
|
|
324
335
|
}> = [];
|
|
325
336
|
const seenTurns = new Set<string>();
|
|
326
337
|
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
+
// #1505 codex P2: gather cue evidence across the ordered LCM read key set
|
|
339
|
+
// (primary overlay → project/root fallbacks) into ONE shared accumulator
|
|
340
|
+
// (`evidenceItems` / `seenTurns`), so a branch-scoped session's project/root
|
|
341
|
+
// fallback cues are RECOVERED instead of being skipped by the old
|
|
342
|
+
// first-non-empty short-circuit. `seenTurns` dedupes across keys by
|
|
343
|
+
// `session_id`+`turn_index`; the budget is applied exactly once in the
|
|
344
|
+
// `buildEvidencePack` call below. `gatherAcrossReadSessions` isolates a
|
|
345
|
+
// per-key read failure (a corrupt/locked fallback index must not discard the
|
|
346
|
+
// other keys' cues); single-key recall runs each collector directly, so a
|
|
347
|
+
// failure propagates exactly as before.
|
|
348
|
+
//
|
|
349
|
+
// Ordering: gather by evidence TYPE first (turn references → content cues →
|
|
350
|
+
// lexical cues), then by read-key priority within each type. This is the
|
|
351
|
+
// section's deliberate value order, and it carries across keys — so a fallback
|
|
352
|
+
// key's high-value turn references precede the primary key's lower-value
|
|
353
|
+
// lexical cues under a tight budget, while a single key is byte-for-byte the
|
|
354
|
+
// pre-#1505 insertion order. We intentionally do NOT score-sort the merged set:
|
|
355
|
+
// explicit-cue's highest-value cues (turn references / content cues) are
|
|
356
|
+
// deliberately UNSCORED while lexical search hits carry numeric scores, so a
|
|
357
|
+
// score-DESC sort would invert the priority and demote turn references below
|
|
358
|
+
// weak lexical hits (cursor[bot] / codex P2 on this PR).
|
|
359
|
+
const readSessionIds = resolveLcmReadSessionIds(options);
|
|
360
|
+
await gatherAcrossReadSessions(readSessionIds, (sessionId) =>
|
|
361
|
+
collectTurnReferenceEvidence({
|
|
338
362
|
engine,
|
|
339
|
-
sessionId
|
|
363
|
+
sessionId,
|
|
340
364
|
query,
|
|
341
365
|
maxReferences,
|
|
342
366
|
evidenceItems,
|
|
343
367
|
seenTurns,
|
|
344
|
-
})
|
|
368
|
+
}),
|
|
369
|
+
);
|
|
345
370
|
|
|
346
|
-
|
|
371
|
+
if (options.includeContentLexicalCues) {
|
|
372
|
+
await gatherAcrossReadSessions(readSessionIds, (sessionId) =>
|
|
373
|
+
collectNamedMeetingFactEvidence({
|
|
374
|
+
engine,
|
|
375
|
+
sessionId,
|
|
376
|
+
query,
|
|
377
|
+
maxReferences,
|
|
378
|
+
evidenceItems,
|
|
379
|
+
seenTurns,
|
|
380
|
+
}),
|
|
381
|
+
);
|
|
382
|
+
|
|
383
|
+
await gatherAcrossReadSessions(readSessionIds, (sessionId) =>
|
|
384
|
+
collectFocusedTranscriptCueEvidence({
|
|
385
|
+
engine,
|
|
386
|
+
sessionId,
|
|
387
|
+
query,
|
|
388
|
+
evidenceItems,
|
|
389
|
+
seenTurns,
|
|
390
|
+
}),
|
|
391
|
+
);
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
await gatherAcrossReadSessions(readSessionIds, (sessionId) =>
|
|
395
|
+
collectLexicalCueEvidence({
|
|
347
396
|
engine,
|
|
348
|
-
sessionId
|
|
397
|
+
sessionId,
|
|
349
398
|
query,
|
|
399
|
+
maxReferences,
|
|
400
|
+
includeBenchmarkAnchorCues: options.includeBenchmarkAnchorCues,
|
|
401
|
+
includeContentLexicalCues: options.includeContentLexicalCues,
|
|
402
|
+
includeStructuredPlanCues: options.includeStructuredPlanCues,
|
|
350
403
|
evidenceItems,
|
|
351
404
|
seenTurns,
|
|
352
|
-
})
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
await collectLexicalCueEvidence({
|
|
356
|
-
engine,
|
|
357
|
-
sessionId: options.sessionId,
|
|
358
|
-
query,
|
|
359
|
-
maxReferences,
|
|
360
|
-
includeBenchmarkAnchorCues: options.includeBenchmarkAnchorCues,
|
|
361
|
-
includeContentLexicalCues: options.includeContentLexicalCues,
|
|
362
|
-
includeStructuredPlanCues: options.includeStructuredPlanCues,
|
|
363
|
-
evidenceItems,
|
|
364
|
-
seenTurns,
|
|
365
|
-
});
|
|
405
|
+
}),
|
|
406
|
+
);
|
|
366
407
|
|
|
367
408
|
const evidenceFocusQuery = buildEvidenceFocusQuery(query, {
|
|
368
409
|
includeBenchmarkAnchorCues: options.includeBenchmarkAnchorCues,
|
|
@@ -4,10 +4,20 @@ import {
|
|
|
4
4
|
type EvidencePackItem,
|
|
5
5
|
} from "./evidence-pack.js";
|
|
6
6
|
import type { ExplicitCueRecallEngine } from "./explicit-cue-recall.js";
|
|
7
|
+
import {
|
|
8
|
+
gatherAcrossReadSessions,
|
|
9
|
+
resolveLcmReadSessionIds,
|
|
10
|
+
} from "./lcm-fallback-read.js";
|
|
7
11
|
|
|
8
12
|
export interface FocusedListRecallOptions {
|
|
9
13
|
engine: ExplicitCueRecallEngine | null | undefined;
|
|
10
14
|
sessionId?: string;
|
|
15
|
+
/**
|
|
16
|
+
* Ordered, read-authorized LCM read key set (primary overlay → project/root
|
|
17
|
+
* fallbacks). When present, evidence is gathered across EVERY key and merged
|
|
18
|
+
* under this section's budget (#1505 codex P2). Falls back to `sessionId`.
|
|
19
|
+
*/
|
|
20
|
+
sessionIds?: readonly (string | undefined)[];
|
|
11
21
|
query: string;
|
|
12
22
|
maxChars: number;
|
|
13
23
|
maxItemChars?: number;
|
|
@@ -52,7 +62,19 @@ export async function buildFocusedListRecallSection(
|
|
|
52
62
|
return "";
|
|
53
63
|
}
|
|
54
64
|
|
|
55
|
-
|
|
65
|
+
// #1505 codex P2: gather candidates across the ordered LCM read key set
|
|
66
|
+
// (primary overlay → project/root fallbacks) and UNION them into the existing
|
|
67
|
+
// rank/dedupe/budget pass, so a stronger project-fallback candidate is not
|
|
68
|
+
// masked by a weak primary-key hit. `rankAndDedupeFocusedListItems` applies
|
|
69
|
+
// the section-appropriate dedupe and relevance rank; the budget is applied
|
|
70
|
+
// exactly once below. `gatherAcrossReadSessions` isolates a per-key read
|
|
71
|
+
// failure so a corrupt/locked fallback index can't discard the primary key's
|
|
72
|
+
// candidates; the single-key path runs exactly one collect and propagates a
|
|
73
|
+
// failure as before — byte-for-byte the pre-#1505 behavior.
|
|
74
|
+
const items: EvidencePackItem[] = [];
|
|
75
|
+
await gatherAcrossReadSessions(resolveLcmReadSessionIds(options), async (sessionId) => {
|
|
76
|
+
items.push(...(await collectFocusedListItems({ ...options, sessionId }, intent)));
|
|
77
|
+
});
|
|
56
78
|
const ranked = rankAndDedupeFocusedListItems(items, options.query, intent)
|
|
57
79
|
.slice(0, maxResults);
|
|
58
80
|
if (ranked.length === 0) {
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared helper for reading LCM-backed recall sections across the ordered,
|
|
3
|
+
* read-authorized fallback key set (#1505 codex P2 "Merge LCM fallback reads
|
|
4
|
+
* instead of short-circuiting").
|
|
5
|
+
*
|
|
6
|
+
* Background: a branch-scoped session archives its LCM rows under whichever
|
|
7
|
+
* coding-overlay namespace was effective at write time, so its evidence can be
|
|
8
|
+
* split across the primary overlay key AND the project / root fallback keys.
|
|
9
|
+
* Normal QMD/file recall already searches the primary namespace PLUS
|
|
10
|
+
* `codingOverlay.readFallbacks` and MERGES the rows. The LCM read path must do
|
|
11
|
+
* the same: query EVERY authorized read key and merge the candidate evidence
|
|
12
|
+
* into each section's existing dedupe + rank + budget pass, instead of stopping
|
|
13
|
+
* at the first key that happens to yield a (possibly weak) hit.
|
|
14
|
+
*
|
|
15
|
+
* Each section already owns a section-appropriate dedupe (a `seen` set or a
|
|
16
|
+
* `rankAndDedupe…` step), so the fan-out only needs to resolve the ordered,
|
|
17
|
+
* deduped read-key set and UNION the per-key candidates into that existing
|
|
18
|
+
* pipeline — the budget is then applied exactly once to the union. Centralizing
|
|
19
|
+
* the key-set resolution here (rather than re-implementing per builder) follows
|
|
20
|
+
* CLAUDE.md rule 22 (scope resolution must be deduplicated).
|
|
21
|
+
*/
|
|
22
|
+
|
|
23
|
+
/** A recall section's LCM read target: either a single key or an ordered set. */
|
|
24
|
+
export interface LcmReadSessionTarget {
|
|
25
|
+
/**
|
|
26
|
+
* The single LCM read `session_id` (pre-#1505 behavior). `undefined` means a
|
|
27
|
+
* sessionless, archive-wide read with no `session_id` filter.
|
|
28
|
+
*/
|
|
29
|
+
sessionId?: string;
|
|
30
|
+
/**
|
|
31
|
+
* The ordered, read-authorized LCM read key set (primary overlay key first,
|
|
32
|
+
* then project / root fallbacks) the orchestrator derived from the same
|
|
33
|
+
* readable namespace set normal recall searches. When present and non-empty,
|
|
34
|
+
* it supersedes `sessionId`.
|
|
35
|
+
*/
|
|
36
|
+
sessionIds?: readonly (string | undefined)[];
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// `undefined` (a sessionless, archive-wide read) is a distinct, legitimate read
|
|
40
|
+
// target, so it needs a non-string sentinel in the dedupe set. A leading space
|
|
41
|
+
// keeps it disjoint from every real session key / namespaced LCM key (which are
|
|
42
|
+
// `[A-Za-z0-9._-]` plus the U+001F namespace sentinel, never leading-space).
|
|
43
|
+
const UNDEFINED_SESSION_SENTINEL = " <lcm-sessionless>";
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Resolve the ordered, deduped set of LCM read `session_id`s a recall section
|
|
47
|
+
* must query.
|
|
48
|
+
*
|
|
49
|
+
* When `sessionIds` is provided (the #1505 fallback unification), it is used
|
|
50
|
+
* verbatim, deduped while preserving first-seen order so the caller queries
|
|
51
|
+
* keys in priority order (primary overlay → fallbacks) without re-querying an
|
|
52
|
+
* identical key (e.g. when two namespaces both collapse to the default store).
|
|
53
|
+
* Otherwise the section reads under the single `sessionId`, so the result is
|
|
54
|
+
* `[sessionId]` — byte-for-byte the pre-#1505 single-key behavior, including a
|
|
55
|
+
* single `undefined` for a sessionless archive-wide read.
|
|
56
|
+
*/
|
|
57
|
+
export function resolveLcmReadSessionIds(
|
|
58
|
+
target: LcmReadSessionTarget,
|
|
59
|
+
): Array<string | undefined> {
|
|
60
|
+
const source =
|
|
61
|
+
target.sessionIds && target.sessionIds.length > 0
|
|
62
|
+
? target.sessionIds
|
|
63
|
+
: [target.sessionId];
|
|
64
|
+
const seen = new Set<string>();
|
|
65
|
+
const out: Array<string | undefined> = [];
|
|
66
|
+
for (const sessionId of source) {
|
|
67
|
+
const key = sessionId === undefined ? UNDEFINED_SESSION_SENTINEL : sessionId;
|
|
68
|
+
if (seen.has(key)) continue;
|
|
69
|
+
seen.add(key);
|
|
70
|
+
out.push(sessionId);
|
|
71
|
+
}
|
|
72
|
+
// Defensive: an all-empty `sessionIds` still collapses to the single-key path.
|
|
73
|
+
return out.length > 0 ? out : [target.sessionId];
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Run a per-key LCM `gather` across the resolved read-key set with FAULT
|
|
78
|
+
* ISOLATION across keys (#1505 codex P2 review follow-up).
|
|
79
|
+
*
|
|
80
|
+
* A recall section that reads every key in a bare `for…await` loop loses the
|
|
81
|
+
* WHOLE section if any one key throws (e.g. a `SqliteError` from a corrupt or
|
|
82
|
+
* locked fallback index) — even when the primary overlay key already gathered
|
|
83
|
+
* evidence. The pre-#1505 first-non-empty read never had this problem: it
|
|
84
|
+
* returned the primary key's non-empty result without ever touching a failing
|
|
85
|
+
* fallback. This helper restores that resilience for the merged path: when more
|
|
86
|
+
* than one key is read, a per-key failure is contained so the other keys'
|
|
87
|
+
* evidence survives (best-effort recall — a total failure degrades to an empty
|
|
88
|
+
* section, which the orchestrator already treats as "no evidence").
|
|
89
|
+
*
|
|
90
|
+
* SINGLE-KEY is byte-for-byte the pre-#1505 behavior: the gather runs directly,
|
|
91
|
+
* so a failure PROPAGATES exactly as before (the caller / orchestrator catch
|
|
92
|
+
* still logs it). Fault isolation only engages once there is a fallback key that
|
|
93
|
+
* could fail independently of the primary.
|
|
94
|
+
*/
|
|
95
|
+
export async function gatherAcrossReadSessions(
|
|
96
|
+
sessionIds: ReadonlyArray<string | undefined>,
|
|
97
|
+
gather: (sessionId: string | undefined) => Promise<void>,
|
|
98
|
+
): Promise<void> {
|
|
99
|
+
if (sessionIds.length <= 1) {
|
|
100
|
+
for (const sessionId of sessionIds) {
|
|
101
|
+
await gather(sessionId);
|
|
102
|
+
}
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
for (const sessionId of sessionIds) {
|
|
106
|
+
try {
|
|
107
|
+
await gather(sessionId);
|
|
108
|
+
} catch {
|
|
109
|
+
// One read key failed; keep the evidence already gathered from the other
|
|
110
|
+
// keys instead of discarding the whole section.
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
}
|
package/src/orchestrator.ts
CHANGED
|
@@ -7243,16 +7243,24 @@ export class Orchestrator {
|
|
|
7243
7243
|
);
|
|
7244
7244
|
// Query an LCM-backed read across the ordered read key set and return the
|
|
7245
7245
|
// FIRST non-empty result (#1505 fallback-namespace unification). The primary
|
|
7246
|
-
// overlay key is tried first; if a branch-scoped session has no rows under
|
|
7247
|
-
//
|
|
7248
|
-
//
|
|
7249
|
-
// (
|
|
7250
|
-
//
|
|
7251
|
-
//
|
|
7252
|
-
//
|
|
7253
|
-
//
|
|
7254
|
-
//
|
|
7255
|
-
//
|
|
7246
|
+
// overlay key is tried first; if a branch-scoped session has no rows under its
|
|
7247
|
+
// branch key, the project / root fallback keys are tried in order.
|
|
7248
|
+
//
|
|
7249
|
+
// #1505 codex P2 ("Merge LCM fallback reads instead of short-circuiting"): the
|
|
7250
|
+
// query-SCORED sections (explicit-cue, targeted-facts, focused-list,
|
|
7251
|
+
// response-guidance, event-order, structured message-parts) no longer use this
|
|
7252
|
+
// helper — they MERGE candidates across EVERY authorized key under their single
|
|
7253
|
+
// budget (a weak primary-key hit must not mask stronger fallback evidence; the
|
|
7254
|
+
// section builders take `sessionIds`, structured-parts merges inline below).
|
|
7255
|
+
// This first-non-empty helper now serves ONLY the compressed-history section,
|
|
7256
|
+
// which is a per-session HOLISTIC DAG narrative with no per-item id to merge or
|
|
7257
|
+
// dedupe on — see its call site for the rationale.
|
|
7258
|
+
//
|
|
7259
|
+
// When the set is a single key (single-user / no-overlay / explicit-namespace),
|
|
7260
|
+
// this is exactly one call — unchanged. `lcmSessionId` is `string | undefined`:
|
|
7261
|
+
// a SESSIONLESS recall yields the single `undefined` key so the read runs ONE
|
|
7262
|
+
// archive-wide read with no `session_id` filter (pre-#1505 behavior). NEVER the
|
|
7263
|
+
// literal "default" session id (codex P2).
|
|
7256
7264
|
const firstNonEmptyLcmRead = async <T>(
|
|
7257
7265
|
read: (lcmSessionId: string | undefined) => Promise<T>,
|
|
7258
7266
|
isEmpty: (value: T) => boolean,
|
|
@@ -9513,24 +9521,21 @@ export class Orchestrator {
|
|
|
9513
9521
|
(recallMode as RecallPlanMode) !== "no_recall"
|
|
9514
9522
|
) {
|
|
9515
9523
|
try {
|
|
9516
|
-
const explicitCueSection = await
|
|
9517
|
-
|
|
9518
|
-
|
|
9519
|
-
|
|
9520
|
-
|
|
9521
|
-
|
|
9522
|
-
|
|
9523
|
-
|
|
9524
|
-
|
|
9525
|
-
|
|
9526
|
-
|
|
9527
|
-
|
|
9528
|
-
|
|
9529
|
-
|
|
9530
|
-
|
|
9531
|
-
(s) => !s,
|
|
9532
|
-
"",
|
|
9533
|
-
);
|
|
9524
|
+
const explicitCueSection = await buildExplicitCueRecallSection({
|
|
9525
|
+
engine: this.lcmEngine,
|
|
9526
|
+
// #1495 thread 3 + #1505 fallback unification: read across the ordered
|
|
9527
|
+
// LCM read key set (primary overlay → coding fallbacks) so a
|
|
9528
|
+
// branch-scoped session finds its own explicit-cue evidence even when
|
|
9529
|
+
// archived at project/root scope (rule 39). #1505 codex P2: the builder
|
|
9530
|
+
// MERGES candidates across every key under its single budget instead of
|
|
9531
|
+
// short-circuiting on the first non-empty key.
|
|
9532
|
+
sessionIds: lcmReadSessionIds,
|
|
9533
|
+
query: retrievalQuery,
|
|
9534
|
+
maxChars: explicitCueMaxChars,
|
|
9535
|
+
maxReferences:
|
|
9536
|
+
this.getRecallSectionNumber("explicit-cue", "maxResults") ??
|
|
9537
|
+
this.config.explicitCueRecallMaxReferences,
|
|
9538
|
+
});
|
|
9534
9539
|
if (explicitCueSection) {
|
|
9535
9540
|
this.appendRecallSection(
|
|
9536
9541
|
sectionBuckets,
|
|
@@ -9560,29 +9565,25 @@ export class Orchestrator {
|
|
|
9560
9565
|
shouldRecallTargetedFactEvidence(retrievalQuery)
|
|
9561
9566
|
) {
|
|
9562
9567
|
try {
|
|
9563
|
-
const targetedFactSection = await
|
|
9564
|
-
|
|
9565
|
-
|
|
9566
|
-
|
|
9567
|
-
|
|
9568
|
-
|
|
9569
|
-
|
|
9570
|
-
|
|
9571
|
-
|
|
9572
|
-
|
|
9573
|
-
|
|
9574
|
-
|
|
9575
|
-
|
|
9576
|
-
|
|
9577
|
-
|
|
9578
|
-
|
|
9579
|
-
|
|
9580
|
-
|
|
9581
|
-
|
|
9582
|
-
}),
|
|
9583
|
-
(s) => !s,
|
|
9584
|
-
"",
|
|
9585
|
-
);
|
|
9568
|
+
const targetedFactSection = await buildTargetedFactRecallSection({
|
|
9569
|
+
engine: this.lcmEngine,
|
|
9570
|
+
// #1495 + #1505 fallback unification: read across the ordered LCM read
|
|
9571
|
+
// key set so a branch-scoped session finds its own targeted-fact
|
|
9572
|
+
// evidence even when archived at project/root scope. #1505 codex P2: the
|
|
9573
|
+
// builder MERGES candidates across every key under its single budget.
|
|
9574
|
+
sessionIds: lcmReadSessionIds,
|
|
9575
|
+
query: retrievalQuery,
|
|
9576
|
+
maxChars: targetedFactMaxChars,
|
|
9577
|
+
maxSearchResults:
|
|
9578
|
+
this.getRecallSectionNumber("targeted-facts", "maxResults") ??
|
|
9579
|
+
this.config.targetedFactRecallMaxResults,
|
|
9580
|
+
maxScanWindowTurns:
|
|
9581
|
+
this.getRecallSectionNumber("targeted-facts", "maxTurns") ??
|
|
9582
|
+
this.config.targetedFactRecallScanWindowTurns,
|
|
9583
|
+
maxScanWindowTokens:
|
|
9584
|
+
this.getRecallSectionNumber("targeted-facts", "maxTokens") ??
|
|
9585
|
+
this.config.targetedFactRecallScanWindowTokens,
|
|
9586
|
+
});
|
|
9586
9587
|
if (targetedFactSection) {
|
|
9587
9588
|
this.appendRecallSection(
|
|
9588
9589
|
sectionBuckets,
|
|
@@ -9613,29 +9614,26 @@ export class Orchestrator {
|
|
|
9613
9614
|
shouldRecallFocusedListEvidence(retrievalQuery)
|
|
9614
9615
|
) {
|
|
9615
9616
|
try {
|
|
9616
|
-
const focusedListSection = await
|
|
9617
|
-
|
|
9618
|
-
|
|
9619
|
-
|
|
9620
|
-
|
|
9621
|
-
|
|
9622
|
-
|
|
9623
|
-
|
|
9624
|
-
|
|
9625
|
-
|
|
9626
|
-
|
|
9627
|
-
|
|
9628
|
-
|
|
9629
|
-
|
|
9630
|
-
|
|
9631
|
-
|
|
9632
|
-
|
|
9633
|
-
|
|
9634
|
-
|
|
9635
|
-
|
|
9636
|
-
(s) => !s,
|
|
9637
|
-
"",
|
|
9638
|
-
);
|
|
9617
|
+
const focusedListSection = await buildFocusedListRecallSection({
|
|
9618
|
+
engine: this.lcmEngine,
|
|
9619
|
+
// #1495 thread 3 + #1505 fallback unification: read across the ordered
|
|
9620
|
+
// LCM read key set so a branch-scoped session reads its own
|
|
9621
|
+
// focused-list/count evidence even at project/root scope (rule 39).
|
|
9622
|
+
// #1505 codex P2: the builder MERGES candidates across every key under
|
|
9623
|
+
// its single budget.
|
|
9624
|
+
sessionIds: lcmReadSessionIds,
|
|
9625
|
+
query: retrievalQuery,
|
|
9626
|
+
maxChars: focusedListMaxChars,
|
|
9627
|
+
maxSearchResults:
|
|
9628
|
+
this.getRecallSectionNumber("focused-list", "maxResults") ??
|
|
9629
|
+
this.config.focusedListRecallMaxResults,
|
|
9630
|
+
maxScanWindowTurns:
|
|
9631
|
+
this.getRecallSectionNumber("focused-list", "maxTurns") ??
|
|
9632
|
+
this.config.focusedListRecallScanWindowTurns,
|
|
9633
|
+
maxScanWindowTokens:
|
|
9634
|
+
this.getRecallSectionNumber("focused-list", "maxTokens") ??
|
|
9635
|
+
this.config.focusedListRecallScanWindowTokens,
|
|
9636
|
+
});
|
|
9639
9637
|
if (focusedListSection) {
|
|
9640
9638
|
this.appendRecallSection(
|
|
9641
9639
|
sectionBuckets,
|
|
@@ -9670,30 +9668,27 @@ export class Orchestrator {
|
|
|
9670
9668
|
(responseGuidanceMatchesQuery || responseGuidanceForcedByPipeline)
|
|
9671
9669
|
) {
|
|
9672
9670
|
try {
|
|
9673
|
-
const responseGuidanceSection = await
|
|
9674
|
-
|
|
9675
|
-
|
|
9676
|
-
|
|
9677
|
-
|
|
9678
|
-
|
|
9679
|
-
|
|
9680
|
-
|
|
9681
|
-
|
|
9682
|
-
|
|
9683
|
-
|
|
9684
|
-
|
|
9685
|
-
|
|
9686
|
-
|
|
9687
|
-
|
|
9688
|
-
|
|
9689
|
-
|
|
9690
|
-
|
|
9691
|
-
|
|
9692
|
-
|
|
9693
|
-
|
|
9694
|
-
(s) => !s,
|
|
9695
|
-
"",
|
|
9696
|
-
);
|
|
9671
|
+
const responseGuidanceSection = await buildResponseGuidanceRecallSection({
|
|
9672
|
+
engine: this.lcmEngine,
|
|
9673
|
+
// #1495 thread 3 + #1505 fallback unification: read across the ordered
|
|
9674
|
+
// LCM read key set so a branch-scoped session reads its own
|
|
9675
|
+
// response-guidance evidence even at project/root scope (rule 39).
|
|
9676
|
+
// #1505 codex P2: the builder MERGES candidates across every key under
|
|
9677
|
+
// its single budget.
|
|
9678
|
+
sessionIds: lcmReadSessionIds,
|
|
9679
|
+
query: retrievalQuery,
|
|
9680
|
+
maxChars: responseGuidanceMaxChars,
|
|
9681
|
+
maxSearchResults:
|
|
9682
|
+
this.getRecallSectionNumber("response-guidance", "maxResults") ??
|
|
9683
|
+
this.config.responseGuidanceRecallMaxResults,
|
|
9684
|
+
maxScanWindowTurns:
|
|
9685
|
+
this.getRecallSectionNumber("response-guidance", "maxTurns") ??
|
|
9686
|
+
this.config.responseGuidanceRecallScanWindowTurns,
|
|
9687
|
+
maxScanWindowTokens:
|
|
9688
|
+
this.getRecallSectionNumber("response-guidance", "maxTokens") ??
|
|
9689
|
+
this.config.responseGuidanceRecallScanWindowTokens,
|
|
9690
|
+
forceGeneric: responseGuidanceForcedByPipeline,
|
|
9691
|
+
});
|
|
9697
9692
|
if (responseGuidanceSection) {
|
|
9698
9693
|
this.appendRecallSection(
|
|
9699
9694
|
sectionBuckets,
|
|
@@ -9722,13 +9717,22 @@ export class Orchestrator {
|
|
|
9722
9717
|
shouldRecallEventOrderEvidence(retrievalQuery)
|
|
9723
9718
|
) {
|
|
9724
9719
|
try {
|
|
9720
|
+
// #1495 thread 3 + #1505 fallback unification: read across the ordered LCM
|
|
9721
|
+
// read key set so a branch-scoped session reads its own chronological
|
|
9722
|
+
// event-order evidence even at project/root scope. UNLIKE the relevance-
|
|
9723
|
+
// ranked sections, event-order must NOT merge across keys: `turn_index` is
|
|
9724
|
+
// LOCAL to each LCM `session_id` (`observe` numbers turns per session via
|
|
9725
|
+
// `getMaxTurnIndex`), so interleaving two keys and sorting by `turn_index`
|
|
9726
|
+
// would place an older project-scope turn after a newer branch-scope turn
|
|
9727
|
+
// and misstate the chronology (#1505 codex P2). Like compressed-history,
|
|
9728
|
+
// event-order is an inherently per-session ORDERED artifact, so it takes
|
|
9729
|
+
// the highest-priority authorized key (primary overlay → project/root)
|
|
9730
|
+
// that actually has chronological evidence — each key's timeline is
|
|
9731
|
+
// internally consistent.
|
|
9725
9732
|
const eventOrderSection = await firstNonEmptyLcmRead(
|
|
9726
9733
|
(lcmSessionId) =>
|
|
9727
9734
|
buildEventOrderRecallSection({
|
|
9728
9735
|
engine: this.lcmEngine,
|
|
9729
|
-
// #1495 thread 3 + #1505 fallback unification: read across the
|
|
9730
|
-
// ordered LCM read key set so a branch-scoped session reads its own
|
|
9731
|
-
// chronological event-order evidence even at project/root scope.
|
|
9732
9736
|
sessionId: lcmSessionId,
|
|
9733
9737
|
query: retrievalQuery,
|
|
9734
9738
|
maxChars: eventOrderMaxChars,
|
|
@@ -9918,21 +9922,59 @@ export class Orchestrator {
|
|
|
9918
9922
|
(recallMode as RecallPlanMode) !== "no_recall"
|
|
9919
9923
|
) {
|
|
9920
9924
|
try {
|
|
9921
|
-
|
|
9922
|
-
|
|
9923
|
-
|
|
9924
|
-
|
|
9925
|
-
|
|
9926
|
-
|
|
9927
|
-
|
|
9928
|
-
|
|
9929
|
-
|
|
9930
|
-
|
|
9931
|
-
|
|
9932
|
-
|
|
9933
|
-
|
|
9934
|
-
|
|
9925
|
+
// #1495 + #1505 fallback unification: read across the ordered LCM read
|
|
9926
|
+
// key set so a branch-scoped session reads its own structured
|
|
9927
|
+
// message-part evidence even when archived at project/root scope.
|
|
9928
|
+
// #1505 codex P2: structured matches are query-SCORED evidence, so MERGE
|
|
9929
|
+
// across EVERY key (primary overlay → project/root fallbacks) instead of
|
|
9930
|
+
// short-circuiting on the first non-empty key — a weak branch-key hit must
|
|
9931
|
+
// not mask stronger project-fallback parts. Keys are queried in priority
|
|
9932
|
+
// order; dedupe by session_id+turn_index+part_id keeps the primary key's
|
|
9933
|
+
// row on collision. `formatStructuredRecall` applies the single budget
|
|
9934
|
+
// below. A sessionless key (`undefined`) normalizes to "" → no matches
|
|
9935
|
+
// (structured parts are inherently per-session; pre-#1505 behavior, codex
|
|
9936
|
+
// P2).
|
|
9937
|
+
// FAULT ISOLATION (allSettled, not all): the pre-#1505 first-non-empty read
|
|
9938
|
+
// short-circuited, so a fallback key was often never queried and its latent
|
|
9939
|
+
// search failure never surfaced. Querying every key eagerly must NOT let one
|
|
9940
|
+
// key's failure (e.g. a SqliteError from a corrupt/locked fallback index)
|
|
9941
|
+
// reject the batch and discard the OTHER keys' parts — or, since this and
|
|
9942
|
+
// the compressed-history read below share one try block, silently drop the
|
|
9943
|
+
// compressed-history section a healthy primary key would still produce. So
|
|
9944
|
+
// read each key independently and keep the fulfilled batches.
|
|
9945
|
+
const structuredSettled = await Promise.allSettled(
|
|
9946
|
+
lcmReadSessionIds.map((lcmSessionId) =>
|
|
9947
|
+
this.lcmEngine!.searchStructuredParts(lcmSessionId ?? "", retrievalQuery),
|
|
9948
|
+
),
|
|
9935
9949
|
);
|
|
9950
|
+
for (const settled of structuredSettled) {
|
|
9951
|
+
if (settled.status === "rejected") {
|
|
9952
|
+
log.debug(
|
|
9953
|
+
`LCM structured-parts read failed for one key: ${settled.reason}`,
|
|
9954
|
+
);
|
|
9955
|
+
}
|
|
9956
|
+
}
|
|
9957
|
+
const seenStructuredParts = new Set<string>();
|
|
9958
|
+
const structuredMatches = structuredSettled
|
|
9959
|
+
.flatMap((settled) => (settled.status === "fulfilled" ? settled.value : []))
|
|
9960
|
+
.filter((match) => {
|
|
9961
|
+
const key = `${match.session_id} ${match.turn_index} ${match.part_id}`;
|
|
9962
|
+
if (seenStructuredParts.has(key)) return false;
|
|
9963
|
+
seenStructuredParts.add(key);
|
|
9964
|
+
return true;
|
|
9965
|
+
})
|
|
9966
|
+
// Restore the archive's per-key ordering (score DESC, then turn DESC)
|
|
9967
|
+
// across the MERGED set so the strongest parts win the shared budget in
|
|
9968
|
+
// `formatStructuredRecall` — otherwise weak primary-key parts could crowd
|
|
9969
|
+
// out stronger fallback parts. Stable sort: a single key is already in
|
|
9970
|
+
// this order, so it stays byte-for-byte the pre-#1505 behavior.
|
|
9971
|
+
// `?? 0` is defensive: `LcmStructuredRecallMatch.score` is always a
|
|
9972
|
+
// number here, but a bare `b.score - a.score` would yield NaN (falsy)
|
|
9973
|
+
// for any future unscored match and silently fall through to turn order.
|
|
9974
|
+
.sort(
|
|
9975
|
+
(a, b) =>
|
|
9976
|
+
(b.score ?? 0) - (a.score ?? 0) || b.turn_index - a.turn_index,
|
|
9977
|
+
);
|
|
9936
9978
|
const structuredSection = this.lcmEngine.formatStructuredRecall(
|
|
9937
9979
|
structuredMatches,
|
|
9938
9980
|
Math.ceil(this.config.recallBudgetChars * 0.08),
|
|
@@ -9955,15 +9997,20 @@ export class Orchestrator {
|
|
|
9955
9997
|
}
|
|
9956
9998
|
}
|
|
9957
9999
|
}
|
|
10000
|
+
// #1495 + #1505 fallback unification: read across the ordered LCM read key
|
|
10001
|
+
// set so a branch-scoped session reads its own compressed-history evidence
|
|
10002
|
+
// even at project/root scope. UNLIKE the query-scored sections above, the
|
|
10003
|
+
// compressed history is a per-session HOLISTIC DAG narrative, not a set of
|
|
10004
|
+
// independently-rankable evidence items — concatenating two sessions'
|
|
10005
|
+
// summaries would double-count the conversation and blow the budget, and
|
|
10006
|
+
// there is no per-item id to dedupe on. So this section deliberately keeps
|
|
10007
|
+
// first-non-empty semantics (#1505 codex P2 scope: "merge the query-matched
|
|
10008
|
+
// sections"): the highest-priority authorized key (primary overlay →
|
|
10009
|
+
// project/root) that actually has a compressed history wins. A sessionless
|
|
10010
|
+
// key (`undefined`) normalizes to empty → no section (pre-#1505 behavior).
|
|
9958
10011
|
const lcmSection = await firstNonEmptyLcmRead(
|
|
9959
10012
|
(lcmSessionId) =>
|
|
9960
10013
|
this.lcmEngine!.assembleRecall(
|
|
9961
|
-
// #1495 + #1505 fallback unification: read across the ordered LCM
|
|
9962
|
-
// read key set so a branch-scoped session reads its own
|
|
9963
|
-
// compressed-history evidence even at project/root scope.
|
|
9964
|
-
// Compressed history is inherently per-session (a per-session DAG),
|
|
9965
|
-
// so a SESSIONLESS read (`undefined`) normalizes to empty → no
|
|
9966
|
-
// section, the correct pre-#1505 behavior (codex P2).
|
|
9967
10014
|
lcmSessionId ?? "",
|
|
9968
10015
|
this.config.recallBudgetChars,
|
|
9969
10016
|
),
|