hippo-memory 0.19.0 → 0.19.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/README.md +6 -0
- package/dist/cli.js +2 -2
- package/dist/cli.js.map +1 -1
- package/dist/dashboard.js +202 -202
- package/dist/embeddings.d.ts +3 -0
- package/dist/embeddings.d.ts.map +1 -1
- package/dist/embeddings.js +135 -39
- package/dist/embeddings.js.map +1 -1
- package/dist/mcp/server.js +1 -1
- package/dist/mcp/server.js.map +1 -1
- package/dist/physics.d.ts.map +1 -1
- package/dist/physics.js +6 -0
- package/dist/physics.js.map +1 -1
- package/dist/search.d.ts.map +1 -1
- package/dist/search.js +17 -7
- package/dist/search.js.map +1 -1
- package/extensions/openclaw-plugin/openclaw.plugin.json +46 -46
- package/extensions/openclaw-plugin/package.json +13 -13
- package/openclaw.plugin.json +45 -45
- package/package.json +68 -68
package/dist/dashboard.js
CHANGED
|
@@ -88,208 +88,208 @@ function buildDashboardData(hippoRoot) {
|
|
|
88
88
|
};
|
|
89
89
|
}
|
|
90
90
|
function dashboardHTML(data) {
|
|
91
|
-
return `<!DOCTYPE html>
|
|
92
|
-
<html lang="en">
|
|
93
|
-
<head>
|
|
94
|
-
<meta charset="utf-8">
|
|
95
|
-
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
96
|
-
<title>Hippo Dashboard</title>
|
|
97
|
-
<style>
|
|
98
|
-
:root {
|
|
99
|
-
--bg: #0f1117; --surface: #1a1d27; --border: #2a2d3a;
|
|
100
|
-
--text: #e1e4ed; --muted: #8b8fa3; --accent: #6c8cff;
|
|
101
|
-
--green: #4ade80; --yellow: #fbbf24; --red: #f87171; --purple: #a78bfa;
|
|
102
|
-
}
|
|
103
|
-
* { box-sizing: border-box; margin: 0; padding: 0; }
|
|
104
|
-
body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', system-ui, sans-serif; background: var(--bg); color: var(--text); padding: 24px; max-width: 1200px; margin: 0 auto; }
|
|
105
|
-
h1 { font-size: 24px; margin-bottom: 4px; }
|
|
106
|
-
h1 span { font-size: 14px; color: var(--muted); font-weight: normal; margin-left: 8px; }
|
|
107
|
-
h2 { font-size: 16px; color: var(--muted); margin-bottom: 12px; text-transform: uppercase; letter-spacing: 0.5px; }
|
|
108
|
-
.grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 12px; margin-bottom: 24px; }
|
|
109
|
-
.card { background: var(--surface); border: 1px solid var(--border); border-radius: 8px; padding: 16px; }
|
|
110
|
-
.card .label { font-size: 12px; color: var(--muted); text-transform: uppercase; letter-spacing: 0.5px; }
|
|
111
|
-
.card .value { font-size: 28px; font-weight: 600; margin-top: 4px; }
|
|
112
|
-
.card .sub { font-size: 12px; color: var(--muted); margin-top: 2px; }
|
|
113
|
-
.bar-chart { display: flex; gap: 4px; align-items: flex-end; height: 120px; margin-top: 8px; }
|
|
114
|
-
.bar { flex: 1; min-width: 4px; background: var(--accent); border-radius: 2px 2px 0 0; position: relative; transition: background 0.2s; cursor: default; }
|
|
115
|
-
.bar:hover { background: #8aa4ff; }
|
|
116
|
-
.bar .tooltip { display: none; position: absolute; bottom: 100%; left: 50%; transform: translateX(-50%); background: var(--surface); border: 1px solid var(--border); padding: 4px 8px; border-radius: 4px; font-size: 11px; white-space: nowrap; z-index: 10; }
|
|
117
|
-
.bar:hover .tooltip { display: block; }
|
|
118
|
-
.strength-high { background: var(--green); }
|
|
119
|
-
.strength-mid { background: var(--yellow); }
|
|
120
|
-
.strength-low { background: var(--red); }
|
|
121
|
-
table { width: 100%; border-collapse: collapse; font-size: 13px; }
|
|
122
|
-
th { text-align: left; padding: 8px; border-bottom: 2px solid var(--border); color: var(--muted); font-size: 11px; text-transform: uppercase; }
|
|
123
|
-
td { padding: 8px; border-bottom: 1px solid var(--border); vertical-align: top; }
|
|
124
|
-
tr:hover { background: rgba(108, 140, 255, 0.05); }
|
|
125
|
-
.tag { display: inline-block; padding: 2px 6px; border-radius: 4px; font-size: 11px; background: var(--border); color: var(--muted); margin: 1px; }
|
|
126
|
-
.tag-error { background: rgba(248, 113, 113, 0.15); color: var(--red); }
|
|
127
|
-
.strength-dot { display: inline-block; width: 8px; height: 8px; border-radius: 50%; margin-right: 4px; }
|
|
128
|
-
.pill { display: inline-block; padding: 2px 8px; border-radius: 10px; font-size: 11px; }
|
|
129
|
-
.pill-pinned { background: rgba(167, 139, 250, 0.15); color: var(--purple); }
|
|
130
|
-
.pill-stale { background: rgba(251, 191, 36, 0.15); color: var(--yellow); }
|
|
131
|
-
.content-preview { max-width: 400px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
132
|
-
.section { margin-bottom: 32px; }
|
|
133
|
-
.conflict-card { background: var(--surface); border: 1px solid var(--border); border-radius: 8px; padding: 12px; margin-bottom: 8px; }
|
|
134
|
-
.conflict-card .reason { color: var(--muted); font-size: 12px; }
|
|
135
|
-
.search { width: 100%; padding: 8px 12px; background: var(--surface); border: 1px solid var(--border); border-radius: 6px; color: var(--text); font-size: 14px; margin-bottom: 12px; outline: none; }
|
|
136
|
-
.search:focus { border-color: var(--accent); }
|
|
137
|
-
.tabs { display: flex; gap: 4px; margin-bottom: 16px; }
|
|
138
|
-
.tab { padding: 6px 14px; border-radius: 6px; font-size: 13px; cursor: pointer; background: var(--surface); border: 1px solid var(--border); color: var(--muted); }
|
|
139
|
-
.tab.active { background: var(--accent); color: #fff; border-color: var(--accent); }
|
|
140
|
-
.footer { text-align: center; color: var(--muted); font-size: 12px; margin-top: 40px; padding-top: 16px; border-top: 1px solid var(--border); }
|
|
141
|
-
</style>
|
|
142
|
-
</head>
|
|
143
|
-
<body>
|
|
144
|
-
<h1>Hippo Dashboard <span>v0.
|
|
145
|
-
<p style="color: var(--muted); margin-bottom: 24px;">Memory health at a glance. Auto-refreshes on page load.</p>
|
|
146
|
-
|
|
147
|
-
<div class="section">
|
|
148
|
-
<h2>Overview</h2>
|
|
149
|
-
<div class="grid" id="stats-grid"></div>
|
|
150
|
-
</div>
|
|
151
|
-
|
|
152
|
-
<div class="section">
|
|
153
|
-
<h2>Strength Distribution</h2>
|
|
154
|
-
<div class="card">
|
|
155
|
-
<div class="bar-chart" id="strength-chart"></div>
|
|
156
|
-
<div style="display: flex; justify-content: space-between; margin-top: 8px; font-size: 11px; color: var(--muted);">
|
|
157
|
-
<span>Weakest</span><span>Strongest</span>
|
|
158
|
-
</div>
|
|
159
|
-
</div>
|
|
160
|
-
</div>
|
|
161
|
-
|
|
162
|
-
<div class="section" id="conflicts-section" style="display:none;">
|
|
163
|
-
<h2>Open Conflicts</h2>
|
|
164
|
-
<div id="conflicts-list"></div>
|
|
165
|
-
</div>
|
|
166
|
-
|
|
167
|
-
<div class="section" id="peers-section" style="display:none;">
|
|
168
|
-
<h2>Shared Memory Peers</h2>
|
|
169
|
-
<div id="peers-list"></div>
|
|
170
|
-
</div>
|
|
171
|
-
|
|
172
|
-
<div class="section">
|
|
173
|
-
<h2>Memories</h2>
|
|
174
|
-
<input type="text" class="search" id="search" placeholder="Filter by content or tag...">
|
|
175
|
-
<div class="tabs" id="tabs">
|
|
176
|
-
<div class="tab active" data-filter="all">All</div>
|
|
177
|
-
<div class="tab" data-filter="strong">Strong (>0.5)</div>
|
|
178
|
-
<div class="tab" data-filter="atrisk">At Risk (<0.1)</div>
|
|
179
|
-
<div class="tab" data-filter="pinned">Pinned</div>
|
|
180
|
-
<div class="tab" data-filter="errors">Errors</div>
|
|
181
|
-
</div>
|
|
182
|
-
<table>
|
|
183
|
-
<thead><tr><th></th><th>Content</th><th>Tags</th><th>Strength</th><th>Half-life</th><th>Retrievals</th><th>Age</th></tr></thead>
|
|
184
|
-
<tbody id="memory-table"></tbody>
|
|
185
|
-
</table>
|
|
186
|
-
</div>
|
|
187
|
-
|
|
188
|
-
<div class="footer">Hippo Memory — biologically-inspired memory for AI agents</div>
|
|
189
|
-
|
|
190
|
-
<script>
|
|
191
|
-
const DATA = ${JSON.stringify(data)};
|
|
192
|
-
|
|
193
|
-
function strengthColor(s) {
|
|
194
|
-
if (s >= 0.5) return 'var(--green)';
|
|
195
|
-
if (s >= 0.1) return 'var(--yellow)';
|
|
196
|
-
return 'var(--red)';
|
|
197
|
-
}
|
|
198
|
-
function strengthClass(s) {
|
|
199
|
-
if (s >= 0.5) return 'strength-high';
|
|
200
|
-
if (s >= 0.1) return 'strength-mid';
|
|
201
|
-
return 'strength-low';
|
|
202
|
-
}
|
|
203
|
-
|
|
204
|
-
// Stats grid
|
|
205
|
-
const statsGrid = document.getElementById('stats-grid');
|
|
206
|
-
const stats = DATA.stats;
|
|
207
|
-
const cards = [
|
|
208
|
-
{ label: 'Total Memories', value: stats.total, sub: stats.pinned + ' pinned' },
|
|
209
|
-
{ label: 'Avg Strength', value: stats.avg_strength.toFixed(2), sub: stats.at_risk + ' at risk' },
|
|
210
|
-
{ label: 'Avg Half-life', value: stats.avg_half_life.toFixed(1) + 'd', sub: 'default: ' + DATA.config.defaultHalfLifeDays + 'd' },
|
|
211
|
-
{ label: 'Error Memories', value: stats.errors, sub: Math.round(stats.errors / Math.max(1, stats.total) * 100) + '% of total' },
|
|
212
|
-
{ label: 'Embedding Coverage', value: Math.round(stats.embedding_coverage * 100) + '%', sub: DATA.config.embeddingsEnabled === 'auto' ? 'auto mode' : String(DATA.config.embeddingsEnabled) },
|
|
213
|
-
{ label: 'Open Conflicts', value: stats.open_conflicts, sub: stats.open_conflicts > 0 ? 'needs resolution' : 'all clear' },
|
|
214
|
-
];
|
|
215
|
-
statsGrid.innerHTML = cards.map(c => '<div class="card"><div class="label">' + c.label + '</div><div class="value">' + c.value + '</div><div class="sub">' + c.sub + '</div></div>').join('');
|
|
216
|
-
|
|
217
|
-
// Strength chart
|
|
218
|
-
const chart = document.getElementById('strength-chart');
|
|
219
|
-
const sorted = [...DATA.memories].sort((a, b) => a.strength - b.strength);
|
|
220
|
-
if (sorted.length > 0) {
|
|
221
|
-
const maxBars = Math.min(sorted.length, 100);
|
|
222
|
-
const step = Math.max(1, Math.floor(sorted.length / maxBars));
|
|
223
|
-
const sampled = [];
|
|
224
|
-
for (let i = 0; i < sorted.length; i += step) sampled.push(sorted[i]);
|
|
225
|
-
chart.innerHTML = sampled.map(m => {
|
|
226
|
-
const h = Math.max(2, Math.round(m.strength * 100));
|
|
227
|
-
return '<div class="bar ' + strengthClass(m.strength) + '" style="height:' + h + '%"><div class="tooltip">' + m.id + ': ' + m.strength.toFixed(2) + '</div></div>';
|
|
228
|
-
}).join('');
|
|
229
|
-
}
|
|
230
|
-
|
|
231
|
-
// Conflicts
|
|
232
|
-
if (DATA.conflicts.length > 0) {
|
|
233
|
-
document.getElementById('conflicts-section').style.display = '';
|
|
234
|
-
document.getElementById('conflicts-list').innerHTML = DATA.conflicts.map(c =>
|
|
235
|
-
'<div class="conflict-card"><strong>conflict_' + c.id + '</strong> (score: ' + c.score.toFixed(2) + ')<br>' +
|
|
236
|
-
'<code>' + c.memory_a_id + '</code> vs <code>' + c.memory_b_id + '</code><br>' +
|
|
237
|
-
'<span class="reason">' + c.reason + '</span></div>'
|
|
238
|
-
).join('');
|
|
239
|
-
}
|
|
240
|
-
|
|
241
|
-
// Peers
|
|
242
|
-
if (DATA.peers.length > 0) {
|
|
243
|
-
document.getElementById('peers-section').style.display = '';
|
|
244
|
-
document.getElementById('peers-list').innerHTML = '<div class="card"><table><thead><tr><th>Project</th><th>Memories</th><th>Latest</th></tr></thead><tbody>' +
|
|
245
|
-
DATA.peers.map(p => '<tr><td>' + p.project + '</td><td>' + p.count + '</td><td>' + p.latest.slice(0, 10) + '</td></tr>').join('') +
|
|
246
|
-
'</tbody></table></div>';
|
|
247
|
-
}
|
|
248
|
-
|
|
249
|
-
// Memory table
|
|
250
|
-
function renderTable(filter, search) {
|
|
251
|
-
const tbody = document.getElementById('memory-table');
|
|
252
|
-
let mems = DATA.memories;
|
|
253
|
-
if (filter === 'strong') mems = mems.filter(m => m.strength >= 0.5);
|
|
254
|
-
else if (filter === 'atrisk') mems = mems.filter(m => m.strength < 0.1 && !m.pinned);
|
|
255
|
-
else if (filter === 'pinned') mems = mems.filter(m => m.pinned);
|
|
256
|
-
else if (filter === 'errors') mems = mems.filter(m => m.tags.includes('error'));
|
|
257
|
-
if (search) {
|
|
258
|
-
const q = search.toLowerCase();
|
|
259
|
-
mems = mems.filter(m => m.content.toLowerCase().includes(q) || m.tags.some(t => t.includes(q)));
|
|
260
|
-
}
|
|
261
|
-
mems.sort((a, b) => b.strength - a.strength);
|
|
262
|
-
tbody.innerHTML = mems.slice(0, 200).map(m => {
|
|
263
|
-
const dot = '<span class="strength-dot" style="background:' + strengthColor(m.strength) + '"></span>';
|
|
264
|
-
const pills = [];
|
|
265
|
-
if (m.pinned) pills.push('<span class="pill pill-pinned">pinned</span>');
|
|
266
|
-
if (m.confidence === 'stale') pills.push('<span class="pill pill-stale">stale</span>');
|
|
267
|
-
const tags = m.tags.map(t => '<span class="tag' + (t === 'error' ? ' tag-error' : '') + '">' + t + '</span>').join(' ');
|
|
268
|
-
return '<tr><td>' + dot + pills.join(' ') + '</td>' +
|
|
269
|
-
'<td class="content-preview" title="' + m.content.replace(/"/g, '"') + '">' + m.content.slice(0, 80) + (m.content.length > 80 ? '...' : '') + '</td>' +
|
|
270
|
-
'<td>' + tags + '</td>' +
|
|
271
|
-
'<td>' + m.strength.toFixed(2) + '</td>' +
|
|
272
|
-
'<td>' + m.half_life_days + 'd</td>' +
|
|
273
|
-
'<td>' + m.retrieval_count + '</td>' +
|
|
274
|
-
'<td>' + m.age_days + 'd</td></tr>';
|
|
275
|
-
}).join('');
|
|
276
|
-
}
|
|
277
|
-
|
|
278
|
-
let activeFilter = 'all';
|
|
279
|
-
document.getElementById('tabs').addEventListener('click', e => {
|
|
280
|
-
const tab = e.target.closest('.tab');
|
|
281
|
-
if (!tab) return;
|
|
282
|
-
document.querySelectorAll('.tab').forEach(t => t.classList.remove('active'));
|
|
283
|
-
tab.classList.add('active');
|
|
284
|
-
activeFilter = tab.dataset.filter;
|
|
285
|
-
renderTable(activeFilter, document.getElementById('search').value);
|
|
286
|
-
});
|
|
287
|
-
document.getElementById('search').addEventListener('input', e => {
|
|
288
|
-
renderTable(activeFilter, e.target.value);
|
|
289
|
-
});
|
|
290
|
-
renderTable('all', '');
|
|
291
|
-
</script>
|
|
292
|
-
</body>
|
|
91
|
+
return `<!DOCTYPE html>
|
|
92
|
+
<html lang="en">
|
|
93
|
+
<head>
|
|
94
|
+
<meta charset="utf-8">
|
|
95
|
+
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
96
|
+
<title>Hippo Dashboard</title>
|
|
97
|
+
<style>
|
|
98
|
+
:root {
|
|
99
|
+
--bg: #0f1117; --surface: #1a1d27; --border: #2a2d3a;
|
|
100
|
+
--text: #e1e4ed; --muted: #8b8fa3; --accent: #6c8cff;
|
|
101
|
+
--green: #4ade80; --yellow: #fbbf24; --red: #f87171; --purple: #a78bfa;
|
|
102
|
+
}
|
|
103
|
+
* { box-sizing: border-box; margin: 0; padding: 0; }
|
|
104
|
+
body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', system-ui, sans-serif; background: var(--bg); color: var(--text); padding: 24px; max-width: 1200px; margin: 0 auto; }
|
|
105
|
+
h1 { font-size: 24px; margin-bottom: 4px; }
|
|
106
|
+
h1 span { font-size: 14px; color: var(--muted); font-weight: normal; margin-left: 8px; }
|
|
107
|
+
h2 { font-size: 16px; color: var(--muted); margin-bottom: 12px; text-transform: uppercase; letter-spacing: 0.5px; }
|
|
108
|
+
.grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 12px; margin-bottom: 24px; }
|
|
109
|
+
.card { background: var(--surface); border: 1px solid var(--border); border-radius: 8px; padding: 16px; }
|
|
110
|
+
.card .label { font-size: 12px; color: var(--muted); text-transform: uppercase; letter-spacing: 0.5px; }
|
|
111
|
+
.card .value { font-size: 28px; font-weight: 600; margin-top: 4px; }
|
|
112
|
+
.card .sub { font-size: 12px; color: var(--muted); margin-top: 2px; }
|
|
113
|
+
.bar-chart { display: flex; gap: 4px; align-items: flex-end; height: 120px; margin-top: 8px; }
|
|
114
|
+
.bar { flex: 1; min-width: 4px; background: var(--accent); border-radius: 2px 2px 0 0; position: relative; transition: background 0.2s; cursor: default; }
|
|
115
|
+
.bar:hover { background: #8aa4ff; }
|
|
116
|
+
.bar .tooltip { display: none; position: absolute; bottom: 100%; left: 50%; transform: translateX(-50%); background: var(--surface); border: 1px solid var(--border); padding: 4px 8px; border-radius: 4px; font-size: 11px; white-space: nowrap; z-index: 10; }
|
|
117
|
+
.bar:hover .tooltip { display: block; }
|
|
118
|
+
.strength-high { background: var(--green); }
|
|
119
|
+
.strength-mid { background: var(--yellow); }
|
|
120
|
+
.strength-low { background: var(--red); }
|
|
121
|
+
table { width: 100%; border-collapse: collapse; font-size: 13px; }
|
|
122
|
+
th { text-align: left; padding: 8px; border-bottom: 2px solid var(--border); color: var(--muted); font-size: 11px; text-transform: uppercase; }
|
|
123
|
+
td { padding: 8px; border-bottom: 1px solid var(--border); vertical-align: top; }
|
|
124
|
+
tr:hover { background: rgba(108, 140, 255, 0.05); }
|
|
125
|
+
.tag { display: inline-block; padding: 2px 6px; border-radius: 4px; font-size: 11px; background: var(--border); color: var(--muted); margin: 1px; }
|
|
126
|
+
.tag-error { background: rgba(248, 113, 113, 0.15); color: var(--red); }
|
|
127
|
+
.strength-dot { display: inline-block; width: 8px; height: 8px; border-radius: 50%; margin-right: 4px; }
|
|
128
|
+
.pill { display: inline-block; padding: 2px 8px; border-radius: 10px; font-size: 11px; }
|
|
129
|
+
.pill-pinned { background: rgba(167, 139, 250, 0.15); color: var(--purple); }
|
|
130
|
+
.pill-stale { background: rgba(251, 191, 36, 0.15); color: var(--yellow); }
|
|
131
|
+
.content-preview { max-width: 400px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
132
|
+
.section { margin-bottom: 32px; }
|
|
133
|
+
.conflict-card { background: var(--surface); border: 1px solid var(--border); border-radius: 8px; padding: 12px; margin-bottom: 8px; }
|
|
134
|
+
.conflict-card .reason { color: var(--muted); font-size: 12px; }
|
|
135
|
+
.search { width: 100%; padding: 8px 12px; background: var(--surface); border: 1px solid var(--border); border-radius: 6px; color: var(--text); font-size: 14px; margin-bottom: 12px; outline: none; }
|
|
136
|
+
.search:focus { border-color: var(--accent); }
|
|
137
|
+
.tabs { display: flex; gap: 4px; margin-bottom: 16px; }
|
|
138
|
+
.tab { padding: 6px 14px; border-radius: 6px; font-size: 13px; cursor: pointer; background: var(--surface); border: 1px solid var(--border); color: var(--muted); }
|
|
139
|
+
.tab.active { background: var(--accent); color: #fff; border-color: var(--accent); }
|
|
140
|
+
.footer { text-align: center; color: var(--muted); font-size: 12px; margin-top: 40px; padding-top: 16px; border-top: 1px solid var(--border); }
|
|
141
|
+
</style>
|
|
142
|
+
</head>
|
|
143
|
+
<body>
|
|
144
|
+
<h1>Hippo Dashboard <span>v0.19.1</span></h1>
|
|
145
|
+
<p style="color: var(--muted); margin-bottom: 24px;">Memory health at a glance. Auto-refreshes on page load.</p>
|
|
146
|
+
|
|
147
|
+
<div class="section">
|
|
148
|
+
<h2>Overview</h2>
|
|
149
|
+
<div class="grid" id="stats-grid"></div>
|
|
150
|
+
</div>
|
|
151
|
+
|
|
152
|
+
<div class="section">
|
|
153
|
+
<h2>Strength Distribution</h2>
|
|
154
|
+
<div class="card">
|
|
155
|
+
<div class="bar-chart" id="strength-chart"></div>
|
|
156
|
+
<div style="display: flex; justify-content: space-between; margin-top: 8px; font-size: 11px; color: var(--muted);">
|
|
157
|
+
<span>Weakest</span><span>Strongest</span>
|
|
158
|
+
</div>
|
|
159
|
+
</div>
|
|
160
|
+
</div>
|
|
161
|
+
|
|
162
|
+
<div class="section" id="conflicts-section" style="display:none;">
|
|
163
|
+
<h2>Open Conflicts</h2>
|
|
164
|
+
<div id="conflicts-list"></div>
|
|
165
|
+
</div>
|
|
166
|
+
|
|
167
|
+
<div class="section" id="peers-section" style="display:none;">
|
|
168
|
+
<h2>Shared Memory Peers</h2>
|
|
169
|
+
<div id="peers-list"></div>
|
|
170
|
+
</div>
|
|
171
|
+
|
|
172
|
+
<div class="section">
|
|
173
|
+
<h2>Memories</h2>
|
|
174
|
+
<input type="text" class="search" id="search" placeholder="Filter by content or tag...">
|
|
175
|
+
<div class="tabs" id="tabs">
|
|
176
|
+
<div class="tab active" data-filter="all">All</div>
|
|
177
|
+
<div class="tab" data-filter="strong">Strong (>0.5)</div>
|
|
178
|
+
<div class="tab" data-filter="atrisk">At Risk (<0.1)</div>
|
|
179
|
+
<div class="tab" data-filter="pinned">Pinned</div>
|
|
180
|
+
<div class="tab" data-filter="errors">Errors</div>
|
|
181
|
+
</div>
|
|
182
|
+
<table>
|
|
183
|
+
<thead><tr><th></th><th>Content</th><th>Tags</th><th>Strength</th><th>Half-life</th><th>Retrievals</th><th>Age</th></tr></thead>
|
|
184
|
+
<tbody id="memory-table"></tbody>
|
|
185
|
+
</table>
|
|
186
|
+
</div>
|
|
187
|
+
|
|
188
|
+
<div class="footer">Hippo Memory — biologically-inspired memory for AI agents</div>
|
|
189
|
+
|
|
190
|
+
<script>
|
|
191
|
+
const DATA = ${JSON.stringify(data)};
|
|
192
|
+
|
|
193
|
+
function strengthColor(s) {
|
|
194
|
+
if (s >= 0.5) return 'var(--green)';
|
|
195
|
+
if (s >= 0.1) return 'var(--yellow)';
|
|
196
|
+
return 'var(--red)';
|
|
197
|
+
}
|
|
198
|
+
function strengthClass(s) {
|
|
199
|
+
if (s >= 0.5) return 'strength-high';
|
|
200
|
+
if (s >= 0.1) return 'strength-mid';
|
|
201
|
+
return 'strength-low';
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
// Stats grid
|
|
205
|
+
const statsGrid = document.getElementById('stats-grid');
|
|
206
|
+
const stats = DATA.stats;
|
|
207
|
+
const cards = [
|
|
208
|
+
{ label: 'Total Memories', value: stats.total, sub: stats.pinned + ' pinned' },
|
|
209
|
+
{ label: 'Avg Strength', value: stats.avg_strength.toFixed(2), sub: stats.at_risk + ' at risk' },
|
|
210
|
+
{ label: 'Avg Half-life', value: stats.avg_half_life.toFixed(1) + 'd', sub: 'default: ' + DATA.config.defaultHalfLifeDays + 'd' },
|
|
211
|
+
{ label: 'Error Memories', value: stats.errors, sub: Math.round(stats.errors / Math.max(1, stats.total) * 100) + '% of total' },
|
|
212
|
+
{ label: 'Embedding Coverage', value: Math.round(stats.embedding_coverage * 100) + '%', sub: DATA.config.embeddingsEnabled === 'auto' ? 'auto mode' : String(DATA.config.embeddingsEnabled) },
|
|
213
|
+
{ label: 'Open Conflicts', value: stats.open_conflicts, sub: stats.open_conflicts > 0 ? 'needs resolution' : 'all clear' },
|
|
214
|
+
];
|
|
215
|
+
statsGrid.innerHTML = cards.map(c => '<div class="card"><div class="label">' + c.label + '</div><div class="value">' + c.value + '</div><div class="sub">' + c.sub + '</div></div>').join('');
|
|
216
|
+
|
|
217
|
+
// Strength chart
|
|
218
|
+
const chart = document.getElementById('strength-chart');
|
|
219
|
+
const sorted = [...DATA.memories].sort((a, b) => a.strength - b.strength);
|
|
220
|
+
if (sorted.length > 0) {
|
|
221
|
+
const maxBars = Math.min(sorted.length, 100);
|
|
222
|
+
const step = Math.max(1, Math.floor(sorted.length / maxBars));
|
|
223
|
+
const sampled = [];
|
|
224
|
+
for (let i = 0; i < sorted.length; i += step) sampled.push(sorted[i]);
|
|
225
|
+
chart.innerHTML = sampled.map(m => {
|
|
226
|
+
const h = Math.max(2, Math.round(m.strength * 100));
|
|
227
|
+
return '<div class="bar ' + strengthClass(m.strength) + '" style="height:' + h + '%"><div class="tooltip">' + m.id + ': ' + m.strength.toFixed(2) + '</div></div>';
|
|
228
|
+
}).join('');
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
// Conflicts
|
|
232
|
+
if (DATA.conflicts.length > 0) {
|
|
233
|
+
document.getElementById('conflicts-section').style.display = '';
|
|
234
|
+
document.getElementById('conflicts-list').innerHTML = DATA.conflicts.map(c =>
|
|
235
|
+
'<div class="conflict-card"><strong>conflict_' + c.id + '</strong> (score: ' + c.score.toFixed(2) + ')<br>' +
|
|
236
|
+
'<code>' + c.memory_a_id + '</code> vs <code>' + c.memory_b_id + '</code><br>' +
|
|
237
|
+
'<span class="reason">' + c.reason + '</span></div>'
|
|
238
|
+
).join('');
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
// Peers
|
|
242
|
+
if (DATA.peers.length > 0) {
|
|
243
|
+
document.getElementById('peers-section').style.display = '';
|
|
244
|
+
document.getElementById('peers-list').innerHTML = '<div class="card"><table><thead><tr><th>Project</th><th>Memories</th><th>Latest</th></tr></thead><tbody>' +
|
|
245
|
+
DATA.peers.map(p => '<tr><td>' + p.project + '</td><td>' + p.count + '</td><td>' + p.latest.slice(0, 10) + '</td></tr>').join('') +
|
|
246
|
+
'</tbody></table></div>';
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
// Memory table
|
|
250
|
+
function renderTable(filter, search) {
|
|
251
|
+
const tbody = document.getElementById('memory-table');
|
|
252
|
+
let mems = DATA.memories;
|
|
253
|
+
if (filter === 'strong') mems = mems.filter(m => m.strength >= 0.5);
|
|
254
|
+
else if (filter === 'atrisk') mems = mems.filter(m => m.strength < 0.1 && !m.pinned);
|
|
255
|
+
else if (filter === 'pinned') mems = mems.filter(m => m.pinned);
|
|
256
|
+
else if (filter === 'errors') mems = mems.filter(m => m.tags.includes('error'));
|
|
257
|
+
if (search) {
|
|
258
|
+
const q = search.toLowerCase();
|
|
259
|
+
mems = mems.filter(m => m.content.toLowerCase().includes(q) || m.tags.some(t => t.includes(q)));
|
|
260
|
+
}
|
|
261
|
+
mems.sort((a, b) => b.strength - a.strength);
|
|
262
|
+
tbody.innerHTML = mems.slice(0, 200).map(m => {
|
|
263
|
+
const dot = '<span class="strength-dot" style="background:' + strengthColor(m.strength) + '"></span>';
|
|
264
|
+
const pills = [];
|
|
265
|
+
if (m.pinned) pills.push('<span class="pill pill-pinned">pinned</span>');
|
|
266
|
+
if (m.confidence === 'stale') pills.push('<span class="pill pill-stale">stale</span>');
|
|
267
|
+
const tags = m.tags.map(t => '<span class="tag' + (t === 'error' ? ' tag-error' : '') + '">' + t + '</span>').join(' ');
|
|
268
|
+
return '<tr><td>' + dot + pills.join(' ') + '</td>' +
|
|
269
|
+
'<td class="content-preview" title="' + m.content.replace(/"/g, '"') + '">' + m.content.slice(0, 80) + (m.content.length > 80 ? '...' : '') + '</td>' +
|
|
270
|
+
'<td>' + tags + '</td>' +
|
|
271
|
+
'<td>' + m.strength.toFixed(2) + '</td>' +
|
|
272
|
+
'<td>' + m.half_life_days + 'd</td>' +
|
|
273
|
+
'<td>' + m.retrieval_count + '</td>' +
|
|
274
|
+
'<td>' + m.age_days + 'd</td></tr>';
|
|
275
|
+
}).join('');
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
let activeFilter = 'all';
|
|
279
|
+
document.getElementById('tabs').addEventListener('click', e => {
|
|
280
|
+
const tab = e.target.closest('.tab');
|
|
281
|
+
if (!tab) return;
|
|
282
|
+
document.querySelectorAll('.tab').forEach(t => t.classList.remove('active'));
|
|
283
|
+
tab.classList.add('active');
|
|
284
|
+
activeFilter = tab.dataset.filter;
|
|
285
|
+
renderTable(activeFilter, document.getElementById('search').value);
|
|
286
|
+
});
|
|
287
|
+
document.getElementById('search').addEventListener('input', e => {
|
|
288
|
+
renderTable(activeFilter, e.target.value);
|
|
289
|
+
});
|
|
290
|
+
renderTable('all', '');
|
|
291
|
+
</script>
|
|
292
|
+
</body>
|
|
293
293
|
</html>`;
|
|
294
294
|
}
|
|
295
295
|
export function serveDashboard(hippoRoot, port = 3333) {
|
package/dist/embeddings.d.ts
CHANGED
|
@@ -8,6 +8,9 @@ import { MemoryEntry } from './memory.js';
|
|
|
8
8
|
* Check (synchronously) if @xenova/transformers or @huggingface/transformers is installed.
|
|
9
9
|
*/
|
|
10
10
|
export declare function isEmbeddingAvailable(): boolean;
|
|
11
|
+
export declare function resolveEmbeddingModel(hippoRoot: string, explicitModel?: string): string;
|
|
12
|
+
export declare function resolveIndexedEmbeddingModel(hippoRoot: string, index?: Record<string, number[]>): string | null;
|
|
13
|
+
export declare function embeddingModelRequiresReindex(hippoRoot: string, model: string, index?: Record<string, number[]>): boolean;
|
|
11
14
|
/**
|
|
12
15
|
* Get an embedding vector for a piece of text.
|
|
13
16
|
* Returns an empty array if transformers is not available or fails.
|
package/dist/embeddings.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"embeddings.d.ts","sourceRoot":"","sources":["../src/embeddings.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAKH,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"embeddings.d.ts","sourceRoot":"","sources":["../src/embeddings.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAKH,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAuB1C;;GAEG;AACH,wBAAgB,oBAAoB,IAAI,OAAO,CAqB9C;AA0CD,wBAAgB,qBAAqB,CAAC,SAAS,EAAE,MAAM,EAAE,aAAa,CAAC,EAAE,MAAM,GAAG,MAAM,CAYvF;AAyBD,wBAAgB,4BAA4B,CAC1C,SAAS,EAAE,MAAM,EACjB,KAAK,GAAE,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,CAAiC,GAC9D,MAAM,GAAG,IAAI,CAIf;AAED,wBAAgB,6BAA6B,CAC3C,SAAS,EAAE,MAAM,EACjB,KAAK,EAAE,MAAM,EACb,KAAK,GAAE,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,CAAiC,GAC9D,OAAO,CAGT;AAoCD;;;GAGG;AACH,wBAAsB,YAAY,CAChC,IAAI,EAAE,MAAM,EACZ,KAAK,SAA0B,GAC9B,OAAO,CAAC,MAAM,EAAE,CAAC,CAanB;AAED;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,CAAC,EAAE,MAAM,EAAE,EAAE,CAAC,EAAE,MAAM,EAAE,GAAG,MAAM,CAiBjE;AAID;;;GAGG;AACH,wBAAgB,kBAAkB,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,CAQ9E;AAED;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,GAAG,IAAI,CAU3F;AAkBD;;GAEG;AACH,wBAAsB,WAAW,CAC/B,SAAS,EAAE,MAAM,EACjB,KAAK,EAAE,WAAW,EAClB,KAAK,CAAC,EAAE,MAAM,GACb,OAAO,CAAC,IAAI,CAAC,CAyCf;AAED;;;;GAIG;AACH,wBAAsB,QAAQ,CAC5B,SAAS,EAAE,MAAM,EACjB,KAAK,CAAC,EAAE,MAAM,GACb,OAAO,CAAC,MAAM,CAAC,CA+CjB"}
|