@remnic/core 9.3.650 → 9.3.652
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 +39 -38
- package/dist/access-cli.js.map +1 -1
- package/dist/access-http.d.ts +3 -2
- package/dist/access-http.js +19 -19
- package/dist/access-mcp.d.ts +3 -2
- package/dist/access-mcp.js +18 -18
- package/dist/access-schema.js +3 -3
- package/dist/{access-service-DIZRHQ7Q.d.ts → access-service-CdJFd3_b.d.ts} +23 -2
- package/dist/access-service.d.ts +3 -2
- package/dist/access-service.js +16 -16
- package/dist/{auto-sync-54QQHOG5.js → auto-sync-5CJBJMPZ.js} +5 -5
- package/dist/bootstrap.d.ts +2 -1
- 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-QT4THOLT.js → chunk-2DGQLOOM.js} +1 -1
- package/dist/chunk-2DGQLOOM.js.map +1 -0
- 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-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-YAFSTKTH.js → chunk-5V3TAB7D.js} +184 -12
- package/dist/chunk-5V3TAB7D.js.map +1 -0
- package/dist/{chunk-DB5A3NHS.js → chunk-7LWRCOP7.js} +9 -2
- package/dist/chunk-7LWRCOP7.js.map +1 -0
- package/dist/{chunk-FOVPSMGI.js → chunk-7WEB3FLJ.js} +2 -2
- 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-RRRCNIPK.js → chunk-GI45G4BK.js} +4 -4
- package/dist/{chunk-AMACWKM4.js → chunk-IJHLC5CH.js} +2 -2
- package/dist/{chunk-OR7R6M5Z.js → chunk-IVYSVAC6.js} +2 -2
- package/dist/{chunk-76QTEJ2Q.js → chunk-JBHXMCYN.js} +2 -2
- package/dist/{chunk-UMKPSD35.js → chunk-JF7SFXTG.js} +2 -2
- package/dist/{chunk-NMIOW7XG.js → chunk-JVRPJ7D4.js} +126 -26
- package/dist/chunk-JVRPJ7D4.js.map +1 -0
- package/dist/{chunk-TQUWNX7C.js → chunk-JX2RINDR.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-TVOPSKOK.js → chunk-MGGNV3H2.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-I4COC5XW.js → chunk-PYWNNF2I.js} +47 -9
- package/dist/chunk-PYWNNF2I.js.map +1 -0
- package/dist/{chunk-IMWFHBG2.js → chunk-QWRC7GIO.js} +2 -2
- package/dist/{chunk-U3GQ33JC.js → chunk-SLTKP5WJ.js} +2 -2
- package/dist/{chunk-23RYLGYA.js → chunk-TCX4WLKK.js} +104 -112
- package/dist/chunk-TCX4WLKK.js.map +1 -0
- 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-TUMH6EDV.js → chunk-WSFNYPAT.js} +26 -26
- package/dist/{chunk-ZT6R3WR3.js → chunk-WTI35CVJ.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-WPCCNSWO.js → chunk-YM3LR4LS.js} +7 -7
- package/dist/{chunk-3IJEQWQX.js → chunk-YOVKPOMD.js} +4 -4
- package/dist/{chunk-M75TBFKQ.js → chunk-Z2OXSMZK.js} +2 -2
- package/dist/{cli-BG4ybtJr.d.ts → cli-DDo7Qgs-.d.ts} +2 -2
- package/dist/cli.d.ts +4 -3
- package/dist/cli.js +34 -34
- 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-capture.d.ts +2 -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.d.ts +5 -4
- package/dist/index.js +87 -86
- 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/mcp-memory-inspector-app.d.ts +3 -2
- package/dist/namespaces/migrate.js +11 -11
- package/dist/namespaces/search.d.ts +18 -1
- package/dist/namespaces/search.js +7 -7
- package/dist/namespaces/storage.js +3 -3
- package/dist/operator-toolkit.js +13 -13
- package/dist/{orchestrator-CX-oqwJq.d.ts → orchestrator-8fTZsa0y.d.ts} +2 -0
- package/dist/orchestrator.d.ts +2 -1
- package/dist/orchestrator.js +33 -32
- package/dist/qmd.d.ts +2 -1
- package/dist/qmd.js +2 -2
- 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/search/factory.js +6 -6
- package/dist/search/index.js +8 -8
- package/dist/search/lancedb-backend.js +2 -2
- package/dist/search/meilisearch-backend.js +2 -2
- package/dist/search/orama-backend.js +2 -2
- package/dist/search/port.d.ts +6 -0
- package/dist/search/port.js +1 -1
- 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/verified-recall.js +3 -3
- package/package.json +1 -1
- package/src/access-service-health.test.ts +402 -0
- package/src/access-service.ts +274 -2
- 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/namespaces/search.test.ts +258 -3
- package/src/namespaces/search.ts +184 -30
- package/src/orchestrator.ts +179 -122
- package/src/qmd.test.ts +102 -0
- package/src/qmd.ts +54 -7
- package/src/response-guidance-recall.ts +21 -1
- package/src/search/port.ts +6 -0
- 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-I4COC5XW.js.map +0 -1
- package/dist/chunk-NMIOW7XG.js.map +0 -1
- package/dist/chunk-OAZ5MFUB.js.map +0 -1
- package/dist/chunk-OZKZ2TRP.js.map +0 -1
- package/dist/chunk-QT4THOLT.js.map +0 -1
- package/dist/chunk-VWT3F4IV.js.map +0 -1
- package/dist/chunk-YAFSTKTH.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-DDRNDPX4.js.map → chunk-4SKKVWLQ.js.map} +0 -0
- /package/dist/{chunk-FOVPSMGI.js.map → chunk-7WEB3FLJ.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-RRRCNIPK.js.map → chunk-GI45G4BK.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-76QTEJ2Q.js.map → chunk-JBHXMCYN.js.map} +0 -0
- /package/dist/{chunk-UMKPSD35.js.map → chunk-JF7SFXTG.js.map} +0 -0
- /package/dist/{chunk-TQUWNX7C.js.map → chunk-JX2RINDR.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-TVOPSKOK.js.map → chunk-MGGNV3H2.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-U3GQ33JC.js.map → chunk-SLTKP5WJ.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-TUMH6EDV.js.map → chunk-WSFNYPAT.js.map} +0 -0
- /package/dist/{chunk-ZT6R3WR3.js.map → chunk-WTI35CVJ.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-WPCCNSWO.js.map → chunk-YM3LR4LS.js.map} +0 -0
- /package/dist/{chunk-3IJEQWQX.js.map → chunk-YOVKPOMD.js.map} +0 -0
- /package/dist/{chunk-M75TBFKQ.js.map → chunk-Z2OXSMZK.js.map} +0 -0
package/src/namespaces/search.ts
CHANGED
|
@@ -44,12 +44,32 @@ type NamespaceBackendRecord = {
|
|
|
44
44
|
filtersNestedNamespaces: boolean;
|
|
45
45
|
};
|
|
46
46
|
|
|
47
|
-
type CollectionState = "present" | "missing" | "unknown" | "skipped";
|
|
47
|
+
export type CollectionState = "present" | "missing" | "unknown" | "skipped";
|
|
48
|
+
|
|
49
|
+
export interface NamespaceSearchHealth {
|
|
50
|
+
collection: string;
|
|
51
|
+
memoryDir: string;
|
|
52
|
+
available: boolean;
|
|
53
|
+
collectionState: CollectionState;
|
|
54
|
+
debugStatus: string;
|
|
55
|
+
installedVersion: string | null;
|
|
56
|
+
supportedVersion: string | null;
|
|
57
|
+
supported: boolean | null;
|
|
58
|
+
upgradeAvailable: boolean | null;
|
|
59
|
+
doctorAvailable: boolean | null;
|
|
60
|
+
daemonMode: boolean | null;
|
|
61
|
+
}
|
|
48
62
|
|
|
49
63
|
type NamespaceScopedSearchConfig = PluginConfig & {
|
|
50
64
|
hostEmbeddingProviderScope?: string;
|
|
51
65
|
};
|
|
52
66
|
|
|
67
|
+
type BackendRecordOptions = {
|
|
68
|
+
autoCreateCollection: boolean;
|
|
69
|
+
abortAsUnavailable: boolean;
|
|
70
|
+
failOpenMissingGuardedCollection: boolean;
|
|
71
|
+
};
|
|
72
|
+
|
|
53
73
|
export class NamespaceSearchRouter {
|
|
54
74
|
private readonly cache = new Map<string, Promise<NamespaceBackendRecord>>();
|
|
55
75
|
|
|
@@ -184,6 +204,67 @@ export class NamespaceSearchRouter {
|
|
|
184
204
|
return record.collectionState;
|
|
185
205
|
}
|
|
186
206
|
|
|
207
|
+
async healthForNamespace(
|
|
208
|
+
namespace: string,
|
|
209
|
+
execution?: SearchExecutionOptions,
|
|
210
|
+
): Promise<NamespaceSearchHealth> {
|
|
211
|
+
const key = namespace.trim() || this.config.defaultNamespace;
|
|
212
|
+
const record = await this.createBackendRecordFor(
|
|
213
|
+
key,
|
|
214
|
+
execution,
|
|
215
|
+
{
|
|
216
|
+
autoCreateCollection: false,
|
|
217
|
+
abortAsUnavailable: true,
|
|
218
|
+
failOpenMissingGuardedCollection: false,
|
|
219
|
+
},
|
|
220
|
+
);
|
|
221
|
+
try {
|
|
222
|
+
const liveRecord = await this.liveCachedRecordForHealth(key, record, execution);
|
|
223
|
+
const diagnosticBackend = liveRecord?.backend ?? record.backend;
|
|
224
|
+
const versionStatus =
|
|
225
|
+
"getVersionStatus" in diagnosticBackend &&
|
|
226
|
+
typeof diagnosticBackend.getVersionStatus === "function"
|
|
227
|
+
? diagnosticBackend.getVersionStatus()
|
|
228
|
+
: null;
|
|
229
|
+
const daemonMode = daemonModeForBackend(diagnosticBackend);
|
|
230
|
+
const collectionState =
|
|
231
|
+
liveRecord?.collectionState === "missing"
|
|
232
|
+
? "missing"
|
|
233
|
+
: record.collectionState;
|
|
234
|
+
|
|
235
|
+
return {
|
|
236
|
+
collection: record.collection,
|
|
237
|
+
memoryDir: record.memoryDir,
|
|
238
|
+
available: liveRecord?.available ?? record.available,
|
|
239
|
+
collectionState,
|
|
240
|
+
debugStatus: diagnosticBackend.debugStatus(),
|
|
241
|
+
installedVersion: versionStatus?.installedVersion ?? null,
|
|
242
|
+
supportedVersion: versionStatus?.supportedVersion ?? null,
|
|
243
|
+
supported: versionStatus?.supported ?? null,
|
|
244
|
+
upgradeAvailable: versionStatus?.upgradeAvailable ?? null,
|
|
245
|
+
doctorAvailable: versionStatus?.capabilities?.doctor ?? null,
|
|
246
|
+
daemonMode,
|
|
247
|
+
};
|
|
248
|
+
} finally {
|
|
249
|
+
const dispose = (record.backend as { dispose?: () => void | Promise<void> }).dispose;
|
|
250
|
+
await dispose?.call(record.backend);
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
private async liveCachedRecordForHealth(
|
|
255
|
+
key: string,
|
|
256
|
+
disposableRecord: NamespaceBackendRecord,
|
|
257
|
+
execution?: SearchExecutionOptions,
|
|
258
|
+
): Promise<NamespaceBackendRecord | null> {
|
|
259
|
+
const pending = this.cache.get(key);
|
|
260
|
+
if (!pending) return null;
|
|
261
|
+
const cachedRecord = await awaitWithAbort(pending, execution?.signal).catch(() => null);
|
|
262
|
+
if (!cachedRecord) return null;
|
|
263
|
+
if (cachedRecord.collection !== disposableRecord.collection) return null;
|
|
264
|
+
if (cachedRecord.memoryDir !== disposableRecord.memoryDir) return null;
|
|
265
|
+
return cachedRecord;
|
|
266
|
+
}
|
|
267
|
+
|
|
187
268
|
/** Clear cached backend records so the next access re-probes availability. */
|
|
188
269
|
clearCache(): void {
|
|
189
270
|
this.cache.clear();
|
|
@@ -211,31 +292,69 @@ export class NamespaceSearchRouter {
|
|
|
211
292
|
const existing = this.cache.get(key);
|
|
212
293
|
if (existing) return await existing;
|
|
213
294
|
|
|
214
|
-
const pending = (
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
295
|
+
const pending = this.createBackendRecordFor(key, execution, {
|
|
296
|
+
autoCreateCollection: true,
|
|
297
|
+
abortAsUnavailable: false,
|
|
298
|
+
failOpenMissingGuardedCollection: true,
|
|
299
|
+
}).catch((error) => {
|
|
300
|
+
this.cache.delete(key);
|
|
301
|
+
throw error;
|
|
302
|
+
});
|
|
303
|
+
|
|
304
|
+
this.cache.set(key, pending);
|
|
305
|
+
return await pending;
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
private async createBackendRecordFor(
|
|
309
|
+
namespace: string,
|
|
310
|
+
execution: SearchExecutionOptions | undefined,
|
|
311
|
+
options: BackendRecordOptions,
|
|
312
|
+
): Promise<NamespaceBackendRecord> {
|
|
313
|
+
const key = namespace.trim() || this.config.defaultNamespace;
|
|
314
|
+
const storage = await this.storageRouter.storageFor(key);
|
|
315
|
+
const useLegacyDefaultCollection =
|
|
316
|
+
key === this.config.defaultNamespace && storage.dir === this.config.memoryDir;
|
|
317
|
+
const filtersNestedNamespaces =
|
|
318
|
+
this.config.namespacesEnabled === true && useLegacyDefaultCollection;
|
|
319
|
+
const rootHostEmbeddingScope =
|
|
320
|
+
(this.config as NamespaceScopedSearchConfig).hostEmbeddingProviderScope ??
|
|
321
|
+
this.config.memoryDir;
|
|
322
|
+
const scopedConfig: NamespaceScopedSearchConfig = {
|
|
323
|
+
...this.config,
|
|
324
|
+
memoryDir: storage.dir,
|
|
325
|
+
hostEmbeddingProviderScope: rootHostEmbeddingScope,
|
|
326
|
+
qmdCollection: namespaceCollectionName(this.config.qmdCollection, key, {
|
|
327
|
+
defaultNamespace: this.config.defaultNamespace,
|
|
328
|
+
useLegacyDefaultCollection,
|
|
329
|
+
}),
|
|
330
|
+
};
|
|
232
331
|
|
|
233
|
-
|
|
234
|
-
|
|
332
|
+
const backend = this.createBackend(scopedConfig);
|
|
333
|
+
try {
|
|
334
|
+
const availabilityProbe =
|
|
335
|
+
options.autoCreateCollection || typeof backend.checkAvailability !== "function"
|
|
336
|
+
? backend.probe()
|
|
337
|
+
: backend.checkAvailability({ signal: execution?.signal });
|
|
338
|
+
const available = await awaitWithAbort(availabilityProbe, execution?.signal).catch((error) => {
|
|
339
|
+
if (error instanceof NamespaceSearchAbortError && !options.abortAsUnavailable) {
|
|
340
|
+
throw error;
|
|
341
|
+
}
|
|
342
|
+
return false;
|
|
343
|
+
});
|
|
235
344
|
const collectionState = available
|
|
236
|
-
? await
|
|
237
|
-
|
|
238
|
-
|
|
345
|
+
? await awaitWithAbort(
|
|
346
|
+
this.collectionStateForBackend(backend, storage.dir, scopedConfig.qmdCollection, {
|
|
347
|
+
autoCreate: options.autoCreateCollection,
|
|
348
|
+
failOpenMissingGuardedCollection: options.failOpenMissingGuardedCollection,
|
|
349
|
+
skipAutoCreate: filtersNestedNamespaces,
|
|
350
|
+
execution,
|
|
351
|
+
}),
|
|
352
|
+
execution?.signal,
|
|
353
|
+
).catch((error) => {
|
|
354
|
+
if (error instanceof NamespaceSearchAbortError && !options.abortAsUnavailable) {
|
|
355
|
+
throw error;
|
|
356
|
+
}
|
|
357
|
+
return "unknown" as const;
|
|
239
358
|
})
|
|
240
359
|
: "unknown";
|
|
241
360
|
return {
|
|
@@ -246,10 +365,13 @@ export class NamespaceSearchRouter {
|
|
|
246
365
|
collectionState,
|
|
247
366
|
filtersNestedNamespaces,
|
|
248
367
|
};
|
|
249
|
-
}
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
368
|
+
} catch (error) {
|
|
369
|
+
const dispose = (backend as { dispose?: () => void | Promise<void> }).dispose;
|
|
370
|
+
if (dispose) {
|
|
371
|
+
await Promise.resolve(dispose.call(backend)).catch(() => {});
|
|
372
|
+
}
|
|
373
|
+
throw error;
|
|
374
|
+
}
|
|
253
375
|
}
|
|
254
376
|
|
|
255
377
|
private async collectionStateForBackend(
|
|
@@ -257,21 +379,47 @@ export class NamespaceSearchRouter {
|
|
|
257
379
|
memoryDir: string,
|
|
258
380
|
collection: string,
|
|
259
381
|
options: {
|
|
382
|
+
autoCreate: boolean;
|
|
383
|
+
failOpenMissingGuardedCollection: boolean;
|
|
260
384
|
skipAutoCreate: boolean;
|
|
261
385
|
execution?: SearchExecutionOptions;
|
|
262
386
|
},
|
|
263
387
|
): Promise<CollectionState> {
|
|
264
|
-
if (options.skipAutoCreate) {
|
|
388
|
+
if (!options.autoCreate || options.skipAutoCreate) {
|
|
265
389
|
if (!backend.checkCollection) return "unknown";
|
|
266
390
|
const collectionState = await backend
|
|
267
391
|
.checkCollection(collection, options.execution)
|
|
268
392
|
.catch(() => "unknown" as const);
|
|
269
|
-
return collectionState === "missing"
|
|
393
|
+
return options.failOpenMissingGuardedCollection && collectionState === "missing"
|
|
394
|
+
? "unknown"
|
|
395
|
+
: collectionState;
|
|
270
396
|
}
|
|
271
397
|
return await backend.ensureCollection(memoryDir, collection, options.execution).catch(() => "unknown" as const);
|
|
272
398
|
}
|
|
273
399
|
}
|
|
274
400
|
|
|
401
|
+
class NamespaceSearchAbortError extends Error {
|
|
402
|
+
constructor() {
|
|
403
|
+
super("operation aborted");
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
function awaitWithAbort<T>(operation: Promise<T>, signal?: AbortSignal): Promise<T> {
|
|
408
|
+
if (!signal) return operation;
|
|
409
|
+
if (signal.aborted) return Promise.reject(new NamespaceSearchAbortError());
|
|
410
|
+
|
|
411
|
+
return new Promise<T>((resolve, reject) => {
|
|
412
|
+
const onAbort = () => {
|
|
413
|
+
signal.removeEventListener("abort", onAbort);
|
|
414
|
+
reject(new NamespaceSearchAbortError());
|
|
415
|
+
};
|
|
416
|
+
signal.addEventListener("abort", onAbort, { once: true });
|
|
417
|
+
operation.then(resolve, reject).finally(() => {
|
|
418
|
+
signal.removeEventListener("abort", onAbort);
|
|
419
|
+
});
|
|
420
|
+
});
|
|
421
|
+
}
|
|
422
|
+
|
|
275
423
|
function filterNamespaceSubtreeResults(
|
|
276
424
|
record: NamespaceBackendRecord,
|
|
277
425
|
results: QmdSearchResult[],
|
|
@@ -294,6 +442,12 @@ function backendSearchLimit(
|
|
|
294
442
|
);
|
|
295
443
|
}
|
|
296
444
|
|
|
445
|
+
function daemonModeForBackend(backend: SearchBackend): boolean | null {
|
|
446
|
+
return "isDaemonMode" in backend && typeof backend.isDaemonMode === "function"
|
|
447
|
+
? backend.isDaemonMode() === true
|
|
448
|
+
: null;
|
|
449
|
+
}
|
|
450
|
+
|
|
297
451
|
function pathIsInsideNamespaceSubtree(
|
|
298
452
|
memoryDir: string,
|
|
299
453
|
collection: string,
|
package/src/orchestrator.ts
CHANGED
|
@@ -311,7 +311,10 @@ import {
|
|
|
311
311
|
resolveCodingNamespaceOverlay,
|
|
312
312
|
} from "./coding/coding-namespace.js";
|
|
313
313
|
import type { CodingContext } from "./types.js";
|
|
314
|
-
import {
|
|
314
|
+
import {
|
|
315
|
+
NamespaceSearchRouter,
|
|
316
|
+
type NamespaceSearchHealth,
|
|
317
|
+
} from "./namespaces/search.js";
|
|
315
318
|
import { SharedContextManager } from "./shared-context/manager.js";
|
|
316
319
|
import {
|
|
317
320
|
CompoundingEngine,
|
|
@@ -2324,6 +2327,13 @@ export class Orchestrator {
|
|
|
2324
2327
|
});
|
|
2325
2328
|
}
|
|
2326
2329
|
|
|
2330
|
+
async searchHealthForNamespace(
|
|
2331
|
+
namespace: string,
|
|
2332
|
+
execution?: SearchExecutionOptions,
|
|
2333
|
+
): Promise<NamespaceSearchHealth> {
|
|
2334
|
+
return await this.namespaceSearchRouter.healthForNamespace(namespace, execution);
|
|
2335
|
+
}
|
|
2336
|
+
|
|
2327
2337
|
private isSearchAvailableForNamespaceRouting(): boolean {
|
|
2328
2338
|
if (this.config.namespacesEnabled) return true;
|
|
2329
2339
|
return this.qmd.isAvailable();
|
|
@@ -7243,16 +7253,24 @@ export class Orchestrator {
|
|
|
7243
7253
|
);
|
|
7244
7254
|
// Query an LCM-backed read across the ordered read key set and return the
|
|
7245
7255
|
// 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
|
-
//
|
|
7256
|
+
// overlay key is tried first; if a branch-scoped session has no rows under its
|
|
7257
|
+
// branch key, the project / root fallback keys are tried in order.
|
|
7258
|
+
//
|
|
7259
|
+
// #1505 codex P2 ("Merge LCM fallback reads instead of short-circuiting"): the
|
|
7260
|
+
// query-SCORED sections (explicit-cue, targeted-facts, focused-list,
|
|
7261
|
+
// response-guidance, event-order, structured message-parts) no longer use this
|
|
7262
|
+
// helper — they MERGE candidates across EVERY authorized key under their single
|
|
7263
|
+
// budget (a weak primary-key hit must not mask stronger fallback evidence; the
|
|
7264
|
+
// section builders take `sessionIds`, structured-parts merges inline below).
|
|
7265
|
+
// This first-non-empty helper now serves ONLY the compressed-history section,
|
|
7266
|
+
// which is a per-session HOLISTIC DAG narrative with no per-item id to merge or
|
|
7267
|
+
// dedupe on — see its call site for the rationale.
|
|
7268
|
+
//
|
|
7269
|
+
// When the set is a single key (single-user / no-overlay / explicit-namespace),
|
|
7270
|
+
// this is exactly one call — unchanged. `lcmSessionId` is `string | undefined`:
|
|
7271
|
+
// a SESSIONLESS recall yields the single `undefined` key so the read runs ONE
|
|
7272
|
+
// archive-wide read with no `session_id` filter (pre-#1505 behavior). NEVER the
|
|
7273
|
+
// literal "default" session id (codex P2).
|
|
7256
7274
|
const firstNonEmptyLcmRead = async <T>(
|
|
7257
7275
|
read: (lcmSessionId: string | undefined) => Promise<T>,
|
|
7258
7276
|
isEmpty: (value: T) => boolean,
|
|
@@ -9513,24 +9531,21 @@ export class Orchestrator {
|
|
|
9513
9531
|
(recallMode as RecallPlanMode) !== "no_recall"
|
|
9514
9532
|
) {
|
|
9515
9533
|
try {
|
|
9516
|
-
const explicitCueSection = await
|
|
9517
|
-
|
|
9518
|
-
|
|
9519
|
-
|
|
9520
|
-
|
|
9521
|
-
|
|
9522
|
-
|
|
9523
|
-
|
|
9524
|
-
|
|
9525
|
-
|
|
9526
|
-
|
|
9527
|
-
|
|
9528
|
-
|
|
9529
|
-
|
|
9530
|
-
|
|
9531
|
-
(s) => !s,
|
|
9532
|
-
"",
|
|
9533
|
-
);
|
|
9534
|
+
const explicitCueSection = await buildExplicitCueRecallSection({
|
|
9535
|
+
engine: this.lcmEngine,
|
|
9536
|
+
// #1495 thread 3 + #1505 fallback unification: read across the ordered
|
|
9537
|
+
// LCM read key set (primary overlay → coding fallbacks) so a
|
|
9538
|
+
// branch-scoped session finds its own explicit-cue evidence even when
|
|
9539
|
+
// archived at project/root scope (rule 39). #1505 codex P2: the builder
|
|
9540
|
+
// MERGES candidates across every key under its single budget instead of
|
|
9541
|
+
// short-circuiting on the first non-empty key.
|
|
9542
|
+
sessionIds: lcmReadSessionIds,
|
|
9543
|
+
query: retrievalQuery,
|
|
9544
|
+
maxChars: explicitCueMaxChars,
|
|
9545
|
+
maxReferences:
|
|
9546
|
+
this.getRecallSectionNumber("explicit-cue", "maxResults") ??
|
|
9547
|
+
this.config.explicitCueRecallMaxReferences,
|
|
9548
|
+
});
|
|
9534
9549
|
if (explicitCueSection) {
|
|
9535
9550
|
this.appendRecallSection(
|
|
9536
9551
|
sectionBuckets,
|
|
@@ -9560,29 +9575,25 @@ export class Orchestrator {
|
|
|
9560
9575
|
shouldRecallTargetedFactEvidence(retrievalQuery)
|
|
9561
9576
|
) {
|
|
9562
9577
|
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
|
-
);
|
|
9578
|
+
const targetedFactSection = await buildTargetedFactRecallSection({
|
|
9579
|
+
engine: this.lcmEngine,
|
|
9580
|
+
// #1495 + #1505 fallback unification: read across the ordered LCM read
|
|
9581
|
+
// key set so a branch-scoped session finds its own targeted-fact
|
|
9582
|
+
// evidence even when archived at project/root scope. #1505 codex P2: the
|
|
9583
|
+
// builder MERGES candidates across every key under its single budget.
|
|
9584
|
+
sessionIds: lcmReadSessionIds,
|
|
9585
|
+
query: retrievalQuery,
|
|
9586
|
+
maxChars: targetedFactMaxChars,
|
|
9587
|
+
maxSearchResults:
|
|
9588
|
+
this.getRecallSectionNumber("targeted-facts", "maxResults") ??
|
|
9589
|
+
this.config.targetedFactRecallMaxResults,
|
|
9590
|
+
maxScanWindowTurns:
|
|
9591
|
+
this.getRecallSectionNumber("targeted-facts", "maxTurns") ??
|
|
9592
|
+
this.config.targetedFactRecallScanWindowTurns,
|
|
9593
|
+
maxScanWindowTokens:
|
|
9594
|
+
this.getRecallSectionNumber("targeted-facts", "maxTokens") ??
|
|
9595
|
+
this.config.targetedFactRecallScanWindowTokens,
|
|
9596
|
+
});
|
|
9586
9597
|
if (targetedFactSection) {
|
|
9587
9598
|
this.appendRecallSection(
|
|
9588
9599
|
sectionBuckets,
|
|
@@ -9613,29 +9624,26 @@ export class Orchestrator {
|
|
|
9613
9624
|
shouldRecallFocusedListEvidence(retrievalQuery)
|
|
9614
9625
|
) {
|
|
9615
9626
|
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
|
-
);
|
|
9627
|
+
const focusedListSection = await buildFocusedListRecallSection({
|
|
9628
|
+
engine: this.lcmEngine,
|
|
9629
|
+
// #1495 thread 3 + #1505 fallback unification: read across the ordered
|
|
9630
|
+
// LCM read key set so a branch-scoped session reads its own
|
|
9631
|
+
// focused-list/count evidence even at project/root scope (rule 39).
|
|
9632
|
+
// #1505 codex P2: the builder MERGES candidates across every key under
|
|
9633
|
+
// its single budget.
|
|
9634
|
+
sessionIds: lcmReadSessionIds,
|
|
9635
|
+
query: retrievalQuery,
|
|
9636
|
+
maxChars: focusedListMaxChars,
|
|
9637
|
+
maxSearchResults:
|
|
9638
|
+
this.getRecallSectionNumber("focused-list", "maxResults") ??
|
|
9639
|
+
this.config.focusedListRecallMaxResults,
|
|
9640
|
+
maxScanWindowTurns:
|
|
9641
|
+
this.getRecallSectionNumber("focused-list", "maxTurns") ??
|
|
9642
|
+
this.config.focusedListRecallScanWindowTurns,
|
|
9643
|
+
maxScanWindowTokens:
|
|
9644
|
+
this.getRecallSectionNumber("focused-list", "maxTokens") ??
|
|
9645
|
+
this.config.focusedListRecallScanWindowTokens,
|
|
9646
|
+
});
|
|
9639
9647
|
if (focusedListSection) {
|
|
9640
9648
|
this.appendRecallSection(
|
|
9641
9649
|
sectionBuckets,
|
|
@@ -9670,30 +9678,27 @@ export class Orchestrator {
|
|
|
9670
9678
|
(responseGuidanceMatchesQuery || responseGuidanceForcedByPipeline)
|
|
9671
9679
|
) {
|
|
9672
9680
|
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
|
-
);
|
|
9681
|
+
const responseGuidanceSection = await buildResponseGuidanceRecallSection({
|
|
9682
|
+
engine: this.lcmEngine,
|
|
9683
|
+
// #1495 thread 3 + #1505 fallback unification: read across the ordered
|
|
9684
|
+
// LCM read key set so a branch-scoped session reads its own
|
|
9685
|
+
// response-guidance evidence even at project/root scope (rule 39).
|
|
9686
|
+
// #1505 codex P2: the builder MERGES candidates across every key under
|
|
9687
|
+
// its single budget.
|
|
9688
|
+
sessionIds: lcmReadSessionIds,
|
|
9689
|
+
query: retrievalQuery,
|
|
9690
|
+
maxChars: responseGuidanceMaxChars,
|
|
9691
|
+
maxSearchResults:
|
|
9692
|
+
this.getRecallSectionNumber("response-guidance", "maxResults") ??
|
|
9693
|
+
this.config.responseGuidanceRecallMaxResults,
|
|
9694
|
+
maxScanWindowTurns:
|
|
9695
|
+
this.getRecallSectionNumber("response-guidance", "maxTurns") ??
|
|
9696
|
+
this.config.responseGuidanceRecallScanWindowTurns,
|
|
9697
|
+
maxScanWindowTokens:
|
|
9698
|
+
this.getRecallSectionNumber("response-guidance", "maxTokens") ??
|
|
9699
|
+
this.config.responseGuidanceRecallScanWindowTokens,
|
|
9700
|
+
forceGeneric: responseGuidanceForcedByPipeline,
|
|
9701
|
+
});
|
|
9697
9702
|
if (responseGuidanceSection) {
|
|
9698
9703
|
this.appendRecallSection(
|
|
9699
9704
|
sectionBuckets,
|
|
@@ -9722,13 +9727,22 @@ export class Orchestrator {
|
|
|
9722
9727
|
shouldRecallEventOrderEvidence(retrievalQuery)
|
|
9723
9728
|
) {
|
|
9724
9729
|
try {
|
|
9730
|
+
// #1495 thread 3 + #1505 fallback unification: read across the ordered LCM
|
|
9731
|
+
// read key set so a branch-scoped session reads its own chronological
|
|
9732
|
+
// event-order evidence even at project/root scope. UNLIKE the relevance-
|
|
9733
|
+
// ranked sections, event-order must NOT merge across keys: `turn_index` is
|
|
9734
|
+
// LOCAL to each LCM `session_id` (`observe` numbers turns per session via
|
|
9735
|
+
// `getMaxTurnIndex`), so interleaving two keys and sorting by `turn_index`
|
|
9736
|
+
// would place an older project-scope turn after a newer branch-scope turn
|
|
9737
|
+
// and misstate the chronology (#1505 codex P2). Like compressed-history,
|
|
9738
|
+
// event-order is an inherently per-session ORDERED artifact, so it takes
|
|
9739
|
+
// the highest-priority authorized key (primary overlay → project/root)
|
|
9740
|
+
// that actually has chronological evidence — each key's timeline is
|
|
9741
|
+
// internally consistent.
|
|
9725
9742
|
const eventOrderSection = await firstNonEmptyLcmRead(
|
|
9726
9743
|
(lcmSessionId) =>
|
|
9727
9744
|
buildEventOrderRecallSection({
|
|
9728
9745
|
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
9746
|
sessionId: lcmSessionId,
|
|
9733
9747
|
query: retrievalQuery,
|
|
9734
9748
|
maxChars: eventOrderMaxChars,
|
|
@@ -9918,21 +9932,59 @@ export class Orchestrator {
|
|
|
9918
9932
|
(recallMode as RecallPlanMode) !== "no_recall"
|
|
9919
9933
|
) {
|
|
9920
9934
|
try {
|
|
9921
|
-
|
|
9922
|
-
|
|
9923
|
-
|
|
9924
|
-
|
|
9925
|
-
|
|
9926
|
-
|
|
9927
|
-
|
|
9928
|
-
|
|
9929
|
-
|
|
9930
|
-
|
|
9931
|
-
|
|
9932
|
-
|
|
9933
|
-
|
|
9934
|
-
|
|
9935
|
+
// #1495 + #1505 fallback unification: read across the ordered LCM read
|
|
9936
|
+
// key set so a branch-scoped session reads its own structured
|
|
9937
|
+
// message-part evidence even when archived at project/root scope.
|
|
9938
|
+
// #1505 codex P2: structured matches are query-SCORED evidence, so MERGE
|
|
9939
|
+
// across EVERY key (primary overlay → project/root fallbacks) instead of
|
|
9940
|
+
// short-circuiting on the first non-empty key — a weak branch-key hit must
|
|
9941
|
+
// not mask stronger project-fallback parts. Keys are queried in priority
|
|
9942
|
+
// order; dedupe by session_id+turn_index+part_id keeps the primary key's
|
|
9943
|
+
// row on collision. `formatStructuredRecall` applies the single budget
|
|
9944
|
+
// below. A sessionless key (`undefined`) normalizes to "" → no matches
|
|
9945
|
+
// (structured parts are inherently per-session; pre-#1505 behavior, codex
|
|
9946
|
+
// P2).
|
|
9947
|
+
// FAULT ISOLATION (allSettled, not all): the pre-#1505 first-non-empty read
|
|
9948
|
+
// short-circuited, so a fallback key was often never queried and its latent
|
|
9949
|
+
// search failure never surfaced. Querying every key eagerly must NOT let one
|
|
9950
|
+
// key's failure (e.g. a SqliteError from a corrupt/locked fallback index)
|
|
9951
|
+
// reject the batch and discard the OTHER keys' parts — or, since this and
|
|
9952
|
+
// the compressed-history read below share one try block, silently drop the
|
|
9953
|
+
// compressed-history section a healthy primary key would still produce. So
|
|
9954
|
+
// read each key independently and keep the fulfilled batches.
|
|
9955
|
+
const structuredSettled = await Promise.allSettled(
|
|
9956
|
+
lcmReadSessionIds.map((lcmSessionId) =>
|
|
9957
|
+
this.lcmEngine!.searchStructuredParts(lcmSessionId ?? "", retrievalQuery),
|
|
9958
|
+
),
|
|
9935
9959
|
);
|
|
9960
|
+
for (const settled of structuredSettled) {
|
|
9961
|
+
if (settled.status === "rejected") {
|
|
9962
|
+
log.debug(
|
|
9963
|
+
`LCM structured-parts read failed for one key: ${settled.reason}`,
|
|
9964
|
+
);
|
|
9965
|
+
}
|
|
9966
|
+
}
|
|
9967
|
+
const seenStructuredParts = new Set<string>();
|
|
9968
|
+
const structuredMatches = structuredSettled
|
|
9969
|
+
.flatMap((settled) => (settled.status === "fulfilled" ? settled.value : []))
|
|
9970
|
+
.filter((match) => {
|
|
9971
|
+
const key = `${match.session_id} ${match.turn_index} ${match.part_id}`;
|
|
9972
|
+
if (seenStructuredParts.has(key)) return false;
|
|
9973
|
+
seenStructuredParts.add(key);
|
|
9974
|
+
return true;
|
|
9975
|
+
})
|
|
9976
|
+
// Restore the archive's per-key ordering (score DESC, then turn DESC)
|
|
9977
|
+
// across the MERGED set so the strongest parts win the shared budget in
|
|
9978
|
+
// `formatStructuredRecall` — otherwise weak primary-key parts could crowd
|
|
9979
|
+
// out stronger fallback parts. Stable sort: a single key is already in
|
|
9980
|
+
// this order, so it stays byte-for-byte the pre-#1505 behavior.
|
|
9981
|
+
// `?? 0` is defensive: `LcmStructuredRecallMatch.score` is always a
|
|
9982
|
+
// number here, but a bare `b.score - a.score` would yield NaN (falsy)
|
|
9983
|
+
// for any future unscored match and silently fall through to turn order.
|
|
9984
|
+
.sort(
|
|
9985
|
+
(a, b) =>
|
|
9986
|
+
(b.score ?? 0) - (a.score ?? 0) || b.turn_index - a.turn_index,
|
|
9987
|
+
);
|
|
9936
9988
|
const structuredSection = this.lcmEngine.formatStructuredRecall(
|
|
9937
9989
|
structuredMatches,
|
|
9938
9990
|
Math.ceil(this.config.recallBudgetChars * 0.08),
|
|
@@ -9955,15 +10007,20 @@ export class Orchestrator {
|
|
|
9955
10007
|
}
|
|
9956
10008
|
}
|
|
9957
10009
|
}
|
|
10010
|
+
// #1495 + #1505 fallback unification: read across the ordered LCM read key
|
|
10011
|
+
// set so a branch-scoped session reads its own compressed-history evidence
|
|
10012
|
+
// even at project/root scope. UNLIKE the query-scored sections above, the
|
|
10013
|
+
// compressed history is a per-session HOLISTIC DAG narrative, not a set of
|
|
10014
|
+
// independently-rankable evidence items — concatenating two sessions'
|
|
10015
|
+
// summaries would double-count the conversation and blow the budget, and
|
|
10016
|
+
// there is no per-item id to dedupe on. So this section deliberately keeps
|
|
10017
|
+
// first-non-empty semantics (#1505 codex P2 scope: "merge the query-matched
|
|
10018
|
+
// sections"): the highest-priority authorized key (primary overlay →
|
|
10019
|
+
// project/root) that actually has a compressed history wins. A sessionless
|
|
10020
|
+
// key (`undefined`) normalizes to empty → no section (pre-#1505 behavior).
|
|
9958
10021
|
const lcmSection = await firstNonEmptyLcmRead(
|
|
9959
10022
|
(lcmSessionId) =>
|
|
9960
10023
|
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
10024
|
lcmSessionId ?? "",
|
|
9968
10025
|
this.config.recallBudgetChars,
|
|
9969
10026
|
),
|