@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,248 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* hypermem Secret Scanner
|
|
3
|
+
*
|
|
4
|
+
* Lightweight regex-based gate to prevent secrets from leaking into
|
|
5
|
+
* shared memory (visibility >= 'org'). Runs before any write that
|
|
6
|
+
* promotes content to org/council/fleet visibility.
|
|
7
|
+
*
|
|
8
|
+
* Design principles:
|
|
9
|
+
* - Fast: regex-only, no LLM dependency
|
|
10
|
+
* - Conservative: false positives are okay; false negatives are not
|
|
11
|
+
* - Auditable: each hit includes the rule name and matched region (redacted)
|
|
12
|
+
* - Not a full DLP system: blocks the obvious leaks; does not guarantee clean content
|
|
13
|
+
*
|
|
14
|
+
* Patterns sourced from:
|
|
15
|
+
* - gitleaks ruleset (https://github.com/gitleaks/gitleaks)
|
|
16
|
+
* - trufflehog common patterns
|
|
17
|
+
* - Manual additions for OpenClaw-specific secrets
|
|
18
|
+
*/
|
|
19
|
+
const RULES = [
|
|
20
|
+
// ── API Keys ──────────────────────────────────────────────
|
|
21
|
+
{
|
|
22
|
+
id: 'anthropic-api-key',
|
|
23
|
+
description: 'Anthropic API key',
|
|
24
|
+
pattern: /\bsk-ant-[a-zA-Z0-9\-_]{20,}\b/g,
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
id: 'openai-api-key',
|
|
28
|
+
description: 'OpenAI API key',
|
|
29
|
+
pattern: /\bsk-[a-zA-Z0-9\-_]{20,}\b/g,
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
id: 'openai-org-id',
|
|
33
|
+
description: 'OpenAI organization ID',
|
|
34
|
+
pattern: /\borg-[a-zA-Z0-9]{16,}\b/g,
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
id: 'github-pat-classic',
|
|
38
|
+
description: 'GitHub personal access token (classic)',
|
|
39
|
+
pattern: /\bghp_[a-zA-Z0-9]{36}\b/g,
|
|
40
|
+
},
|
|
41
|
+
{
|
|
42
|
+
id: 'github-pat-fine',
|
|
43
|
+
description: 'GitHub fine-grained personal access token',
|
|
44
|
+
pattern: /\bgithub_pat_[a-zA-Z0-9_]{82}\b/g,
|
|
45
|
+
},
|
|
46
|
+
{
|
|
47
|
+
id: 'github-oauth-token',
|
|
48
|
+
description: 'GitHub OAuth token',
|
|
49
|
+
pattern: /\bgho_[a-zA-Z0-9]{36}\b/g,
|
|
50
|
+
},
|
|
51
|
+
{
|
|
52
|
+
id: 'github-app-token',
|
|
53
|
+
description: 'GitHub App installation token',
|
|
54
|
+
pattern: /\bghs_[a-zA-Z0-9]{36}\b/g,
|
|
55
|
+
},
|
|
56
|
+
{
|
|
57
|
+
id: 'github-refresh-token',
|
|
58
|
+
description: 'GitHub refresh token',
|
|
59
|
+
pattern: /\bghr_[a-zA-Z0-9]{76}\b/g,
|
|
60
|
+
},
|
|
61
|
+
{
|
|
62
|
+
id: 'aws-access-key',
|
|
63
|
+
description: 'AWS access key ID',
|
|
64
|
+
pattern: /\b(A3T[A-Z0-9]|AKIA|AGPA|AIDA|AROA|AIPA|ANPA|ANVA|ASIA)[A-Z0-9]{16}\b/g,
|
|
65
|
+
},
|
|
66
|
+
{
|
|
67
|
+
id: 'aws-secret-key',
|
|
68
|
+
description: 'AWS secret access key (heuristic)',
|
|
69
|
+
pattern: /(?:aws_secret_access_key|AWS_SECRET_ACCESS_KEY)["\s:=]+([A-Za-z0-9\/+]{40})\b/gi,
|
|
70
|
+
},
|
|
71
|
+
{
|
|
72
|
+
id: 'google-api-key',
|
|
73
|
+
description: 'Google API key',
|
|
74
|
+
pattern: /\bAIza[0-9A-Za-z_\-]{35}\b/g,
|
|
75
|
+
},
|
|
76
|
+
{
|
|
77
|
+
id: 'google-oauth',
|
|
78
|
+
description: 'Google OAuth client secret',
|
|
79
|
+
pattern: /\bGOCSPX-[0-9A-Za-z_\-]{28}\b/g,
|
|
80
|
+
},
|
|
81
|
+
{
|
|
82
|
+
id: 'slack-token',
|
|
83
|
+
description: 'Slack API token',
|
|
84
|
+
pattern: /\bxox[baprs]-[0-9]{10,13}-[0-9]{10,13}-[a-zA-Z0-9]{24,32}\b/g,
|
|
85
|
+
},
|
|
86
|
+
{
|
|
87
|
+
id: 'slack-webhook',
|
|
88
|
+
description: 'Slack incoming webhook URL',
|
|
89
|
+
pattern: /https:\/\/hooks\.slack\.com\/services\/[A-Z0-9]{9,11}\/[A-Z0-9]{9,11}\/[a-zA-Z0-9]{24}/g,
|
|
90
|
+
},
|
|
91
|
+
{
|
|
92
|
+
id: 'discord-token',
|
|
93
|
+
description: 'Discord bot token',
|
|
94
|
+
pattern: /\b[MNO][a-zA-Z0-9]{23}\.[a-zA-Z0-9_\-]{6}\.[a-zA-Z0-9_\-]{27}\b/g,
|
|
95
|
+
},
|
|
96
|
+
{
|
|
97
|
+
id: 'discord-webhook',
|
|
98
|
+
description: 'Discord webhook URL',
|
|
99
|
+
pattern: /https:\/\/discord(?:app)?\.com\/api\/webhooks\/[0-9]{17,19}\/[a-zA-Z0-9_\-]{68}/g,
|
|
100
|
+
},
|
|
101
|
+
{
|
|
102
|
+
id: 'stripe-key',
|
|
103
|
+
description: 'Stripe API key',
|
|
104
|
+
pattern: /\b(?:sk|pk|rk)_(?:live|test)_[a-zA-Z0-9]{24,99}\b/g,
|
|
105
|
+
},
|
|
106
|
+
{
|
|
107
|
+
id: 'sendgrid-key',
|
|
108
|
+
description: 'SendGrid API key',
|
|
109
|
+
pattern: /\bSG\.[a-zA-Z0-9\-_]{22,}\.[a-zA-Z0-9\-_]{43,}\b/g,
|
|
110
|
+
},
|
|
111
|
+
{
|
|
112
|
+
id: 'twilio-account-sid',
|
|
113
|
+
description: 'Twilio Account SID',
|
|
114
|
+
pattern: /\bAC[0-9a-fA-F]{32}\b/g,
|
|
115
|
+
},
|
|
116
|
+
{
|
|
117
|
+
id: 'twilio-auth-token',
|
|
118
|
+
description: 'Twilio Auth Token (heuristic)',
|
|
119
|
+
pattern: /(?:twilio[_\s\-]*(?:auth[_\s\-]*)?token)["\s:=]+([0-9a-f]{32})\b/gi,
|
|
120
|
+
},
|
|
121
|
+
// ── Private Keys / Certificates ───────────────────────────
|
|
122
|
+
{
|
|
123
|
+
id: 'pem-private-key',
|
|
124
|
+
description: 'PEM private key block',
|
|
125
|
+
pattern: /-----BEGIN (?:RSA |EC |DSA |OPENSSH |ENCRYPTED )?PRIVATE KEY-----/g,
|
|
126
|
+
},
|
|
127
|
+
{
|
|
128
|
+
id: 'pem-certificate',
|
|
129
|
+
description: 'PEM certificate (may contain private data)',
|
|
130
|
+
pattern: /-----BEGIN CERTIFICATE-----/g,
|
|
131
|
+
},
|
|
132
|
+
// ── Passwords in common config patterns ───────────────────
|
|
133
|
+
{
|
|
134
|
+
id: 'password-assignment',
|
|
135
|
+
description: 'Password in config-like assignment',
|
|
136
|
+
// Matches: password=<value>, "password": "<value>", PASSWORD=<value>
|
|
137
|
+
pattern: /(?:^|[,{;\n])\s*(?:password|passwd|secret|token|api_key|apikey|api-key|access_token|auth_token|client_secret)\s*[=:]\s*["']([^"'\n]{8,})["']/gim,
|
|
138
|
+
minEntropy: 2.5,
|
|
139
|
+
},
|
|
140
|
+
// ── Database connection strings ────────────────────────────
|
|
141
|
+
{
|
|
142
|
+
id: 'db-connection-string',
|
|
143
|
+
description: 'Database connection string with credentials',
|
|
144
|
+
pattern: /(?:postgres|postgresql|mysql|mongodb|redis):\/\/[^:]+:[^@]{6,}@/gi,
|
|
145
|
+
},
|
|
146
|
+
// ── Bearer tokens ─────────────────────────────────────────
|
|
147
|
+
{
|
|
148
|
+
id: 'bearer-token-header',
|
|
149
|
+
description: 'Bearer token in Authorization header value',
|
|
150
|
+
pattern: /Authorization[:\s]+Bearer\s+([a-zA-Z0-9\-_=.]{20,})/gi,
|
|
151
|
+
},
|
|
152
|
+
// ── JWT ───────────────────────────────────────────────────
|
|
153
|
+
{
|
|
154
|
+
id: 'jwt-token',
|
|
155
|
+
description: 'JSON Web Token',
|
|
156
|
+
pattern: /\beyJ[a-zA-Z0-9\-_]+\.eyJ[a-zA-Z0-9\-_]+\.[a-zA-Z0-9\-_]+\b/g,
|
|
157
|
+
minEntropy: 3.0,
|
|
158
|
+
},
|
|
159
|
+
// ── OpenClaw-specific ──────────────────────────────────────
|
|
160
|
+
{
|
|
161
|
+
id: 'openclaw-api-key',
|
|
162
|
+
description: 'OpenClaw API key',
|
|
163
|
+
pattern: /\boc_[a-zA-Z0-9\-_]{24,}\b/g,
|
|
164
|
+
},
|
|
165
|
+
];
|
|
166
|
+
// ─── Entropy Calculation ─────────────────────────────────────────
|
|
167
|
+
/**
|
|
168
|
+
* Calculate Shannon entropy of a string (bits per character, base 2).
|
|
169
|
+
* Used to filter out low-entropy false positives like "password=example".
|
|
170
|
+
*/
|
|
171
|
+
function shannonEntropy(s) {
|
|
172
|
+
if (s.length === 0)
|
|
173
|
+
return 0;
|
|
174
|
+
const freq = new Map();
|
|
175
|
+
for (const ch of s) {
|
|
176
|
+
freq.set(ch, (freq.get(ch) || 0) + 1);
|
|
177
|
+
}
|
|
178
|
+
let entropy = 0;
|
|
179
|
+
for (const count of freq.values()) {
|
|
180
|
+
const p = count / s.length;
|
|
181
|
+
entropy -= p * Math.log2(p);
|
|
182
|
+
}
|
|
183
|
+
return entropy;
|
|
184
|
+
}
|
|
185
|
+
// ─── Redaction ───────────────────────────────────────────────────
|
|
186
|
+
/**
|
|
187
|
+
* Redact a matched value: show first 3 and last 3 characters, mask the middle.
|
|
188
|
+
*/
|
|
189
|
+
function redact(value) {
|
|
190
|
+
if (value.length <= 8)
|
|
191
|
+
return '[REDACTED]';
|
|
192
|
+
const prefix = value.slice(0, 3);
|
|
193
|
+
const suffix = value.slice(-3);
|
|
194
|
+
const masked = '*'.repeat(Math.min(value.length - 6, 20));
|
|
195
|
+
return `${prefix}${masked}${suffix}`;
|
|
196
|
+
}
|
|
197
|
+
// ─── Public API ──────────────────────────────────────────────────
|
|
198
|
+
/**
|
|
199
|
+
* Scan content for secrets before promoting to shared visibility.
|
|
200
|
+
*
|
|
201
|
+
* Returns { clean: true } when no secrets found.
|
|
202
|
+
* Returns { clean: false, hits } when secrets are detected.
|
|
203
|
+
*
|
|
204
|
+
* Callers must NOT write content with visibility >= 'org' when clean is false.
|
|
205
|
+
*/
|
|
206
|
+
export function scanForSecrets(content) {
|
|
207
|
+
const hits = [];
|
|
208
|
+
for (const rule of RULES) {
|
|
209
|
+
// Reset lastIndex for global regexes
|
|
210
|
+
rule.pattern.lastIndex = 0;
|
|
211
|
+
let match;
|
|
212
|
+
while ((match = rule.pattern.exec(content)) !== null) {
|
|
213
|
+
const matched = match[1] ?? match[0]; // prefer capture group 1 if present
|
|
214
|
+
// Entropy filter
|
|
215
|
+
if (rule.minEntropy && shannonEntropy(matched) < rule.minEntropy) {
|
|
216
|
+
continue;
|
|
217
|
+
}
|
|
218
|
+
hits.push({
|
|
219
|
+
rule: rule.id,
|
|
220
|
+
description: rule.description,
|
|
221
|
+
redactedMatch: redact(matched),
|
|
222
|
+
offset: match.index,
|
|
223
|
+
});
|
|
224
|
+
// Cap at 10 hits per content to avoid O(n²) explosion on adversarial input
|
|
225
|
+
if (hits.length >= 10)
|
|
226
|
+
break;
|
|
227
|
+
}
|
|
228
|
+
if (hits.length >= 10)
|
|
229
|
+
break;
|
|
230
|
+
}
|
|
231
|
+
return { clean: hits.length === 0, hits };
|
|
232
|
+
}
|
|
233
|
+
/**
|
|
234
|
+
* Returns true when content is safe to promote to shared visibility (>= 'org').
|
|
235
|
+
* Convenience wrapper around scanForSecrets.
|
|
236
|
+
*/
|
|
237
|
+
export function isSafeForSharedVisibility(content) {
|
|
238
|
+
return scanForSecrets(content).clean;
|
|
239
|
+
}
|
|
240
|
+
/**
|
|
241
|
+
* Check whether a visibility level requires secret scanning.
|
|
242
|
+
* Scanning is required for 'org', 'council', and 'fleet'.
|
|
243
|
+
* 'private' content stays with the owner — no cross-agent risk.
|
|
244
|
+
*/
|
|
245
|
+
export function requiresScan(visibility) {
|
|
246
|
+
return visibility === 'org' || visibility === 'council' || visibility === 'fleet';
|
|
247
|
+
}
|
|
248
|
+
//# sourceMappingURL=secret-scanner.js.map
|
package/dist/seed.d.ts
ADDED
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* hypermem Workspace Seeder
|
|
3
|
+
*
|
|
4
|
+
* Reads ACA workspace files, chunks them by logical section, and indexes
|
|
5
|
+
* into hypermem for demand-loaded retrieval (ACA offload).
|
|
6
|
+
*
|
|
7
|
+
* Usage:
|
|
8
|
+
* const seeder = new WorkspaceSeeder(hypermem);
|
|
9
|
+
* const result = await seeder.seedWorkspace('/path/to/workspace', { agentId: 'forge' });
|
|
10
|
+
*
|
|
11
|
+
* Idempotent: skips files whose source hash hasn't changed since last index.
|
|
12
|
+
* Atomic: each file's chunks are swapped in a single transaction.
|
|
13
|
+
*
|
|
14
|
+
* Files seeded (from ACA_COLLECTIONS):
|
|
15
|
+
* POLICY.md → governance/policy (shared-fleet)
|
|
16
|
+
* CHARTER.md → governance/charter (per-tier)
|
|
17
|
+
* COMMS.md → governance/comms (shared-fleet)
|
|
18
|
+
* AGENTS.md → operations/agents (per-tier)
|
|
19
|
+
* TOOLS.md → operations/tools (per-agent)
|
|
20
|
+
* SOUL.md → identity/soul (per-agent)
|
|
21
|
+
* JOB.md → identity/job (per-agent)
|
|
22
|
+
* MOTIVATIONS.md → identity/motivations(per-agent)
|
|
23
|
+
* MEMORY.md → memory/decisions (per-agent)
|
|
24
|
+
* memory/*.md → memory/daily (per-agent)
|
|
25
|
+
*/
|
|
26
|
+
import { type IndexResult } from './doc-chunk-store.js';
|
|
27
|
+
export interface SeedOptions {
|
|
28
|
+
/** Agent ID for per-agent scoped chunks */
|
|
29
|
+
agentId?: string;
|
|
30
|
+
/** Tier for per-tier scoped chunks (council/director) */
|
|
31
|
+
tier?: string;
|
|
32
|
+
/** Whether to force re-index even if hash unchanged */
|
|
33
|
+
force?: boolean;
|
|
34
|
+
/** Only seed specific collections (e.g., ['governance/policy']) */
|
|
35
|
+
collections?: string[];
|
|
36
|
+
/** Whether to include daily memory files (memory/YYYY-MM-DD.md) */
|
|
37
|
+
includeDailyMemory?: boolean;
|
|
38
|
+
/** Max daily memory files to include (most recent first) */
|
|
39
|
+
dailyMemoryLimit?: number;
|
|
40
|
+
}
|
|
41
|
+
export interface SeedFileResult {
|
|
42
|
+
filePath: string;
|
|
43
|
+
collection: string;
|
|
44
|
+
result: IndexResult;
|
|
45
|
+
}
|
|
46
|
+
export interface SeedResult {
|
|
47
|
+
/** Files successfully processed */
|
|
48
|
+
files: SeedFileResult[];
|
|
49
|
+
/** Total new chunks inserted */
|
|
50
|
+
totalInserted: number;
|
|
51
|
+
/** Total stale chunks deleted */
|
|
52
|
+
totalDeleted: number;
|
|
53
|
+
/** Files that were up to date (skipped) */
|
|
54
|
+
skipped: number;
|
|
55
|
+
/** Files that were re-indexed */
|
|
56
|
+
reindexed: number;
|
|
57
|
+
/** Files that had errors */
|
|
58
|
+
errors: Array<{
|
|
59
|
+
filePath: string;
|
|
60
|
+
error: string;
|
|
61
|
+
}>;
|
|
62
|
+
}
|
|
63
|
+
export declare class WorkspaceSeeder {
|
|
64
|
+
private db;
|
|
65
|
+
private chunkStore;
|
|
66
|
+
constructor(db: import('node:sqlite').DatabaseSync);
|
|
67
|
+
/**
|
|
68
|
+
* Seed all ACA files from a workspace directory.
|
|
69
|
+
*/
|
|
70
|
+
seedWorkspace(workspaceDir: string, opts?: SeedOptions): Promise<SeedResult>;
|
|
71
|
+
/**
|
|
72
|
+
* Seed a single file explicitly.
|
|
73
|
+
*/
|
|
74
|
+
seedFile(filePath: string, collection: string, opts?: SeedOptions): SeedFileResult;
|
|
75
|
+
/**
|
|
76
|
+
* Check which workspace files need re-indexing.
|
|
77
|
+
*/
|
|
78
|
+
checkStaleness(workspaceDir: string, opts?: SeedOptions): Array<{
|
|
79
|
+
filePath: string;
|
|
80
|
+
collection: string;
|
|
81
|
+
needsReindex: boolean;
|
|
82
|
+
}>;
|
|
83
|
+
/**
|
|
84
|
+
* Get stats about what's currently indexed.
|
|
85
|
+
*/
|
|
86
|
+
getIndexStats(): {
|
|
87
|
+
collection: string;
|
|
88
|
+
count: number;
|
|
89
|
+
sources: number;
|
|
90
|
+
totalTokens: number;
|
|
91
|
+
}[];
|
|
92
|
+
/**
|
|
93
|
+
* Query indexed chunks by collection.
|
|
94
|
+
*/
|
|
95
|
+
queryChunks(collection: string, opts?: {
|
|
96
|
+
agentId?: string;
|
|
97
|
+
tier?: string;
|
|
98
|
+
limit?: number;
|
|
99
|
+
keyword?: string;
|
|
100
|
+
}): import("./doc-chunk-store.js").DocChunkRow[];
|
|
101
|
+
private discoverFiles;
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Seed a workspace directly from a DatabaseSync instance.
|
|
105
|
+
* Convenience wrapper for use in the hook handler and CLI.
|
|
106
|
+
*/
|
|
107
|
+
export declare function seedWorkspace(db: import('node:sqlite').DatabaseSync, workspaceDir: string, opts?: SeedOptions): Promise<SeedResult>;
|
|
108
|
+
//# sourceMappingURL=seed.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"seed.d.ts","sourceRoot":"","sources":["../src/seed.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAKH,OAAO,EAAiB,KAAK,WAAW,EAAE,MAAM,sBAAsB,CAAC;AAIvE,MAAM,WAAW,WAAW;IAC1B,2CAA2C;IAC3C,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,yDAAyD;IACzD,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,uDAAuD;IACvD,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,mEAAmE;IACnE,WAAW,CAAC,EAAE,MAAM,EAAE,CAAC;IACvB,mEAAmE;IACnE,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAC7B,4DAA4D;IAC5D,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC3B;AAED,MAAM,WAAW,cAAc;IAC7B,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,WAAW,CAAC;CACrB;AAED,MAAM,WAAW,UAAU;IACzB,mCAAmC;IACnC,KAAK,EAAE,cAAc,EAAE,CAAC;IACxB,gCAAgC;IAChC,aAAa,EAAE,MAAM,CAAC;IACtB,iCAAiC;IACjC,YAAY,EAAE,MAAM,CAAC;IACrB,2CAA2C;IAC3C,OAAO,EAAE,MAAM,CAAC;IAChB,iCAAiC;IACjC,SAAS,EAAE,MAAM,CAAC;IAClB,4BAA4B;IAC5B,MAAM,EAAE,KAAK,CAAC;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CACpD;AAID,qBAAa,eAAe;IAGd,OAAO,CAAC,EAAE;IAFtB,OAAO,CAAC,UAAU,CAAgB;gBAEd,EAAE,EAAE,OAAO,aAAa,EAAE,YAAY;IAI1D;;OAEG;IACG,aAAa,CAAC,YAAY,EAAE,MAAM,EAAE,IAAI,GAAE,WAAgB,GAAG,OAAO,CAAC,UAAU,CAAC;IAmDtF;;OAEG;IACH,QAAQ,CAAC,QAAQ,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,IAAI,GAAE,WAAgB,GAAG,cAAc;IAmBtF;;OAEG;IACH,cAAc,CAAC,YAAY,EAAE,MAAM,EAAE,IAAI,GAAE,WAAgB,GAAG,KAAK,CAAC;QAClE,QAAQ,EAAE,MAAM,CAAC;QACjB,UAAU,EAAE,MAAM,CAAC;QACnB,YAAY,EAAE,OAAO,CAAC;KACvB,CAAC;IAeF;;OAEG;IACH,aAAa;;;;;;IAIb;;OAEG;IACH,WAAW,CAAC,UAAU,EAAE,MAAM,EAAE,IAAI,GAAE;QAAE,OAAO,CAAC,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAC;QAAC,OAAO,CAAC,EAAE,MAAM,CAAA;KAAO;IAMhH,OAAO,CAAC,aAAa;CAuCtB;AAID;;;GAGG;AACH,wBAAsB,aAAa,CACjC,EAAE,EAAE,OAAO,aAAa,EAAE,YAAY,EACtC,YAAY,EAAE,MAAM,EACpB,IAAI,GAAE,WAAgB,GACrB,OAAO,CAAC,UAAU,CAAC,CAGrB"}
|
package/dist/seed.js
ADDED
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* hypermem Workspace Seeder
|
|
3
|
+
*
|
|
4
|
+
* Reads ACA workspace files, chunks them by logical section, and indexes
|
|
5
|
+
* into hypermem for demand-loaded retrieval (ACA offload).
|
|
6
|
+
*
|
|
7
|
+
* Usage:
|
|
8
|
+
* const seeder = new WorkspaceSeeder(hypermem);
|
|
9
|
+
* const result = await seeder.seedWorkspace('/path/to/workspace', { agentId: 'forge' });
|
|
10
|
+
*
|
|
11
|
+
* Idempotent: skips files whose source hash hasn't changed since last index.
|
|
12
|
+
* Atomic: each file's chunks are swapped in a single transaction.
|
|
13
|
+
*
|
|
14
|
+
* Files seeded (from ACA_COLLECTIONS):
|
|
15
|
+
* POLICY.md → governance/policy (shared-fleet)
|
|
16
|
+
* CHARTER.md → governance/charter (per-tier)
|
|
17
|
+
* COMMS.md → governance/comms (shared-fleet)
|
|
18
|
+
* AGENTS.md → operations/agents (per-tier)
|
|
19
|
+
* TOOLS.md → operations/tools (per-agent)
|
|
20
|
+
* SOUL.md → identity/soul (per-agent)
|
|
21
|
+
* JOB.md → identity/job (per-agent)
|
|
22
|
+
* MOTIVATIONS.md → identity/motivations(per-agent)
|
|
23
|
+
* MEMORY.md → memory/decisions (per-agent)
|
|
24
|
+
* memory/*.md → memory/daily (per-agent)
|
|
25
|
+
*/
|
|
26
|
+
import { existsSync, readdirSync, readFileSync } from 'node:fs';
|
|
27
|
+
import * as path from 'node:path';
|
|
28
|
+
import { chunkFile, inferCollection, ACA_COLLECTIONS, hashContent } from './doc-chunker.js';
|
|
29
|
+
import { DocChunkStore } from './doc-chunk-store.js';
|
|
30
|
+
// ─── Seeder ──────────────────────────────────────────────────────
|
|
31
|
+
export class WorkspaceSeeder {
|
|
32
|
+
db;
|
|
33
|
+
chunkStore;
|
|
34
|
+
constructor(db) {
|
|
35
|
+
this.db = db;
|
|
36
|
+
this.chunkStore = new DocChunkStore(db);
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Seed all ACA files from a workspace directory.
|
|
40
|
+
*/
|
|
41
|
+
async seedWorkspace(workspaceDir, opts = {}) {
|
|
42
|
+
const result = {
|
|
43
|
+
files: [],
|
|
44
|
+
totalInserted: 0,
|
|
45
|
+
totalDeleted: 0,
|
|
46
|
+
skipped: 0,
|
|
47
|
+
reindexed: 0,
|
|
48
|
+
errors: [],
|
|
49
|
+
};
|
|
50
|
+
const filesToProcess = this.discoverFiles(workspaceDir, opts);
|
|
51
|
+
for (const { filePath, collectionDef } of filesToProcess) {
|
|
52
|
+
// Skip if collection filter provided
|
|
53
|
+
if (opts.collections && !opts.collections.includes(collectionDef.collection)) {
|
|
54
|
+
continue;
|
|
55
|
+
}
|
|
56
|
+
try {
|
|
57
|
+
const chunks = chunkFile(filePath, {
|
|
58
|
+
collection: collectionDef.collection,
|
|
59
|
+
scope: collectionDef.scope,
|
|
60
|
+
tier: collectionDef.scope === 'per-tier' ? (opts.tier ?? 'all') : undefined,
|
|
61
|
+
agentId: collectionDef.scope === 'per-agent' ? opts.agentId : undefined,
|
|
62
|
+
});
|
|
63
|
+
if (chunks.length === 0)
|
|
64
|
+
continue;
|
|
65
|
+
// Force re-index if requested
|
|
66
|
+
if (opts.force) {
|
|
67
|
+
this.chunkStore.deleteSource(filePath, collectionDef.collection);
|
|
68
|
+
}
|
|
69
|
+
const indexResult = this.chunkStore.indexChunks(chunks);
|
|
70
|
+
result.files.push({ filePath, collection: collectionDef.collection, result: indexResult });
|
|
71
|
+
result.totalInserted += indexResult.inserted;
|
|
72
|
+
result.totalDeleted += indexResult.deleted;
|
|
73
|
+
if (indexResult.skipped)
|
|
74
|
+
result.skipped++;
|
|
75
|
+
if (indexResult.reindexed)
|
|
76
|
+
result.reindexed++;
|
|
77
|
+
}
|
|
78
|
+
catch (err) {
|
|
79
|
+
result.errors.push({
|
|
80
|
+
filePath,
|
|
81
|
+
error: err instanceof Error ? err.message : String(err),
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
return result;
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Seed a single file explicitly.
|
|
89
|
+
*/
|
|
90
|
+
seedFile(filePath, collection, opts = {}) {
|
|
91
|
+
const collectionDef = Object.values(ACA_COLLECTIONS).find(d => d.collection === collection)
|
|
92
|
+
?? { collection, scope: 'per-agent', description: '' };
|
|
93
|
+
if (opts.force) {
|
|
94
|
+
this.chunkStore.deleteSource(filePath, collection);
|
|
95
|
+
}
|
|
96
|
+
const chunks = chunkFile(filePath, {
|
|
97
|
+
collection,
|
|
98
|
+
scope: collectionDef.scope,
|
|
99
|
+
tier: collectionDef.scope === 'per-tier' ? (opts.tier ?? 'all') : undefined,
|
|
100
|
+
agentId: collectionDef.scope === 'per-agent' ? opts.agentId : undefined,
|
|
101
|
+
});
|
|
102
|
+
const indexResult = this.chunkStore.indexChunks(chunks);
|
|
103
|
+
return { filePath, collection, result: indexResult };
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* Check which workspace files need re-indexing.
|
|
107
|
+
*/
|
|
108
|
+
checkStaleness(workspaceDir, opts = {}) {
|
|
109
|
+
const filesToProcess = this.discoverFiles(workspaceDir, opts);
|
|
110
|
+
return filesToProcess.map(({ filePath, collectionDef }) => {
|
|
111
|
+
try {
|
|
112
|
+
const content = readFileSync(filePath, 'utf-8');
|
|
113
|
+
const hash = hashContent(content);
|
|
114
|
+
const needsReindex = this.chunkStore.needsReindex(filePath, collectionDef.collection, hash);
|
|
115
|
+
return { filePath, collection: collectionDef.collection, needsReindex };
|
|
116
|
+
}
|
|
117
|
+
catch {
|
|
118
|
+
return { filePath, collection: collectionDef.collection, needsReindex: true };
|
|
119
|
+
}
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* Get stats about what's currently indexed.
|
|
124
|
+
*/
|
|
125
|
+
getIndexStats() {
|
|
126
|
+
return this.chunkStore.getStats();
|
|
127
|
+
}
|
|
128
|
+
/**
|
|
129
|
+
* Query indexed chunks by collection.
|
|
130
|
+
*/
|
|
131
|
+
queryChunks(collection, opts = {}) {
|
|
132
|
+
return this.chunkStore.queryChunks({ collection, ...opts });
|
|
133
|
+
}
|
|
134
|
+
// ─── Private helpers ─────────────────────────────────────────
|
|
135
|
+
discoverFiles(workspaceDir, opts) {
|
|
136
|
+
const files = [];
|
|
137
|
+
// Known ACA files in workspace root
|
|
138
|
+
for (const fileName of Object.keys(ACA_COLLECTIONS)) {
|
|
139
|
+
const filePath = path.join(workspaceDir, fileName);
|
|
140
|
+
if (!existsSync(filePath))
|
|
141
|
+
continue;
|
|
142
|
+
const collectionDef = inferCollection(fileName, opts.agentId);
|
|
143
|
+
if (!collectionDef)
|
|
144
|
+
continue;
|
|
145
|
+
files.push({ filePath, collectionDef });
|
|
146
|
+
}
|
|
147
|
+
// Daily memory files
|
|
148
|
+
if (opts.includeDailyMemory !== false) {
|
|
149
|
+
const memoryDir = path.join(workspaceDir, 'memory');
|
|
150
|
+
if (existsSync(memoryDir)) {
|
|
151
|
+
const memFiles = readdirSync(memoryDir)
|
|
152
|
+
.filter(f => /^\d{4}-\d{2}-\d{2}\.md$/.test(f))
|
|
153
|
+
.sort()
|
|
154
|
+
.reverse(); // Most recent first
|
|
155
|
+
const limit = opts.dailyMemoryLimit ?? 30;
|
|
156
|
+
for (const memFile of memFiles.slice(0, limit)) {
|
|
157
|
+
const filePath = path.join(memoryDir, memFile);
|
|
158
|
+
const collectionDef = inferCollection(memFile, opts.agentId);
|
|
159
|
+
if (collectionDef) {
|
|
160
|
+
files.push({ filePath, collectionDef });
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
return files;
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
// ─── Standalone seed function ────────────────────────────────────
|
|
169
|
+
/**
|
|
170
|
+
* Seed a workspace directly from a DatabaseSync instance.
|
|
171
|
+
* Convenience wrapper for use in the hook handler and CLI.
|
|
172
|
+
*/
|
|
173
|
+
export async function seedWorkspace(db, workspaceDir, opts = {}) {
|
|
174
|
+
const seeder = new WorkspaceSeeder(db);
|
|
175
|
+
return seeder.seedWorkspace(workspaceDir, opts);
|
|
176
|
+
}
|
|
177
|
+
//# sourceMappingURL=seed.js.map
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* session-flusher.ts
|
|
3
|
+
*
|
|
4
|
+
* Provides a clean, operator-safe way to flush a session's hot cache from Redis
|
|
5
|
+
* without touching long-term memory (facts, vectors, episodes, knowledge graph).
|
|
6
|
+
*
|
|
7
|
+
* Used to implement the /fresh slash command — lets users start a new unwarmed
|
|
8
|
+
* session on demand without requiring a gateway restart.
|
|
9
|
+
*
|
|
10
|
+
* Long-term memory (facts, vectors, episodes) is intentionally preserved.
|
|
11
|
+
* It will re-warm naturally on the next session bootstrap.
|
|
12
|
+
*/
|
|
13
|
+
import type { CacheLayer } from './cache.js';
|
|
14
|
+
export interface FlushSessionOptions {
|
|
15
|
+
/** If true, also clears topic-level Redis keys for this session */
|
|
16
|
+
includeTopics?: boolean;
|
|
17
|
+
}
|
|
18
|
+
export interface FlushSessionResult {
|
|
19
|
+
success: boolean;
|
|
20
|
+
agentId: string;
|
|
21
|
+
sessionKey: string;
|
|
22
|
+
/** ISO timestamp of the flush */
|
|
23
|
+
flushedAt: string;
|
|
24
|
+
/** What was cleared */
|
|
25
|
+
cleared: string[];
|
|
26
|
+
error?: string;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Flush a session's cache hot layer.
|
|
30
|
+
*
|
|
31
|
+
* Safe to call at any time. Long-term stores (SQLite facts, vectors, episodes,
|
|
32
|
+
* knowledge graph) are not touched. The next session bootstrap will re-warm
|
|
33
|
+
* from those stores naturally.
|
|
34
|
+
*
|
|
35
|
+
* @param cache Connected CacheLayer instance
|
|
36
|
+
* @param agentId Agent identifier (e.g. "forge")
|
|
37
|
+
* @param sessionKey Full session key (e.g. "agent:forge:webchat:scratchpad")
|
|
38
|
+
* @param opts Optional flags
|
|
39
|
+
*/
|
|
40
|
+
export declare function flushSession(cache: CacheLayer, agentId: string, sessionKey: string, opts?: FlushSessionOptions): Promise<FlushSessionResult>;
|
|
41
|
+
/**
|
|
42
|
+
* Convenience class for operator tooling that holds a bound agentId.
|
|
43
|
+
*/
|
|
44
|
+
export declare class SessionFlusher {
|
|
45
|
+
private readonly cache;
|
|
46
|
+
private readonly agentId;
|
|
47
|
+
constructor(cache: CacheLayer, agentId: string);
|
|
48
|
+
/**
|
|
49
|
+
* Flush the hot cache for a specific session.
|
|
50
|
+
*/
|
|
51
|
+
flush(sessionKey: string, opts?: FlushSessionOptions): Promise<FlushSessionResult>;
|
|
52
|
+
}
|
|
53
|
+
//# sourceMappingURL=session-flusher.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"session-flusher.d.ts","sourceRoot":"","sources":["../src/session-flusher.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAE7C,MAAM,WAAW,mBAAmB;IAClC,mEAAmE;IACnE,aAAa,CAAC,EAAE,OAAO,CAAC;CACzB;AAED,MAAM,WAAW,kBAAkB;IACjC,OAAO,EAAE,OAAO,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,MAAM,CAAC;IACnB,iCAAiC;IACjC,SAAS,EAAE,MAAM,CAAC;IAClB,uBAAuB;IACvB,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED;;;;;;;;;;;GAWG;AACH,wBAAsB,YAAY,CAChC,KAAK,EAAE,UAAU,EACjB,OAAO,EAAE,MAAM,EACf,UAAU,EAAE,MAAM,EAClB,IAAI,GAAE,mBAAwB,GAC7B,OAAO,CAAC,kBAAkB,CAAC,CA2B7B;AAED;;GAEG;AACH,qBAAa,cAAc;IAEvB,OAAO,CAAC,QAAQ,CAAC,KAAK;IACtB,OAAO,CAAC,QAAQ,CAAC,OAAO;gBADP,KAAK,EAAE,UAAU,EACjB,OAAO,EAAE,MAAM;IAGlC;;OAEG;IACG,KAAK,CAAC,UAAU,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,mBAAmB,GAAG,OAAO,CAAC,kBAAkB,CAAC;CAGzF"}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* session-flusher.ts
|
|
3
|
+
*
|
|
4
|
+
* Provides a clean, operator-safe way to flush a session's hot cache from Redis
|
|
5
|
+
* without touching long-term memory (facts, vectors, episodes, knowledge graph).
|
|
6
|
+
*
|
|
7
|
+
* Used to implement the /fresh slash command — lets users start a new unwarmed
|
|
8
|
+
* session on demand without requiring a gateway restart.
|
|
9
|
+
*
|
|
10
|
+
* Long-term memory (facts, vectors, episodes) is intentionally preserved.
|
|
11
|
+
* It will re-warm naturally on the next session bootstrap.
|
|
12
|
+
*/
|
|
13
|
+
/**
|
|
14
|
+
* Flush a session's cache hot layer.
|
|
15
|
+
*
|
|
16
|
+
* Safe to call at any time. Long-term stores (SQLite facts, vectors, episodes,
|
|
17
|
+
* knowledge graph) are not touched. The next session bootstrap will re-warm
|
|
18
|
+
* from those stores naturally.
|
|
19
|
+
*
|
|
20
|
+
* @param cache Connected CacheLayer instance
|
|
21
|
+
* @param agentId Agent identifier (e.g. "forge")
|
|
22
|
+
* @param sessionKey Full session key (e.g. "agent:forge:webchat:scratchpad")
|
|
23
|
+
* @param opts Optional flags
|
|
24
|
+
*/
|
|
25
|
+
export async function flushSession(cache, agentId, sessionKey, opts = {}) {
|
|
26
|
+
const cleared = [];
|
|
27
|
+
try {
|
|
28
|
+
// Core eviction — clears: system, identity, history, window, cursor,
|
|
29
|
+
// context, facts, tools, meta — and removes from active sessions set
|
|
30
|
+
await cache.evictSession(agentId, sessionKey);
|
|
31
|
+
cleared.push('system', 'identity', 'history', 'window', 'cursor', 'context', 'facts', 'tools', 'meta', 'active-sessions');
|
|
32
|
+
return {
|
|
33
|
+
success: true,
|
|
34
|
+
agentId,
|
|
35
|
+
sessionKey,
|
|
36
|
+
flushedAt: new Date().toISOString(),
|
|
37
|
+
cleared,
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
catch (err) {
|
|
41
|
+
const error = err instanceof Error ? err.message : String(err);
|
|
42
|
+
return {
|
|
43
|
+
success: false,
|
|
44
|
+
agentId,
|
|
45
|
+
sessionKey,
|
|
46
|
+
flushedAt: new Date().toISOString(),
|
|
47
|
+
cleared,
|
|
48
|
+
error,
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Convenience class for operator tooling that holds a bound agentId.
|
|
54
|
+
*/
|
|
55
|
+
export class SessionFlusher {
|
|
56
|
+
cache;
|
|
57
|
+
agentId;
|
|
58
|
+
constructor(cache, agentId) {
|
|
59
|
+
this.cache = cache;
|
|
60
|
+
this.agentId = agentId;
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Flush the hot cache for a specific session.
|
|
64
|
+
*/
|
|
65
|
+
async flush(sessionKey, opts) {
|
|
66
|
+
return flushSession(this.cache, this.agentId, sessionKey, opts);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
//# sourceMappingURL=session-flusher.js.map
|