@psiclawops/hypermem 0.9.7 → 0.9.9
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/CHANGELOG.md +16 -0
- package/INSTALL.md +29 -9
- package/README.md +5 -1
- package/assets/default-config.json +20 -5
- package/assets/runtime-validation-fixture.json +123 -0
- package/bin/hypermem-cleanup.mjs +334 -0
- package/bin/hypermem-doctor.mjs +71 -0
- package/bin/hypermem-validate-runtime.mjs +282 -0
- package/dist/compositor.d.ts +43 -5
- package/dist/compositor.d.ts.map +1 -1
- package/dist/compositor.js +802 -30
- package/dist/entity-bridge-backfill.d.ts +66 -0
- package/dist/entity-bridge-backfill.d.ts.map +1 -0
- package/dist/entity-bridge-backfill.js +145 -0
- package/dist/entity-bridge-store.d.ts +164 -0
- package/dist/entity-bridge-store.d.ts.map +1 -0
- package/dist/entity-bridge-store.js +488 -0
- package/dist/entity-extractor.d.ts +124 -0
- package/dist/entity-extractor.d.ts.map +1 -0
- package/dist/entity-extractor.js +382 -0
- package/dist/entity-ppr.d.ts +55 -0
- package/dist/entity-ppr.d.ts.map +1 -0
- package/dist/entity-ppr.js +180 -0
- package/dist/hybrid-retrieval.d.ts +27 -0
- package/dist/hybrid-retrieval.d.ts.map +1 -1
- package/dist/hybrid-retrieval.js +26 -1
- package/dist/index.d.ts +19 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +63 -13
- package/dist/message-store.d.ts +36 -0
- package/dist/message-store.d.ts.map +1 -1
- package/dist/message-store.js +155 -1
- package/dist/open-domain.d.ts +13 -4
- package/dist/open-domain.d.ts.map +1 -1
- package/dist/open-domain.js +222 -20
- package/dist/profiles.js +13 -13
- package/dist/question-shape.d.ts +73 -0
- package/dist/question-shape.d.ts.map +1 -0
- package/dist/question-shape.js +230 -0
- package/dist/schema.d.ts +1 -1
- package/dist/schema.d.ts.map +1 -1
- package/dist/schema.js +92 -1
- package/dist/topic-detector.d.ts.map +1 -1
- package/dist/topic-detector.js +22 -9
- package/dist/types.d.ts +176 -2
- package/dist/types.d.ts.map +1 -1
- package/dist/vector-store.d.ts +6 -0
- package/dist/vector-store.d.ts.map +1 -1
- package/dist/vector-store.js +3 -0
- package/docs/DIAGNOSTICS.md +32 -0
- package/docs/INTEGRATION_VALIDATION.md +9 -4
- package/docs/TUNING.md +21 -21
- package/memory-plugin/dist/index.js +3 -1
- package/memory-plugin/package.json +8 -7
- package/package.json +10 -4
- package/plugin/dist/index.d.ts.map +1 -1
- package/plugin/dist/index.js +114 -11
- package/plugin/dist/index.js.map +1 -1
- package/plugin/package.json +9 -8
- package/scripts/install-runtime.mjs +4 -1
package/bin/hypermem-doctor.mjs
CHANGED
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
9
|
import { existsSync, readFileSync, readdirSync, statSync } from 'node:fs';
|
|
10
|
+
import { DatabaseSync } from 'node:sqlite';
|
|
10
11
|
import os from 'node:os';
|
|
11
12
|
import path from 'node:path';
|
|
12
13
|
import { spawnSync } from 'node:child_process';
|
|
@@ -37,6 +38,11 @@ Examples:
|
|
|
37
38
|
hypermem-doctor
|
|
38
39
|
hypermem-doctor --fix-plan
|
|
39
40
|
hypermem-doctor --json --strict
|
|
41
|
+
|
|
42
|
+
Replay duplicate warning:
|
|
43
|
+
If doctor reports stamped replay duplicate debt, run:
|
|
44
|
+
hypermem-cleanup --data-dir <path>
|
|
45
|
+
hypermem-cleanup --data-dir <path> --apply
|
|
40
46
|
`);
|
|
41
47
|
}
|
|
42
48
|
|
|
@@ -155,6 +161,53 @@ function findMessageDb(dir) {
|
|
|
155
161
|
return null;
|
|
156
162
|
}
|
|
157
163
|
|
|
164
|
+
function scanStampedReplayDuplicateDebt(dir) {
|
|
165
|
+
const agentsDir = path.join(dir, 'agents');
|
|
166
|
+
const summary = { agents: 0, duplicateGroups: 0, duplicateRows: 0, topAgents: [] };
|
|
167
|
+
if (!existsSync(agentsDir)) return summary;
|
|
168
|
+
let entries = [];
|
|
169
|
+
try {
|
|
170
|
+
entries = readdirSync(agentsDir, { withFileTypes: true }).filter(e => e.isDirectory()).map(e => e.name).sort();
|
|
171
|
+
} catch {
|
|
172
|
+
return summary;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
for (const agent of entries) {
|
|
176
|
+
const dbPath = path.join(agentsDir, agent, 'messages.db');
|
|
177
|
+
if (!existsSync(dbPath)) continue;
|
|
178
|
+
let db;
|
|
179
|
+
try {
|
|
180
|
+
db = new DatabaseSync(dbPath, { readOnly: true });
|
|
181
|
+
const row = db.prepare(`
|
|
182
|
+
SELECT COUNT(*) AS duplicate_groups, COALESCE(SUM(n - 1), 0) AS duplicate_rows
|
|
183
|
+
FROM (
|
|
184
|
+
SELECT COUNT(*) AS n
|
|
185
|
+
FROM messages
|
|
186
|
+
WHERE role = 'user'
|
|
187
|
+
AND text_content LIKE '[___ ____-__-__ __:__ %]%'
|
|
188
|
+
GROUP BY conversation_id, role, text_content, COALESCE(tool_calls, ''), COALESCE(tool_results, '')
|
|
189
|
+
HAVING COUNT(*) > 1
|
|
190
|
+
)
|
|
191
|
+
`).get() || { duplicate_groups: 0, duplicate_rows: 0 };
|
|
192
|
+
const duplicateGroups = Number(row.duplicate_groups || 0);
|
|
193
|
+
const duplicateRows = Number(row.duplicate_rows || 0);
|
|
194
|
+
if (duplicateRows > 0) {
|
|
195
|
+
summary.agents += 1;
|
|
196
|
+
summary.duplicateGroups += duplicateGroups;
|
|
197
|
+
summary.duplicateRows += duplicateRows;
|
|
198
|
+
summary.topAgents.push({ agent, duplicateGroups, duplicateRows });
|
|
199
|
+
}
|
|
200
|
+
} catch {
|
|
201
|
+
// Doctor is advisory here. unreadable DBs are covered by data-file checks.
|
|
202
|
+
} finally {
|
|
203
|
+
try { db?.close(); } catch {}
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
summary.topAgents.sort((a, b) => b.duplicateRows - a.duplicateRows || a.agent.localeCompare(b.agent));
|
|
207
|
+
summary.topAgents = summary.topAgents.slice(0, 5);
|
|
208
|
+
return summary;
|
|
209
|
+
}
|
|
210
|
+
|
|
158
211
|
function checkConfigReadable() {
|
|
159
212
|
if (!openclawRead.exists) {
|
|
160
213
|
required('fail', 'openclaw-config-present', `OpenClaw config not found: ${flags.openclawConfig}`);
|
|
@@ -335,6 +388,23 @@ function checkOpenClawRecommendations() {
|
|
|
335
388
|
}
|
|
336
389
|
}
|
|
337
390
|
|
|
391
|
+
|
|
392
|
+
function checkReplayDuplicateDebt() {
|
|
393
|
+
const debt = scanStampedReplayDuplicateDebt(dataDir);
|
|
394
|
+
if (debt.duplicateRows > 0) {
|
|
395
|
+
recommended('warn', 'stamped-replay-duplicate-debt',
|
|
396
|
+
`Found ${debt.duplicateRows} timestamp-stamped replay duplicate message rows across ${debt.agents} agent DB(s); run hypermem-cleanup dry-run, then apply during a maintenance window`,
|
|
397
|
+
{
|
|
398
|
+
duplicateGroups: debt.duplicateGroups,
|
|
399
|
+
duplicateRows: debt.duplicateRows,
|
|
400
|
+
topAgents: debt.topAgents,
|
|
401
|
+
command: `hypermem-cleanup --data-dir ${shellQuote(dataDir)} --json`,
|
|
402
|
+
});
|
|
403
|
+
} else {
|
|
404
|
+
recommended('ok', 'stamped-replay-duplicate-debt', 'No timestamp-stamped replay duplicate debt detected');
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
|
|
338
408
|
function checkDataDir() {
|
|
339
409
|
const dirStat = statExists(dataDir);
|
|
340
410
|
required(dirStat?.isDirectory() ? 'ok' : 'fail', 'data-dir', dirStat?.isDirectory() ? `HyperMem data dir exists: ${dataDir}` : `HyperMem data dir missing: ${dataDir}`);
|
|
@@ -442,6 +512,7 @@ if (openclawRead.value) {
|
|
|
442
512
|
}
|
|
443
513
|
checkRecallSurfaceRecommendations();
|
|
444
514
|
checkDataDir();
|
|
515
|
+
checkReplayDuplicateDebt();
|
|
445
516
|
checkRuntimePlugins();
|
|
446
517
|
|
|
447
518
|
const failedRequired = checks.filter(c => c.kind === 'required' && c.status === 'fail');
|
|
@@ -0,0 +1,282 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* hypermem-validate-runtime — deterministic installed-runtime validator.
|
|
4
|
+
*
|
|
5
|
+
* Seeds a tiny fixture into an isolated agent and validates the full runtime
|
|
6
|
+
* path without using an answer LLM: message write/read, FTS, library facts,
|
|
7
|
+
* vector indexing/search, warm, and compose.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { existsSync, readFileSync, writeFileSync, mkdirSync } from 'node:fs';
|
|
11
|
+
import os from 'node:os';
|
|
12
|
+
import path from 'node:path';
|
|
13
|
+
import { fileURLToPath } from 'node:url';
|
|
14
|
+
|
|
15
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
16
|
+
const root = path.resolve(__dirname, '..');
|
|
17
|
+
const args = process.argv.slice(2);
|
|
18
|
+
|
|
19
|
+
function usage() {
|
|
20
|
+
console.log(`
|
|
21
|
+
hypermem validate-runtime — deterministic HyperMem runtime validation
|
|
22
|
+
|
|
23
|
+
Usage:
|
|
24
|
+
hypermem-validate-runtime [options]
|
|
25
|
+
|
|
26
|
+
Options:
|
|
27
|
+
--config <path> HyperMem config JSON
|
|
28
|
+
default: ~/.openclaw/hypermem/config.json when present
|
|
29
|
+
--data-dir <path> Override runtime data dir
|
|
30
|
+
--fixture <path> Validation fixture JSON
|
|
31
|
+
default: bundled assets/runtime-validation-fixture.json
|
|
32
|
+
--run-id <id> Stable run id. Default: timestamped isolated id
|
|
33
|
+
--out <path> Write JSON report to path
|
|
34
|
+
--json Print machine-readable JSON only
|
|
35
|
+
--allow-no-embedding Do not fail semantic probes when embedding provider is none
|
|
36
|
+
-h, --help Show this help
|
|
37
|
+
|
|
38
|
+
Examples:
|
|
39
|
+
hypermem-validate-runtime
|
|
40
|
+
hypermem-validate-runtime --json --out /tmp/hypermem-runtime-validation.json
|
|
41
|
+
`);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
if (args.includes('-h') || args.includes('--help')) {
|
|
45
|
+
usage();
|
|
46
|
+
process.exit(0);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function getArg(flag) {
|
|
50
|
+
const idx = args.indexOf(flag);
|
|
51
|
+
return idx === -1 ? undefined : args[idx + 1];
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const flags = {
|
|
55
|
+
json: args.includes('--json'),
|
|
56
|
+
allowNoEmbedding: args.includes('--allow-no-embedding'),
|
|
57
|
+
config: getArg('--config'),
|
|
58
|
+
dataDir: getArg('--data-dir'),
|
|
59
|
+
fixture: getArg('--fixture') || path.join(root, 'assets', 'runtime-validation-fixture.json'),
|
|
60
|
+
runId: getArg('--run-id') || `runtime-${new Date().toISOString().replace(/[^0-9]/g, '').slice(0, 14)}`,
|
|
61
|
+
out: getArg('--out'),
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
if (flags.json) {
|
|
65
|
+
console.log = (...parts) => process.stderr.write(`${parts.join(' ')}\n`);
|
|
66
|
+
console.warn = (...parts) => process.stderr.write(`${parts.join(' ')}\n`);
|
|
67
|
+
console.error = (...parts) => process.stderr.write(`${parts.join(' ')}\n`);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function readJson(filePath) {
|
|
71
|
+
return JSON.parse(readFileSync(filePath, 'utf8'));
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function loadConfig() {
|
|
75
|
+
const configPath = flags.config || path.join(os.homedir(), '.openclaw', 'hypermem', 'config.json');
|
|
76
|
+
const bundledDefaultPath = path.join(root, 'assets', 'default-config.json');
|
|
77
|
+
const sourcePath = existsSync(configPath) ? configPath : bundledDefaultPath;
|
|
78
|
+
const fromFile = existsSync(sourcePath) ? readJson(sourcePath) : {};
|
|
79
|
+
return {
|
|
80
|
+
configPath: existsSync(configPath) ? configPath : null,
|
|
81
|
+
configSource: sourcePath,
|
|
82
|
+
config: {
|
|
83
|
+
...fromFile,
|
|
84
|
+
dataDir: path.resolve(flags.dataDir || process.env.HYPERMEM_DATA_DIR || fromFile.dataDir || path.join(os.homedir(), '.openclaw', 'hypermem')),
|
|
85
|
+
startupFleetSeeding: false,
|
|
86
|
+
},
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
function normalizeText(value) {
|
|
91
|
+
return String(value ?? '').toLowerCase();
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
function containsAll(text, needles) {
|
|
95
|
+
const lower = normalizeText(text);
|
|
96
|
+
return needles.filter((needle) => !lower.includes(String(needle).toLowerCase()));
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
function flattenMessages(messages) {
|
|
100
|
+
return messages.map((m) => `[${m.date || m.sessionId || ''}] ${m.content}`.trim()).join('\n');
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
function renderComposeText(result) {
|
|
104
|
+
if (typeof result?.contextBlock === 'string') return result.contextBlock;
|
|
105
|
+
if (Array.isArray(result?.messages)) {
|
|
106
|
+
return result.messages.map((m) => {
|
|
107
|
+
if (typeof m?.content === 'string') return m.content;
|
|
108
|
+
if (Array.isArray(m?.content)) return m.content.map((p) => p?.text || '').join('\n');
|
|
109
|
+
return m?.textContent || '';
|
|
110
|
+
}).join('\n');
|
|
111
|
+
}
|
|
112
|
+
return JSON.stringify(result ?? {});
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
function round(value) {
|
|
116
|
+
return Math.round(value * 10000) / 10000;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
function pass(report, id, message, details = {}) {
|
|
120
|
+
report.checks.push({ id, status: 'ok', message, ...details });
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
function fail(report, id, message, details = {}) {
|
|
124
|
+
report.checks.push({ id, status: 'fail', message, ...details });
|
|
125
|
+
report.failures.push({ id, message, ...details });
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
async function main() {
|
|
129
|
+
const { HyperMem } = await import(path.join(root, 'dist', 'index.js'));
|
|
130
|
+
const fixture = readJson(flags.fixture);
|
|
131
|
+
const { configPath, configSource, config } = loadConfig();
|
|
132
|
+
const agentId = `${fixture.agentPrefix || 'hypermem-runtime-validation'}-${flags.runId}`;
|
|
133
|
+
const report = {
|
|
134
|
+
ok: false,
|
|
135
|
+
schemaVersion: 1,
|
|
136
|
+
runId: flags.runId,
|
|
137
|
+
agentId,
|
|
138
|
+
configPath,
|
|
139
|
+
configSource,
|
|
140
|
+
dataDir: config.dataDir,
|
|
141
|
+
fixture: flags.fixture,
|
|
142
|
+
embedding: config.embedding || null,
|
|
143
|
+
checks: [],
|
|
144
|
+
failures: [],
|
|
145
|
+
probes: [],
|
|
146
|
+
summary: {},
|
|
147
|
+
writtenAt: new Date().toISOString(),
|
|
148
|
+
};
|
|
149
|
+
|
|
150
|
+
const hm = await HyperMem.create(config);
|
|
151
|
+
pass(report, 'hypermem-create', 'HyperMem.create completed', { dataDir: config.dataDir });
|
|
152
|
+
|
|
153
|
+
for (const msg of fixture.messages || []) {
|
|
154
|
+
const sessionKey = `agent:${agentId}:runtime-validation:${msg.sessionId}`;
|
|
155
|
+
const content = `[${msg.date || msg.sessionId}] ${msg.content}`;
|
|
156
|
+
if (msg.role === 'assistant') {
|
|
157
|
+
await hm.recordAssistantMessage(agentId, sessionKey, { role: 'assistant', textContent: content, toolCalls: null, toolResults: null });
|
|
158
|
+
} else {
|
|
159
|
+
await hm.recordUserMessage(agentId, sessionKey, content, { channelType: 'webchat', provider: 'runtime-validation', model: 'fixture', isHeartbeat: false });
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
pass(report, 'message-write', `Recorded ${(fixture.messages || []).length} fixture messages`);
|
|
163
|
+
|
|
164
|
+
for (const fact of fixture.facts || []) {
|
|
165
|
+
hm.addFact(agentId, fact.content, { scope: 'agent', domain: fact.domain || 'runtime-validation', confidence: 1.0, visibility: 'agent' });
|
|
166
|
+
}
|
|
167
|
+
pass(report, 'fact-write', `Recorded ${(fixture.facts || []).length} structured facts`);
|
|
168
|
+
|
|
169
|
+
const firstSession = `agent:${agentId}:runtime-validation:${fixture.messages?.[0]?.sessionId || 'session-1'}`;
|
|
170
|
+
const sessionIds = [...new Set((fixture.messages || []).map((m) => m.sessionId).filter(Boolean))];
|
|
171
|
+
for (const sessionId of sessionIds) {
|
|
172
|
+
await hm.warm(agentId, `agent:${agentId}:runtime-validation:${sessionId}`);
|
|
173
|
+
}
|
|
174
|
+
pass(report, 'warm-session', `Warm completed for ${sessionIds.length} validation sessions`);
|
|
175
|
+
|
|
176
|
+
const indexResult = await hm.indexAgent(agentId);
|
|
177
|
+
const stats = hm.getVectorStats(agentId);
|
|
178
|
+
report.indexResult = indexResult;
|
|
179
|
+
report.vectorStats = stats;
|
|
180
|
+
if (config.embedding?.provider === 'none') {
|
|
181
|
+
const msg = 'Embedding provider is none; semantic validation skipped by configuration';
|
|
182
|
+
flags.allowNoEmbedding ? pass(report, 'embedding-provider', msg) : fail(report, 'embedding-provider', msg);
|
|
183
|
+
} else if (stats && stats.totalVectors >= (fixture.facts || []).length) {
|
|
184
|
+
pass(report, 'vector-index', `Vector index contains ${stats.totalVectors} vectors`, { indexResult, stats });
|
|
185
|
+
} else {
|
|
186
|
+
fail(report, 'vector-index', 'Vector index did not contain expected structured fact vectors', { indexResult, stats });
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
const probes = fixture.probes || [];
|
|
190
|
+
for (const probe of probes) {
|
|
191
|
+
const row = { id: probe.id, component: probe.component, query: probe.query, required: probe.required || [] };
|
|
192
|
+
try {
|
|
193
|
+
let text = '';
|
|
194
|
+
let meta = {};
|
|
195
|
+
if (probe.component === 'message-fts') {
|
|
196
|
+
const results = hm.search(agentId, probe.query, 8);
|
|
197
|
+
text = results.map((r) => r.textContent || '').join('\n');
|
|
198
|
+
meta = { count: results.length };
|
|
199
|
+
} else if (probe.component === 'semantic') {
|
|
200
|
+
if (config.embedding?.provider === 'none' && flags.allowNoEmbedding) {
|
|
201
|
+
row.skipped = true;
|
|
202
|
+
row.ok = true;
|
|
203
|
+
row.meta = { reason: 'embedding provider none' };
|
|
204
|
+
report.probes.push(row);
|
|
205
|
+
continue;
|
|
206
|
+
}
|
|
207
|
+
const results = await hm.semanticSearch(agentId, probe.query, { limit: 8 });
|
|
208
|
+
text = results.map((r) => r.content || '').join('\n');
|
|
209
|
+
meta = { count: results.length, top: results.slice(0, 3).map((r) => ({ table: r.sourceTable, distance: r.distance, preview: String(r.content || '').slice(0, 160) })) };
|
|
210
|
+
} else if (probe.component === 'compose') {
|
|
211
|
+
const result = await hm.compose({
|
|
212
|
+
agentId,
|
|
213
|
+
sessionKey: `agent:${agentId}:runtime-validation:${probe.sessionId || fixture.messages?.[0]?.sessionId || 'session-1'}`,
|
|
214
|
+
prompt: probe.query,
|
|
215
|
+
tokenBudget: 12000,
|
|
216
|
+
provider: 'anthropic',
|
|
217
|
+
model: 'claude-sonnet-4-6',
|
|
218
|
+
includeHistory: true,
|
|
219
|
+
includeFacts: true,
|
|
220
|
+
includeContext: true,
|
|
221
|
+
includeLibrary: true,
|
|
222
|
+
});
|
|
223
|
+
text = renderComposeText(result);
|
|
224
|
+
meta = { tokenCount: result.tokenCount, slots: result.slots, diagnostics: result.diagnostics };
|
|
225
|
+
} else {
|
|
226
|
+
throw new Error(`Unknown probe component: ${probe.component}`);
|
|
227
|
+
}
|
|
228
|
+
const missing = containsAll(text, probe.required || []);
|
|
229
|
+
row.ok = missing.length === 0;
|
|
230
|
+
row.missing = missing;
|
|
231
|
+
row.meta = meta;
|
|
232
|
+
row.preview = text.slice(0, 1000);
|
|
233
|
+
if (!row.ok) fail(report, probe.id, `${probe.component} probe missing anchors: ${missing.join(', ')}`, { query: probe.query, meta });
|
|
234
|
+
} catch (err) {
|
|
235
|
+
row.ok = false;
|
|
236
|
+
row.error = err.message;
|
|
237
|
+
fail(report, probe.id, `${probe.component} probe errored: ${err.message}`, { query: probe.query });
|
|
238
|
+
}
|
|
239
|
+
report.probes.push(row);
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
const scoredProbes = report.probes.filter((p) => !p.skipped);
|
|
243
|
+
const passedProbes = scoredProbes.filter((p) => p.ok).length;
|
|
244
|
+
const checkPassed = report.checks.filter((c) => c.status === 'ok').length;
|
|
245
|
+
report.summary = {
|
|
246
|
+
checksPassed: checkPassed,
|
|
247
|
+
checksTotal: report.checks.length,
|
|
248
|
+
probesPassed: passedProbes,
|
|
249
|
+
probesTotal: scoredProbes.length,
|
|
250
|
+
consistencyScore: scoredProbes.length ? round(passedProbes / scoredProbes.length) : 0,
|
|
251
|
+
};
|
|
252
|
+
report.ok = report.failures.length === 0;
|
|
253
|
+
|
|
254
|
+
if (flags.out) {
|
|
255
|
+
mkdirSync(path.dirname(path.resolve(flags.out)), { recursive: true });
|
|
256
|
+
writeFileSync(flags.out, `${JSON.stringify(report, null, 2)}\n`);
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
if (flags.json) {
|
|
260
|
+
process.stdout.write(`${JSON.stringify(report, null, 2)}\n`);
|
|
261
|
+
} else {
|
|
262
|
+
console.log(`\nhypermem runtime validation: ${report.ok ? 'PASS' : 'FAIL'}`);
|
|
263
|
+
console.log(` runId: ${report.runId}`);
|
|
264
|
+
console.log(` agentId: ${report.agentId}`);
|
|
265
|
+
console.log(` dataDir: ${report.dataDir}`);
|
|
266
|
+
console.log(` probes: ${report.summary.probesPassed}/${report.summary.probesTotal}`);
|
|
267
|
+
console.log(` consistencyScore: ${report.summary.consistencyScore}`);
|
|
268
|
+
if (report.failures.length) {
|
|
269
|
+
console.log('\nFailures:');
|
|
270
|
+
for (const f of report.failures) console.log(` - ${f.id}: ${f.message}`);
|
|
271
|
+
}
|
|
272
|
+
if (flags.out) console.log(`\nReport: ${path.resolve(flags.out)}`);
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
process.exit(report.ok ? 0 : 1);
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
main().catch((err) => {
|
|
279
|
+
if (flags.json) process.stdout.write(`${JSON.stringify({ ok: false, error: err.message }, null, 2)}\n`);
|
|
280
|
+
else console.error(`hypermem-validate-runtime: ${err.message}`);
|
|
281
|
+
process.exit(1);
|
|
282
|
+
});
|
package/dist/compositor.d.ts
CHANGED
|
@@ -137,11 +137,11 @@ export declare function computeUnifiedPressure(usedTokens: number, budgetTokens:
|
|
|
137
137
|
* and a /new surge does not blow up hybrid search cost.
|
|
138
138
|
*/
|
|
139
139
|
export declare const RECALL_BREADTH_BASE: Readonly<{
|
|
140
|
-
mainBudgetFraction: 0.
|
|
141
|
-
fallbackBudgetFraction: 0.
|
|
142
|
-
candidateLimit:
|
|
143
|
-
candidateLimitMin:
|
|
144
|
-
candidateLimitMax:
|
|
140
|
+
mainBudgetFraction: 0.18;
|
|
141
|
+
fallbackBudgetFraction: 0.14;
|
|
142
|
+
candidateLimit: 18;
|
|
143
|
+
candidateLimitMin: 8;
|
|
144
|
+
candidateLimitMax: 32;
|
|
145
145
|
}>;
|
|
146
146
|
export interface ScaledRecallBreadth {
|
|
147
147
|
mainBudgetTokens: number;
|
|
@@ -458,6 +458,44 @@ export declare class Compositor {
|
|
|
458
458
|
* When provided, the Ollama call inside VectorStore.search() is skipped.
|
|
459
459
|
*/
|
|
460
460
|
private buildSemanticRecall;
|
|
461
|
+
/**
|
|
462
|
+
* Bounded prompt-only FTS recall over raw message history.
|
|
463
|
+
*
|
|
464
|
+
* This is intentionally separate from benchmark evidence tracing. The only
|
|
465
|
+
* input is the user query, so it is safe for product compose and for LoCoMo
|
|
466
|
+
* evaluation. Neighbor expansion gives the reader local dialogue context for
|
|
467
|
+
* multi-hop questions where the first FTS hit is only one side of the answer.
|
|
468
|
+
*/
|
|
469
|
+
/**
|
|
470
|
+
* Sprint B: build the entity-bridge conversation memory block.
|
|
471
|
+
*
|
|
472
|
+
* Pipeline:
|
|
473
|
+
* 1. Detect question shape → seed entity/facet keys.
|
|
474
|
+
* 2. Use EntityBridgeStore to build a capped graph snapshot.
|
|
475
|
+
* 3. Run sparse personalized PageRank.
|
|
476
|
+
* 4. Pull top-K candidate messages, hydrate text, emit a capped block.
|
|
477
|
+
*
|
|
478
|
+
* Degrades safely:
|
|
479
|
+
* - Tables missing: returns null with reason=tables_missing.
|
|
480
|
+
* - No seeds: returns null with reason=no_seeds.
|
|
481
|
+
* - Empty graph or no candidates: returns null with reason set.
|
|
482
|
+
* - PPR or DB error: thrown to caller, which records reason=failed.
|
|
483
|
+
*/
|
|
484
|
+
private buildEntityBridgeRecall;
|
|
485
|
+
/**
|
|
486
|
+
* Metadata-only FTS rank over the PPR candidate set. This lets the Sprint B
|
|
487
|
+
* bridge lane use the same generic RRF math for message-FTS + PPR ordering
|
|
488
|
+
* without changing the existing raw recall block or hybridSearch() semantics.
|
|
489
|
+
*/
|
|
490
|
+
private rankBridgeCandidatesByFts;
|
|
491
|
+
/**
|
|
492
|
+
* Best-effort lookup for an agent id usable by the entity-bridge lane.
|
|
493
|
+
* The bridge index is per-agent, so we need to resolve which agent's
|
|
494
|
+
* messages belong to this DB. Falls back to the most recent conversation
|
|
495
|
+
* row's `agent_id`.
|
|
496
|
+
*/
|
|
497
|
+
private getCurrentAgentIdForBridge;
|
|
498
|
+
private buildQueryMessageRecall;
|
|
461
499
|
/**
|
|
462
500
|
* Format a hybrid search result for injection into context.
|
|
463
501
|
* Shows retrieval source(s) and relevance score.
|
package/dist/compositor.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"compositor.d.ts","sourceRoot":"","sources":["../src/compositor.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAEhD,OAAO,KAAK,EACV,cAAc,EACd,aAAa,EAKb,cAAc,EAGd,gBAAgB,
|
|
1
|
+
{"version":3,"file":"compositor.d.ts","sourceRoot":"","sources":["../src/compositor.ts"],"names":[],"mappings":"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"}
|