@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,381 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* dreaming-promoter.ts
|
|
3
|
+
*
|
|
4
|
+
* hypermem-native dreaming promotion pass.
|
|
5
|
+
*
|
|
6
|
+
* Unlike the stock memory-core dreaming feature (which appends raw content to
|
|
7
|
+
* MEMORY.md), this promoter generates pointer-format entries that match the
|
|
8
|
+
* council's MEMORY.md convention:
|
|
9
|
+
*
|
|
10
|
+
* - **{domain} — {title}:** {summary}
|
|
11
|
+
* → `memory_search("{query}")`
|
|
12
|
+
*
|
|
13
|
+
* Scoring uses confidence, decay, recency, and domain cluster weight.
|
|
14
|
+
* Dedup prevents re-promoting topics already covered by existing pointers.
|
|
15
|
+
*
|
|
16
|
+
* Dry-run mode returns what would be written without modifying any files.
|
|
17
|
+
*/
|
|
18
|
+
import * as fs from 'node:fs/promises';
|
|
19
|
+
import * as path from 'node:path';
|
|
20
|
+
import * as os from 'node:os';
|
|
21
|
+
export const DEFAULT_DREAMER_CONFIG = {
|
|
22
|
+
enabled: false,
|
|
23
|
+
minScore: 0.75,
|
|
24
|
+
minConfidence: 0.70,
|
|
25
|
+
maxPromotionsPerRun: 5,
|
|
26
|
+
tickInterval: 12,
|
|
27
|
+
dryRun: false,
|
|
28
|
+
recencyHalfLifeDays: 7,
|
|
29
|
+
maxAgeDays: 30,
|
|
30
|
+
};
|
|
31
|
+
// ─── Workspace path resolution ───────────────────────────────────────────────
|
|
32
|
+
/**
|
|
33
|
+
* Resolve the workspace directory for an agent.
|
|
34
|
+
* Council agents live at ~/.openclaw/workspace-council/{agentId}/
|
|
35
|
+
* Other agents at ~/.openclaw/workspace/{agentId}/
|
|
36
|
+
*/
|
|
37
|
+
export async function resolveAgentWorkspacePath(agentId) {
|
|
38
|
+
const home = os.homedir();
|
|
39
|
+
const councilPath = path.join(home, '.openclaw', 'workspace-council', agentId);
|
|
40
|
+
const workspacePath = path.join(home, '.openclaw', 'workspace', agentId);
|
|
41
|
+
try {
|
|
42
|
+
await fs.access(councilPath);
|
|
43
|
+
return councilPath;
|
|
44
|
+
}
|
|
45
|
+
catch {
|
|
46
|
+
try {
|
|
47
|
+
await fs.access(workspacePath);
|
|
48
|
+
return workspacePath;
|
|
49
|
+
}
|
|
50
|
+
catch {
|
|
51
|
+
return null;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
// ─── Scoring ─────────────────────────────────────────────────────────────────
|
|
56
|
+
/**
|
|
57
|
+
* Composite promotion score.
|
|
58
|
+
*
|
|
59
|
+
* score = confidence_factor × recency_factor × quality_factor
|
|
60
|
+
*
|
|
61
|
+
* confidence_factor: 0..1 — the raw confidence, penalized by decay
|
|
62
|
+
* recency_factor: 0..1 — exponential decay from age (half-life = config)
|
|
63
|
+
* quality_factor: 0..1 — length/richness proxy (bonus for medium-length facts)
|
|
64
|
+
*/
|
|
65
|
+
function scoreCandidate(fact, config) {
|
|
66
|
+
const confidenceFactor = fact.confidence * (1 - fact.decayScore * 0.5);
|
|
67
|
+
const halfLife = config.recencyHalfLifeDays;
|
|
68
|
+
const recencyFactor = Math.exp(-(Math.LN2 / halfLife) * fact.ageDays);
|
|
69
|
+
// Quality: medium-length facts (80–180 chars) score best. Very long or very
|
|
70
|
+
// short get penalized — they're likely fragments or noisy captures.
|
|
71
|
+
const len = fact.content.length;
|
|
72
|
+
const qualityFactor = len < 60 ? 0.6 : len > 220 ? 0.75 : 1.0;
|
|
73
|
+
return confidenceFactor * recencyFactor * qualityFactor;
|
|
74
|
+
}
|
|
75
|
+
// ─── Pointer generation ───────────────────────────────────────────────────────
|
|
76
|
+
/**
|
|
77
|
+
* Extract a concise title and search query from fact content.
|
|
78
|
+
* Avoids NLP — purely string heuristics, fast and deterministic.
|
|
79
|
+
*/
|
|
80
|
+
function extractPointerMeta(content, domain) {
|
|
81
|
+
// Clean up trailing code artifacts
|
|
82
|
+
const cleaned = content
|
|
83
|
+
.replace(/['"]\s*\).*$/, '') // strip trailing '); or '),
|
|
84
|
+
.replace(/',\s*tool_calls:.*$/, '') // strip tool_calls artifacts
|
|
85
|
+
.replace(/…$/, '') // strip ellipsis
|
|
86
|
+
.replace(/\s+/g, ' ')
|
|
87
|
+
.trim();
|
|
88
|
+
// Title: first meaningful clause (up to first period, comma, or 50 chars)
|
|
89
|
+
let title = cleaned.split(/[.,;:]/)[0].trim();
|
|
90
|
+
if (title.length > 55)
|
|
91
|
+
title = title.slice(0, 52) + '…';
|
|
92
|
+
// Query: leading noun phrase — first 6-8 significant words, excluding articles/prepositions
|
|
93
|
+
const stopWords = new Set([
|
|
94
|
+
'the', 'a', 'an', 'is', 'are', 'was', 'were', 'be', 'been', 'being',
|
|
95
|
+
'to', 'of', 'in', 'on', 'at', 'by', 'for', 'with', 'as', 'it', 'its',
|
|
96
|
+
'this', 'that', 'we', 'i', 'you', 'they', 'and', 'or', 'but', 'so',
|
|
97
|
+
]);
|
|
98
|
+
const words = cleaned
|
|
99
|
+
.split(/\s+/)
|
|
100
|
+
.map(w => w.replace(/[^a-zA-Z0-9_-]/g, '').toLowerCase())
|
|
101
|
+
.filter(w => w.length > 2 && !stopWords.has(w));
|
|
102
|
+
const queryWords = words.slice(0, 7);
|
|
103
|
+
// Prepend domain as context anchor so search hits the right agent scope
|
|
104
|
+
const query = `${domain} ${queryWords.join(' ')}`.slice(0, 60).trim();
|
|
105
|
+
return { title, query };
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* Format a promoted fact as a MEMORY.md pointer entry.
|
|
109
|
+
*/
|
|
110
|
+
function formatPointer(domain, title, summary, query) {
|
|
111
|
+
// Capitalize domain label for display
|
|
112
|
+
const domainLabel = domain.charAt(0).toUpperCase() + domain.slice(1);
|
|
113
|
+
return `- **${domainLabel} — ${title}:** ${summary}\n → \`memory_search("${query}")\``;
|
|
114
|
+
}
|
|
115
|
+
// ─── Dedup ───────────────────────────────────────────────────────────────────
|
|
116
|
+
/**
|
|
117
|
+
* Parse existing memory_search() calls from MEMORY.md content.
|
|
118
|
+
* Returns a set of normalized query strings already indexed.
|
|
119
|
+
*/
|
|
120
|
+
function parseExistingPointers(memoryContent) {
|
|
121
|
+
const existing = new Set();
|
|
122
|
+
const re = /memory_search\("([^"]+)"\)/g;
|
|
123
|
+
let m;
|
|
124
|
+
while ((m = re.exec(memoryContent)) !== null) {
|
|
125
|
+
existing.add(m[1].toLowerCase().trim());
|
|
126
|
+
}
|
|
127
|
+
return existing;
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* Check if a proposed query overlaps significantly with any existing pointer.
|
|
131
|
+
* Uses word-level Jaccard similarity (threshold 0.4).
|
|
132
|
+
*/
|
|
133
|
+
function isDuplicatePointer(proposedQuery, existing) {
|
|
134
|
+
if (existing.has(proposedQuery.toLowerCase().trim()))
|
|
135
|
+
return true;
|
|
136
|
+
const proposedWords = new Set(proposedQuery.toLowerCase().split(/\s+/).filter(w => w.length > 2));
|
|
137
|
+
if (proposedWords.size === 0)
|
|
138
|
+
return false;
|
|
139
|
+
for (const existingQuery of existing) {
|
|
140
|
+
const existingWords = new Set(existingQuery.split(/\s+/).filter(w => w.length > 2));
|
|
141
|
+
const intersection = [...proposedWords].filter(w => existingWords.has(w)).length;
|
|
142
|
+
const union = new Set([...proposedWords, ...existingWords]).size;
|
|
143
|
+
const jaccard = union > 0 ? intersection / union : 0;
|
|
144
|
+
if (jaccard >= 0.4)
|
|
145
|
+
return true;
|
|
146
|
+
}
|
|
147
|
+
return false;
|
|
148
|
+
}
|
|
149
|
+
// ─── MEMORY.md write ─────────────────────────────────────────────────────────
|
|
150
|
+
const PROMOTED_SECTION_HEADER = '## Promoted Facts';
|
|
151
|
+
const PROMOTED_SECTION_MARKER = '<!-- hypermem:dreaming-promoted -->';
|
|
152
|
+
/**
|
|
153
|
+
* Append promoted pointer entries to MEMORY.md.
|
|
154
|
+
* Writes into an existing "## Promoted Facts" section, or appends one.
|
|
155
|
+
* Non-destructive: only appends, never rewrites existing content.
|
|
156
|
+
*/
|
|
157
|
+
async function appendToMemoryFile(memoryPath, entries) {
|
|
158
|
+
if (entries.length === 0)
|
|
159
|
+
return;
|
|
160
|
+
let existing = '';
|
|
161
|
+
try {
|
|
162
|
+
existing = await fs.readFile(memoryPath, 'utf-8');
|
|
163
|
+
}
|
|
164
|
+
catch {
|
|
165
|
+
// File doesn't exist — start fresh (unlikely but handle gracefully)
|
|
166
|
+
existing = `# MEMORY.md\n\n_This is an index, not a store. Use \`memory_search\` for full context on any topic._\n\n`;
|
|
167
|
+
}
|
|
168
|
+
const newLines = entries.map(e => e.pointer).join('\n');
|
|
169
|
+
const timestamp = new Date().toISOString().slice(0, 10);
|
|
170
|
+
if (existing.includes(PROMOTED_SECTION_HEADER)) {
|
|
171
|
+
// Inject after the section header line
|
|
172
|
+
const headerIdx = existing.indexOf(PROMOTED_SECTION_HEADER);
|
|
173
|
+
const afterHeader = existing.indexOf('\n', headerIdx) + 1;
|
|
174
|
+
const insertPoint = afterHeader;
|
|
175
|
+
const updated = existing.slice(0, insertPoint) +
|
|
176
|
+
`<!-- promoted ${timestamp} -->\n${newLines}\n` +
|
|
177
|
+
existing.slice(insertPoint);
|
|
178
|
+
await fs.writeFile(memoryPath, updated, 'utf-8');
|
|
179
|
+
}
|
|
180
|
+
else {
|
|
181
|
+
// Append a new section at end of file
|
|
182
|
+
const appendBlock = `\n${PROMOTED_SECTION_HEADER}\n${PROMOTED_SECTION_MARKER}\n` +
|
|
183
|
+
`<!-- promoted ${timestamp} -->\n${newLines}\n`;
|
|
184
|
+
await fs.writeFile(memoryPath, existing.trimEnd() + '\n' + appendBlock, 'utf-8');
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
// ─── Promotion-time content filter ─────────────────────────────────────────
|
|
188
|
+
/**
|
|
189
|
+
* Reject facts that are clearly noise at promotion time.
|
|
190
|
+
* A second line of defense — the indexer's isQualityFact() is the primary filter,
|
|
191
|
+
* but legacy noise in the DB (pre-TUNE-013) still needs to be caught here.
|
|
192
|
+
*/
|
|
193
|
+
function isPromotable(content) {
|
|
194
|
+
// Multi-line content — reject both actual newlines AND escaped \n sequences
|
|
195
|
+
// (some facts stored pre-TUNE-013 have literal \n in the string value)
|
|
196
|
+
if (content.includes('\n') || content.includes('\\n'))
|
|
197
|
+
return false;
|
|
198
|
+
// External content markers
|
|
199
|
+
if (content.includes('EXTERNAL_UNTRUSTED_CONTENT') || content.includes('<<<'))
|
|
200
|
+
return false;
|
|
201
|
+
// Markdown heading fragments
|
|
202
|
+
if (/^#{1,4}\s/.test(content))
|
|
203
|
+
return false;
|
|
204
|
+
// Code/tool artifacts
|
|
205
|
+
if (/tool_calls:|\)\s*[;,]\s*$|',\s*$/.test(content))
|
|
206
|
+
return false;
|
|
207
|
+
// Escaped newlines embedded in content (stored as literal \n in DB)
|
|
208
|
+
if (/\\n/.test(content))
|
|
209
|
+
return false;
|
|
210
|
+
// URL-heavy (external research/docs, not fleet knowledge)
|
|
211
|
+
const urlCount = (content.match(/https?:\/\//g) || []).length;
|
|
212
|
+
if (urlCount >= 2)
|
|
213
|
+
return false;
|
|
214
|
+
// Fragment starts — no subject (lowercase/article/conjunction lead-ins)
|
|
215
|
+
if (/^(and |or |but |to |in |of |the |a |an |by |for |at |on |with |\d+\.|\* |\- )/.test(content.trim()))
|
|
216
|
+
return false;
|
|
217
|
+
// Context-free references: starts with a possessive/pronoun referencing something outside
|
|
218
|
+
if (/^(it |its |this |that |these |those |they |their |we |our |i |my )/.test(content.toLowerCase().trim()))
|
|
219
|
+
return false;
|
|
220
|
+
// External research noise: benchmark/tool comparison content
|
|
221
|
+
if (/\b(LOCOMO|LoCoMo|LangMem|SuperMemory|Zep|Honcho|Mem0)\b/i.test(content) &&
|
|
222
|
+
/\b(study|benchmark|dataset|employed|researchers|similarity|retrieval|accuracy)\b/i.test(content))
|
|
223
|
+
return false;
|
|
224
|
+
// Apache license boilerplate
|
|
225
|
+
if (/AS IS.*BASIS|distributed under the License/i.test(content))
|
|
226
|
+
return false;
|
|
227
|
+
// Pure CLI output or file path patterns
|
|
228
|
+
if (/^(git |npm |node |python3? |bash |echo |cat |ls |curl )/.test(content.trim()))
|
|
229
|
+
return false;
|
|
230
|
+
// Templated TODO/example patterns (from docs, not real fleet state)
|
|
231
|
+
if (/TODO:|implement Z|capture\.py/.test(content))
|
|
232
|
+
return false;
|
|
233
|
+
// Must start with a capital letter or number (complete sentences only)
|
|
234
|
+
if (!/^[A-Z0-9]/.test(content.trim()))
|
|
235
|
+
return false;
|
|
236
|
+
// Minimum meaningful length: 50 chars as promotion floor (stricter than indexer's 40)
|
|
237
|
+
if (content.trim().length < 50)
|
|
238
|
+
return false;
|
|
239
|
+
return true;
|
|
240
|
+
}
|
|
241
|
+
/**
|
|
242
|
+
* Run the dreaming promotion pass for a single agent.
|
|
243
|
+
*
|
|
244
|
+
* Reads qualified facts from library.db, scores them, deduplicates against
|
|
245
|
+
* existing MEMORY.md pointers, and writes new pointer entries.
|
|
246
|
+
*/
|
|
247
|
+
export async function runDreamingPromoter(agentId, libraryDb, config = {}) {
|
|
248
|
+
const cfg = { ...DEFAULT_DREAMER_CONFIG, ...config };
|
|
249
|
+
const result = {
|
|
250
|
+
agentId,
|
|
251
|
+
candidates: 0,
|
|
252
|
+
promoted: 0,
|
|
253
|
+
skippedDuplicate: 0,
|
|
254
|
+
skippedThreshold: 0,
|
|
255
|
+
entries: [],
|
|
256
|
+
memoryPath: null,
|
|
257
|
+
dryRun: cfg.dryRun,
|
|
258
|
+
};
|
|
259
|
+
// 1. Resolve workspace path
|
|
260
|
+
const wsPath = await resolveAgentWorkspacePath(agentId);
|
|
261
|
+
if (!wsPath) {
|
|
262
|
+
return result; // No workspace — skip silently
|
|
263
|
+
}
|
|
264
|
+
const memoryPath = path.join(wsPath, 'MEMORY.md');
|
|
265
|
+
result.memoryPath = memoryPath;
|
|
266
|
+
// 2. Read existing MEMORY.md (for dedup)
|
|
267
|
+
let memoryContent = '';
|
|
268
|
+
try {
|
|
269
|
+
memoryContent = await fs.readFile(memoryPath, 'utf-8');
|
|
270
|
+
}
|
|
271
|
+
catch {
|
|
272
|
+
// File may not exist — will be created on first promotion
|
|
273
|
+
}
|
|
274
|
+
const existingPointers = parseExistingPointers(memoryContent);
|
|
275
|
+
// 3. Query candidate facts from library.db
|
|
276
|
+
// Criteria: active (not superseded), has domain, meets confidence floor,
|
|
277
|
+
// within age window, not null content
|
|
278
|
+
const cutoffDate = new Date(Date.now() - cfg.maxAgeDays * 86400_000).toISOString();
|
|
279
|
+
const rawFacts = libraryDb.prepare(`
|
|
280
|
+
SELECT
|
|
281
|
+
id,
|
|
282
|
+
agent_id,
|
|
283
|
+
domain,
|
|
284
|
+
content,
|
|
285
|
+
confidence,
|
|
286
|
+
decay_score,
|
|
287
|
+
ROUND((julianday('now') - julianday(created_at)), 2) AS age_days
|
|
288
|
+
FROM facts
|
|
289
|
+
WHERE agent_id = ?
|
|
290
|
+
AND superseded_by IS NULL
|
|
291
|
+
AND domain IS NOT NULL
|
|
292
|
+
AND confidence >= ?
|
|
293
|
+
AND created_at >= ?
|
|
294
|
+
AND LENGTH(content) >= 40
|
|
295
|
+
AND LENGTH(content) <= 300
|
|
296
|
+
ORDER BY confidence DESC, decay_score ASC, created_at DESC
|
|
297
|
+
LIMIT 200
|
|
298
|
+
`).all(agentId, cfg.minConfidence, cutoffDate);
|
|
299
|
+
result.candidates = rawFacts.length;
|
|
300
|
+
// 4. Score and rank
|
|
301
|
+
const scored = rawFacts
|
|
302
|
+
.filter(f => isPromotable(f.content))
|
|
303
|
+
.map(f => ({
|
|
304
|
+
id: f.id,
|
|
305
|
+
agentId: f.agent_id,
|
|
306
|
+
domain: f.domain,
|
|
307
|
+
content: f.content,
|
|
308
|
+
confidence: f.confidence,
|
|
309
|
+
decayScore: f.decay_score,
|
|
310
|
+
ageDays: f.age_days,
|
|
311
|
+
score: scoreCandidate({ confidence: f.confidence, decayScore: f.decay_score, ageDays: f.age_days, content: f.content }, cfg),
|
|
312
|
+
}));
|
|
313
|
+
scored.sort((a, b) => b.score - a.score);
|
|
314
|
+
// 5. Select up to maxPromotionsPerRun entries, with dedup
|
|
315
|
+
// Track which queries we've already added this run (cross-entry dedup)
|
|
316
|
+
const addedThisRun = new Set(existingPointers);
|
|
317
|
+
const toPromote = [];
|
|
318
|
+
for (const fact of scored) {
|
|
319
|
+
if (toPromote.length >= cfg.maxPromotionsPerRun)
|
|
320
|
+
break;
|
|
321
|
+
// Score threshold
|
|
322
|
+
if (fact.score < cfg.minScore) {
|
|
323
|
+
result.skippedThreshold++;
|
|
324
|
+
continue;
|
|
325
|
+
}
|
|
326
|
+
const { title, query } = extractPointerMeta(fact.content, fact.domain);
|
|
327
|
+
// Dedup check against existing pointers + already-selected entries this run
|
|
328
|
+
if (isDuplicatePointer(query, addedThisRun)) {
|
|
329
|
+
result.skippedDuplicate++;
|
|
330
|
+
continue;
|
|
331
|
+
}
|
|
332
|
+
// Trim summary to a clean one-liner (max 120 chars)
|
|
333
|
+
const rawSummary = fact.content
|
|
334
|
+
.replace(/['"]\s*\).*$/, '')
|
|
335
|
+
.replace(/\s+/g, ' ')
|
|
336
|
+
.trim();
|
|
337
|
+
const summary = rawSummary.length > 120 ? rawSummary.slice(0, 117) + '…' : rawSummary;
|
|
338
|
+
const pointer = formatPointer(fact.domain, title, summary, query);
|
|
339
|
+
const entry = {
|
|
340
|
+
factId: fact.id,
|
|
341
|
+
domain: fact.domain,
|
|
342
|
+
pointer,
|
|
343
|
+
title,
|
|
344
|
+
summary,
|
|
345
|
+
query,
|
|
346
|
+
score: Math.round(fact.score * 1000) / 1000,
|
|
347
|
+
dryRun: cfg.dryRun,
|
|
348
|
+
};
|
|
349
|
+
toPromote.push(entry);
|
|
350
|
+
addedThisRun.add(query.toLowerCase().trim());
|
|
351
|
+
}
|
|
352
|
+
result.entries = toPromote;
|
|
353
|
+
result.promoted = toPromote.length;
|
|
354
|
+
// 6. Write to MEMORY.md (unless dry-run)
|
|
355
|
+
if (!cfg.dryRun && toPromote.length > 0) {
|
|
356
|
+
await appendToMemoryFile(memoryPath, toPromote);
|
|
357
|
+
console.log(`[dreaming] Promoted ${toPromote.length} facts to ${memoryPath} ` +
|
|
358
|
+
`(${result.skippedDuplicate} dupes, ${result.skippedThreshold} below threshold)`);
|
|
359
|
+
}
|
|
360
|
+
return result;
|
|
361
|
+
}
|
|
362
|
+
/**
|
|
363
|
+
* Run the dreaming promotion pass for all agents in a fleet.
|
|
364
|
+
* Called from the BackgroundIndexer on every N ticks.
|
|
365
|
+
*/
|
|
366
|
+
export async function runDreamingPassForFleet(agentIds, libraryDb, config = {}) {
|
|
367
|
+
const results = [];
|
|
368
|
+
for (const agentId of agentIds) {
|
|
369
|
+
try {
|
|
370
|
+
const r = await runDreamingPromoter(agentId, libraryDb, config);
|
|
371
|
+
if (r.promoted > 0 || r.dryRun) {
|
|
372
|
+
results.push(r);
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
catch (err) {
|
|
376
|
+
console.warn(`[dreaming] Failed for agent ${agentId} (non-fatal):`, err.message);
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
return results;
|
|
380
|
+
}
|
|
381
|
+
//# sourceMappingURL=dreaming-promoter.js.map
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* hypermem Episode Store
|
|
3
|
+
*
|
|
4
|
+
* Significant events in an agent's lifetime.
|
|
5
|
+
* Lives in the central library DB.
|
|
6
|
+
* Replaces daily log files with structured, queryable episodes.
|
|
7
|
+
*/
|
|
8
|
+
import type { DatabaseSync } from 'node:sqlite';
|
|
9
|
+
import type { Episode, EpisodeType } from './types.js';
|
|
10
|
+
export declare class EpisodeStore {
|
|
11
|
+
private readonly db;
|
|
12
|
+
constructor(db: DatabaseSync);
|
|
13
|
+
/**
|
|
14
|
+
* Record a new episode.
|
|
15
|
+
*/
|
|
16
|
+
record(agentId: string, eventType: EpisodeType, summary: string, opts?: {
|
|
17
|
+
significance?: number;
|
|
18
|
+
visibility?: string;
|
|
19
|
+
participants?: string[];
|
|
20
|
+
sessionKey?: string;
|
|
21
|
+
sourceMessageId?: number;
|
|
22
|
+
}): Episode;
|
|
23
|
+
/**
|
|
24
|
+
* Get recent episodes for an agent.
|
|
25
|
+
*/
|
|
26
|
+
getRecent(agentId: string, opts?: {
|
|
27
|
+
eventType?: EpisodeType;
|
|
28
|
+
minSignificance?: number;
|
|
29
|
+
limit?: number;
|
|
30
|
+
since?: string;
|
|
31
|
+
}): Episode[];
|
|
32
|
+
/**
|
|
33
|
+
* Get the most significant episodes (across all time).
|
|
34
|
+
*/
|
|
35
|
+
getMostSignificant(agentId: string, limit?: number): Episode[];
|
|
36
|
+
/**
|
|
37
|
+
* Decay all episodes.
|
|
38
|
+
*/
|
|
39
|
+
decay(agentId: string, decayRate?: number): number;
|
|
40
|
+
/**
|
|
41
|
+
* Prune fully decayed episodes.
|
|
42
|
+
*/
|
|
43
|
+
prune(agentId: string): number;
|
|
44
|
+
/**
|
|
45
|
+
* Get episode summary for a time range.
|
|
46
|
+
*/
|
|
47
|
+
getDailySummary(agentId: string, date: string): Episode[];
|
|
48
|
+
}
|
|
49
|
+
//# sourceMappingURL=episode-store.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"episode-store.d.ts","sourceRoot":"","sources":["../src/episode-store.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAChD,OAAO,KAAK,EAAE,OAAO,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAsBvD,qBAAa,YAAY;IACX,OAAO,CAAC,QAAQ,CAAC,EAAE;gBAAF,EAAE,EAAE,YAAY;IAE7C;;OAEG;IACH,MAAM,CACJ,OAAO,EAAE,MAAM,EACf,SAAS,EAAE,WAAW,EACtB,OAAO,EAAE,MAAM,EACf,IAAI,CAAC,EAAE;QACL,YAAY,CAAC,EAAE,MAAM,CAAC;QACtB,UAAU,CAAC,EAAE,MAAM,CAAC;QACpB,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;QACxB,UAAU,CAAC,EAAE,MAAM,CAAC;QACpB,eAAe,CAAC,EAAE,MAAM,CAAC;KAC1B,GACA,OAAO;IA2CV;;OAEG;IACH,SAAS,CACP,OAAO,EAAE,MAAM,EACf,IAAI,CAAC,EAAE;QACL,SAAS,CAAC,EAAE,WAAW,CAAC;QACxB,eAAe,CAAC,EAAE,MAAM,CAAC;QACzB,KAAK,CAAC,EAAE,MAAM,CAAC;QACf,KAAK,CAAC,EAAE,MAAM,CAAC;KAChB,GACA,OAAO,EAAE;IA4BZ;;OAEG;IACH,kBAAkB,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,GAAE,MAAW,GAAG,OAAO,EAAE;IAWlE;;OAEG;IACH,KAAK,CAAC,OAAO,EAAE,MAAM,EAAE,SAAS,GAAE,MAAc,GAAG,MAAM;IAUzD;;OAEG;IACH,KAAK,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM;IAQ9B;;OAEG;IACH,eAAe,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,EAAE;CAa1D"}
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* hypermem Episode Store
|
|
3
|
+
*
|
|
4
|
+
* Significant events in an agent's lifetime.
|
|
5
|
+
* Lives in the central library DB.
|
|
6
|
+
* Replaces daily log files with structured, queryable episodes.
|
|
7
|
+
*/
|
|
8
|
+
import { isSafeForSharedVisibility, requiresScan } from './secret-scanner.js';
|
|
9
|
+
function nowIso() {
|
|
10
|
+
return new Date().toISOString();
|
|
11
|
+
}
|
|
12
|
+
function parseEpisodeRow(row) {
|
|
13
|
+
return {
|
|
14
|
+
id: row.id,
|
|
15
|
+
agentId: row.agent_id,
|
|
16
|
+
eventType: row.event_type,
|
|
17
|
+
summary: row.summary,
|
|
18
|
+
significance: row.significance,
|
|
19
|
+
visibility: row.visibility || 'org',
|
|
20
|
+
participants: row.participants ? JSON.parse(row.participants) : null,
|
|
21
|
+
sessionKey: row.session_key || null,
|
|
22
|
+
createdAt: row.created_at,
|
|
23
|
+
decayScore: row.decay_score,
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
export class EpisodeStore {
|
|
27
|
+
db;
|
|
28
|
+
constructor(db) {
|
|
29
|
+
this.db = db;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Record a new episode.
|
|
33
|
+
*/
|
|
34
|
+
record(agentId, eventType, summary, opts) {
|
|
35
|
+
const now = nowIso();
|
|
36
|
+
const significance = opts?.significance || 0.5;
|
|
37
|
+
// Secret gate: if requested visibility is shared, verify content is clean.
|
|
38
|
+
// Downgrade to 'private' rather than throw — better to lose the share than leak a secret.
|
|
39
|
+
let resolvedVisibility = opts?.visibility || 'org';
|
|
40
|
+
if (requiresScan(resolvedVisibility) && !isSafeForSharedVisibility(summary)) {
|
|
41
|
+
resolvedVisibility = 'private';
|
|
42
|
+
}
|
|
43
|
+
const result = this.db.prepare(`
|
|
44
|
+
INSERT INTO episodes (agent_id, event_type, summary, significance,
|
|
45
|
+
visibility, participants, session_key, source_message_id, created_at, decay_score)
|
|
46
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, 0.0)
|
|
47
|
+
`).run(agentId, eventType, summary, significance, resolvedVisibility, opts?.participants ? JSON.stringify(opts.participants) : null, opts?.sessionKey || null, opts?.sourceMessageId ?? null, now);
|
|
48
|
+
const id = Number(result.lastInsertRowid);
|
|
49
|
+
return {
|
|
50
|
+
id,
|
|
51
|
+
agentId,
|
|
52
|
+
eventType,
|
|
53
|
+
summary,
|
|
54
|
+
significance,
|
|
55
|
+
visibility: resolvedVisibility,
|
|
56
|
+
participants: opts?.participants || null,
|
|
57
|
+
sessionKey: opts?.sessionKey || null,
|
|
58
|
+
createdAt: now,
|
|
59
|
+
decayScore: 0,
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Get recent episodes for an agent.
|
|
64
|
+
*/
|
|
65
|
+
getRecent(agentId, opts) {
|
|
66
|
+
let sql = 'SELECT * FROM episodes WHERE agent_id = ? AND decay_score < 0.8';
|
|
67
|
+
const params = [agentId];
|
|
68
|
+
if (opts?.eventType) {
|
|
69
|
+
sql += ' AND event_type = ?';
|
|
70
|
+
params.push(opts.eventType);
|
|
71
|
+
}
|
|
72
|
+
if (opts?.minSignificance) {
|
|
73
|
+
sql += ' AND significance >= ?';
|
|
74
|
+
params.push(opts.minSignificance);
|
|
75
|
+
}
|
|
76
|
+
if (opts?.since) {
|
|
77
|
+
sql += ' AND created_at > ?';
|
|
78
|
+
params.push(opts.since);
|
|
79
|
+
}
|
|
80
|
+
sql += ' ORDER BY created_at DESC';
|
|
81
|
+
if (opts?.limit) {
|
|
82
|
+
sql += ' LIMIT ?';
|
|
83
|
+
params.push(opts.limit);
|
|
84
|
+
}
|
|
85
|
+
const rows = this.db.prepare(sql).all(...params);
|
|
86
|
+
return rows.map(parseEpisodeRow);
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Get the most significant episodes (across all time).
|
|
90
|
+
*/
|
|
91
|
+
getMostSignificant(agentId, limit = 10) {
|
|
92
|
+
const rows = this.db.prepare(`
|
|
93
|
+
SELECT * FROM episodes
|
|
94
|
+
WHERE agent_id = ? AND decay_score < 0.5
|
|
95
|
+
ORDER BY significance DESC, created_at DESC
|
|
96
|
+
LIMIT ?
|
|
97
|
+
`).all(agentId, limit);
|
|
98
|
+
return rows.map(parseEpisodeRow);
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* Decay all episodes.
|
|
102
|
+
*/
|
|
103
|
+
decay(agentId, decayRate = 0.005) {
|
|
104
|
+
const result = this.db.prepare(`
|
|
105
|
+
UPDATE episodes
|
|
106
|
+
SET decay_score = MIN(decay_score + ?, 1.0)
|
|
107
|
+
WHERE agent_id = ? AND decay_score < 1.0
|
|
108
|
+
`).run(decayRate, agentId);
|
|
109
|
+
return result.changes;
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* Prune fully decayed episodes.
|
|
113
|
+
*/
|
|
114
|
+
prune(agentId) {
|
|
115
|
+
const result = this.db.prepare(`
|
|
116
|
+
DELETE FROM episodes WHERE agent_id = ? AND decay_score >= 1.0
|
|
117
|
+
`).run(agentId);
|
|
118
|
+
return result.changes;
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* Get episode summary for a time range.
|
|
122
|
+
*/
|
|
123
|
+
getDailySummary(agentId, date) {
|
|
124
|
+
const startOfDay = `${date}T00:00:00.000Z`;
|
|
125
|
+
const endOfDay = `${date}T23:59:59.999Z`;
|
|
126
|
+
const rows = this.db.prepare(`
|
|
127
|
+
SELECT * FROM episodes
|
|
128
|
+
WHERE agent_id = ?
|
|
129
|
+
AND created_at >= ? AND created_at <= ?
|
|
130
|
+
ORDER BY significance DESC, created_at ASC
|
|
131
|
+
`).all(agentId, startOfDay, endOfDay);
|
|
132
|
+
return rows.map(parseEpisodeRow);
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
//# sourceMappingURL=episode-store.js.map
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* hypermem Fact Store
|
|
3
|
+
*
|
|
4
|
+
* CRUD operations for facts (extracted knowledge that spans sessions).
|
|
5
|
+
* Facts live in the central library DB, tagged by agent_id.
|
|
6
|
+
* Facts have scope (agent/session/user), confidence, and decay.
|
|
7
|
+
*/
|
|
8
|
+
import type { DatabaseSync } from 'node:sqlite';
|
|
9
|
+
import type { Fact, FactScope } from './types.js';
|
|
10
|
+
export declare class FactStore {
|
|
11
|
+
private readonly db;
|
|
12
|
+
constructor(db: DatabaseSync);
|
|
13
|
+
/**
|
|
14
|
+
* Add a new fact. Checks for duplicates by content.
|
|
15
|
+
*/
|
|
16
|
+
addFact(agentId: string, content: string, opts?: {
|
|
17
|
+
scope?: FactScope;
|
|
18
|
+
domain?: string;
|
|
19
|
+
confidence?: number;
|
|
20
|
+
visibility?: string;
|
|
21
|
+
sourceType?: string;
|
|
22
|
+
sourceSessionKey?: string;
|
|
23
|
+
sourceRef?: string;
|
|
24
|
+
expiresAt?: string;
|
|
25
|
+
}): Fact;
|
|
26
|
+
/**
|
|
27
|
+
* Get active facts for an agent.
|
|
28
|
+
*/
|
|
29
|
+
getActiveFacts(agentId: string, opts?: {
|
|
30
|
+
scope?: FactScope;
|
|
31
|
+
domain?: string;
|
|
32
|
+
limit?: number;
|
|
33
|
+
minConfidence?: number;
|
|
34
|
+
}): Fact[];
|
|
35
|
+
/**
|
|
36
|
+
* Full-text search facts.
|
|
37
|
+
*/
|
|
38
|
+
searchFacts(query: string, opts?: {
|
|
39
|
+
agentId?: string;
|
|
40
|
+
domain?: string;
|
|
41
|
+
visibility?: string;
|
|
42
|
+
limit?: number;
|
|
43
|
+
}): Fact[];
|
|
44
|
+
/**
|
|
45
|
+
* Mark an old fact as superseded by a new one.
|
|
46
|
+
*
|
|
47
|
+
* Sets `superseded_by` on the old fact row so it is excluded from active
|
|
48
|
+
* retrieval queries (both FTS and KNN paths check `superseded_by IS NULL`).
|
|
49
|
+
* Returns false if the fact is already superseded or does not exist.
|
|
50
|
+
*/
|
|
51
|
+
markSuperseded(oldFactId: number, newFactId: number): boolean;
|
|
52
|
+
/**
|
|
53
|
+
* Find the most recent active fact for an agent whose content is a near-duplicate
|
|
54
|
+
* of the given content (same first 100 chars, different suffix, or same domain+topic).
|
|
55
|
+
* Used by the background indexer to detect supersedes relationships.
|
|
56
|
+
*
|
|
57
|
+
* Returns the existing fact id if a candidate is found, otherwise null.
|
|
58
|
+
*/
|
|
59
|
+
findSupersedableByContent(agentId: string, content: string, opts?: {
|
|
60
|
+
domain?: string;
|
|
61
|
+
}): number | null;
|
|
62
|
+
/**
|
|
63
|
+
* Decay all facts by a fixed rate.
|
|
64
|
+
*/
|
|
65
|
+
decayFacts(agentId: string, decayRate?: number): number;
|
|
66
|
+
/**
|
|
67
|
+
* Remove expired and fully decayed facts.
|
|
68
|
+
*/
|
|
69
|
+
pruneFacts(agentId: string): number;
|
|
70
|
+
/**
|
|
71
|
+
* Get fact count for an agent.
|
|
72
|
+
*/
|
|
73
|
+
getFactCount(agentId: string): number;
|
|
74
|
+
}
|
|
75
|
+
//# sourceMappingURL=fact-store.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fact-store.d.ts","sourceRoot":"","sources":["../src/fact-store.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAChD,OAAO,KAAK,EAAE,IAAI,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AA2BlD,qBAAa,SAAS;IACR,OAAO,CAAC,QAAQ,CAAC,EAAE;gBAAF,EAAE,EAAE,YAAY;IAE7C;;OAEG;IACH,OAAO,CACL,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,MAAM,EACf,IAAI,CAAC,EAAE;QACL,KAAK,CAAC,EAAE,SAAS,CAAC;QAClB,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,UAAU,CAAC,EAAE,MAAM,CAAC;QACpB,UAAU,CAAC,EAAE,MAAM,CAAC;QACpB,UAAU,CAAC,EAAE,MAAM,CAAC;QACpB,gBAAgB,CAAC,EAAE,MAAM,CAAC;QAC1B,SAAS,CAAC,EAAE,MAAM,CAAC;QACnB,SAAS,CAAC,EAAE,MAAM,CAAC;KACpB,GACA,IAAI;IA4EP;;OAEG;IACH,cAAc,CACZ,OAAO,EAAE,MAAM,EACf,IAAI,CAAC,EAAE;QACL,KAAK,CAAC,EAAE,SAAS,CAAC;QAClB,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,KAAK,CAAC,EAAE,MAAM,CAAC;QACf,aAAa,CAAC,EAAE,MAAM,CAAC;KACxB,GACA,IAAI,EAAE;IAkCT;;OAEG;IACH,WAAW,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE;QAChC,OAAO,CAAC,EAAE,MAAM,CAAC;QACjB,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,UAAU,CAAC,EAAE,MAAM,CAAC;QACpB,KAAK,CAAC,EAAE,MAAM,CAAC;KAChB,GAAG,IAAI,EAAE;IAoCV;;;;;;OAMG;IACH,cAAc,CAAC,SAAS,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO;IAY7D;;;;;;OAMG;IACH,yBAAyB,CACvB,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,MAAM,EACf,IAAI,CAAC,EAAE;QAAE,MAAM,CAAC,EAAE,MAAM,CAAA;KAAE,GACzB,MAAM,GAAG,IAAI;IA0BhB;;OAEG;IACH,UAAU,CAAC,OAAO,EAAE,MAAM,EAAE,SAAS,GAAE,MAAa,GAAG,MAAM;IAU7D;;OAEG;IACH,UAAU,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM;IAanC;;OAEG;IACH,YAAY,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM;CAMtC"}
|