@psiclawops/hypermem 0.8.5 → 0.9.0

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.
Files changed (87) hide show
  1. package/CHANGELOG.md +26 -0
  2. package/INSTALL.md +132 -9
  3. package/README.md +119 -272
  4. package/bench/README.md +42 -0
  5. package/bench/data-access-bench.mjs +380 -0
  6. package/bin/hypermem-bench.mjs +2 -0
  7. package/bin/hypermem-doctor.mjs +412 -0
  8. package/bin/hypermem-model-audit.mjs +339 -0
  9. package/bin/hypermem-status.mjs +491 -70
  10. package/dist/adaptive-lifecycle.d.ts +81 -0
  11. package/dist/adaptive-lifecycle.d.ts.map +1 -0
  12. package/dist/adaptive-lifecycle.js +190 -0
  13. package/dist/budget-policy.d.ts +1 -1
  14. package/dist/budget-policy.d.ts.map +1 -1
  15. package/dist/budget-policy.js +10 -5
  16. package/dist/cache.d.ts +1 -0
  17. package/dist/cache.d.ts.map +1 -1
  18. package/dist/cache.js +2 -0
  19. package/dist/composition-snapshot-integrity.d.ts +36 -0
  20. package/dist/composition-snapshot-integrity.d.ts.map +1 -0
  21. package/dist/composition-snapshot-integrity.js +131 -0
  22. package/dist/composition-snapshot-runtime.d.ts +59 -0
  23. package/dist/composition-snapshot-runtime.d.ts.map +1 -0
  24. package/dist/composition-snapshot-runtime.js +250 -0
  25. package/dist/composition-snapshot-store.d.ts +44 -0
  26. package/dist/composition-snapshot-store.d.ts.map +1 -0
  27. package/dist/composition-snapshot-store.js +117 -0
  28. package/dist/compositor.d.ts +125 -1
  29. package/dist/compositor.d.ts.map +1 -1
  30. package/dist/compositor.js +692 -44
  31. package/dist/doc-chunk-store.d.ts +19 -0
  32. package/dist/doc-chunk-store.d.ts.map +1 -1
  33. package/dist/doc-chunk-store.js +56 -6
  34. package/dist/hybrid-retrieval.d.ts +38 -0
  35. package/dist/hybrid-retrieval.d.ts.map +1 -1
  36. package/dist/hybrid-retrieval.js +86 -1
  37. package/dist/index.d.ts +12 -3
  38. package/dist/index.d.ts.map +1 -1
  39. package/dist/index.js +28 -2
  40. package/dist/knowledge-store.d.ts +4 -1
  41. package/dist/knowledge-store.d.ts.map +1 -1
  42. package/dist/knowledge-store.js +27 -4
  43. package/dist/library-schema.d.ts +12 -8
  44. package/dist/library-schema.d.ts.map +1 -1
  45. package/dist/library-schema.js +22 -8
  46. package/dist/message-store.d.ts.map +1 -1
  47. package/dist/message-store.js +7 -3
  48. package/dist/metrics-dashboard.d.ts +18 -1
  49. package/dist/metrics-dashboard.d.ts.map +1 -1
  50. package/dist/metrics-dashboard.js +52 -14
  51. package/dist/reranker.d.ts +1 -1
  52. package/dist/reranker.js +2 -2
  53. package/dist/schema.d.ts +1 -1
  54. package/dist/schema.d.ts.map +1 -1
  55. package/dist/schema.js +28 -1
  56. package/dist/seed.d.ts.map +1 -1
  57. package/dist/seed.js +2 -0
  58. package/dist/topic-synthesizer.d.ts +20 -0
  59. package/dist/topic-synthesizer.d.ts.map +1 -1
  60. package/dist/topic-synthesizer.js +113 -3
  61. package/dist/trigger-registry.d.ts.map +1 -1
  62. package/dist/trigger-registry.js +10 -2
  63. package/dist/types.d.ts +271 -1
  64. package/dist/types.d.ts.map +1 -1
  65. package/dist/version.d.ts +7 -7
  66. package/dist/version.d.ts.map +1 -1
  67. package/dist/version.js +17 -7
  68. package/docs/DIAGNOSTICS.md +205 -0
  69. package/docs/INTEGRATION_VALIDATION.md +186 -0
  70. package/docs/MIGRATION.md +9 -6
  71. package/docs/MIGRATION_GUIDE.md +125 -101
  72. package/docs/ROADMAP.md +238 -20
  73. package/docs/TUNING.md +19 -5
  74. package/install.sh +152 -401
  75. package/memory-plugin/LICENSE +190 -0
  76. package/memory-plugin/README.md +20 -0
  77. package/memory-plugin/dist/index.js +50 -0
  78. package/memory-plugin/package.json +2 -2
  79. package/package.json +18 -4
  80. package/plugin/LICENSE +190 -0
  81. package/plugin/README.md +20 -0
  82. package/plugin/dist/index.d.ts +29 -0
  83. package/plugin/dist/index.d.ts.map +1 -1
  84. package/plugin/dist/index.js +288 -23
  85. package/plugin/dist/index.js.map +1 -1
  86. package/plugin/package.json +2 -2
  87. package/scripts/install-runtime.mjs +12 -1
