@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
|
@@ -0,0 +1,402 @@
|
|
|
1
|
+
import assert from "node:assert/strict";
|
|
2
|
+
import { mkdtemp, rm } from "node:fs/promises";
|
|
3
|
+
import os from "node:os";
|
|
4
|
+
import path from "node:path";
|
|
5
|
+
import test from "node:test";
|
|
6
|
+
|
|
7
|
+
import { EngramAccessService } from "./access-service.js";
|
|
8
|
+
import { parseConfig } from "./config.js";
|
|
9
|
+
import { namespaceCollectionName } from "./namespaces/search.js";
|
|
10
|
+
import type { Orchestrator } from "./orchestrator.js";
|
|
11
|
+
import type { SearchBackend } from "./search/port.js";
|
|
12
|
+
|
|
13
|
+
function makeQmd(overrides: Partial<SearchBackend> & Record<string, unknown>): SearchBackend {
|
|
14
|
+
return {
|
|
15
|
+
async probe() {
|
|
16
|
+
return false;
|
|
17
|
+
},
|
|
18
|
+
isAvailable() {
|
|
19
|
+
return false;
|
|
20
|
+
},
|
|
21
|
+
debugStatus() {
|
|
22
|
+
return "backend=noop";
|
|
23
|
+
},
|
|
24
|
+
async search() {
|
|
25
|
+
return [];
|
|
26
|
+
},
|
|
27
|
+
async searchGlobal() {
|
|
28
|
+
return [];
|
|
29
|
+
},
|
|
30
|
+
async bm25Search() {
|
|
31
|
+
return [];
|
|
32
|
+
},
|
|
33
|
+
async vectorSearch() {
|
|
34
|
+
return [];
|
|
35
|
+
},
|
|
36
|
+
async hybridSearch() {
|
|
37
|
+
return [];
|
|
38
|
+
},
|
|
39
|
+
async update() {},
|
|
40
|
+
async updateCollection() {},
|
|
41
|
+
async embed() {},
|
|
42
|
+
async embedCollection() {},
|
|
43
|
+
async ensureCollection() {
|
|
44
|
+
return "skipped";
|
|
45
|
+
},
|
|
46
|
+
...overrides,
|
|
47
|
+
} as SearchBackend;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
test("health reports active QMD version and collection state", async () => {
|
|
51
|
+
const memoryDir = await mkdtemp(path.join(os.tmpdir(), "remnic-health-qmd-"));
|
|
52
|
+
try {
|
|
53
|
+
const config = parseConfig({
|
|
54
|
+
memoryDir,
|
|
55
|
+
searchBackend: "qmd",
|
|
56
|
+
qmdEnabled: true,
|
|
57
|
+
qmdCollection: "remnic-memory",
|
|
58
|
+
});
|
|
59
|
+
const qmd = makeQmd({
|
|
60
|
+
probe: async () => true,
|
|
61
|
+
isAvailable: () => true,
|
|
62
|
+
debugStatus: () => "cli=true daemon=false cliPath=qmd cliVersion=qmd 2.5.3",
|
|
63
|
+
checkCollection: async (collection, execution) => {
|
|
64
|
+
assert.equal(collection, "remnic-memory");
|
|
65
|
+
assert.ok(execution?.signal instanceof AbortSignal);
|
|
66
|
+
return "present";
|
|
67
|
+
},
|
|
68
|
+
getVersionStatus: () => ({
|
|
69
|
+
installedVersion: "qmd 2.5.3",
|
|
70
|
+
supportedVersion: "2.5.3",
|
|
71
|
+
supported: true,
|
|
72
|
+
newerThanSupported: false,
|
|
73
|
+
upgradeAvailable: false,
|
|
74
|
+
capabilities: { doctor: true },
|
|
75
|
+
}),
|
|
76
|
+
isDaemonMode: () => false,
|
|
77
|
+
});
|
|
78
|
+
const service = new EngramAccessService({
|
|
79
|
+
config,
|
|
80
|
+
qmd,
|
|
81
|
+
async getStorage() {
|
|
82
|
+
return { dir: memoryDir };
|
|
83
|
+
},
|
|
84
|
+
} as unknown as Orchestrator);
|
|
85
|
+
|
|
86
|
+
const health = await service.health();
|
|
87
|
+
|
|
88
|
+
assert.equal(health.qmdEnabled, true);
|
|
89
|
+
assert.deepEqual(health.qmd, {
|
|
90
|
+
enabled: true,
|
|
91
|
+
active: true,
|
|
92
|
+
degraded: false,
|
|
93
|
+
mode: "cli",
|
|
94
|
+
collection: "remnic-memory",
|
|
95
|
+
collectionState: "present",
|
|
96
|
+
installedVersion: "qmd 2.5.3",
|
|
97
|
+
supportedVersion: "2.5.3",
|
|
98
|
+
supported: true,
|
|
99
|
+
upgradeAvailable: false,
|
|
100
|
+
doctorAvailable: true,
|
|
101
|
+
debugStatus: "cli=true daemon=false cliPath=qmd cliVersion=qmd 2.5.3",
|
|
102
|
+
});
|
|
103
|
+
} finally {
|
|
104
|
+
await rm(memoryDir, { recursive: true, force: true });
|
|
105
|
+
}
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
test("health marks QMD as degraded when configured search falls back to noop", async () => {
|
|
109
|
+
const memoryDir = await mkdtemp(path.join(os.tmpdir(), "remnic-health-qmd-degraded-"));
|
|
110
|
+
try {
|
|
111
|
+
const config = parseConfig({
|
|
112
|
+
memoryDir,
|
|
113
|
+
searchBackend: "qmd",
|
|
114
|
+
qmdEnabled: true,
|
|
115
|
+
qmdCollection: "remnic-memory",
|
|
116
|
+
});
|
|
117
|
+
const service = new EngramAccessService({
|
|
118
|
+
config,
|
|
119
|
+
qmd: makeQmd({}),
|
|
120
|
+
async getStorage() {
|
|
121
|
+
return { dir: memoryDir };
|
|
122
|
+
},
|
|
123
|
+
} as unknown as Orchestrator);
|
|
124
|
+
|
|
125
|
+
const health = await service.health();
|
|
126
|
+
|
|
127
|
+
assert.equal(health.qmd.active, false);
|
|
128
|
+
assert.equal(health.qmd.degraded, true);
|
|
129
|
+
assert.equal(health.qmd.mode, "fallback");
|
|
130
|
+
assert.equal(health.qmd.collectionState, "unknown");
|
|
131
|
+
assert.equal(health.qmd.debugStatus, "backend=noop");
|
|
132
|
+
} finally {
|
|
133
|
+
await rm(memoryDir, { recursive: true, force: true });
|
|
134
|
+
}
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
test("health marks QMD as degraded when the checked collection is missing", async () => {
|
|
138
|
+
const memoryDir = await mkdtemp(path.join(os.tmpdir(), "remnic-health-qmd-missing-"));
|
|
139
|
+
try {
|
|
140
|
+
const config = parseConfig({
|
|
141
|
+
memoryDir,
|
|
142
|
+
searchBackend: "qmd",
|
|
143
|
+
qmdEnabled: true,
|
|
144
|
+
qmdCollection: "remnic-memory",
|
|
145
|
+
});
|
|
146
|
+
const service = new EngramAccessService({
|
|
147
|
+
config,
|
|
148
|
+
qmd: makeQmd({
|
|
149
|
+
probe: async () => true,
|
|
150
|
+
isAvailable: () => true,
|
|
151
|
+
debugStatus: () => "cli=true daemon=false cliPath=qmd cliVersion=qmd 2.5.3",
|
|
152
|
+
checkCollection: async () => "missing",
|
|
153
|
+
isDaemonMode: () => false,
|
|
154
|
+
}),
|
|
155
|
+
async getStorage() {
|
|
156
|
+
return { dir: memoryDir };
|
|
157
|
+
},
|
|
158
|
+
} as unknown as Orchestrator);
|
|
159
|
+
|
|
160
|
+
const health = await service.health();
|
|
161
|
+
|
|
162
|
+
assert.equal(health.qmd.collectionState, "missing");
|
|
163
|
+
assert.equal(health.qmd.active, false);
|
|
164
|
+
assert.equal(health.qmd.degraded, true);
|
|
165
|
+
assert.equal(health.qmd.mode, "fallback");
|
|
166
|
+
} finally {
|
|
167
|
+
await rm(memoryDir, { recursive: true, force: true });
|
|
168
|
+
}
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
test("health reports degraded diagnostics when read-only root QMD probe fails", async () => {
|
|
172
|
+
const memoryDir = await mkdtemp(path.join(os.tmpdir(), "remnic-health-qmd-stale-probe-"));
|
|
173
|
+
try {
|
|
174
|
+
const config = parseConfig({
|
|
175
|
+
memoryDir,
|
|
176
|
+
searchBackend: "qmd",
|
|
177
|
+
qmdEnabled: true,
|
|
178
|
+
qmdCollection: "remnic-memory",
|
|
179
|
+
});
|
|
180
|
+
const service = new EngramAccessService({
|
|
181
|
+
config,
|
|
182
|
+
qmd: makeQmd({
|
|
183
|
+
probe: async () => false,
|
|
184
|
+
isAvailable: () => true,
|
|
185
|
+
debugStatus: () => "cli=true daemon=false cliPath=qmd cliVersion=qmd 2.5.3",
|
|
186
|
+
checkCollection: async () => "present",
|
|
187
|
+
isDaemonMode: () => false,
|
|
188
|
+
}),
|
|
189
|
+
async getStorage() {
|
|
190
|
+
return { dir: memoryDir };
|
|
191
|
+
},
|
|
192
|
+
} as unknown as Orchestrator);
|
|
193
|
+
|
|
194
|
+
const health = await service.health();
|
|
195
|
+
|
|
196
|
+
assert.equal(health.qmd.active, true);
|
|
197
|
+
assert.equal(health.qmd.degraded, true);
|
|
198
|
+
assert.equal(health.qmd.mode, "cli");
|
|
199
|
+
assert.equal(health.qmd.collectionState, "unknown");
|
|
200
|
+
} finally {
|
|
201
|
+
await rm(memoryDir, { recursive: true, force: true });
|
|
202
|
+
}
|
|
203
|
+
});
|
|
204
|
+
|
|
205
|
+
test("health uses read-only root QMD availability checks", async () => {
|
|
206
|
+
const memoryDir = await mkdtemp(path.join(os.tmpdir(), "remnic-health-qmd-readonly-"));
|
|
207
|
+
try {
|
|
208
|
+
const config = parseConfig({
|
|
209
|
+
memoryDir,
|
|
210
|
+
searchBackend: "qmd",
|
|
211
|
+
qmdEnabled: true,
|
|
212
|
+
qmdCollection: "remnic-memory",
|
|
213
|
+
});
|
|
214
|
+
let checkAvailabilityCalled = false;
|
|
215
|
+
let probeCalled = false;
|
|
216
|
+
const service = new EngramAccessService({
|
|
217
|
+
config,
|
|
218
|
+
qmd: makeQmd({
|
|
219
|
+
probe: async () => {
|
|
220
|
+
probeCalled = true;
|
|
221
|
+
return true;
|
|
222
|
+
},
|
|
223
|
+
checkAvailability: async (execution) => {
|
|
224
|
+
checkAvailabilityCalled = true;
|
|
225
|
+
assert.ok(execution?.signal instanceof AbortSignal);
|
|
226
|
+
return true;
|
|
227
|
+
},
|
|
228
|
+
isAvailable: () => true,
|
|
229
|
+
debugStatus: () => "cli=true daemon=false cliPath=qmd cliVersion=qmd 2.5.3",
|
|
230
|
+
checkCollection: async () => "present",
|
|
231
|
+
isDaemonMode: () => false,
|
|
232
|
+
}),
|
|
233
|
+
async getStorage() {
|
|
234
|
+
return { dir: memoryDir };
|
|
235
|
+
},
|
|
236
|
+
} as unknown as Orchestrator);
|
|
237
|
+
|
|
238
|
+
const health = await service.health();
|
|
239
|
+
|
|
240
|
+
assert.equal(checkAvailabilityCalled, true);
|
|
241
|
+
assert.equal(probeCalled, false);
|
|
242
|
+
assert.equal(health.qmd.active, true);
|
|
243
|
+
assert.equal(health.qmd.degraded, false);
|
|
244
|
+
} finally {
|
|
245
|
+
await rm(memoryDir, { recursive: true, force: true });
|
|
246
|
+
}
|
|
247
|
+
});
|
|
248
|
+
|
|
249
|
+
test("health reports namespace-scoped QMD backend state", async () => {
|
|
250
|
+
const rootDir = await mkdtemp(path.join(os.tmpdir(), "remnic-health-qmd-ns-"));
|
|
251
|
+
try {
|
|
252
|
+
const teamDir = path.join(rootDir, "namespaces", "team");
|
|
253
|
+
const config = parseConfig({
|
|
254
|
+
memoryDir: rootDir,
|
|
255
|
+
namespacesEnabled: true,
|
|
256
|
+
defaultNamespace: "default",
|
|
257
|
+
searchBackend: "qmd",
|
|
258
|
+
qmdEnabled: true,
|
|
259
|
+
qmdCollection: "remnic-memory",
|
|
260
|
+
namespacePolicies: [{ name: "team", readPrincipals: [], writePrincipals: [] }],
|
|
261
|
+
});
|
|
262
|
+
const expectedCollection = namespaceCollectionName("remnic-memory", "team", {
|
|
263
|
+
defaultNamespace: "default",
|
|
264
|
+
});
|
|
265
|
+
const service = new EngramAccessService({
|
|
266
|
+
config,
|
|
267
|
+
qmd: makeQmd({}),
|
|
268
|
+
async getStorage(namespace: string) {
|
|
269
|
+
return { dir: namespace === "team" ? teamDir : rootDir };
|
|
270
|
+
},
|
|
271
|
+
async searchHealthForNamespace(namespace: string, execution?: { signal?: AbortSignal }) {
|
|
272
|
+
assert.equal(namespace, "team");
|
|
273
|
+
assert.ok(execution?.signal instanceof AbortSignal);
|
|
274
|
+
return {
|
|
275
|
+
collection: expectedCollection,
|
|
276
|
+
available: true,
|
|
277
|
+
collectionState: "present",
|
|
278
|
+
debugStatus: "cli=true daemon=false cliPath=qmd cliVersion=qmd 2.5.3",
|
|
279
|
+
installedVersion: "qmd 2.5.3",
|
|
280
|
+
supportedVersion: "2.5.3",
|
|
281
|
+
supported: true,
|
|
282
|
+
upgradeAvailable: false,
|
|
283
|
+
doctorAvailable: true,
|
|
284
|
+
daemonMode: false,
|
|
285
|
+
};
|
|
286
|
+
},
|
|
287
|
+
} as unknown as Orchestrator);
|
|
288
|
+
|
|
289
|
+
const health = await service.health("team");
|
|
290
|
+
|
|
291
|
+
assert.equal(health.memoryDir, teamDir);
|
|
292
|
+
assert.equal(health.qmd.active, true);
|
|
293
|
+
assert.equal(health.qmd.degraded, false);
|
|
294
|
+
assert.equal(health.qmd.collection, expectedCollection);
|
|
295
|
+
assert.equal(health.qmd.collectionState, "present");
|
|
296
|
+
assert.equal(health.qmd.installedVersion, "qmd 2.5.3");
|
|
297
|
+
} finally {
|
|
298
|
+
await rm(rootDir, { recursive: true, force: true });
|
|
299
|
+
}
|
|
300
|
+
});
|
|
301
|
+
|
|
302
|
+
test("health marks namespace QMD unknown collection state degraded", async () => {
|
|
303
|
+
const rootDir = await mkdtemp(path.join(os.tmpdir(), "remnic-health-qmd-ns-unknown-"));
|
|
304
|
+
try {
|
|
305
|
+
const teamDir = path.join(rootDir, "namespaces", "team");
|
|
306
|
+
const config = parseConfig({
|
|
307
|
+
memoryDir: rootDir,
|
|
308
|
+
namespacesEnabled: true,
|
|
309
|
+
defaultNamespace: "default",
|
|
310
|
+
searchBackend: "qmd",
|
|
311
|
+
qmdEnabled: true,
|
|
312
|
+
qmdCollection: "remnic-memory",
|
|
313
|
+
namespacePolicies: [{ name: "team", readPrincipals: [], writePrincipals: [] }],
|
|
314
|
+
});
|
|
315
|
+
const expectedCollection = namespaceCollectionName("remnic-memory", "team", {
|
|
316
|
+
defaultNamespace: "default",
|
|
317
|
+
});
|
|
318
|
+
const service = new EngramAccessService({
|
|
319
|
+
config,
|
|
320
|
+
qmd: makeQmd({}),
|
|
321
|
+
async getStorage(namespace: string) {
|
|
322
|
+
return { dir: namespace === "team" ? teamDir : rootDir };
|
|
323
|
+
},
|
|
324
|
+
async searchHealthForNamespace(namespace: string, execution?: { signal?: AbortSignal }) {
|
|
325
|
+
assert.equal(namespace, "team");
|
|
326
|
+
assert.ok(execution?.signal instanceof AbortSignal);
|
|
327
|
+
return {
|
|
328
|
+
collection: expectedCollection,
|
|
329
|
+
available: true,
|
|
330
|
+
collectionState: "unknown",
|
|
331
|
+
debugStatus: "cli=true collection=unknown",
|
|
332
|
+
installedVersion: "qmd 2.5.3",
|
|
333
|
+
supportedVersion: "2.5.3",
|
|
334
|
+
supported: true,
|
|
335
|
+
upgradeAvailable: false,
|
|
336
|
+
doctorAvailable: true,
|
|
337
|
+
daemonMode: false,
|
|
338
|
+
};
|
|
339
|
+
},
|
|
340
|
+
} as unknown as Orchestrator);
|
|
341
|
+
|
|
342
|
+
const health = await service.health("team");
|
|
343
|
+
|
|
344
|
+
assert.equal(health.memoryDir, teamDir);
|
|
345
|
+
assert.equal(health.qmd.active, true);
|
|
346
|
+
assert.equal(health.qmd.degraded, true);
|
|
347
|
+
assert.equal(health.qmd.collection, expectedCollection);
|
|
348
|
+
assert.equal(health.qmd.collectionState, "unknown");
|
|
349
|
+
assert.equal(health.qmd.mode, "cli");
|
|
350
|
+
} finally {
|
|
351
|
+
await rm(rootDir, { recursive: true, force: true });
|
|
352
|
+
}
|
|
353
|
+
});
|
|
354
|
+
|
|
355
|
+
test("health keeps namespace QMD failures scoped to the namespace", async () => {
|
|
356
|
+
const rootDir = await mkdtemp(path.join(os.tmpdir(), "remnic-health-qmd-ns-fail-"));
|
|
357
|
+
try {
|
|
358
|
+
const teamDir = path.join(rootDir, "namespaces", "team");
|
|
359
|
+
const config = parseConfig({
|
|
360
|
+
memoryDir: rootDir,
|
|
361
|
+
namespacesEnabled: true,
|
|
362
|
+
defaultNamespace: "default",
|
|
363
|
+
searchBackend: "qmd",
|
|
364
|
+
qmdEnabled: true,
|
|
365
|
+
qmdCollection: "remnic-memory",
|
|
366
|
+
namespacePolicies: [{ name: "team", readPrincipals: [], writePrincipals: [] }],
|
|
367
|
+
});
|
|
368
|
+
const expectedCollection = namespaceCollectionName("remnic-memory", "team", {
|
|
369
|
+
defaultNamespace: "default",
|
|
370
|
+
});
|
|
371
|
+
const service = new EngramAccessService({
|
|
372
|
+
config,
|
|
373
|
+
qmd: makeQmd({
|
|
374
|
+
probe: async () => true,
|
|
375
|
+
isAvailable: () => true,
|
|
376
|
+
debugStatus: () => "root-qmd-should-not-leak",
|
|
377
|
+
checkCollection: async () => "present",
|
|
378
|
+
}),
|
|
379
|
+
async getStorage(namespace: string) {
|
|
380
|
+
return { dir: namespace === "team" ? teamDir : rootDir };
|
|
381
|
+
},
|
|
382
|
+
async searchHealthForNamespace() {
|
|
383
|
+
throw new Error(`namespace probe timed out at ${teamDir}/qmd/index.sqlite`);
|
|
384
|
+
},
|
|
385
|
+
} as unknown as Orchestrator);
|
|
386
|
+
|
|
387
|
+
const health = await service.health("team");
|
|
388
|
+
|
|
389
|
+
assert.equal(health.memoryDir, teamDir);
|
|
390
|
+
assert.equal(health.qmd.collection, expectedCollection);
|
|
391
|
+
assert.equal(health.qmd.active, false);
|
|
392
|
+
assert.equal(health.qmd.degraded, true);
|
|
393
|
+
assert.equal(health.qmd.collectionState, "unknown");
|
|
394
|
+
assert.equal(health.qmd.mode, "fallback");
|
|
395
|
+
assert.equal(health.qmd.debugStatus, "backend=namespace-unavailable error=Error");
|
|
396
|
+
assert.doesNotMatch(health.qmd.debugStatus, /root-qmd-should-not-leak/);
|
|
397
|
+
assert.doesNotMatch(health.qmd.debugStatus, /index\.sqlite/);
|
|
398
|
+
assert.doesNotMatch(health.qmd.debugStatus, /remnic-health-qmd-ns-fail/);
|
|
399
|
+
} finally {
|
|
400
|
+
await rm(rootDir, { recursive: true, force: true });
|
|
401
|
+
}
|
|
402
|
+
});
|
package/src/access-service.ts
CHANGED
|
@@ -38,6 +38,7 @@ import {
|
|
|
38
38
|
runMemoryGovernance,
|
|
39
39
|
} from "./maintenance/memory-governance.js";
|
|
40
40
|
import { runProcedureMining } from "./procedural/procedure-miner.js";
|
|
41
|
+
import { displayErrorDetail } from "./runtime/better-sqlite.js";
|
|
41
42
|
import type { PatternReinforcementResult } from "./maintenance/pattern-reinforcement.js";
|
|
42
43
|
import type { LiveConnectorsRunSummary } from "./live-connectors-runner.js";
|
|
43
44
|
import type { WearablesService } from "./wearables/service.js";
|
|
@@ -279,10 +280,32 @@ export interface EngramAccessHealthResponse {
|
|
|
279
280
|
defaultNamespace: string;
|
|
280
281
|
searchBackend: string;
|
|
281
282
|
qmdEnabled: boolean;
|
|
283
|
+
qmd: EngramAccessQmdHealthResponse;
|
|
282
284
|
nativeKnowledgeEnabled: boolean;
|
|
283
285
|
projectionAvailable: boolean;
|
|
284
286
|
}
|
|
285
287
|
|
|
288
|
+
export type EngramAccessQmdCollectionState =
|
|
289
|
+
| "present"
|
|
290
|
+
| "missing"
|
|
291
|
+
| "unknown"
|
|
292
|
+
| "skipped";
|
|
293
|
+
|
|
294
|
+
export interface EngramAccessQmdHealthResponse {
|
|
295
|
+
enabled: boolean;
|
|
296
|
+
active: boolean;
|
|
297
|
+
degraded: boolean;
|
|
298
|
+
mode: "cli" | "daemon" | "fallback" | "disabled" | "not-selected";
|
|
299
|
+
collection: string;
|
|
300
|
+
collectionState: EngramAccessQmdCollectionState;
|
|
301
|
+
installedVersion: string | null;
|
|
302
|
+
supportedVersion: string | null;
|
|
303
|
+
supported: boolean | null;
|
|
304
|
+
upgradeAvailable: boolean | null;
|
|
305
|
+
doctorAvailable: boolean | null;
|
|
306
|
+
debugStatus: string;
|
|
307
|
+
}
|
|
308
|
+
|
|
286
309
|
export interface EngramAccessRecallRequest {
|
|
287
310
|
query: string;
|
|
288
311
|
sessionKey?: string;
|
|
@@ -2412,6 +2435,8 @@ export class EngramAccessService {
|
|
|
2412
2435
|
async health(namespace?: string): Promise<EngramAccessHealthResponse> {
|
|
2413
2436
|
const resolvedNamespace = this.resolveNamespace(namespace);
|
|
2414
2437
|
const storage = await this.orchestrator.getStorage(resolvedNamespace);
|
|
2438
|
+
const searchBackend = this.orchestrator.config.searchBackend ?? "qmd";
|
|
2439
|
+
const qmdEnabled = this.orchestrator.config.qmdEnabled === true;
|
|
2415
2440
|
let projectionAvailable = false;
|
|
2416
2441
|
try {
|
|
2417
2442
|
await stat(getMemoryProjectionPath(storage.dir));
|
|
@@ -2425,13 +2450,260 @@ export class EngramAccessService {
|
|
|
2425
2450
|
memoryDir: storage.dir,
|
|
2426
2451
|
namespacesEnabled: this.orchestrator.config.namespacesEnabled === true,
|
|
2427
2452
|
defaultNamespace: this.orchestrator.config.defaultNamespace,
|
|
2428
|
-
searchBackend
|
|
2429
|
-
qmdEnabled
|
|
2453
|
+
searchBackend,
|
|
2454
|
+
qmdEnabled,
|
|
2455
|
+
qmd: await this.qmdHealth(
|
|
2456
|
+
searchBackend,
|
|
2457
|
+
qmdEnabled,
|
|
2458
|
+
resolvedNamespace,
|
|
2459
|
+
this.qmdCollectionForHealth(resolvedNamespace, storage.dir),
|
|
2460
|
+
),
|
|
2430
2461
|
nativeKnowledgeEnabled: this.orchestrator.config.nativeKnowledge?.enabled === true,
|
|
2431
2462
|
projectionAvailable,
|
|
2432
2463
|
};
|
|
2433
2464
|
}
|
|
2434
2465
|
|
|
2466
|
+
private qmdCollectionForHealth(namespace: string, storageDir: string): string {
|
|
2467
|
+
if (this.orchestrator.config.namespacesEnabled !== true) {
|
|
2468
|
+
return this.orchestrator.config.qmdCollection;
|
|
2469
|
+
}
|
|
2470
|
+
|
|
2471
|
+
const useLegacyDefaultCollection =
|
|
2472
|
+
namespace === this.orchestrator.config.defaultNamespace &&
|
|
2473
|
+
storageDir === this.orchestrator.config.memoryDir;
|
|
2474
|
+
return namespaceCollectionName(this.orchestrator.config.qmdCollection, namespace, {
|
|
2475
|
+
defaultNamespace: this.orchestrator.config.defaultNamespace,
|
|
2476
|
+
useLegacyDefaultCollection,
|
|
2477
|
+
});
|
|
2478
|
+
}
|
|
2479
|
+
|
|
2480
|
+
private async qmdHealth(
|
|
2481
|
+
searchBackend: string,
|
|
2482
|
+
qmdEnabled: boolean,
|
|
2483
|
+
namespace: string,
|
|
2484
|
+
collection: string,
|
|
2485
|
+
): Promise<EngramAccessQmdHealthResponse> {
|
|
2486
|
+
if (searchBackend !== "qmd" || !qmdEnabled) {
|
|
2487
|
+
return {
|
|
2488
|
+
enabled: qmdEnabled,
|
|
2489
|
+
active: false,
|
|
2490
|
+
degraded: false,
|
|
2491
|
+
mode: searchBackend !== "qmd" ? "not-selected" : "disabled",
|
|
2492
|
+
collection,
|
|
2493
|
+
collectionState: "skipped",
|
|
2494
|
+
installedVersion: null,
|
|
2495
|
+
supportedVersion: null,
|
|
2496
|
+
supported: null,
|
|
2497
|
+
upgradeAvailable: null,
|
|
2498
|
+
doctorAvailable: null,
|
|
2499
|
+
debugStatus: searchBackend !== "qmd" ? `backend=${searchBackend}` : "backend=disabled",
|
|
2500
|
+
};
|
|
2501
|
+
}
|
|
2502
|
+
|
|
2503
|
+
if (this.orchestrator.config.namespacesEnabled === true) {
|
|
2504
|
+
const namespaceHealth = await this.namespaceQmdHealth(searchBackend, qmdEnabled, namespace, collection);
|
|
2505
|
+
if (namespaceHealth) return namespaceHealth;
|
|
2506
|
+
}
|
|
2507
|
+
|
|
2508
|
+
const qmd = this.orchestrator.qmd;
|
|
2509
|
+
if (!qmd) {
|
|
2510
|
+
return {
|
|
2511
|
+
enabled: true,
|
|
2512
|
+
active: false,
|
|
2513
|
+
degraded: true,
|
|
2514
|
+
mode: "fallback",
|
|
2515
|
+
collection,
|
|
2516
|
+
collectionState: "unknown",
|
|
2517
|
+
installedVersion: null,
|
|
2518
|
+
supportedVersion: null,
|
|
2519
|
+
supported: null,
|
|
2520
|
+
upgradeAvailable: null,
|
|
2521
|
+
doctorAvailable: null,
|
|
2522
|
+
debugStatus: "backend=unavailable",
|
|
2523
|
+
};
|
|
2524
|
+
}
|
|
2525
|
+
const diagnosticAvailable = await this.qmdProbeAvailable(searchBackend, qmdEnabled);
|
|
2526
|
+
const operationalAvailable = diagnosticAvailable || qmd.isAvailable();
|
|
2527
|
+
const collectionState = diagnosticAvailable
|
|
2528
|
+
? await this.qmdCollectionState(searchBackend, qmdEnabled, collection)
|
|
2529
|
+
: "unknown";
|
|
2530
|
+
const active = operationalAvailable && collectionState !== "missing";
|
|
2531
|
+
const degraded =
|
|
2532
|
+
searchBackend === "qmd" &&
|
|
2533
|
+
qmdEnabled &&
|
|
2534
|
+
(!active || !diagnosticAvailable || collectionState === "unknown");
|
|
2535
|
+
const debugStatus = qmd.debugStatus();
|
|
2536
|
+
const versionStatus =
|
|
2537
|
+
"getVersionStatus" in qmd && typeof qmd.getVersionStatus === "function"
|
|
2538
|
+
? qmd.getVersionStatus()
|
|
2539
|
+
: null;
|
|
2540
|
+
const daemonMode =
|
|
2541
|
+
"isDaemonMode" in qmd && typeof qmd.isDaemonMode === "function"
|
|
2542
|
+
? qmd.isDaemonMode() === true
|
|
2543
|
+
: false;
|
|
2544
|
+
const mode =
|
|
2545
|
+
searchBackend !== "qmd"
|
|
2546
|
+
? "not-selected"
|
|
2547
|
+
: !qmdEnabled
|
|
2548
|
+
? "disabled"
|
|
2549
|
+
: !active
|
|
2550
|
+
? "fallback"
|
|
2551
|
+
: daemonMode
|
|
2552
|
+
? "daemon"
|
|
2553
|
+
: "cli";
|
|
2554
|
+
|
|
2555
|
+
return {
|
|
2556
|
+
enabled: qmdEnabled,
|
|
2557
|
+
active,
|
|
2558
|
+
degraded,
|
|
2559
|
+
mode,
|
|
2560
|
+
collection,
|
|
2561
|
+
collectionState,
|
|
2562
|
+
installedVersion: versionStatus?.installedVersion ?? null,
|
|
2563
|
+
supportedVersion: versionStatus?.supportedVersion ?? null,
|
|
2564
|
+
supported: versionStatus?.supported ?? null,
|
|
2565
|
+
upgradeAvailable: versionStatus?.upgradeAvailable ?? null,
|
|
2566
|
+
doctorAvailable: versionStatus?.capabilities?.doctor ?? null,
|
|
2567
|
+
debugStatus,
|
|
2568
|
+
};
|
|
2569
|
+
}
|
|
2570
|
+
|
|
2571
|
+
private async namespaceQmdHealth(
|
|
2572
|
+
searchBackend: string,
|
|
2573
|
+
qmdEnabled: boolean,
|
|
2574
|
+
namespace: string,
|
|
2575
|
+
fallbackCollection: string,
|
|
2576
|
+
): Promise<EngramAccessQmdHealthResponse | null> {
|
|
2577
|
+
if (searchBackend !== "qmd" || !qmdEnabled) return null;
|
|
2578
|
+
const searchHealthForNamespace = (
|
|
2579
|
+
this.orchestrator as Orchestrator & {
|
|
2580
|
+
searchHealthForNamespace?: (
|
|
2581
|
+
namespace: string,
|
|
2582
|
+
execution?: { signal?: AbortSignal },
|
|
2583
|
+
) => Promise<{
|
|
2584
|
+
collection: string;
|
|
2585
|
+
available: boolean;
|
|
2586
|
+
collectionState: EngramAccessQmdCollectionState;
|
|
2587
|
+
debugStatus: string;
|
|
2588
|
+
installedVersion: string | null;
|
|
2589
|
+
supportedVersion: string | null;
|
|
2590
|
+
supported: boolean | null;
|
|
2591
|
+
upgradeAvailable: boolean | null;
|
|
2592
|
+
doctorAvailable: boolean | null;
|
|
2593
|
+
daemonMode: boolean | null;
|
|
2594
|
+
}>;
|
|
2595
|
+
}
|
|
2596
|
+
).searchHealthForNamespace;
|
|
2597
|
+
if (typeof searchHealthForNamespace !== "function") return null;
|
|
2598
|
+
|
|
2599
|
+
const controller = new AbortController();
|
|
2600
|
+
const timer = setTimeout(() => controller.abort(), 2_000);
|
|
2601
|
+
timer.unref?.();
|
|
2602
|
+
try {
|
|
2603
|
+
const health = await searchHealthForNamespace.call(this.orchestrator, namespace, {
|
|
2604
|
+
signal: controller.signal,
|
|
2605
|
+
});
|
|
2606
|
+
const active = health.available && health.collectionState !== "missing";
|
|
2607
|
+
const degraded = !active || health.collectionState === "unknown";
|
|
2608
|
+
const mode =
|
|
2609
|
+
!active
|
|
2610
|
+
? "fallback"
|
|
2611
|
+
: health.daemonMode === true
|
|
2612
|
+
? "daemon"
|
|
2613
|
+
: "cli";
|
|
2614
|
+
|
|
2615
|
+
return {
|
|
2616
|
+
enabled: true,
|
|
2617
|
+
active,
|
|
2618
|
+
degraded,
|
|
2619
|
+
mode,
|
|
2620
|
+
collection: health.collection || fallbackCollection,
|
|
2621
|
+
collectionState: health.collectionState,
|
|
2622
|
+
installedVersion: health.installedVersion,
|
|
2623
|
+
supportedVersion: health.supportedVersion,
|
|
2624
|
+
supported: health.supported,
|
|
2625
|
+
upgradeAvailable: health.upgradeAvailable,
|
|
2626
|
+
doctorAvailable: health.doctorAvailable,
|
|
2627
|
+
debugStatus: health.debugStatus,
|
|
2628
|
+
};
|
|
2629
|
+
} catch (error) {
|
|
2630
|
+
const detail = displayErrorDetail(error) || "unknown";
|
|
2631
|
+
return {
|
|
2632
|
+
enabled: true,
|
|
2633
|
+
active: false,
|
|
2634
|
+
degraded: true,
|
|
2635
|
+
mode: "fallback",
|
|
2636
|
+
collection: fallbackCollection,
|
|
2637
|
+
collectionState: "unknown",
|
|
2638
|
+
installedVersion: null,
|
|
2639
|
+
supportedVersion: null,
|
|
2640
|
+
supported: null,
|
|
2641
|
+
upgradeAvailable: null,
|
|
2642
|
+
doctorAvailable: null,
|
|
2643
|
+
debugStatus: `backend=namespace-unavailable error=${detail}`,
|
|
2644
|
+
};
|
|
2645
|
+
} finally {
|
|
2646
|
+
clearTimeout(timer);
|
|
2647
|
+
}
|
|
2648
|
+
}
|
|
2649
|
+
|
|
2650
|
+
private async qmdCollectionState(
|
|
2651
|
+
searchBackend: string,
|
|
2652
|
+
qmdEnabled: boolean,
|
|
2653
|
+
collection: string,
|
|
2654
|
+
): Promise<EngramAccessQmdCollectionState> {
|
|
2655
|
+
if (searchBackend !== "qmd" || !qmdEnabled) return "skipped";
|
|
2656
|
+
const qmd = this.orchestrator.qmd;
|
|
2657
|
+
if (!qmd.isAvailable()) return "unknown";
|
|
2658
|
+
if (!qmd.checkCollection) return "skipped";
|
|
2659
|
+
|
|
2660
|
+
const controller = new AbortController();
|
|
2661
|
+
const timer = setTimeout(() => controller.abort(), 2_000);
|
|
2662
|
+
timer.unref?.();
|
|
2663
|
+
try {
|
|
2664
|
+
return await qmd.checkCollection(collection, {
|
|
2665
|
+
signal: controller.signal,
|
|
2666
|
+
});
|
|
2667
|
+
} catch {
|
|
2668
|
+
return "unknown";
|
|
2669
|
+
} finally {
|
|
2670
|
+
clearTimeout(timer);
|
|
2671
|
+
}
|
|
2672
|
+
}
|
|
2673
|
+
|
|
2674
|
+
private async qmdProbeAvailable(
|
|
2675
|
+
searchBackend: string,
|
|
2676
|
+
qmdEnabled: boolean,
|
|
2677
|
+
): Promise<boolean> {
|
|
2678
|
+
if (searchBackend !== "qmd" || !qmdEnabled) return false;
|
|
2679
|
+
const qmd = this.orchestrator.qmd;
|
|
2680
|
+
if (!qmd) return false;
|
|
2681
|
+
|
|
2682
|
+
const controller = new AbortController();
|
|
2683
|
+
const timer = setTimeout(() => controller.abort(), 2_000);
|
|
2684
|
+
timer.unref?.();
|
|
2685
|
+
try {
|
|
2686
|
+
return await new Promise<boolean>((resolve) => {
|
|
2687
|
+
const onAbort = () => {
|
|
2688
|
+
controller.signal.removeEventListener("abort", onAbort);
|
|
2689
|
+
resolve(false);
|
|
2690
|
+
};
|
|
2691
|
+
controller.signal.addEventListener("abort", onAbort, { once: true });
|
|
2692
|
+
const probe =
|
|
2693
|
+
typeof qmd.checkAvailability === "function"
|
|
2694
|
+
? qmd.checkAvailability({ signal: controller.signal })
|
|
2695
|
+
: qmd.probe();
|
|
2696
|
+
probe
|
|
2697
|
+
.then(resolve, () => resolve(false))
|
|
2698
|
+
.finally(() => {
|
|
2699
|
+
controller.signal.removeEventListener("abort", onAbort);
|
|
2700
|
+
});
|
|
2701
|
+
});
|
|
2702
|
+
} finally {
|
|
2703
|
+
clearTimeout(timer);
|
|
2704
|
+
}
|
|
2705
|
+
}
|
|
2706
|
+
|
|
2435
2707
|
async actionConfidence(
|
|
2436
2708
|
request: EngramAccessActionConfidenceRequest = {},
|
|
2437
2709
|
): Promise<EngramAccessActionConfidenceResponse> {
|