@psiclawops/hypermem 0.5.0 → 0.5.1
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/background-indexer.d.ts +132 -0
- package/dist/background-indexer.d.ts.map +1 -0
- package/dist/background-indexer.js +1044 -0
- package/dist/cache.d.ts +110 -0
- package/dist/cache.d.ts.map +1 -0
- package/dist/cache.js +495 -0
- package/dist/compaction-fence.d.ts +89 -0
- package/dist/compaction-fence.d.ts.map +1 -0
- package/dist/compaction-fence.js +153 -0
- package/dist/compositor.d.ts +226 -0
- package/dist/compositor.d.ts.map +1 -0
- package/dist/compositor.js +2558 -0
- package/dist/content-type-classifier.d.ts +41 -0
- package/dist/content-type-classifier.d.ts.map +1 -0
- package/dist/content-type-classifier.js +181 -0
- package/dist/cross-agent.d.ts +62 -0
- package/dist/cross-agent.d.ts.map +1 -0
- package/dist/cross-agent.js +259 -0
- package/dist/db.d.ts +131 -0
- package/dist/db.d.ts.map +1 -0
- package/dist/db.js +402 -0
- package/dist/desired-state-store.d.ts +100 -0
- package/dist/desired-state-store.d.ts.map +1 -0
- package/dist/desired-state-store.js +222 -0
- package/dist/doc-chunk-store.d.ts +140 -0
- package/dist/doc-chunk-store.d.ts.map +1 -0
- package/dist/doc-chunk-store.js +391 -0
- package/dist/doc-chunker.d.ts +99 -0
- package/dist/doc-chunker.d.ts.map +1 -0
- package/dist/doc-chunker.js +324 -0
- package/dist/dreaming-promoter.d.ts +86 -0
- package/dist/dreaming-promoter.d.ts.map +1 -0
- package/dist/dreaming-promoter.js +381 -0
- package/dist/episode-store.d.ts +49 -0
- package/dist/episode-store.d.ts.map +1 -0
- package/dist/episode-store.js +135 -0
- package/dist/fact-store.d.ts +75 -0
- package/dist/fact-store.d.ts.map +1 -0
- package/dist/fact-store.js +236 -0
- package/dist/fleet-store.d.ts +144 -0
- package/dist/fleet-store.d.ts.map +1 -0
- package/dist/fleet-store.js +276 -0
- package/dist/fos-mod.d.ts +178 -0
- package/dist/fos-mod.d.ts.map +1 -0
- package/dist/fos-mod.js +416 -0
- package/dist/hybrid-retrieval.d.ts +64 -0
- package/dist/hybrid-retrieval.d.ts.map +1 -0
- package/dist/hybrid-retrieval.js +344 -0
- package/dist/image-eviction.d.ts +49 -0
- package/dist/image-eviction.d.ts.map +1 -0
- package/dist/image-eviction.js +251 -0
- package/dist/index.d.ts +650 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +1072 -0
- package/dist/keystone-scorer.d.ts +51 -0
- package/dist/keystone-scorer.d.ts.map +1 -0
- package/dist/keystone-scorer.js +52 -0
- package/dist/knowledge-graph.d.ts +110 -0
- package/dist/knowledge-graph.d.ts.map +1 -0
- package/dist/knowledge-graph.js +305 -0
- package/dist/knowledge-lint.d.ts +29 -0
- package/dist/knowledge-lint.d.ts.map +1 -0
- package/dist/knowledge-lint.js +116 -0
- package/dist/knowledge-store.d.ts +72 -0
- package/dist/knowledge-store.d.ts.map +1 -0
- package/dist/knowledge-store.js +247 -0
- package/dist/library-schema.d.ts +22 -0
- package/dist/library-schema.d.ts.map +1 -0
- package/dist/library-schema.js +1038 -0
- package/dist/message-store.d.ts +89 -0
- package/dist/message-store.d.ts.map +1 -0
- package/dist/message-store.js +323 -0
- package/dist/metrics-dashboard.d.ts +114 -0
- package/dist/metrics-dashboard.d.ts.map +1 -0
- package/dist/metrics-dashboard.js +260 -0
- package/dist/obsidian-exporter.d.ts +57 -0
- package/dist/obsidian-exporter.d.ts.map +1 -0
- package/dist/obsidian-exporter.js +274 -0
- package/dist/obsidian-watcher.d.ts +147 -0
- package/dist/obsidian-watcher.d.ts.map +1 -0
- package/dist/obsidian-watcher.js +403 -0
- package/dist/open-domain.d.ts +46 -0
- package/dist/open-domain.d.ts.map +1 -0
- package/dist/open-domain.js +125 -0
- package/dist/preference-store.d.ts +54 -0
- package/dist/preference-store.d.ts.map +1 -0
- package/dist/preference-store.js +109 -0
- package/dist/preservation-gate.d.ts +82 -0
- package/dist/preservation-gate.d.ts.map +1 -0
- package/dist/preservation-gate.js +150 -0
- package/dist/proactive-pass.d.ts +63 -0
- package/dist/proactive-pass.d.ts.map +1 -0
- package/dist/proactive-pass.js +239 -0
- package/dist/profiles.d.ts +44 -0
- package/dist/profiles.d.ts.map +1 -0
- package/dist/profiles.js +227 -0
- package/dist/provider-translator.d.ts +50 -0
- package/dist/provider-translator.d.ts.map +1 -0
- package/dist/provider-translator.js +403 -0
- package/dist/rate-limiter.d.ts +76 -0
- package/dist/rate-limiter.d.ts.map +1 -0
- package/dist/rate-limiter.js +179 -0
- package/dist/repair-tool-pairs.d.ts +38 -0
- package/dist/repair-tool-pairs.d.ts.map +1 -0
- package/dist/repair-tool-pairs.js +138 -0
- package/dist/retrieval-policy.d.ts +51 -0
- package/dist/retrieval-policy.d.ts.map +1 -0
- package/dist/retrieval-policy.js +77 -0
- package/dist/schema.d.ts +15 -0
- package/dist/schema.d.ts.map +1 -0
- package/dist/schema.js +229 -0
- package/dist/secret-scanner.d.ts +51 -0
- package/dist/secret-scanner.d.ts.map +1 -0
- package/dist/secret-scanner.js +248 -0
- package/dist/seed.d.ts +108 -0
- package/dist/seed.d.ts.map +1 -0
- package/dist/seed.js +177 -0
- package/dist/session-flusher.d.ts +53 -0
- package/dist/session-flusher.d.ts.map +1 -0
- package/dist/session-flusher.js +69 -0
- package/dist/session-topic-map.d.ts +41 -0
- package/dist/session-topic-map.d.ts.map +1 -0
- package/dist/session-topic-map.js +77 -0
- package/dist/spawn-context.d.ts +54 -0
- package/dist/spawn-context.d.ts.map +1 -0
- package/dist/spawn-context.js +159 -0
- package/dist/system-store.d.ts +73 -0
- package/dist/system-store.d.ts.map +1 -0
- package/dist/system-store.js +182 -0
- package/dist/temporal-store.d.ts +80 -0
- package/dist/temporal-store.d.ts.map +1 -0
- package/dist/temporal-store.js +149 -0
- package/dist/topic-detector.d.ts +35 -0
- package/dist/topic-detector.d.ts.map +1 -0
- package/dist/topic-detector.js +249 -0
- package/dist/topic-store.d.ts +45 -0
- package/dist/topic-store.d.ts.map +1 -0
- package/dist/topic-store.js +136 -0
- package/dist/topic-synthesizer.d.ts +51 -0
- package/dist/topic-synthesizer.d.ts.map +1 -0
- package/dist/topic-synthesizer.js +315 -0
- package/dist/trigger-registry.d.ts +63 -0
- package/dist/trigger-registry.d.ts.map +1 -0
- package/dist/trigger-registry.js +163 -0
- package/dist/types.d.ts +533 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +9 -0
- package/dist/vector-store.d.ts +170 -0
- package/dist/vector-store.d.ts.map +1 -0
- package/dist/vector-store.js +677 -0
- package/dist/version.d.ts +34 -0
- package/dist/version.d.ts.map +1 -0
- package/dist/version.js +34 -0
- package/dist/wiki-page-emitter.d.ts +65 -0
- package/dist/wiki-page-emitter.d.ts.map +1 -0
- package/dist/wiki-page-emitter.js +258 -0
- package/dist/work-store.d.ts +112 -0
- package/dist/work-store.d.ts.map +1 -0
- package/dist/work-store.js +273 -0
- package/package.json +1 -1
|
@@ -0,0 +1,239 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* hypermem Proactive Passes
|
|
3
|
+
*
|
|
4
|
+
* Background maintenance passes that run between indexer ticks to keep
|
|
5
|
+
* message storage lean. Two passes:
|
|
6
|
+
*
|
|
7
|
+
* 1. Noise Sweep — deletes low/zero-signal messages outside the recent
|
|
8
|
+
* window (heartbeats, acks, empty strings, control tokens).
|
|
9
|
+
*
|
|
10
|
+
* 2. Tool Decay — truncates oversized tool_results outside the recent
|
|
11
|
+
* window in-place, preserving JSON structure but collapsing large
|
|
12
|
+
* content blobs into a byte-count placeholder.
|
|
13
|
+
*
|
|
14
|
+
* Both passes are:
|
|
15
|
+
* - Synchronous (DatabaseSync, no async)
|
|
16
|
+
* - Wrapped in transactions (atomic)
|
|
17
|
+
* - Best-effort: catch all errors, log, and return a zero-change result
|
|
18
|
+
*
|
|
19
|
+
* Ported and adapted from ClawText proactive-pass.ts.
|
|
20
|
+
* hypermem schema differences vs ClawText:
|
|
21
|
+
* - No content_type column — we classify on the fly via classifyContentType()
|
|
22
|
+
* - No external payload store — we truncate content inline in tool_results JSON
|
|
23
|
+
* - No ClawText-specific dependencies (payload-store, tool-tracker, etc.)
|
|
24
|
+
*/
|
|
25
|
+
import { classifyContentType } from './content-type-classifier.js';
|
|
26
|
+
// ─── Internal helpers ────────────────────────────────────────────
|
|
27
|
+
/**
|
|
28
|
+
* Resolve the safe window to a finite positive integer.
|
|
29
|
+
* Mirrors the ClawText resolveSafeWindow() guard.
|
|
30
|
+
*/
|
|
31
|
+
function resolveSafeWindow(recentWindowSize) {
|
|
32
|
+
if (Number.isFinite(recentWindowSize) && recentWindowSize > 0) {
|
|
33
|
+
return Math.floor(recentWindowSize);
|
|
34
|
+
}
|
|
35
|
+
return 20;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Get the maximum message_index for a conversation.
|
|
39
|
+
* Returns -1 if no messages exist.
|
|
40
|
+
*/
|
|
41
|
+
function getMaxMessageIndex(db, conversationId) {
|
|
42
|
+
const row = db
|
|
43
|
+
.prepare('SELECT COALESCE(MAX(message_index), -1) AS max_index FROM messages WHERE conversation_id = ?')
|
|
44
|
+
.get(conversationId);
|
|
45
|
+
return typeof row.max_index === 'number' ? row.max_index : -1;
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Decide if a message is noise based on content + is_heartbeat flag.
|
|
49
|
+
*
|
|
50
|
+
* A message is noise when:
|
|
51
|
+
* - is_heartbeat = 1 (explicit heartbeat marker), OR
|
|
52
|
+
* - text_content is NULL or empty (≤3 chars after trimming), OR
|
|
53
|
+
* - classifyContentType() returns 'noise' or 'ack'
|
|
54
|
+
*
|
|
55
|
+
* We call the classifier rather than duplicating its patterns here.
|
|
56
|
+
*/
|
|
57
|
+
function isNoiseMessage(textContent, isHeartbeat) {
|
|
58
|
+
if (isHeartbeat === 1)
|
|
59
|
+
return true;
|
|
60
|
+
if (textContent === null || textContent.trim().length <= 3)
|
|
61
|
+
return true;
|
|
62
|
+
const { type } = classifyContentType(textContent);
|
|
63
|
+
return type === 'noise' || type === 'ack';
|
|
64
|
+
}
|
|
65
|
+
// ─── Noise Sweep ─────────────────────────────────────────────────
|
|
66
|
+
/**
|
|
67
|
+
* Delete noise and heartbeat messages outside the recent window.
|
|
68
|
+
*
|
|
69
|
+
* "Outside the recent window" means message_index < maxIndex - recentWindowSize.
|
|
70
|
+
* Messages inside the window are never deleted, even if they are noise —
|
|
71
|
+
* the model may still reference them in the current turn.
|
|
72
|
+
*
|
|
73
|
+
* Deletions are wrapped in a single transaction. The FTS5 trigger handles
|
|
74
|
+
* index cleanup automatically (msg_fts_ad fires on DELETE).
|
|
75
|
+
*/
|
|
76
|
+
export function runNoiseSweep(db, conversationId, recentWindowSize = 20) {
|
|
77
|
+
const ZERO = { messagesDeleted: 0, passType: 'noise_sweep' };
|
|
78
|
+
try {
|
|
79
|
+
const safeWindow = resolveSafeWindow(recentWindowSize);
|
|
80
|
+
const maxIndex = getMaxMessageIndex(db, conversationId);
|
|
81
|
+
if (maxIndex < 0)
|
|
82
|
+
return ZERO;
|
|
83
|
+
// Messages with message_index strictly below this value are eligible.
|
|
84
|
+
const cutoff = maxIndex - safeWindow;
|
|
85
|
+
if (cutoff <= 0)
|
|
86
|
+
return ZERO; // Not enough history yet
|
|
87
|
+
// Fetch all candidate messages outside the recent window.
|
|
88
|
+
// Exclude messages whose content lives entirely in tool_results — those
|
|
89
|
+
// are tool result rows handled by runToolDecay(), not noise sweep.
|
|
90
|
+
// We deliberately avoid a content-based WHERE clause for the classifier
|
|
91
|
+
// because SQLite can't use the index for JS classification logic;
|
|
92
|
+
// it's cheaper to fetch a small batch and classify in JS.
|
|
93
|
+
const candidates = db
|
|
94
|
+
.prepare(`
|
|
95
|
+
SELECT id, text_content, is_heartbeat
|
|
96
|
+
FROM messages
|
|
97
|
+
WHERE conversation_id = ?
|
|
98
|
+
AND message_index < ?
|
|
99
|
+
AND (tool_results IS NULL OR tool_results = '')
|
|
100
|
+
`)
|
|
101
|
+
.all(conversationId, cutoff);
|
|
102
|
+
if (candidates.length === 0)
|
|
103
|
+
return ZERO;
|
|
104
|
+
// Filter to noise messages
|
|
105
|
+
const toDelete = candidates.filter(row => isNoiseMessage(row.text_content, row.is_heartbeat));
|
|
106
|
+
if (toDelete.length === 0)
|
|
107
|
+
return ZERO;
|
|
108
|
+
const ids = toDelete.map(r => r.id);
|
|
109
|
+
// Delete in a transaction; use chunked IN clauses to avoid
|
|
110
|
+
// SQLite's SQLITE_LIMIT_VARIABLE_NUMBER (default 999).
|
|
111
|
+
let totalDeleted = 0;
|
|
112
|
+
const CHUNK = 500;
|
|
113
|
+
db.prepare('BEGIN').run();
|
|
114
|
+
try {
|
|
115
|
+
for (let i = 0; i < ids.length; i += CHUNK) {
|
|
116
|
+
const chunk = ids.slice(i, i + CHUNK);
|
|
117
|
+
const placeholders = chunk.map(() => '?').join(', ');
|
|
118
|
+
const result = db
|
|
119
|
+
.prepare(`DELETE FROM messages WHERE id IN (${placeholders})`)
|
|
120
|
+
.run(...chunk);
|
|
121
|
+
totalDeleted += typeof result.changes === 'number' ? result.changes : chunk.length;
|
|
122
|
+
}
|
|
123
|
+
db.prepare('COMMIT').run();
|
|
124
|
+
}
|
|
125
|
+
catch (innerErr) {
|
|
126
|
+
db.prepare('ROLLBACK').run();
|
|
127
|
+
throw innerErr;
|
|
128
|
+
}
|
|
129
|
+
return { messagesDeleted: totalDeleted, passType: 'noise_sweep' };
|
|
130
|
+
}
|
|
131
|
+
catch (err) {
|
|
132
|
+
console.warn(`[proactive-pass] Noise sweep failed for conversation ${conversationId}: ${err instanceof Error ? err.message : String(err)}`);
|
|
133
|
+
return ZERO;
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
// ─── Tool Decay ──────────────────────────────────────────────────
|
|
137
|
+
/**
|
|
138
|
+
* Truncate oversized tool_results outside the recent window.
|
|
139
|
+
*
|
|
140
|
+
* Strategy:
|
|
141
|
+
* 1. Find messages whose tool_results JSON string is > 2000 chars total,
|
|
142
|
+
* outside the recent window.
|
|
143
|
+
* 2. Parse the JSON array.
|
|
144
|
+
* 3. For each result entry where the `content` field exceeds 500 chars,
|
|
145
|
+
* replace `content` with `[tool result truncated — N bytes]`.
|
|
146
|
+
* 4. Re-serialize and write back.
|
|
147
|
+
*
|
|
148
|
+
* The JSON structure is preserved (array of result objects). Only the
|
|
149
|
+
* oversized `content` values are collapsed.
|
|
150
|
+
*
|
|
151
|
+
* Mutations are committed in a single transaction.
|
|
152
|
+
*/
|
|
153
|
+
export function runToolDecay(db, conversationId, recentWindowSize = 40) {
|
|
154
|
+
const ZERO = { messagesUpdated: 0, bytesFreed: 0, passType: 'tool_decay' };
|
|
155
|
+
try {
|
|
156
|
+
const safeWindow = resolveSafeWindow(recentWindowSize);
|
|
157
|
+
const maxIndex = getMaxMessageIndex(db, conversationId);
|
|
158
|
+
if (maxIndex < 0)
|
|
159
|
+
return ZERO;
|
|
160
|
+
const cutoff = maxIndex - safeWindow;
|
|
161
|
+
if (cutoff <= 0)
|
|
162
|
+
return ZERO;
|
|
163
|
+
// Fetch messages with large tool_results outside the recent window.
|
|
164
|
+
const candidates = db
|
|
165
|
+
.prepare(`
|
|
166
|
+
SELECT id, tool_results
|
|
167
|
+
FROM messages
|
|
168
|
+
WHERE conversation_id = ?
|
|
169
|
+
AND message_index < ?
|
|
170
|
+
AND tool_results IS NOT NULL
|
|
171
|
+
AND length(tool_results) > 2000
|
|
172
|
+
`)
|
|
173
|
+
.all(conversationId, cutoff);
|
|
174
|
+
if (candidates.length === 0)
|
|
175
|
+
return ZERO;
|
|
176
|
+
// Build the update list by processing each candidate.
|
|
177
|
+
const updates = [];
|
|
178
|
+
for (const row of candidates) {
|
|
179
|
+
let parsed;
|
|
180
|
+
try {
|
|
181
|
+
parsed = JSON.parse(row.tool_results);
|
|
182
|
+
}
|
|
183
|
+
catch {
|
|
184
|
+
// Corrupt JSON — skip this row
|
|
185
|
+
continue;
|
|
186
|
+
}
|
|
187
|
+
if (!Array.isArray(parsed))
|
|
188
|
+
continue;
|
|
189
|
+
let changed = false;
|
|
190
|
+
const newResults = parsed.map(entry => {
|
|
191
|
+
const content = entry.content;
|
|
192
|
+
if (typeof content === 'string' && content.length > 500) {
|
|
193
|
+
const originalBytes = Buffer.byteLength(content, 'utf8');
|
|
194
|
+
changed = true;
|
|
195
|
+
return {
|
|
196
|
+
...entry,
|
|
197
|
+
content: `[tool result truncated — ${originalBytes} bytes]`,
|
|
198
|
+
};
|
|
199
|
+
}
|
|
200
|
+
return entry;
|
|
201
|
+
});
|
|
202
|
+
if (!changed)
|
|
203
|
+
continue;
|
|
204
|
+
const newJson = JSON.stringify(newResults);
|
|
205
|
+
const savedBytes = row.tool_results.length - newJson.length;
|
|
206
|
+
if (savedBytes > 0) {
|
|
207
|
+
updates.push({ id: row.id, newJson, savedBytes });
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
if (updates.length === 0)
|
|
211
|
+
return ZERO;
|
|
212
|
+
let totalUpdated = 0;
|
|
213
|
+
let totalBytesFreed = 0;
|
|
214
|
+
db.prepare('BEGIN').run();
|
|
215
|
+
try {
|
|
216
|
+
const stmt = db.prepare('UPDATE messages SET tool_results = ? WHERE id = ?');
|
|
217
|
+
for (const { id, newJson, savedBytes } of updates) {
|
|
218
|
+
stmt.run(newJson, id);
|
|
219
|
+
totalUpdated++;
|
|
220
|
+
totalBytesFreed += savedBytes;
|
|
221
|
+
}
|
|
222
|
+
db.prepare('COMMIT').run();
|
|
223
|
+
}
|
|
224
|
+
catch (innerErr) {
|
|
225
|
+
db.prepare('ROLLBACK').run();
|
|
226
|
+
throw innerErr;
|
|
227
|
+
}
|
|
228
|
+
return {
|
|
229
|
+
messagesUpdated: totalUpdated,
|
|
230
|
+
bytesFreed: totalBytesFreed,
|
|
231
|
+
passType: 'tool_decay',
|
|
232
|
+
};
|
|
233
|
+
}
|
|
234
|
+
catch (err) {
|
|
235
|
+
console.warn(`[proactive-pass] Tool decay failed for conversation ${conversationId}: ${err instanceof Error ? err.message : String(err)}`);
|
|
236
|
+
return ZERO;
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
//# sourceMappingURL=proactive-pass.js.map
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* hypermem configuration profiles
|
|
3
|
+
*
|
|
4
|
+
* Pre-built configs for common deployment patterns. Pass to createHyperMem()
|
|
5
|
+
* directly or use as a base for custom configs via mergeProfile().
|
|
6
|
+
*
|
|
7
|
+
* Profiles:
|
|
8
|
+
* minimal — 64k context, single agent, low resource usage
|
|
9
|
+
* standard — 128k context, fleet default, balanced
|
|
10
|
+
* rich — 200k+ context, multi-agent, full feature set
|
|
11
|
+
*/
|
|
12
|
+
import type { HyperMemConfig } from './types.js';
|
|
13
|
+
export declare const lightProfile: HyperMemConfig;
|
|
14
|
+
export declare const standardProfile: HyperMemConfig;
|
|
15
|
+
export declare const fullProfile: HyperMemConfig;
|
|
16
|
+
export type ProfileName = 'light' | 'standard' | 'full';
|
|
17
|
+
export declare const minimalProfile: HyperMemConfig;
|
|
18
|
+
export declare const extendedProfile: HyperMemConfig;
|
|
19
|
+
export declare const richProfile: HyperMemConfig;
|
|
20
|
+
export declare const PROFILES: Record<ProfileName, HyperMemConfig>;
|
|
21
|
+
/**
|
|
22
|
+
* Load a named profile.
|
|
23
|
+
*
|
|
24
|
+
* @example
|
|
25
|
+
* const config = getProfile('light');
|
|
26
|
+
* const hm = createHyperMem(config);
|
|
27
|
+
*/
|
|
28
|
+
export declare function getProfile(name: ProfileName | 'extended'): HyperMemConfig;
|
|
29
|
+
/**
|
|
30
|
+
* Merge a partial config on top of a named profile.
|
|
31
|
+
* Deep-merges compositor and indexer; top-level fields are replaced.
|
|
32
|
+
*
|
|
33
|
+
* @example
|
|
34
|
+
* const config = mergeProfile('light', {
|
|
35
|
+
* cache: { keyPrefix: 'myapp:' },
|
|
36
|
+
* compositor: { outputProfile: 'standard' }, // upgrade tier
|
|
37
|
+
* });
|
|
38
|
+
*/
|
|
39
|
+
export declare function mergeProfile(name: ProfileName | 'extended', overrides: DeepPartial<HyperMemConfig>): HyperMemConfig;
|
|
40
|
+
type DeepPartial<T> = {
|
|
41
|
+
[K in keyof T]?: T[K] extends object ? DeepPartial<T[K]> : T[K];
|
|
42
|
+
};
|
|
43
|
+
export {};
|
|
44
|
+
//# sourceMappingURL=profiles.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"profiles.d.ts","sourceRoot":"","sources":["../src/profiles.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,KAAK,EAAE,cAAc,EAAyE,MAAM,YAAY,CAAC;AAiExH,eAAO,MAAM,YAAY,EAAE,cAa1B,CAAC;AA6CF,eAAO,MAAM,eAAe,EAAE,cAO7B,CAAC;AA8CF,eAAO,MAAM,WAAW,EAAE,cAWzB,CAAC;AAMF,MAAM,MAAM,WAAW,GAAG,OAAO,GAAG,UAAU,GAAG,MAAM,CAAC;AAGxD,eAAO,MAAM,cAAc,gBAAe,CAAC;AAC3C,eAAO,MAAM,eAAe,gBAAc,CAAC;AAC3C,eAAO,MAAM,WAAW,gBAAc,CAAC;AAEvC,eAAO,MAAM,QAAQ,EAAE,MAAM,CAAC,WAAW,EAAE,cAAc,CAIxD,CAAC;AAEF;;;;;;GAMG;AACH,wBAAgB,UAAU,CAAC,IAAI,EAAE,WAAW,GAAG,UAAU,GAAG,cAAc,CAIzE;AAED;;;;;;;;;GASG;AACH,wBAAgB,YAAY,CAC1B,IAAI,EAAE,WAAW,GAAG,UAAU,EAC9B,SAAS,EAAE,WAAW,CAAC,cAAc,CAAC,GACrC,cAAc,CAUhB;AAMD,KAAK,WAAW,CAAC,CAAC,IAAI;KACnB,CAAC,IAAI,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,SAAS,MAAM,GAAG,WAAW,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;CAChE,CAAC"}
|
package/dist/profiles.js
ADDED
|
@@ -0,0 +1,227 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* hypermem configuration profiles
|
|
3
|
+
*
|
|
4
|
+
* Pre-built configs for common deployment patterns. Pass to createHyperMem()
|
|
5
|
+
* directly or use as a base for custom configs via mergeProfile().
|
|
6
|
+
*
|
|
7
|
+
* Profiles:
|
|
8
|
+
* minimal — 64k context, single agent, low resource usage
|
|
9
|
+
* standard — 128k context, fleet default, balanced
|
|
10
|
+
* rich — 200k+ context, multi-agent, full feature set
|
|
11
|
+
*/
|
|
12
|
+
// ---------------------------------------------------------------------------
|
|
13
|
+
// Shared base (fields common across all profiles)
|
|
14
|
+
// ---------------------------------------------------------------------------
|
|
15
|
+
const BASE_CACHE = {
|
|
16
|
+
keyPrefix: 'hm:',
|
|
17
|
+
sessionTTL: 3600,
|
|
18
|
+
historyTTL: 86400,
|
|
19
|
+
};
|
|
20
|
+
const BASE_EMBEDDING = {
|
|
21
|
+
ollamaUrl: 'http://localhost:11434',
|
|
22
|
+
model: 'nomic-embed-text',
|
|
23
|
+
dimensions: 768,
|
|
24
|
+
timeout: 10000,
|
|
25
|
+
batchSize: 32,
|
|
26
|
+
};
|
|
27
|
+
// ---------------------------------------------------------------------------
|
|
28
|
+
// light — 64k context window, single agent, constrained resources
|
|
29
|
+
//
|
|
30
|
+
// Design intent:
|
|
31
|
+
// - Small local models (Mistral 7B, Phi-3, Llama 3 8B) at 64k context
|
|
32
|
+
// - Single agent — no cross-session context needed
|
|
33
|
+
// - Minimal ACA stack — no dreaming, no background indexing overhead
|
|
34
|
+
// - Low Redis churn — longer flush intervals, shorter history window
|
|
35
|
+
// - outputProfile: 'light' — ~100 token standalone directives, no fleet concepts
|
|
36
|
+
// - No parallel operations — sequential fact extraction only
|
|
37
|
+
// ---------------------------------------------------------------------------
|
|
38
|
+
const LIGHT_COMPOSITOR = {
|
|
39
|
+
defaultTokenBudget: 40000, // leaves ~24k for model output at 64k window
|
|
40
|
+
maxHistoryMessages: 200, // keep window tight — small models lose coherence past ~150 msgs
|
|
41
|
+
maxFacts: 15, // surface top facts only, don't swamp the window
|
|
42
|
+
maxCrossSessionContext: 0, // single agent — no cross-session
|
|
43
|
+
maxRecentToolPairs: 2, // minimal tool history
|
|
44
|
+
maxProseToolPairs: 4,
|
|
45
|
+
warmHistoryBudgetFraction: 0.35, // slightly less history, more room for context
|
|
46
|
+
contextWindowReserve: 0.35, // generous reserve — small models need output headroom
|
|
47
|
+
dynamicReserveEnabled: true,
|
|
48
|
+
dynamicReserveTurnHorizon: 3, // shorter horizon — small sessions
|
|
49
|
+
dynamicReserveMax: 0.50,
|
|
50
|
+
keystoneHistoryFraction: 0.1, // light keystone — history window is already small
|
|
51
|
+
keystoneMaxMessages: 5,
|
|
52
|
+
keystoneMinSignificance: 0.7, // higher bar — only high-signal keystone messages
|
|
53
|
+
targetBudgetFraction: 0.50, // Anvil spec: 0.50 for light
|
|
54
|
+
maxTotalTriggerTokens: 1500, // tight trigger ceiling
|
|
55
|
+
outputProfile: 'light', // standalone density directives only, no fleet concepts
|
|
56
|
+
wikiTokenCap: 300, // Anvil spec: 300 for light
|
|
57
|
+
};
|
|
58
|
+
const LIGHT_INDEXER = {
|
|
59
|
+
enabled: true,
|
|
60
|
+
factExtractionMode: 'pattern', // pattern only — tiered extraction is heavier
|
|
61
|
+
topicDormantAfter: '12h', // faster dormancy — small systems don't need long windows
|
|
62
|
+
topicClosedAfter: '48h',
|
|
63
|
+
factDecayRate: 0.05, // slightly faster decay — fewer facts, keep them fresh
|
|
64
|
+
episodeSignificanceThreshold: 0.6, // higher bar — only store meaningful episodes
|
|
65
|
+
periodicInterval: 120000, // 2min — less frequent background work on small systems
|
|
66
|
+
batchSize: 64,
|
|
67
|
+
maxMessagesPerTick: 200,
|
|
68
|
+
};
|
|
69
|
+
export const lightProfile = {
|
|
70
|
+
enabled: true,
|
|
71
|
+
dataDir: './hypermem-data',
|
|
72
|
+
cache: BASE_CACHE,
|
|
73
|
+
compositor: LIGHT_COMPOSITOR,
|
|
74
|
+
indexer: LIGHT_INDEXER,
|
|
75
|
+
embedding: {
|
|
76
|
+
...BASE_EMBEDDING,
|
|
77
|
+
batchSize: 8, // smaller batches — don't spike memory on embed
|
|
78
|
+
timeout: 15000, // more generous timeout — local hardware can be slow
|
|
79
|
+
},
|
|
80
|
+
// dreaming: disabled (default) — don't run background promotion on small systems
|
|
81
|
+
// obsidian: disabled (default)
|
|
82
|
+
};
|
|
83
|
+
// ---------------------------------------------------------------------------
|
|
84
|
+
// standard — 128k context window, fleet default, balanced
|
|
85
|
+
//
|
|
86
|
+
// Design intent:
|
|
87
|
+
// - Mid-range models (Sonnet, GPT-5-mini, Gemini Flash) at 128k
|
|
88
|
+
// - Small multi-agent setups or single power-user agents
|
|
89
|
+
// - Full ACA stack — dreaming optional, background indexing on
|
|
90
|
+
// - outputProfile: 'standard' — full FOS, no MOD
|
|
91
|
+
// ---------------------------------------------------------------------------
|
|
92
|
+
const STANDARD_COMPOSITOR = {
|
|
93
|
+
defaultTokenBudget: 90000,
|
|
94
|
+
maxHistoryMessages: 500,
|
|
95
|
+
maxFacts: 30,
|
|
96
|
+
maxCrossSessionContext: 4000,
|
|
97
|
+
maxRecentToolPairs: 3,
|
|
98
|
+
maxProseToolPairs: 10,
|
|
99
|
+
warmHistoryBudgetFraction: 0.40,
|
|
100
|
+
contextWindowReserve: 0.25,
|
|
101
|
+
dynamicReserveEnabled: true,
|
|
102
|
+
dynamicReserveTurnHorizon: 5,
|
|
103
|
+
dynamicReserveMax: 0.50,
|
|
104
|
+
keystoneHistoryFraction: 0.20,
|
|
105
|
+
keystoneMaxMessages: 15,
|
|
106
|
+
keystoneMinSignificance: 0.5,
|
|
107
|
+
targetBudgetFraction: 0.65, // Anvil spec: 0.65 for standard
|
|
108
|
+
maxTotalTriggerTokens: 4000,
|
|
109
|
+
outputProfile: 'standard', // full FOS, MOD suppressed
|
|
110
|
+
wikiTokenCap: 600, // Anvil spec: 600 for standard
|
|
111
|
+
};
|
|
112
|
+
const STANDARD_INDEXER = {
|
|
113
|
+
enabled: true,
|
|
114
|
+
factExtractionMode: 'tiered',
|
|
115
|
+
topicDormantAfter: '24h',
|
|
116
|
+
topicClosedAfter: '72h',
|
|
117
|
+
factDecayRate: 0.02,
|
|
118
|
+
episodeSignificanceThreshold: 0.5,
|
|
119
|
+
periodicInterval: 60000, // 1min — standard background cadence
|
|
120
|
+
batchSize: 128,
|
|
121
|
+
maxMessagesPerTick: 500,
|
|
122
|
+
};
|
|
123
|
+
export const standardProfile = {
|
|
124
|
+
enabled: true,
|
|
125
|
+
dataDir: './hypermem-data',
|
|
126
|
+
cache: BASE_CACHE,
|
|
127
|
+
compositor: STANDARD_COMPOSITOR,
|
|
128
|
+
indexer: STANDARD_INDEXER,
|
|
129
|
+
embedding: BASE_EMBEDDING,
|
|
130
|
+
};
|
|
131
|
+
// ---------------------------------------------------------------------------
|
|
132
|
+
// extended — 200k+ context window, multi-agent, full feature set
|
|
133
|
+
//
|
|
134
|
+
// Design intent:
|
|
135
|
+
// - Large context models (Opus, GPT-5.4, Gemini Pro) at 200k+
|
|
136
|
+
// - Council / multi-agent fleet deployments
|
|
137
|
+
// - Full ACA stack including dreaming, background indexing, cross-session
|
|
138
|
+
// - outputProfile: 'full' — FOS + MOD, full spec
|
|
139
|
+
// - Higher keystone threshold — more historical context worth surfacing
|
|
140
|
+
// ---------------------------------------------------------------------------
|
|
141
|
+
const EXTENDED_COMPOSITOR = {
|
|
142
|
+
defaultTokenBudget: 160000,
|
|
143
|
+
maxHistoryMessages: 1000,
|
|
144
|
+
maxFacts: 60,
|
|
145
|
+
maxCrossSessionContext: 12000,
|
|
146
|
+
maxRecentToolPairs: 5,
|
|
147
|
+
maxProseToolPairs: 15,
|
|
148
|
+
warmHistoryBudgetFraction: 0.45,
|
|
149
|
+
contextWindowReserve: 0.20,
|
|
150
|
+
dynamicReserveEnabled: true,
|
|
151
|
+
dynamicReserveTurnHorizon: 7,
|
|
152
|
+
dynamicReserveMax: 0.45,
|
|
153
|
+
keystoneHistoryFraction: 0.25,
|
|
154
|
+
keystoneMaxMessages: 30,
|
|
155
|
+
keystoneMinSignificance: 0.4,
|
|
156
|
+
targetBudgetFraction: 0.55, // Anvil spec: 0.55 for extended (history is huge, budget carefully)
|
|
157
|
+
maxTotalTriggerTokens: 10000,
|
|
158
|
+
outputProfile: 'full', // FOS + MOD — full fleet spec
|
|
159
|
+
wikiTokenCap: 800, // Anvil spec: 800 for extended
|
|
160
|
+
};
|
|
161
|
+
const EXTENDED_INDEXER = {
|
|
162
|
+
enabled: true,
|
|
163
|
+
factExtractionMode: 'tiered',
|
|
164
|
+
topicDormantAfter: '48h',
|
|
165
|
+
topicClosedAfter: '168h', // 7 days — long-running council topics stay warm
|
|
166
|
+
factDecayRate: 0.01, // slow decay — preserve more institutional memory
|
|
167
|
+
episodeSignificanceThreshold: 0.4,
|
|
168
|
+
periodicInterval: 30000, // 30s — frequent background work for fleet throughput
|
|
169
|
+
batchSize: 256,
|
|
170
|
+
maxMessagesPerTick: 1000,
|
|
171
|
+
};
|
|
172
|
+
export const fullProfile = {
|
|
173
|
+
enabled: true,
|
|
174
|
+
dataDir: './hypermem-data',
|
|
175
|
+
cache: BASE_CACHE,
|
|
176
|
+
compositor: EXTENDED_COMPOSITOR,
|
|
177
|
+
indexer: EXTENDED_INDEXER,
|
|
178
|
+
embedding: {
|
|
179
|
+
...BASE_EMBEDDING,
|
|
180
|
+
batchSize: 64, // larger batches — more throughput for fleet ingest
|
|
181
|
+
timeout: 8000, // tighter timeout — expect capable hardware
|
|
182
|
+
},
|
|
183
|
+
};
|
|
184
|
+
// Legacy aliases — kept for backward compat, removed in 1.0
|
|
185
|
+
export const minimalProfile = lightProfile;
|
|
186
|
+
export const extendedProfile = fullProfile;
|
|
187
|
+
export const richProfile = fullProfile;
|
|
188
|
+
export const PROFILES = {
|
|
189
|
+
light: lightProfile,
|
|
190
|
+
standard: standardProfile,
|
|
191
|
+
full: fullProfile,
|
|
192
|
+
};
|
|
193
|
+
/**
|
|
194
|
+
* Load a named profile.
|
|
195
|
+
*
|
|
196
|
+
* @example
|
|
197
|
+
* const config = getProfile('light');
|
|
198
|
+
* const hm = createHyperMem(config);
|
|
199
|
+
*/
|
|
200
|
+
export function getProfile(name) {
|
|
201
|
+
// backward compat
|
|
202
|
+
if (name === 'extended')
|
|
203
|
+
return structuredClone(fullProfile);
|
|
204
|
+
return structuredClone(PROFILES[name]);
|
|
205
|
+
}
|
|
206
|
+
/**
|
|
207
|
+
* Merge a partial config on top of a named profile.
|
|
208
|
+
* Deep-merges compositor and indexer; top-level fields are replaced.
|
|
209
|
+
*
|
|
210
|
+
* @example
|
|
211
|
+
* const config = mergeProfile('light', {
|
|
212
|
+
* cache: { keyPrefix: 'myapp:' },
|
|
213
|
+
* compositor: { outputProfile: 'standard' }, // upgrade tier
|
|
214
|
+
* });
|
|
215
|
+
*/
|
|
216
|
+
export function mergeProfile(name, overrides) {
|
|
217
|
+
const base = getProfile(name);
|
|
218
|
+
return {
|
|
219
|
+
...base,
|
|
220
|
+
...overrides,
|
|
221
|
+
compositor: { ...base.compositor, ...(overrides.compositor ?? {}) },
|
|
222
|
+
indexer: { ...base.indexer, ...(overrides.indexer ?? {}) },
|
|
223
|
+
embedding: { ...base.embedding, ...(overrides.embedding ?? {}) },
|
|
224
|
+
cache: { ...base.cache, ...(overrides.cache ?? {}) },
|
|
225
|
+
};
|
|
226
|
+
}
|
|
227
|
+
//# sourceMappingURL=profiles.js.map
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* hypermem Provider Translator
|
|
3
|
+
*
|
|
4
|
+
* Converts between provider-neutral (NeutralMessage) and provider-specific formats.
|
|
5
|
+
* This is the ONLY place where provider-specific formatting exists.
|
|
6
|
+
* Storage is always neutral. Translation happens at the send/receive boundary.
|
|
7
|
+
*
|
|
8
|
+
* This eliminates grafting/stripping entirely — tool calls are stored as structured
|
|
9
|
+
* data, and each provider gets the format it expects at send time.
|
|
10
|
+
*/
|
|
11
|
+
import type { NeutralMessage, NeutralToolResult, ProviderMessage } from './types.js';
|
|
12
|
+
/**
|
|
13
|
+
* Final pair-integrity sweep before provider translation.
|
|
14
|
+
*
|
|
15
|
+
* Invariant: never emit a tool_result unless its matching tool_use/tool_call
|
|
16
|
+
* exists in the immediately prior assistant message with the same ID.
|
|
17
|
+
*
|
|
18
|
+
* If the pair is broken, degrade the orphan tool_result into plain user text
|
|
19
|
+
* so providers never see an invalid tool_result block.
|
|
20
|
+
*/
|
|
21
|
+
export declare function repairToolCallPairs(messages: NeutralMessage[]): NeutralMessage[];
|
|
22
|
+
/**
|
|
23
|
+
* Generate a hypermem-native tool call ID.
|
|
24
|
+
* These are provider-neutral and deterministic within a session.
|
|
25
|
+
*/
|
|
26
|
+
export declare function generateToolCallId(): string;
|
|
27
|
+
/**
|
|
28
|
+
* Convert a provider-specific tool call ID to a hypermem ID.
|
|
29
|
+
* Deterministic: same input always produces same output.
|
|
30
|
+
*/
|
|
31
|
+
export declare function normalizeToolCallId(providerId: string): string;
|
|
32
|
+
export type ProviderType = 'anthropic' | 'openai' | 'openai-responses' | 'unknown';
|
|
33
|
+
export declare function detectProvider(providerString: string | null | undefined): ProviderType;
|
|
34
|
+
/**
|
|
35
|
+
* Convert neutral messages to provider-specific format.
|
|
36
|
+
*/
|
|
37
|
+
export declare function toProviderFormat(messages: NeutralMessage[], provider: string | null | undefined): ProviderMessage[];
|
|
38
|
+
/**
|
|
39
|
+
* Convert a provider-specific response to neutral format.
|
|
40
|
+
*/
|
|
41
|
+
export declare function fromProviderFormat(response: Record<string, unknown>, provider: string): NeutralMessage;
|
|
42
|
+
/**
|
|
43
|
+
* Convert a user message (from chat input) to neutral format.
|
|
44
|
+
*/
|
|
45
|
+
export declare function userMessageToNeutral(content: string, metadata?: Record<string, unknown>): NeutralMessage;
|
|
46
|
+
/**
|
|
47
|
+
* Convert tool results to a neutral user message.
|
|
48
|
+
*/
|
|
49
|
+
export declare function toolResultsToNeutral(results: NeutralToolResult[]): NeutralMessage;
|
|
50
|
+
//# sourceMappingURL=provider-translator.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"provider-translator.d.ts","sourceRoot":"","sources":["../src/provider-translator.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,KAAK,EACV,cAAc,EAEd,iBAAiB,EACjB,eAAe,EAChB,MAAM,YAAY,CAAC;AAYpB;;;;;;;;GAQG;AACH,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,cAAc,EAAE,GAAG,cAAc,EAAE,CA4ChF;AAOD;;;GAGG;AACH,wBAAgB,kBAAkB,IAAI,MAAM,CAK3C;AAED;;;GAGG;AACH,wBAAgB,mBAAmB,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,CAI9D;AAID,MAAM,MAAM,YAAY,GAAG,WAAW,GAAG,QAAQ,GAAG,kBAAkB,GAAG,SAAS,CAAC;AAEnF,wBAAgB,cAAc,CAAC,cAAc,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,YAAY,CAOtF;AAgMD;;GAEG;AACH,wBAAgB,gBAAgB,CAC9B,QAAQ,EAAE,cAAc,EAAE,EAC1B,QAAQ,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAClC,eAAe,EAAE,CAenB;AA8ED;;GAEG;AACH,wBAAgB,kBAAkB,CAChC,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACjC,QAAQ,EAAE,MAAM,GACf,cAAc,CAYhB;AAED;;GAEG;AACH,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,cAAc,CAQxG;AAED;;GAEG;AACH,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,iBAAiB,EAAE,GAAG,cAAc,CAOjF"}
|