@@ -0,0 +1,339 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { existsSync, readFileSync } from 'node:fs';
4
+ import os from 'node:os';
5
+ import path from 'node:path';
6
+
7
+ const args = process.argv.slice(2);
8
+
9
+ const MODEL_CONTEXT_WINDOWS = [
10
+ { pattern: 'claude-opus-4', tokens: 200_000 },
11
+ { pattern: 'claude-sonnet-4', tokens: 200_000 },
12
+ { pattern: 'claude-3-5', tokens: 200_000 },
13
+ { pattern: 'claude-3-7', tokens: 200_000 },
14
+ { pattern: 'claude', tokens: 200_000 },
15
+ { pattern: 'gpt-5', tokens: 128_000 },
16
+ { pattern: 'gpt-4o', tokens: 128_000 },
17
+ { pattern: 'gpt-4', tokens: 128_000 },
18
+ { pattern: 'o3', tokens: 128_000 },
19
+ { pattern: 'o4', tokens: 128_000 },
20
+ { pattern: 'gemini-3.1-pro', tokens: 1_000_000 },
21
+ { pattern: 'gemini-3.1-flash', tokens: 1_000_000 },
22
+ { pattern: 'gemini-2.5-pro', tokens: 1_000_000 },
23
+ { pattern: 'gemini-2', tokens: 1_000_000 },
24
+ { pattern: 'gemini', tokens: 1_000_000 },
25
+ { pattern: 'glm-5', tokens: 131_072 },
26
+ { pattern: 'glm-4', tokens: 131_072 },
27
+ { pattern: 'qwen3', tokens: 262_144 },
28
+ { pattern: 'qwen', tokens: 131_072 },
29
+ { pattern: 'deepseek-v3', tokens: 131_072 },
30
+ { pattern: 'deepseek', tokens: 131_072 },
31
+ ];
32
+
33
+ const HIGH_RISK_PROVIDERS = [
34
+ 'openai/',
35
+ 'openai-codex/',
36
+ 'openrouter/',
37
+ 'lmstudio/',
38
+ 'vllm/',
39
+ 'ollama/',
40
+ 'litellm/',
41
+ 'copilot-local/',
42
+ ];
43
+
44
+ function usage() {
45
+ console.log(`
46
+ hypermem-model-audit, detect models that need explicit context window metadata
47
+
48
+ Usage:
49
+ hypermem-model-audit [options]
50
+
51
+ Options:
52
+ --openclaw-config <path> OpenClaw config to inspect
53
+ default: ~/.openclaw/openclaw.json
54
+ --hypermem-config <path> HyperMem config to inspect
55
+ default: ~/.openclaw/hypermem/config.json
56
+ --models <list> Comma-separated provider/model keys to audit directly
57
+ --configured-only Only audit actively configured agent/default models
58
+ default: also audits registered provider catalog models
59
+ --json Output machine-readable JSON
60
+ --strict Exit 1 when any model is not ready
61
+ -h, --help Show this help
62
+
63
+ Examples:
64
+ hypermem-model-audit
65
+ hypermem-model-audit --models openai-codex/gpt-5.4,ollama/llama-3.3-70b
66
+ hypermem-model-audit --strict
67
+ `);
68
+ }
69
+
70
+ if (args.includes('--help') || args.includes('-h')) {
71
+ usage();
72
+ process.exit(0);
73
+ }
74
+
75
+ function getArg(flag) {
76
+ const idx = args.indexOf(flag);
77
+ return idx !== -1 ? args[idx + 1] : undefined;
78
+ }
79
+
80
+ const flags = {
81
+ json: args.includes('--json'),
82
+ strict: args.includes('--strict'),
83
+ configuredOnly: args.includes('--configured-only'),
84
+ openclawConfig: path.resolve(getArg('--openclaw-config') || path.join(os.homedir(), '.openclaw', 'openclaw.json')),
85
+ hypermemConfig: path.resolve(getArg('--hypermem-config') || path.join(os.homedir(), '.openclaw', 'hypermem', 'config.json')),
86
+ models: (getArg('--models') || '').split(',').map(s => s.trim().toLowerCase()).filter(Boolean),
87
+ };
88
+
89
+ function readJsonIfExists(filePath) {
90
+ if (!existsSync(filePath)) return null;
91
+ return JSON.parse(readFileSync(filePath, 'utf8'));
92
+ }
93
+
94
+ function normalizeModelKey(value) {
95
+ return typeof value === 'string' ? value.trim().toLowerCase() : '';
96
+ }
97
+
98
+ function addModelKey(out, value) {
99
+ const normalized = normalizeModelKey(value);
100
+ if (normalized.includes('/')) out.add(normalized);
101
+ }
102
+
103
+ function joinProviderModel(providerId, modelId) {
104
+ const provider = normalizeModelKey(providerId);
105
+ const model = normalizeModelKey(modelId);
106
+ if (!provider || !model) return '';
107
+ return model.includes('/') ? model : `${provider}/${model}`;
108
+ }
109
+
110
+ function addRegisteredModel(out, providerId, value) {
111
+ if (typeof value === 'string') {
112
+ addModelKey(out, joinProviderModel(providerId, value));
113
+ return;
114
+ }
115
+
116
+ if (Array.isArray(value)) {
117
+ for (const item of value) addRegisteredModel(out, providerId, item);
118
+ return;
119
+ }
120
+
121
+ if (!value || typeof value !== 'object') return;
122
+
123
+ const modelId = value.model || value.id || value.name;
124
+ addModelKey(out, joinProviderModel(value.provider || providerId, modelId));
125
+ }
126
+
127
+ function addModelishValue(out, value) {
128
+ if (typeof value === 'string') {
129
+ addModelKey(out, value);
130
+ return;
131
+ }
132
+
133
+ if (Array.isArray(value)) {
134
+ for (const item of value) addModelishValue(out, item);
135
+ return;
136
+ }
137
+
138
+ if (!value || typeof value !== 'object') return;
139
+
140
+ addModelishValue(out, value.primary);
141
+ if (Array.isArray(value.fallbacks)) {
142
+ for (const fb of value.fallbacks) addModelishValue(out, fb);
143
+ }
144
+ }
145
+
146
+ function detectPattern(model) {
147
+ const normalized = normalizeModelKey(model);
148
+ return MODEL_CONTEXT_WINDOWS.find(entry => normalized.includes(entry.pattern));
149
+ }
150
+
151
+ function getOverrides(config) {
152
+ const raw = config?.compositor?.contextWindowOverrides;
153
+ return raw && typeof raw === 'object' ? raw : {};
154
+ }
155
+
156
+ function collectConfiguredModels(config) {
157
+ const out = new Set();
158
+
159
+ if (Array.isArray(config?.agents?.list)) {
160
+ for (const agent of config.agents.list) {
161
+ addModelishValue(out, agent?.model);
162
+ addModelishValue(out, agent?.fallbacks);
163
+ }
164
+ }
165
+
166
+ addModelishValue(out, config?.agents?.defaults?.model);
167
+ addModelishValue(out, config?.agents?.defaults?.fallbacks);
168
+ addModelishValue(out, config?.agents?.defaults?.heartbeat?.model);
169
+ addModelishValue(out, config?.agents?.defaults?.subagents?.model);
170
+ addModelishValue(out, config?.agents?.defaults?.imageModel);
171
+
172
+ if (config?.channels?.modelByChannel && typeof config.channels.modelByChannel === 'object') {
173
+ for (const channelModels of Object.values(config.channels.modelByChannel)) {
174
+ if (!channelModels || typeof channelModels !== 'object') continue;
175
+ for (const model of Object.values(channelModels)) addModelishValue(out, model);
176
+ }
177
+ }
178
+
179
+ return [...out].sort();
180
+ }
181
+
182
+ function collectRegisteredModels(config) {
183
+ const out = new Set();
184
+
185
+ const defaultsModels = config?.agents?.defaults?.models;
186
+ if (defaultsModels && typeof defaultsModels === 'object' && !Array.isArray(defaultsModels)) {
187
+ for (const model of Object.keys(defaultsModels)) addModelKey(out, model);
188
+ }
189
+
190
+ const providers = config?.models?.providers;
191
+ if (providers && typeof providers === 'object' && !Array.isArray(providers)) {
192
+ for (const [providerId, provider] of Object.entries(providers)) {
193
+ addRegisteredModel(out, providerId, provider?.models);
194
+ }
195
+ }
196
+
197
+ if (Array.isArray(config?.tools?.media?.models)) {
198
+ for (const item of config.tools.media.models) addRegisteredModel(out, item?.provider, item);
199
+ }
200
+
201
+ return [...out].sort();
202
+ }
203
+
204
+ function isHighRiskProvider(model) {
205
+ return HIGH_RISK_PROVIDERS.some(prefix => model.startsWith(prefix));
206
+ }
207
+
208
+ function inspectModel(model, overrides) {
209
+ const override = overrides[model];
210
+ const detected = detectPattern(model);
211
+ const highRisk = isHighRiskProvider(model);
212
+ const contextTokens = override?.contextTokens ?? null;
213
+ const contextWindow = override?.contextWindow ?? null;
214
+ const hasOverride = !!override;
215
+ const hasBothOverrideNumbers = Number.isInteger(contextTokens) && Number.isInteger(contextWindow);
216
+
217
+ let status = 'ok';
218
+ const reasons = [];
219
+ const recommendations = [];
220
+ const validationActions = [];
221
+
222
+ if (!detected && !hasOverride) {
223
+ status = 'fail';
224
+ reasons.push('model does not match HyperMem autodetect patterns and has no explicit override');
225
+ recommendations.push('add compositor.contextWindowOverrides["provider/model"] with contextTokens and contextWindow');
226
+ validationActions.push('validate the real usable context window empirically, then add an explicit override');
227
+ } else if (highRisk && !hasOverride) {
228
+ status = 'warn';
229
+ reasons.push('provider family is frequently missing or misreporting runtime token budgets');
230
+ recommendations.push('prefer an explicit override even if pattern autodetect matches');
231
+ recommendations.push('verify runtime logs show `budget source: runtime tokenBudget=...` for this exact model');
232
+ validationActions.push('treat OpenAI-compatible/runtime-tokenBudget-missing APIs as untrusted until a live context-window probe confirms the limit');
233
+ } else if (hasOverride && !hasBothOverrideNumbers) {
234
+ status = 'warn';
235
+ reasons.push('override exists but only one of contextTokens/contextWindow is set');
236
+ recommendations.push('declare both numbers so usable budget and advertised window are explicit');
237
+ validationActions.push('fill the missing override field from provider docs or an empirical context-window probe');
238
+ }
239
+
240
+ if (hasOverride && Number.isInteger(contextTokens) && Number.isInteger(contextWindow) && contextTokens > contextWindow) {
241
+ status = 'fail';
242
+ reasons.push('override is invalid: contextTokens exceeds contextWindow');
243
+ recommendations.push('fix the override so contextTokens <= contextWindow');
244
+ validationActions.push('correct the override before trusting HyperMem budgeting for this model');
245
+ }
246
+
247
+ if (status === 'ok') {
248
+ if (hasOverride) reasons.push('explicit override present');
249
+ else if (detected) reasons.push(`autodetect pattern match: ${detected.pattern} (${detected.tokens.toLocaleString()} tokens)`);
250
+ }
251
+
252
+ return {
253
+ model,
254
+ status,
255
+ autodetectPattern: detected?.pattern || null,
256
+ autodetectTokens: detected?.tokens || null,
257
+ highRiskProvider: highRisk,
258
+ override: hasOverride ? { contextTokens, contextWindow } : null,
259
+ reasons,
260
+ recommendations,
261
+ validationActions,
262
+ };
263
+ }
264
+
265
+ let openclawConfig = null;
266
+ let hypermemConfig = null;
267
+
268
+ try {
269
+ openclawConfig = readJsonIfExists(flags.openclawConfig);
270
+ hypermemConfig = readJsonIfExists(flags.hypermemConfig);
271
+ } catch (error) {
272
+ console.error(`Failed to read config: ${error.message}`);
273
+ process.exit(1);
274
+ }
275
+
276
+ const overrides = getOverrides(hypermemConfig);
277
+ const modelSet = new Set(flags.models);
278
+ for (const model of collectConfiguredModels(openclawConfig)) modelSet.add(model);
279
+ if (!flags.configuredOnly) {
280
+ for (const model of collectRegisteredModels(openclawConfig)) modelSet.add(model);
281
+ }
282
+ for (const model of Object.keys(overrides)) modelSet.add(normalizeModelKey(model));
283
+
284
+ const models = [...modelSet].filter(Boolean).sort();
285
+ if (models.length === 0) {
286
+ const result = {
287
+ status: 'empty',
288
+ message: 'No models found. Pass --models or point --openclaw-config at a config file with agent models.',
289
+ };
290
+ if (flags.json) console.log(JSON.stringify(result, null, 2));
291
+ else console.log(result.message);
292
+ process.exit(1);
293
+ }
294
+
295
+ const report = models.map(model => inspectModel(model, overrides));
296
+ const counts = {
297
+ ok: report.filter(r => r.status === 'ok').length,
298
+ warn: report.filter(r => r.status === 'warn').length,
299
+ fail: report.filter(r => r.status === 'fail').length,
300
+ };
301
+
302
+ const exitCode = flags.strict && (counts.warn > 0 || counts.fail > 0)
303
+ ? 1
304
+ : counts.fail > 0 ? 1 : 0;
305
+
306
+ if (flags.json) {
307
+ console.log(JSON.stringify({
308
+ openclawConfig: existsSync(flags.openclawConfig) ? flags.openclawConfig : null,
309
+ hypermemConfig: existsSync(flags.hypermemConfig) ? flags.hypermemConfig : null,
310
+ scope: flags.configuredOnly ? 'configured' : 'configured+registered',
311
+ counts,
312
+ models: report,
313
+ }, null, 2));
314
+ process.exit(exitCode);
315
+ }
316
+
317
+ console.log('HyperMem Model Audit');
318
+ console.log('');
319
+ if (existsSync(flags.openclawConfig)) console.log(`OpenClaw config: ${flags.openclawConfig}`);
320
+ if (existsSync(flags.hypermemConfig)) console.log(`HyperMem config: ${flags.hypermemConfig}`);
321
+ console.log('');
322
+
323
+ console.log(`Scope: ${flags.configuredOnly ? 'configured models only' : 'configured + registered provider catalog models'}`);
324
+ console.log('');
325
+
326
+ for (const item of report) {
327
+ const icon = item.status === 'ok' ? '✅' : item.status === 'warn' ? '⚠️' : '❌';
328
+ console.log(`${icon} ${item.model}`);
329
+ for (const reason of item.reasons) console.log(` - ${reason}`);
330
+ if (item.override) {
331
+ console.log(` - override: contextTokens=${item.override.contextTokens ?? 'unset'}, contextWindow=${item.override.contextWindow ?? 'unset'}`);
332
+ }
333
+ for (const recommendation of item.recommendations) console.log(` - action: ${recommendation}`);
334
+ for (const action of item.validationActions) console.log(` - validate: ${action}`);
335
+ console.log('');
336
+ }
337
+
338
+ console.log(`Summary: ${counts.ok} ok, ${counts.warn} warn, ${counts.fail} fail`);
339
+ process.exit(exitCode);