@psiclawops/hypermem 0.9.2 → 0.9.4
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 +73 -70
- package/README.md +33 -51
- package/assets/default-config.json +47 -0
- package/bin/hypermem-doctor.mjs +76 -2
- package/bin/hypermem-status.mjs +255 -7
- package/dist/adaptive-lifecycle.d.ts +39 -0
- package/dist/adaptive-lifecycle.d.ts.map +1 -1
- package/dist/adaptive-lifecycle.js +87 -9
- package/dist/background-indexer.d.ts.map +1 -1
- package/dist/background-indexer.js +7 -5
- package/dist/compositor.d.ts.map +1 -1
- package/dist/compositor.js +239 -20
- package/dist/hybrid-retrieval.d.ts +8 -0
- package/dist/hybrid-retrieval.d.ts.map +1 -1
- package/dist/hybrid-retrieval.js +112 -10
- package/dist/index.d.ts +15 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +17 -0
- package/dist/message-store.d.ts +62 -1
- package/dist/message-store.d.ts.map +1 -1
- package/dist/message-store.js +355 -2
- package/dist/open-domain.d.ts.map +1 -1
- package/dist/open-domain.js +3 -2
- package/dist/proactive-pass.d.ts +42 -2
- package/dist/proactive-pass.d.ts.map +1 -1
- package/dist/proactive-pass.js +294 -39
- package/dist/topic-synthesizer.d.ts.map +1 -1
- package/dist/topic-synthesizer.js +9 -3
- package/dist/types.d.ts +99 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/vector-store.d.ts +10 -1
- package/dist/vector-store.d.ts.map +1 -1
- package/dist/vector-store.js +45 -9
- package/docs/DIAGNOSTICS.md +87 -0
- package/docs/INTEGRATION_VALIDATION.md +40 -1
- package/docs/ROADMAP.md +25 -12
- package/docs/TUNING.md +45 -4
- package/install.sh +5 -60
- package/memory-plugin/dist/index.d.ts +24 -0
- package/memory-plugin/dist/index.js +570 -0
- package/memory-plugin/openclaw.plugin.json +199 -2
- package/memory-plugin/package.json +3 -3
- package/package.json +24 -10
- package/plugin/dist/index.d.ts +210 -0
- package/plugin/dist/index.d.ts.map +1 -0
- package/plugin/dist/index.js +3641 -0
- package/plugin/dist/index.js.map +1 -0
- package/plugin/openclaw.plugin.json +199 -2
- package/plugin/package.json +4 -4
- package/scripts/install-packed-runtime.mjs +99 -0
- package/scripts/install-runtime.mjs +164 -4
package/bin/hypermem-doctor.mjs
CHANGED
|
@@ -123,6 +123,10 @@ function setCommand(pathKey, value, strictJson = false) {
|
|
|
123
123
|
return `openclaw config set ${pathKey} ${shellQuote(rendered)}${strictJson ? ' --strict-json' : ''}`;
|
|
124
124
|
}
|
|
125
125
|
|
|
126
|
+
function unsetCommand(pathKey) {
|
|
127
|
+
return `openclaw config unset ${pathKey}`;
|
|
128
|
+
}
|
|
129
|
+
|
|
126
130
|
function shellQuote(value) {
|
|
127
131
|
if (/^[A-Za-z0-9_./:@=-]+$/.test(value)) return value;
|
|
128
132
|
return `'${String(value).replaceAll("'", "'\\''")}'`;
|
|
@@ -165,7 +169,7 @@ function checkConfigReadable() {
|
|
|
165
169
|
} else if (hypermemRead.exists) {
|
|
166
170
|
required('ok', 'hypermem-config-json', `HyperMem config readable: ${flags.hypermemConfig}`);
|
|
167
171
|
} else {
|
|
168
|
-
recommended('warn', 'hypermem-config-present', `
|
|
172
|
+
recommended('warn', 'hypermem-config-present', `HyperMem config missing at ${flags.hypermemConfig}; run hypermem-install to create the 0.9.4 default config, or declare equivalent plugin config in openclaw.json`);
|
|
169
173
|
}
|
|
170
174
|
}
|
|
171
175
|
|
|
@@ -212,6 +216,15 @@ function checkRuntimePlugins() {
|
|
|
212
216
|
recommended('ok', 'runtime-plugin-list', 'Runtime plugin load check skipped');
|
|
213
217
|
return;
|
|
214
218
|
}
|
|
219
|
+
const registryRefresh = spawnSync('openclaw', ['plugins', 'registry', '--refresh'], { encoding: 'utf8', timeout: 15000 });
|
|
220
|
+
if (registryRefresh.error) {
|
|
221
|
+
recommended('warn', 'plugin-registry-refresh', `Could not refresh OpenClaw plugin registry: ${registryRefresh.error.message}`);
|
|
222
|
+
} else if (registryRefresh.status === 0) {
|
|
223
|
+
recommended('ok', 'plugin-registry-refresh', 'OpenClaw plugin registry refresh command completed');
|
|
224
|
+
} else {
|
|
225
|
+
recommended('warn', 'plugin-registry-refresh', `OpenClaw plugin registry refresh exited ${registryRefresh.status}; run openclaw plugins registry --refresh after staging HyperMem`);
|
|
226
|
+
}
|
|
227
|
+
|
|
215
228
|
const result = spawnSync('openclaw', ['plugins', 'list'], { encoding: 'utf8', timeout: 8000 });
|
|
216
229
|
if (result.error) {
|
|
217
230
|
recommended('warn', 'runtime-plugin-list', `Could not run openclaw plugins list: ${result.error.message}`);
|
|
@@ -224,7 +237,51 @@ function checkRuntimePlugins() {
|
|
|
224
237
|
'runtime-plugin-list',
|
|
225
238
|
hasComposer && hasMemory
|
|
226
239
|
? 'Runtime plugin list mentions hypercompositor and hypermem'
|
|
227
|
-
: 'Runtime plugin list did not clearly show both hypercompositor and hypermem; restart gateway after config changes');
|
|
240
|
+
: 'Runtime plugin list did not clearly show both hypercompositor and hypermem; run openclaw plugins registry --refresh, openclaw doctor --fix --yes, then restart gateway after config changes');
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
|
|
244
|
+
function effectiveHyperMemConfig() {
|
|
245
|
+
return {
|
|
246
|
+
...hypermem,
|
|
247
|
+
...pluginConfig,
|
|
248
|
+
compositor: { ...(hypermem.compositor ?? {}), ...(pluginConfig.compositor ?? {}) },
|
|
249
|
+
embedding: { ...(hypermem.embedding ?? {}), ...(pluginConfig.embedding ?? {}) },
|
|
250
|
+
};
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
function checkRecallSurfaceRecommendations() {
|
|
254
|
+
const config = effectiveHyperMemConfig();
|
|
255
|
+
const compositor = config.compositor ?? {};
|
|
256
|
+
const expected = [
|
|
257
|
+
['compositor.turnBudget.budgetFraction', 0.6],
|
|
258
|
+
['compositor.turnBudget.minContextFraction', 0.18],
|
|
259
|
+
['compositor.warming.protectedFloorEnabled', true],
|
|
260
|
+
['compositor.warming.shapedWarmupDecay', true],
|
|
261
|
+
['compositor.adjacency.enabled', true],
|
|
262
|
+
['compositor.adjacency.boostMultiplier', 1.3],
|
|
263
|
+
['compositor.adjacency.maxLookback', 5],
|
|
264
|
+
['compositor.adjacency.maxClockDeltaMin', 10],
|
|
265
|
+
['compositor.adjacency.evictionGuardMessages', 3],
|
|
266
|
+
['compositor.adjacency.evictionGuardTokenCap', 4000],
|
|
267
|
+
];
|
|
268
|
+
|
|
269
|
+
for (const [pathKey, value] of expected) {
|
|
270
|
+
const actual = get({ compositor }, pathKey);
|
|
271
|
+
const ok = actual === value;
|
|
272
|
+
recommended(ok ? 'ok' : 'warn', pathKey,
|
|
273
|
+
ok
|
|
274
|
+
? `${pathKey} is recommended 0.9.4 value ${JSON.stringify(value)}`
|
|
275
|
+
: `${pathKey} is ${JSON.stringify(actual)}; recommended ${JSON.stringify(value)} for 0.9.4 recall-surface protection`);
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
const dims = config.embedding?.dims;
|
|
279
|
+
const dimensions = config.embedding?.dimensions;
|
|
280
|
+
if (dims != null && dimensions != null && dims !== dimensions) {
|
|
281
|
+
recommended('warn', 'embedding.dims-consistency', `embedding.dims (${dims}) and embedding.dimensions (${dimensions}) differ; keep them aligned while both aliases are supported`);
|
|
282
|
+
} else {
|
|
283
|
+
recommended('ok', 'embedding.dims-consistency', 'Embedding dimension aliases are absent or aligned');
|
|
284
|
+
}
|
|
228
285
|
}
|
|
229
286
|
|
|
230
287
|
function checkOpenClawRecommendations() {
|
|
@@ -254,6 +311,22 @@ function checkOpenClawRecommendations() {
|
|
|
254
311
|
else recommended(ok ? 'ok' : 'warn', pathKey, message, details);
|
|
255
312
|
}
|
|
256
313
|
|
|
314
|
+
const legacyMemorySearch = get(openclaw, 'agents.defaults.memorySearch');
|
|
315
|
+
recommended(legacyMemorySearch == null ? 'ok' : 'warn',
|
|
316
|
+
'agents.defaults.memorySearch',
|
|
317
|
+
legacyMemorySearch == null
|
|
318
|
+
? 'Legacy agents.defaults.memorySearch is unset'
|
|
319
|
+
: `Legacy agents.defaults.memorySearch is ${JSON.stringify(legacyMemorySearch)}; HyperMem supersedes this path and it should be unset to avoid stale operator assumptions`,
|
|
320
|
+
legacyMemorySearch == null ? {} : { command: unsetCommand('agents.defaults.memorySearch') });
|
|
321
|
+
|
|
322
|
+
const maxActiveTranscriptBytes = get(openclaw, 'agents.defaults.compaction.maxActiveTranscriptBytes');
|
|
323
|
+
recommended(maxActiveTranscriptBytes == null ? 'ok' : 'warn',
|
|
324
|
+
'agents.defaults.compaction.maxActiveTranscriptBytes',
|
|
325
|
+
maxActiveTranscriptBytes == null
|
|
326
|
+
? 'agents.defaults.compaction.maxActiveTranscriptBytes is unset, as recommended for HyperMem-managed compaction'
|
|
327
|
+
: `agents.defaults.compaction.maxActiveTranscriptBytes is ${JSON.stringify(maxActiveTranscriptBytes)}; recommended unset so OpenClaw transcript rotation does not fight HyperMem fences`,
|
|
328
|
+
maxActiveTranscriptBytes == null ? {} : { command: unsetCommand('agents.defaults.compaction.maxActiveTranscriptBytes') });
|
|
329
|
+
|
|
257
330
|
const injection = get(openclaw, 'agents.defaults.contextInjection');
|
|
258
331
|
if (injection == null || injection === 'always' || injection === 'continuation-skip') {
|
|
259
332
|
recommended('ok', 'agents.defaults.contextInjection', `Context injection mode is ${JSON.stringify(injection ?? 'default')}`);
|
|
@@ -367,6 +440,7 @@ if (openclawRead.value) {
|
|
|
367
440
|
checkOpenClawRecommendations();
|
|
368
441
|
checkModels();
|
|
369
442
|
}
|
|
443
|
+
checkRecallSurfaceRecommendations();
|
|
370
444
|
checkDataDir();
|
|
371
445
|
checkRuntimePlugins();
|
|
372
446
|
|
package/bin/hypermem-status.mjs
CHANGED
|
@@ -39,9 +39,28 @@ const flags = {
|
|
|
39
39
|
health: args.includes('--health'),
|
|
40
40
|
master: args.includes('--master') || args.includes('--planks'),
|
|
41
41
|
help: args.includes('--help') || args.includes('-h'),
|
|
42
|
+
repairReferencedNoise: args.includes('--repair-referenced-noise'),
|
|
43
|
+
fleetRepair: args.includes('--fleet-repair'),
|
|
42
44
|
agent: null,
|
|
43
45
|
};
|
|
44
46
|
|
|
47
|
+
function numericArg(name, fallback) {
|
|
48
|
+
const idx = args.indexOf(name);
|
|
49
|
+
if (idx === -1 || !args[idx + 1]) return fallback;
|
|
50
|
+
const value = Number(args[idx + 1]);
|
|
51
|
+
return Number.isFinite(value) && value >= 0 ? Math.floor(value) : fallback;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const MAX_REPAIR_LIMIT = 500;
|
|
55
|
+
const repairLimit = Math.min(numericArg('--repair-limit', 100), MAX_REPAIR_LIMIT);
|
|
56
|
+
const DEFAULT_FLEET_AGENT_LIMIT = 20;
|
|
57
|
+
const DEFAULT_FLEET_CANDIDATES_PER_CONVERSATION = 500;
|
|
58
|
+
const DEFAULT_TOP_AGENTS = 10;
|
|
59
|
+
const fleetAgentLimit = numericArg('--fleet-agent-limit', DEFAULT_FLEET_AGENT_LIMIT);
|
|
60
|
+
const maxAgents = numericArg('--max-agents', fleetAgentLimit);
|
|
61
|
+
const maxCandidatesPerConversation = numericArg('--max-candidates-per-conversation', DEFAULT_FLEET_CANDIDATES_PER_CONVERSATION);
|
|
62
|
+
const topAgentsLimit = Math.max(1, numericArg('--top-agents', DEFAULT_TOP_AGENTS));
|
|
63
|
+
|
|
45
64
|
const agentIdx = args.indexOf('--agent');
|
|
46
65
|
if (agentIdx !== -1 && args[agentIdx + 1]) {
|
|
47
66
|
flags.agent = args[agentIdx + 1];
|
|
@@ -59,11 +78,25 @@ Options:
|
|
|
59
78
|
--agent <id> Scope metrics to a specific agent
|
|
60
79
|
--json Output raw JSON instead of formatted summary
|
|
61
80
|
--health Health checks only (exits 1 if any check fails)
|
|
81
|
+
--repair-referenced-noise Run conservative tree-safe referenced-noise compaction
|
|
82
|
+
--repair-limit <n> Max referenced-noise mutations per run (default: 100, capped at 500)
|
|
83
|
+
--fleet-repair Allow referenced-noise repair without --agent (off by default)
|
|
84
|
+
--fleet-agent-limit <n> Max agent DBs scanned in fleet maintenance summary (default: ${DEFAULT_FLEET_AGENT_LIMIT})
|
|
85
|
+
--max-agents <n> Alias for --fleet-agent-limit
|
|
86
|
+
--top-agents <n> Max agents shown in maintenance top list (default: ${DEFAULT_TOP_AGENTS})
|
|
87
|
+
--max-candidates-per-conversation <n>
|
|
88
|
+
Max noise candidates inspected per conversation in fleet mode (default: ${DEFAULT_FLEET_CANDIDATES_PER_CONVERSATION})
|
|
62
89
|
-h, --help Show this help
|
|
63
90
|
`);
|
|
64
91
|
process.exit(0);
|
|
65
92
|
}
|
|
66
93
|
|
|
94
|
+
|
|
95
|
+
if (flags.repairReferencedNoise && !flags.agent && !flags.fleetRepair) {
|
|
96
|
+
console.error('Error: --repair-referenced-noise requires --agent <id>. Use --fleet-repair only for explicit fleet-wide repair.');
|
|
97
|
+
process.exit(2);
|
|
98
|
+
}
|
|
99
|
+
|
|
67
100
|
// ── Resolve config and data directory ────────────────────────────
|
|
68
101
|
function readJsonIfExists(filePath) {
|
|
69
102
|
if (!existsSync(filePath)) return null;
|
|
@@ -141,6 +174,38 @@ function resolveRuntimeConfig() {
|
|
|
141
174
|
const runtime = resolveRuntimeConfig();
|
|
142
175
|
const dataDir = runtime.dataDir;
|
|
143
176
|
|
|
177
|
+
function nestedValue(obj, dotted) {
|
|
178
|
+
return dotted.split('.').reduce((acc, key) => (acc && typeof acc === 'object' ? acc[key] : undefined), obj);
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
function collectRecallSurfaceConfig(config) {
|
|
182
|
+
const checks = [
|
|
183
|
+
['turnBudget.budgetFraction', 0.6],
|
|
184
|
+
['turnBudget.minContextFraction', 0.18],
|
|
185
|
+
['warming.protectedFloorEnabled', true],
|
|
186
|
+
['warming.shapedWarmupDecay', true],
|
|
187
|
+
['adjacency.enabled', true],
|
|
188
|
+
['adjacency.boostMultiplier', 1.3],
|
|
189
|
+
['adjacency.maxLookback', 5],
|
|
190
|
+
['adjacency.maxClockDeltaMin', 10],
|
|
191
|
+
['adjacency.evictionGuardMessages', 3],
|
|
192
|
+
['adjacency.evictionGuardTokenCap', 4000],
|
|
193
|
+
];
|
|
194
|
+
const compositor = config.compositor ?? {};
|
|
195
|
+
const items = checks.map(([pathKey, expected]) => {
|
|
196
|
+
const actual = nestedValue(compositor, pathKey);
|
|
197
|
+
return { path: `compositor.${pathKey}`, expected, actual: actual ?? null, ok: actual === expected };
|
|
198
|
+
});
|
|
199
|
+
const missing = items.filter(item => !item.ok);
|
|
200
|
+
return {
|
|
201
|
+
status: missing.length === 0 ? 'ok' : 'attention',
|
|
202
|
+
ok: items.length - missing.length,
|
|
203
|
+
total: items.length,
|
|
204
|
+
missing,
|
|
205
|
+
items,
|
|
206
|
+
};
|
|
207
|
+
}
|
|
208
|
+
|
|
144
209
|
if (!existsSync(dataDir)) {
|
|
145
210
|
console.error(`Error: data directory not found: ${dataDir}`);
|
|
146
211
|
console.error('Is HyperMem installed? Set HYPERMEM_DATA_DIR if using a custom path.');
|
|
@@ -216,12 +281,121 @@ function findMessageDb(agentId) {
|
|
|
216
281
|
return null;
|
|
217
282
|
}
|
|
218
283
|
|
|
219
|
-
|
|
284
|
+
|
|
285
|
+
function listMessageDbs(agentId, limit = Infinity) {
|
|
220
286
|
const agentsDir = join(dataDir, 'agents');
|
|
221
|
-
if (!existsSync(agentsDir)) return {
|
|
287
|
+
if (!existsSync(agentsDir)) return { items: [], totalAvailable: 0, skipped: 0, truncated: false };
|
|
222
288
|
const agents = agentId ? [agentId] : readdirSync(agentsDir, { withFileTypes: true })
|
|
289
|
+
.filter(d => d.isDirectory())
|
|
290
|
+
.map(d => d.name)
|
|
291
|
+
.sort();
|
|
292
|
+
const existing = agents
|
|
293
|
+
.map(agent => ({ agent, path: join(agentsDir, agent, 'messages.db') }))
|
|
294
|
+
.filter(item => existsSync(item.path));
|
|
295
|
+
const boundedLimit = Number.isFinite(limit) ? Math.max(0, limit) : Infinity;
|
|
296
|
+
const items = existing.slice(0, boundedLimit);
|
|
297
|
+
return {
|
|
298
|
+
items,
|
|
299
|
+
totalAvailable: existing.length,
|
|
300
|
+
skipped: Math.max(0, existing.length - items.length),
|
|
301
|
+
truncated: existing.length > items.length,
|
|
302
|
+
};
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
function emptyReferencedNoiseHealth() {
|
|
306
|
+
return {
|
|
307
|
+
status: 'ok',
|
|
308
|
+
agentsScanned: 0,
|
|
309
|
+
conversationsScanned: 0,
|
|
310
|
+
noiseCandidates: 0,
|
|
311
|
+
referencedNoise: 0,
|
|
312
|
+
parentReferencedNoise: 0,
|
|
313
|
+
contextReferencedNoise: 0,
|
|
314
|
+
snapshotReferencedNoise: 0,
|
|
315
|
+
otherReferencedNoise: 0,
|
|
316
|
+
topAgents: [],
|
|
317
|
+
sampleRefs: [],
|
|
318
|
+
repair: null,
|
|
319
|
+
truncated: false,
|
|
320
|
+
agentsAvailable: 0,
|
|
321
|
+
agentsSkipped: 0,
|
|
322
|
+
caps: null,
|
|
323
|
+
};
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
async function collectReferencedNoiseHealth(agentId, repair = false) {
|
|
327
|
+
const health = emptyReferencedNoiseHealth();
|
|
328
|
+
const fleetMode = !agentId;
|
|
329
|
+
const agentLimit = fleetMode ? maxAgents : Infinity;
|
|
330
|
+
const candidateLimit = fleetMode ? maxCandidatesPerConversation : Infinity;
|
|
331
|
+
health.caps = fleetMode
|
|
332
|
+
? { agents: agentLimit, candidatesPerConversation: candidateLimit, topAgents: topAgentsLimit }
|
|
333
|
+
: null;
|
|
334
|
+
const distPath = join(root, 'dist', 'proactive-pass.js');
|
|
335
|
+
if (!existsSync(distPath)) {
|
|
336
|
+
health.status = 'unknown';
|
|
337
|
+
health.error = 'dist/proactive-pass.js missing; run npm run build';
|
|
338
|
+
return health;
|
|
339
|
+
}
|
|
340
|
+
const { collectReferencedNoiseDebt, runTreeSafeNoiseCompaction } = await import(distPath);
|
|
341
|
+
const perAgent = [];
|
|
342
|
+
const repairs = [];
|
|
343
|
+
|
|
344
|
+
const messageDbs = listMessageDbs(agentId, agentLimit);
|
|
345
|
+
health.agentsAvailable = messageDbs.totalAvailable;
|
|
346
|
+
health.agentsSkipped = messageDbs.skipped;
|
|
347
|
+
health.truncated = messageDbs.truncated;
|
|
348
|
+
|
|
349
|
+
for (const item of messageDbs.items) {
|
|
350
|
+
const db = openDb(item.path, `${item.agent} messages.db`, false);
|
|
351
|
+
if (!db) continue;
|
|
352
|
+
try {
|
|
353
|
+
if (repair) {
|
|
354
|
+
const repaired = runTreeSafeNoiseCompaction(db, undefined, 20, repairLimit);
|
|
355
|
+
repairs.push({ agent: item.agent, ...repaired });
|
|
356
|
+
}
|
|
357
|
+
const debt = collectReferencedNoiseDebt(db, undefined, 20, candidateLimit);
|
|
358
|
+
health.agentsScanned += 1;
|
|
359
|
+
health.conversationsScanned += debt.conversationsScanned;
|
|
360
|
+
health.noiseCandidates += debt.noiseCandidates;
|
|
361
|
+
health.referencedNoise += debt.referencedNoise;
|
|
362
|
+
health.parentReferencedNoise += debt.parentReferencedNoise;
|
|
363
|
+
health.contextReferencedNoise += debt.contextReferencedNoise;
|
|
364
|
+
health.snapshotReferencedNoise += debt.snapshotReferencedNoise;
|
|
365
|
+
health.otherReferencedNoise += debt.otherReferencedNoise;
|
|
366
|
+
for (const ref of debt.sampleRefs) if (health.sampleRefs.length < 12) health.sampleRefs.push(`${item.agent}:${ref}`);
|
|
367
|
+
if (debt.referencedNoise > 0) perAgent.push({ agent: item.agent, referencedNoise: debt.referencedNoise, parentReferencedNoise: debt.parentReferencedNoise });
|
|
368
|
+
} finally {
|
|
369
|
+
try { db.close(); } catch {}
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
health.topAgents = perAgent
|
|
374
|
+
.sort((a, b) => b.referencedNoise - a.referencedNoise)
|
|
375
|
+
.slice(0, topAgentsLimit);
|
|
376
|
+
health.status = health.referencedNoise > 0 ? 'attention' : 'ok';
|
|
377
|
+
if (repair) {
|
|
378
|
+
health.repair = {
|
|
379
|
+
limit: repairLimit,
|
|
380
|
+
agentsAttempted: repairs.length,
|
|
381
|
+
deleted: repairs.reduce((sum, item) => sum + (item.deleted || 0), 0),
|
|
382
|
+
reparented: repairs.reduce((sum, item) => sum + (item.reparented || 0), 0),
|
|
383
|
+
skippedBlocked: repairs.reduce((sum, item) => sum + (item.skippedBlocked || 0), 0),
|
|
384
|
+
skippedRoot: repairs.reduce((sum, item) => sum + (item.skippedRoot || 0), 0),
|
|
385
|
+
failures: repairs.filter(item => item.fkCheck && item.fkCheck !== 'none'),
|
|
386
|
+
};
|
|
387
|
+
}
|
|
388
|
+
return health;
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
function collectMessageStats(agentId) {
|
|
392
|
+
const agentsDir = join(dataDir, 'agents');
|
|
393
|
+
if (!existsSync(agentsDir)) return { agentsWithMessages: 0, totalMessages: 0, newestMessageAt: null, agentsAvailable: 0, agentsSkipped: 0, truncated: false };
|
|
394
|
+
const allAgents = agentId ? [agentId] : readdirSync(agentsDir, { withFileTypes: true })
|
|
223
395
|
.filter(d => d.isDirectory())
|
|
224
396
|
.map(d => d.name);
|
|
397
|
+
const limit = agentId ? Infinity : maxAgents;
|
|
398
|
+
const agents = allAgents.slice(0, Number.isFinite(limit) ? Math.max(0, limit) : undefined);
|
|
225
399
|
let agentsWithMessages = 0;
|
|
226
400
|
let totalMessages = 0;
|
|
227
401
|
let newestMessageAt = null;
|
|
@@ -237,7 +411,14 @@ function collectMessageStats(agentId) {
|
|
|
237
411
|
if (newest && (!newestMessageAt || newest > newestMessageAt)) newestMessageAt = newest;
|
|
238
412
|
try { db.close(); } catch {}
|
|
239
413
|
}
|
|
240
|
-
return {
|
|
414
|
+
return {
|
|
415
|
+
agentsWithMessages,
|
|
416
|
+
totalMessages,
|
|
417
|
+
newestMessageAt,
|
|
418
|
+
agentsAvailable: allAgents.length,
|
|
419
|
+
agentsSkipped: Math.max(0, allAgents.length - agents.length),
|
|
420
|
+
truncated: allAgents.length > agents.length,
|
|
421
|
+
};
|
|
241
422
|
}
|
|
242
423
|
|
|
243
424
|
function getVectorDimensions(vectorDb, tableName) {
|
|
@@ -252,7 +433,35 @@ function getQuickCheck(db) {
|
|
|
252
433
|
return row ? Object.values(row)[0] : 'unknown';
|
|
253
434
|
}
|
|
254
435
|
|
|
255
|
-
function
|
|
436
|
+
function fileContains(filePath, text) {
|
|
437
|
+
try {
|
|
438
|
+
return existsSync(filePath) && readFileSync(filePath, 'utf8').includes(text);
|
|
439
|
+
} catch {
|
|
440
|
+
return false;
|
|
441
|
+
}
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
function collectHistoryQueryHealth(mainDb) {
|
|
445
|
+
const coreDts = join(root, 'dist', 'index.d.ts');
|
|
446
|
+
const memoryPluginJs = join(root, 'memory-plugin', 'dist', 'index.js');
|
|
447
|
+
const coreApi = fileContains(coreDts, 'queryHistory(query: HistoryQuery): HistoryQueryResult');
|
|
448
|
+
const pluginTool = fileContains(memoryPluginJs, 'history_query') && fileContains(memoryPluginJs, 'history.query');
|
|
449
|
+
const telemetry = fileContains(memoryPluginJs, 'history-query') && fileContains(memoryPluginJs, 'HYPERMEM_TELEMETRY_PATH');
|
|
450
|
+
const messagesTable = safeGet(mainDb, `SELECT sql FROM sqlite_master WHERE type = 'table' AND name = 'messages'`)?.sql ?? '';
|
|
451
|
+
const fencesTable = safeGet(mainDb, `SELECT sql FROM sqlite_master WHERE type = 'table' AND name = 'compaction_fences'`)?.sql ?? '';
|
|
452
|
+
const schemaReady = ['tool_calls', 'tool_results', 'context_id', 'topic_id'].every(col => messagesTable.includes(col))
|
|
453
|
+
&& fencesTable.includes('fence_message_id');
|
|
454
|
+
|
|
455
|
+
return {
|
|
456
|
+
status: coreApi && pluginTool && telemetry && schemaReady ? 'ok' : 'fail',
|
|
457
|
+
coreApi,
|
|
458
|
+
pluginTool,
|
|
459
|
+
telemetry,
|
|
460
|
+
schemaReady,
|
|
461
|
+
};
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
async function collectMasterHealth(libraryDb, vectorDb, mainDb) {
|
|
256
465
|
const agentClause = flags.agent ? 'AND agent_id = ?' : '';
|
|
257
466
|
const params = flags.agent ? [flags.agent] : [];
|
|
258
467
|
|
|
@@ -374,11 +583,15 @@ function collectMasterHealth(libraryDb, vectorDb, mainDb) {
|
|
|
374
583
|
const dbStatus = libraryQuickCheck === 'ok' && (vectorQuickCheck === 'ok' || vectorQuickCheck === 'missing') && (mainQuickCheck === 'ok' || mainQuickCheck === 'missing') ? 'ok' : 'fail';
|
|
375
584
|
|
|
376
585
|
const messageStats = collectMessageStats(flags.agent);
|
|
586
|
+
const historyQueryHealth = collectHistoryQueryHealth(mainDb);
|
|
587
|
+
const recallSurfaceConfig = collectRecallSurfaceConfig(runtime.config);
|
|
377
588
|
const totalTurns = safeGet(libraryDb, `SELECT COUNT(*) AS count FROM output_metrics WHERE 1=1 ${agentClause}`, params)?.count ?? 0;
|
|
378
589
|
const avgLatency = safeGet(libraryDb, `SELECT AVG(latency_ms) AS value FROM output_metrics WHERE latency_ms IS NOT NULL ${agentClause}`, params)?.value ?? null;
|
|
379
590
|
const avgInput = safeGet(libraryDb, `SELECT AVG(input_tokens) AS value FROM output_metrics WHERE input_tokens IS NOT NULL ${agentClause}`, params)?.value ?? null;
|
|
380
591
|
const avgOutput = safeGet(libraryDb, `SELECT AVG(output_tokens) AS value FROM output_metrics WHERE output_tokens IS NOT NULL ${agentClause}`, params)?.value ?? null;
|
|
381
592
|
|
|
593
|
+
const referencedNoiseHealth = await collectReferencedNoiseHealth(flags.agent, flags.repairReferencedNoise);
|
|
594
|
+
|
|
382
595
|
const issues = [];
|
|
383
596
|
if (dbStatus === 'fail') issues.push('database quick_check failed');
|
|
384
597
|
if (dimensionMatches === 'fail') issues.push('vector table dimensions do not match configured embedding dimensions');
|
|
@@ -386,8 +599,12 @@ function collectMasterHealth(libraryDb, vectorDb, mainDb) {
|
|
|
386
599
|
if (episodeStatus === 'warn') issues.push(`episode vector coverage low (${episodeCoverage}%)`);
|
|
387
600
|
if (knowledgeStatus === 'warn') issues.push(`knowledge vector coverage low (${knowledgeCoverage}%)`);
|
|
388
601
|
if (!vectorDb) issues.push('shared vectors.db missing');
|
|
602
|
+
if (historyQueryHealth.status !== 'ok') issues.push('history.query surface incomplete');
|
|
603
|
+
if (recallSurfaceConfig.status !== 'ok') issues.push(`0.9.4 recall-surface config incomplete (${recallSurfaceConfig.ok}/${recallSurfaceConfig.total} recommended knobs)`);
|
|
604
|
+
if (referencedNoiseHealth.status === 'attention') issues.push(`referenced-noise compaction debt (${referencedNoiseHealth.referencedNoise} messages${referencedNoiseHealth.truncated ? ', bounded fleet sample' : ''})`);
|
|
605
|
+
if (referencedNoiseHealth.status === 'unknown') issues.push(`referenced-noise health unknown: ${referencedNoiseHealth.error}`);
|
|
389
606
|
|
|
390
|
-
const overall = issues.length === 0 ? 'healthy' : (dbStatus === 'fail' || dimensionMatches === 'fail' ? 'degraded' : 'attention');
|
|
607
|
+
const overall = issues.length === 0 ? 'healthy' : (dbStatus === 'fail' || dimensionMatches === 'fail' || historyQueryHealth.status === 'fail' ? 'degraded' : 'attention');
|
|
391
608
|
|
|
392
609
|
return {
|
|
393
610
|
snapshotAt: new Date().toISOString(),
|
|
@@ -405,6 +622,7 @@ function collectMasterHealth(libraryDb, vectorDb, mainDb) {
|
|
|
405
622
|
baseUrl: runtime.embedding.openaiBaseUrl ?? runtime.embedding.ollamaUrl ?? runtime.embedding.geminiBaseUrl ?? null,
|
|
406
623
|
},
|
|
407
624
|
reranker: runtime.config.reranker ? stripSecretFields(runtime.config.reranker) : null,
|
|
625
|
+
recallSurface: recallSurfaceConfig,
|
|
408
626
|
},
|
|
409
627
|
databases: {
|
|
410
628
|
library: { path: libraryDbPath, ...fileInfo(libraryDbPath), quickCheck: libraryQuickCheck },
|
|
@@ -437,6 +655,12 @@ function collectMasterHealth(libraryDb, vectorDb, mainDb) {
|
|
|
437
655
|
avgOutputTokens: avgOutput == null ? null : Math.round(avgOutput),
|
|
438
656
|
semanticDiagnosticsPersisted: false,
|
|
439
657
|
},
|
|
658
|
+
maintenance: {
|
|
659
|
+
referencedNoise: referencedNoiseHealth,
|
|
660
|
+
},
|
|
661
|
+
querySurfaces: {
|
|
662
|
+
historyQuery: historyQueryHealth,
|
|
663
|
+
},
|
|
440
664
|
};
|
|
441
665
|
}
|
|
442
666
|
|
|
@@ -461,6 +685,11 @@ function formatMasterHealth(h) {
|
|
|
461
685
|
lines.push(` embed: ${h.config.embedding.provider ?? 'unknown'} / ${h.config.embedding.model ?? 'unknown'}${h.config.embedding.dimensions ? ` (${h.config.embedding.dimensions}d)` : ''}${h.config.embedding.batchSize ? ` batch=${h.config.embedding.batchSize}` : ''}`);
|
|
462
686
|
if (h.config.embedding.baseUrl) lines.push(` base: ${h.config.embedding.baseUrl}`);
|
|
463
687
|
lines.push(` rerank: ${h.config.reranker?.provider ?? 'none'}`);
|
|
688
|
+
const rs = h.config.recallSurface;
|
|
689
|
+
lines.push(` recall: ${icon(rs.status === 'ok' ? 'ok' : 'warn')} 0.9.4 surface ${rs.ok}/${rs.total} recommended knobs`);
|
|
690
|
+
for (const item of rs.missing.slice(0, 5)) {
|
|
691
|
+
lines.push(` missing ${item.path}=${JSON.stringify(item.expected)}`);
|
|
692
|
+
}
|
|
464
693
|
|
|
465
694
|
lines.push('');
|
|
466
695
|
lines.push('## Databases');
|
|
@@ -475,7 +704,7 @@ function formatMasterHealth(h) {
|
|
|
475
704
|
lines.push(` episodes: ${h.memory.episodes.eligible.toLocaleString()} significant / ${h.memory.episodes.total.toLocaleString()} total`);
|
|
476
705
|
lines.push(` knowledge:${String(h.memory.knowledge.active.toLocaleString()).padStart(7)} active`);
|
|
477
706
|
lines.push(` chunks: ${h.memory.docChunks.total.toLocaleString()}`);
|
|
478
|
-
lines.push(` messages: ${h.memory.messages.totalMessages.toLocaleString()} across ${h.memory.messages.agentsWithMessages} agent DBs${h.memory.messages.newestMessageAt ? `, newest ${h.memory.messages.newestMessageAt}` : ''}`);
|
|
707
|
+
lines.push(` messages: ${h.memory.messages.totalMessages.toLocaleString()} across ${h.memory.messages.agentsWithMessages} agent DBs${h.memory.messages.truncated ? `/${h.memory.messages.agentsAvailable} bounded` : ''}${h.memory.messages.newestMessageAt ? `, newest ${h.memory.messages.newestMessageAt}` : ''}`);
|
|
479
708
|
|
|
480
709
|
lines.push('');
|
|
481
710
|
lines.push('## Vector coverage');
|
|
@@ -503,6 +732,25 @@ function formatMasterHealth(h) {
|
|
|
503
732
|
}
|
|
504
733
|
lines.push(' semantic recall counts: runtime log only, not persisted yet');
|
|
505
734
|
|
|
735
|
+
lines.push('');
|
|
736
|
+
lines.push('## Maintenance');
|
|
737
|
+
const rn = h.maintenance.referencedNoise;
|
|
738
|
+
lines.push(` ${icon(rn.status === 'ok' ? 'ok' : rn.status === 'attention' ? 'warn' : 'fail')} referenced-noise debt=${rn.referencedNoise.toLocaleString()} parent=${rn.parentReferencedNoise.toLocaleString()} context=${rn.contextReferencedNoise.toLocaleString()} snapshot=${rn.snapshotReferencedNoise.toLocaleString()} agents=${rn.agentsScanned}${rn.truncated ? `/${rn.agentsAvailable} bounded` : ''}`);
|
|
739
|
+
if (rn.truncated) {
|
|
740
|
+
lines.push(` fleet scan bounded: skipped ${rn.agentsSkipped} agent DBs; rerun with --fleet-agent-limit ${rn.agentsAvailable} for a full scan`);
|
|
741
|
+
}
|
|
742
|
+
if (rn.topAgents.length > 0) {
|
|
743
|
+
lines.push(` top agents: ${rn.topAgents.map(item => `${item.agent}=${Number(item.referencedNoise).toLocaleString()}`).join(' ')}`);
|
|
744
|
+
}
|
|
745
|
+
if (rn.repair) {
|
|
746
|
+
lines.push(` repair: limit=${rn.repair.limit} deleted=${rn.repair.deleted} reparented=${rn.repair.reparented} skippedBlocked=${rn.repair.skippedBlocked} skippedRoot=${rn.repair.skippedRoot}`);
|
|
747
|
+
}
|
|
748
|
+
|
|
749
|
+
lines.push('');
|
|
750
|
+
lines.push('## Query surfaces');
|
|
751
|
+
const hq = h.querySurfaces.historyQuery;
|
|
752
|
+
lines.push(` ${icon(hq.status)} history.query coreApi=${hq.coreApi ? 'yes' : 'no'} pluginTool=${hq.pluginTool ? 'yes' : 'no'} telemetry=${hq.telemetry ? 'yes' : 'no'} schema=${hq.schemaReady ? 'yes' : 'no'}`);
|
|
753
|
+
|
|
506
754
|
return lines.join('\n');
|
|
507
755
|
}
|
|
508
756
|
|
|
@@ -538,7 +786,7 @@ if (vectorDb) {
|
|
|
538
786
|
|
|
539
787
|
try {
|
|
540
788
|
if (flags.master) {
|
|
541
|
-
const master = collectMasterHealth(libraryDb, vectorDb, mainDb);
|
|
789
|
+
const master = await collectMasterHealth(libraryDb, vectorDb, mainDb);
|
|
542
790
|
if (flags.json) console.log(JSON.stringify(master, null, 2));
|
|
543
791
|
else console.log(formatMasterHealth(master));
|
|
544
792
|
process.exit(master.overall === 'degraded' ? 1 : 0);
|
|
@@ -16,6 +16,8 @@ export interface AdaptiveLifecycleInput {
|
|
|
16
16
|
pressureFraction?: number;
|
|
17
17
|
/** Number of user turns observed in the session. */
|
|
18
18
|
userTurnCount?: number;
|
|
19
|
+
/** Number of topic-bearing (substantive) user turns observed in the session. */
|
|
20
|
+
topicBearingTurnCount?: number;
|
|
19
21
|
/** True when the incoming user turn explicitly starts with /new. */
|
|
20
22
|
explicitNewSession?: boolean;
|
|
21
23
|
/** Topic-shift confidence from the detector, 0..1. */
|
|
@@ -27,6 +29,33 @@ export interface AdaptiveLifecycleInput {
|
|
|
27
29
|
/** Parent-session user turns observed when the fork was prepared. */
|
|
28
30
|
forkedParentUserTurnCount?: number;
|
|
29
31
|
}
|
|
32
|
+
/**
|
|
33
|
+
* Minimal message shape for the lightweight topic-bearing classifier.
|
|
34
|
+
* Used by compose paths to count substantive user turns from existing
|
|
35
|
+
* message data without content telemetry or schema migration.
|
|
36
|
+
*/
|
|
37
|
+
export interface TopicBearingMessageLike {
|
|
38
|
+
role: string;
|
|
39
|
+
textContent: string | null;
|
|
40
|
+
isHeartbeat?: boolean;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Determine whether a user turn is "topic-bearing" (substantive).
|
|
44
|
+
*
|
|
45
|
+
* Heartbeat, empty, and small-talk turns are NOT topic-bearing and do not
|
|
46
|
+
* extend the warmup window. This is intentionally a lightweight heuristic:
|
|
47
|
+
* no topic-detector architecture change, no model calls.
|
|
48
|
+
*
|
|
49
|
+
* Mirrors the plugin afterTurn gradient semantics so compose-path band
|
|
50
|
+
* decisions stay consistent with afterTurn-path band decisions.
|
|
51
|
+
*/
|
|
52
|
+
export declare function isTopicBearingTurn(msg: TopicBearingMessageLike): boolean;
|
|
53
|
+
/**
|
|
54
|
+
* Count topic-bearing turns in a message array.
|
|
55
|
+
*
|
|
56
|
+
* Pure helper: no side effects, no store access. Returns 0 for empty arrays.
|
|
57
|
+
*/
|
|
58
|
+
export declare function countTopicBearingTurns(messages: TopicBearingMessageLike[]): number;
|
|
30
59
|
/**
|
|
31
60
|
* Eviction-pipeline step labels. The order in `AdaptiveEvictionPlan.steps`
|
|
32
61
|
* is the order the compose-window cluster-drop path should attempt them.
|
|
@@ -47,6 +76,14 @@ export interface AdaptiveEvictionPlan {
|
|
|
47
76
|
/** Ballast-reduction steps run before any cluster drop. Always true today. */
|
|
48
77
|
preferBallastFirst: boolean;
|
|
49
78
|
}
|
|
79
|
+
interface ProtectedWarmingMetadata {
|
|
80
|
+
/** Whether warming is currently protected by a hard floor. True for high/critical. */
|
|
81
|
+
isProtected: boolean;
|
|
82
|
+
/** The floor value warming cannot drop below. 0 when not protected. */
|
|
83
|
+
floor: number;
|
|
84
|
+
/** Human-readable explanation. */
|
|
85
|
+
reason: string;
|
|
86
|
+
}
|
|
50
87
|
export interface AdaptiveLifecyclePolicy {
|
|
51
88
|
band: AdaptiveLifecycleBand;
|
|
52
89
|
pressureFraction: number;
|
|
@@ -59,6 +96,7 @@ export interface AdaptiveLifecyclePolicy {
|
|
|
59
96
|
enableTopicCentroidEviction: boolean;
|
|
60
97
|
triggerProactiveCompaction: boolean;
|
|
61
98
|
evictionPlan: AdaptiveEvictionPlan;
|
|
99
|
+
protectedWarmingMetadata: ProtectedWarmingMetadata;
|
|
62
100
|
reasons: string[];
|
|
63
101
|
}
|
|
64
102
|
/**
|
|
@@ -78,4 +116,5 @@ export declare function resolveAdaptiveEvictionPlan(band: AdaptiveLifecycleBand)
|
|
|
78
116
|
* Resolve the adaptive lifecycle posture for one compose/afterTurn cycle.
|
|
79
117
|
*/
|
|
80
118
|
export declare function resolveAdaptiveLifecyclePolicy(input?: AdaptiveLifecycleInput): AdaptiveLifecyclePolicy;
|
|
119
|
+
export {};
|
|
81
120
|
//# sourceMappingURL=adaptive-lifecycle.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"adaptive-lifecycle.d.ts","sourceRoot":"","sources":["../src/adaptive-lifecycle.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;
|
|
1
|
+
{"version":3,"file":"adaptive-lifecycle.d.ts","sourceRoot":"","sources":["../src/adaptive-lifecycle.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAIH,MAAM,MAAM,qBAAqB,GAC7B,WAAW,GACX,QAAQ,GACR,QAAQ,GACR,UAAU,GACV,MAAM,GACN,UAAU,CAAC;AAEf,MAAM,WAAW,sBAAsB;IACrC,kEAAkE;IAClE,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,mEAAmE;IACnE,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,oFAAoF;IACpF,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,oDAAoD;IACpD,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,gFAAgF;IAChF,qBAAqB,CAAC,EAAE,MAAM,CAAC;IAC/B,oEAAoE;IACpE,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAC7B,sDAAsD;IACtD,oBAAoB,CAAC,EAAE,MAAM,CAAC;IAC9B,wEAAwE;IACxE,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,0EAA0E;IAC1E,4BAA4B,CAAC,EAAE,MAAM,CAAC;IACtC,qEAAqE;IACrE,yBAAyB,CAAC,EAAE,MAAM,CAAC;CACpC;AAED;;;;GAIG;AACH,MAAM,WAAW,uBAAuB;IACtC,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,WAAW,CAAC,EAAE,OAAO,CAAC;CACvB;AAED;;;;;;;;;GASG;AACH,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,uBAAuB,GAAG,OAAO,CAmBxE;AAED;;;;GAIG;AACH,wBAAgB,sBAAsB,CAAC,QAAQ,EAAE,uBAAuB,EAAE,GAAG,MAAM,CAElF;AAED;;;;;;;;;;GAUG;AACH,MAAM,MAAM,oBAAoB,GAC5B,eAAe,GACf,qBAAqB,GACrB,kBAAkB,GAClB,0BAA0B,GAC1B,qBAAqB,CAAC;AAE1B,MAAM,WAAW,oBAAoB;IACnC,IAAI,EAAE,qBAAqB,CAAC;IAC5B,KAAK,EAAE,SAAS,oBAAoB,EAAE,CAAC;IACvC,wEAAwE;IACxE,oBAAoB,EAAE,OAAO,CAAC;IAC9B,8EAA8E;IAC9E,kBAAkB,EAAE,OAAO,CAAC;CAC7B;AAED,UAAU,wBAAwB;IAChC,sFAAsF;IACtF,WAAW,EAAE,OAAO,CAAC;IACrB,uEAAuE;IACvE,KAAK,EAAE,MAAM,CAAC;IACd,kCAAkC;IAClC,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,uBAAuB;IACtC,IAAI,EAAE,qBAAqB,CAAC;IAC5B,gBAAgB,EAAE,MAAM,CAAC;IACzB,WAAW,EAAE,MAAM,CAAC;IACpB,yBAAyB,EAAE,MAAM,CAAC;IAClC,qBAAqB,EAAE,MAAM,CAAC;IAC9B,cAAc,EAAE,MAAM,CAAC;IACvB,wBAAwB,EAAE,MAAM,CAAC;IACjC,qBAAqB,EAAE,OAAO,CAAC;IAC/B,2BAA2B,EAAE,OAAO,CAAC;IACrC,0BAA0B,EAAE,OAAO,CAAC;IACpC,YAAY,EAAE,oBAAoB,CAAC;IACnC,wBAAwB,EAAE,wBAAwB,CAAC;IACnD,OAAO,EAAE,MAAM,EAAE,CAAC;CACnB;AAiBD;;;;;;;;;;;GAWG;AACH,wBAAgB,2BAA2B,CAAC,IAAI,EAAE,qBAAqB,GAAG,oBAAoB,CAsB7F;AA4ID;;GAEG;AACH,wBAAgB,8BAA8B,CAC5C,KAAK,GAAE,sBAA2B,GACjC,uBAAuB,CAqBzB"}
|