claude-memory-layer 1.0.7 → 1.0.9
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/.claude/settings.local.json +10 -1
- package/.claude-memory/test.sqlite +0 -0
- package/.history/package_20260201192048.json +47 -0
- package/.history/package_20260202114053.json +49 -0
- package/HANDOFF.md +92 -0
- package/dist/cli/index.js +1711 -102
- package/dist/cli/index.js.map +4 -4
- package/dist/core/index.js +1257 -84
- package/dist/core/index.js.map +4 -4
- package/dist/hooks/post-tool-use.js +5589 -0
- package/dist/hooks/post-tool-use.js.map +7 -0
- package/dist/hooks/session-end.js +1382 -85
- package/dist/hooks/session-end.js.map +4 -4
- package/dist/hooks/session-start.js +1377 -84
- package/dist/hooks/session-start.js.map +4 -4
- package/dist/hooks/stop.js +1383 -86
- package/dist/hooks/stop.js.map +4 -4
- package/dist/hooks/user-prompt-submit.js +1412 -84
- package/dist/hooks/user-prompt-submit.js.map +4 -4
- package/dist/server/api/index.js +1576 -136
- package/dist/server/api/index.js.map +4 -4
- package/dist/server/index.js +1585 -143
- package/dist/server/index.js.map +4 -4
- package/dist/services/memory-service.js +1392 -84
- package/dist/services/memory-service.js.map +4 -4
- package/dist/ui/app.js +304 -0
- package/dist/ui/index.html +202 -715
- package/dist/ui/style.css +595 -0
- package/package.json +4 -1
- package/scripts/build.ts +5 -2
- package/src/cli/index.ts +226 -0
- package/src/core/db-wrapper.ts +8 -1
- package/src/core/event-store.ts +70 -3
- package/src/core/graduation-worker.ts +171 -0
- package/src/core/graduation.ts +15 -2
- package/src/core/index.ts +4 -0
- package/src/core/retriever.ts +21 -0
- package/src/core/sqlite-event-store.ts +849 -0
- package/src/core/sqlite-wrapper.ts +108 -0
- package/src/core/sync-worker.ts +228 -0
- package/src/core/vector-worker.ts +44 -14
- package/src/hooks/user-prompt-submit.ts +53 -4
- package/src/server/api/citations.ts +7 -3
- package/src/server/api/events.ts +7 -3
- package/src/server/api/search.ts +7 -3
- package/src/server/api/sessions.ts +7 -3
- package/src/server/api/stats.ts +159 -12
- package/src/server/index.ts +18 -9
- package/src/services/memory-service.ts +263 -46
- package/src/ui/app.js +304 -0
- package/src/ui/index.html +202 -715
- package/src/ui/style.css +595 -0
- package/test_access.js +49 -0
package/src/ui/index.html
CHANGED
|
@@ -3,743 +3,230 @@
|
|
|
3
3
|
<head>
|
|
4
4
|
<meta charset="UTF-8">
|
|
5
5
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
6
|
-
<title>Code Memory Dashboard</title>
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
* {
|
|
22
|
-
margin: 0;
|
|
23
|
-
padding: 0;
|
|
24
|
-
box-sizing: border-box;
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
body {
|
|
28
|
-
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, sans-serif;
|
|
29
|
-
background: var(--bg-primary);
|
|
30
|
-
color: var(--text-primary);
|
|
31
|
-
min-height: 100vh;
|
|
32
|
-
line-height: 1.6;
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
.container {
|
|
36
|
-
max-width: 1400px;
|
|
37
|
-
margin: 0 auto;
|
|
38
|
-
padding: 20px;
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
header {
|
|
42
|
-
display: flex;
|
|
43
|
-
justify-content: space-between;
|
|
44
|
-
align-items: center;
|
|
45
|
-
padding: 20px 0;
|
|
46
|
-
border-bottom: 1px solid var(--border);
|
|
47
|
-
margin-bottom: 30px;
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
.logo {
|
|
51
|
-
display: flex;
|
|
52
|
-
align-items: center;
|
|
53
|
-
gap: 12px;
|
|
54
|
-
font-size: 1.5rem;
|
|
55
|
-
font-weight: 600;
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
.logo-icon {
|
|
59
|
-
font-size: 2rem;
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
.refresh-btn {
|
|
63
|
-
background: var(--bg-card);
|
|
64
|
-
border: 1px solid var(--border);
|
|
65
|
-
color: var(--text-primary);
|
|
66
|
-
padding: 10px 20px;
|
|
67
|
-
border-radius: 8px;
|
|
68
|
-
cursor: pointer;
|
|
69
|
-
display: flex;
|
|
70
|
-
align-items: center;
|
|
71
|
-
gap: 8px;
|
|
72
|
-
transition: all 0.2s;
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
.refresh-btn:hover {
|
|
76
|
-
background: var(--accent);
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
.refresh-btn.loading {
|
|
80
|
-
opacity: 0.6;
|
|
81
|
-
pointer-events: none;
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
.stats-grid {
|
|
85
|
-
display: grid;
|
|
86
|
-
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
|
|
87
|
-
gap: 20px;
|
|
88
|
-
margin-bottom: 30px;
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
.stat-card {
|
|
92
|
-
background: var(--bg-card);
|
|
93
|
-
border-radius: 12px;
|
|
94
|
-
padding: 24px;
|
|
95
|
-
text-align: center;
|
|
96
|
-
border: 1px solid var(--border);
|
|
97
|
-
transition: transform 0.2s;
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
.stat-card:hover {
|
|
101
|
-
transform: translateY(-2px);
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
.stat-value {
|
|
105
|
-
font-size: 2.5rem;
|
|
106
|
-
font-weight: 700;
|
|
107
|
-
color: var(--accent);
|
|
108
|
-
margin-bottom: 8px;
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
.stat-label {
|
|
112
|
-
color: var(--text-secondary);
|
|
113
|
-
font-size: 0.9rem;
|
|
114
|
-
text-transform: uppercase;
|
|
115
|
-
letter-spacing: 1px;
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
.search-container {
|
|
119
|
-
margin-bottom: 30px;
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
.search-input {
|
|
123
|
-
width: 100%;
|
|
124
|
-
padding: 16px 20px;
|
|
125
|
-
background: var(--bg-secondary);
|
|
126
|
-
border: 1px solid var(--border);
|
|
127
|
-
border-radius: 12px;
|
|
128
|
-
color: var(--text-primary);
|
|
129
|
-
font-size: 1rem;
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
.search-input::placeholder {
|
|
133
|
-
color: var(--text-secondary);
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
.search-input:focus {
|
|
137
|
-
outline: none;
|
|
138
|
-
border-color: var(--accent);
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
.main-grid {
|
|
142
|
-
display: grid;
|
|
143
|
-
grid-template-columns: 1fr 1fr;
|
|
144
|
-
gap: 30px;
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
@media (max-width: 900px) {
|
|
148
|
-
.main-grid {
|
|
149
|
-
grid-template-columns: 1fr;
|
|
150
|
-
}
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
.section {
|
|
154
|
-
background: var(--bg-secondary);
|
|
155
|
-
border-radius: 12px;
|
|
156
|
-
padding: 24px;
|
|
157
|
-
border: 1px solid var(--border);
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
.section-title {
|
|
161
|
-
font-size: 1.2rem;
|
|
162
|
-
margin-bottom: 20px;
|
|
163
|
-
display: flex;
|
|
164
|
-
align-items: center;
|
|
165
|
-
gap: 10px;
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
.session-list {
|
|
169
|
-
list-style: none;
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
.session-item {
|
|
173
|
-
padding: 16px;
|
|
174
|
-
background: var(--bg-card);
|
|
175
|
-
border-radius: 8px;
|
|
176
|
-
margin-bottom: 12px;
|
|
177
|
-
cursor: pointer;
|
|
178
|
-
transition: all 0.2s;
|
|
179
|
-
border: 1px solid transparent;
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
.session-item:hover {
|
|
183
|
-
border-color: var(--accent);
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
.session-header {
|
|
187
|
-
display: flex;
|
|
188
|
-
justify-content: space-between;
|
|
189
|
-
align-items: center;
|
|
190
|
-
margin-bottom: 8px;
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
.session-id {
|
|
194
|
-
font-family: monospace;
|
|
195
|
-
font-size: 0.9rem;
|
|
196
|
-
color: var(--accent);
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
.session-time {
|
|
200
|
-
color: var(--text-secondary);
|
|
201
|
-
font-size: 0.85rem;
|
|
202
|
-
}
|
|
203
|
-
|
|
204
|
-
.session-meta {
|
|
205
|
-
display: flex;
|
|
206
|
-
gap: 16px;
|
|
207
|
-
color: var(--text-secondary);
|
|
208
|
-
font-size: 0.85rem;
|
|
209
|
-
}
|
|
210
|
-
|
|
211
|
-
.shared-item {
|
|
212
|
-
padding: 16px;
|
|
213
|
-
background: var(--bg-card);
|
|
214
|
-
border-radius: 8px;
|
|
215
|
-
margin-bottom: 12px;
|
|
216
|
-
display: flex;
|
|
217
|
-
justify-content: space-between;
|
|
218
|
-
align-items: center;
|
|
219
|
-
}
|
|
220
|
-
|
|
221
|
-
.shared-type {
|
|
222
|
-
display: flex;
|
|
223
|
-
align-items: center;
|
|
224
|
-
gap: 10px;
|
|
225
|
-
}
|
|
226
|
-
|
|
227
|
-
.shared-icon {
|
|
228
|
-
font-size: 1.5rem;
|
|
229
|
-
}
|
|
230
|
-
|
|
231
|
-
.shared-count {
|
|
232
|
-
background: var(--accent);
|
|
233
|
-
padding: 4px 12px;
|
|
234
|
-
border-radius: 20px;
|
|
235
|
-
font-weight: 600;
|
|
236
|
-
}
|
|
237
|
-
|
|
238
|
-
.timeline-container {
|
|
239
|
-
margin-top: 30px;
|
|
240
|
-
}
|
|
241
|
-
|
|
242
|
-
.timeline-bar {
|
|
243
|
-
display: flex;
|
|
244
|
-
gap: 4px;
|
|
245
|
-
height: 60px;
|
|
246
|
-
align-items: flex-end;
|
|
247
|
-
}
|
|
248
|
-
|
|
249
|
-
.timeline-day {
|
|
250
|
-
flex: 1;
|
|
251
|
-
background: var(--accent);
|
|
252
|
-
border-radius: 4px 4px 0 0;
|
|
253
|
-
min-height: 4px;
|
|
254
|
-
transition: opacity 0.2s;
|
|
255
|
-
position: relative;
|
|
256
|
-
}
|
|
257
|
-
|
|
258
|
-
.timeline-day:hover {
|
|
259
|
-
opacity: 0.8;
|
|
260
|
-
}
|
|
261
|
-
|
|
262
|
-
.timeline-day:hover::after {
|
|
263
|
-
content: attr(data-tooltip);
|
|
264
|
-
position: absolute;
|
|
265
|
-
bottom: 100%;
|
|
266
|
-
left: 50%;
|
|
267
|
-
transform: translateX(-50%);
|
|
268
|
-
background: var(--bg-card);
|
|
269
|
-
padding: 8px 12px;
|
|
270
|
-
border-radius: 6px;
|
|
271
|
-
font-size: 0.8rem;
|
|
272
|
-
white-space: nowrap;
|
|
273
|
-
z-index: 10;
|
|
274
|
-
}
|
|
275
|
-
|
|
276
|
-
.timeline-labels {
|
|
277
|
-
display: flex;
|
|
278
|
-
justify-content: space-between;
|
|
279
|
-
margin-top: 8px;
|
|
280
|
-
color: var(--text-secondary);
|
|
281
|
-
font-size: 0.8rem;
|
|
282
|
-
}
|
|
283
|
-
|
|
284
|
-
.endless-status {
|
|
285
|
-
display: flex;
|
|
286
|
-
align-items: center;
|
|
287
|
-
gap: 12px;
|
|
288
|
-
padding: 16px;
|
|
289
|
-
background: var(--bg-card);
|
|
290
|
-
border-radius: 8px;
|
|
291
|
-
margin-bottom: 16px;
|
|
292
|
-
}
|
|
293
|
-
|
|
294
|
-
.status-indicator {
|
|
295
|
-
width: 12px;
|
|
296
|
-
height: 12px;
|
|
297
|
-
border-radius: 50%;
|
|
298
|
-
}
|
|
299
|
-
|
|
300
|
-
.status-indicator.active {
|
|
301
|
-
background: var(--success);
|
|
302
|
-
box-shadow: 0 0 10px var(--success);
|
|
303
|
-
}
|
|
304
|
-
|
|
305
|
-
.status-indicator.inactive {
|
|
306
|
-
background: var(--text-secondary);
|
|
307
|
-
}
|
|
308
|
-
|
|
309
|
-
.progress-bar {
|
|
310
|
-
height: 8px;
|
|
311
|
-
background: var(--bg-primary);
|
|
312
|
-
border-radius: 4px;
|
|
313
|
-
overflow: hidden;
|
|
314
|
-
margin-top: 8px;
|
|
315
|
-
}
|
|
316
|
-
|
|
317
|
-
.progress-fill {
|
|
318
|
-
height: 100%;
|
|
319
|
-
background: linear-gradient(90deg, var(--accent), var(--accent-secondary));
|
|
320
|
-
border-radius: 4px;
|
|
321
|
-
transition: width 0.3s;
|
|
322
|
-
}
|
|
323
|
-
|
|
324
|
-
.loading-spinner {
|
|
325
|
-
text-align: center;
|
|
326
|
-
padding: 40px;
|
|
327
|
-
color: var(--text-secondary);
|
|
328
|
-
}
|
|
329
|
-
|
|
330
|
-
.error-message {
|
|
331
|
-
background: rgba(233, 69, 96, 0.2);
|
|
332
|
-
border: 1px solid var(--accent);
|
|
333
|
-
padding: 16px;
|
|
334
|
-
border-radius: 8px;
|
|
335
|
-
color: var(--accent);
|
|
336
|
-
}
|
|
337
|
-
|
|
338
|
-
.empty-state {
|
|
339
|
-
text-align: center;
|
|
340
|
-
padding: 40px;
|
|
341
|
-
color: var(--text-secondary);
|
|
342
|
-
}
|
|
343
|
-
|
|
344
|
-
.badge {
|
|
345
|
-
display: inline-block;
|
|
346
|
-
padding: 2px 8px;
|
|
347
|
-
border-radius: 4px;
|
|
348
|
-
font-size: 0.75rem;
|
|
349
|
-
font-weight: 600;
|
|
350
|
-
}
|
|
351
|
-
|
|
352
|
-
.badge-success {
|
|
353
|
-
background: rgba(74, 222, 128, 0.2);
|
|
354
|
-
color: var(--success);
|
|
355
|
-
}
|
|
356
|
-
|
|
357
|
-
.badge-warning {
|
|
358
|
-
background: rgba(251, 191, 36, 0.2);
|
|
359
|
-
color: var(--warning);
|
|
360
|
-
}
|
|
361
|
-
</style>
|
|
6
|
+
<title>Code Memory | Deep Space Dashboard</title>
|
|
7
|
+
|
|
8
|
+
<!-- Fonts -->
|
|
9
|
+
<link rel="preconnect" href="https://fonts.googleapis.com">
|
|
10
|
+
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
|
11
|
+
<link href="https://fonts.googleapis.com/css2?family=Outfit:wght@300;400;500;600;700&display=swap" rel="stylesheet">
|
|
12
|
+
|
|
13
|
+
<!-- Icons -->
|
|
14
|
+
<link href="https://cdn.jsdelivr.net/npm/remixicon@3.5.0/fonts/remixicon.css" rel="stylesheet">
|
|
15
|
+
|
|
16
|
+
<!-- Styles -->
|
|
17
|
+
<link rel="stylesheet" href="style.css">
|
|
18
|
+
|
|
19
|
+
<!-- Charts -->
|
|
20
|
+
<script src="https://cdn.jsdelivr.net/npm/apexcharts"></script>
|
|
362
21
|
</head>
|
|
363
22
|
<body>
|
|
364
|
-
<div class="container">
|
|
365
|
-
<header>
|
|
366
|
-
<div class="logo">
|
|
367
|
-
<span class="logo-icon">🧠</span>
|
|
368
|
-
<span>Code Memory Dashboard</span>
|
|
369
|
-
</div>
|
|
370
|
-
<button class="refresh-btn" onclick="refreshData()">
|
|
371
|
-
<span id="refresh-icon">🔄</span>
|
|
372
|
-
<span>Refresh</span>
|
|
373
|
-
</button>
|
|
374
|
-
</header>
|
|
375
23
|
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
24
|
+
<div class="app-container">
|
|
25
|
+
|
|
26
|
+
<!-- Sidebar -->
|
|
27
|
+
<aside class="sidebar">
|
|
28
|
+
<div class="logo-area">
|
|
29
|
+
<div class="logo-icon">🧠</div>
|
|
30
|
+
<div class="logo-text">CodeMemory</div>
|
|
380
31
|
</div>
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
<
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
id="search-input"
|
|
401
|
-
onkeyup="handleSearch(event)"
|
|
402
|
-
>
|
|
403
|
-
</div>
|
|
404
|
-
|
|
405
|
-
<div id="search-results" style="display: none; margin-bottom: 30px;">
|
|
406
|
-
<div class="section">
|
|
407
|
-
<h2 class="section-title">🔍 Search Results</h2>
|
|
408
|
-
<div id="search-results-content"></div>
|
|
409
|
-
</div>
|
|
410
|
-
</div>
|
|
411
|
-
|
|
412
|
-
<div class="main-grid">
|
|
413
|
-
<div class="section">
|
|
414
|
-
<h2 class="section-title">📋 Recent Sessions</h2>
|
|
415
|
-
<ul class="session-list" id="session-list">
|
|
416
|
-
<li class="loading-spinner">Loading...</li>
|
|
32
|
+
|
|
33
|
+
<nav>
|
|
34
|
+
<ul class="nav-menu">
|
|
35
|
+
<li class="nav-item active">
|
|
36
|
+
<i class="ri-dashboard-line"></i>
|
|
37
|
+
<span>Overview</span>
|
|
38
|
+
</li>
|
|
39
|
+
<li class="nav-item">
|
|
40
|
+
<i class="ri-database-2-line"></i>
|
|
41
|
+
<span>Knowledge Graph</span>
|
|
42
|
+
</li>
|
|
43
|
+
<li class="nav-item">
|
|
44
|
+
<i class="ri-brain-line"></i>
|
|
45
|
+
<span>Memory Banks</span>
|
|
46
|
+
</li>
|
|
47
|
+
<li class="nav-item">
|
|
48
|
+
<i class="ri-settings-4-line"></i>
|
|
49
|
+
<span>Configuration</span>
|
|
50
|
+
</li>
|
|
417
51
|
</ul>
|
|
52
|
+
</nav>
|
|
53
|
+
</aside>
|
|
54
|
+
|
|
55
|
+
<!-- Main Content -->
|
|
56
|
+
<main class="main-content">
|
|
57
|
+
|
|
58
|
+
<!-- Header -->
|
|
59
|
+
<header class="top-header">
|
|
60
|
+
<div class="page-title">
|
|
61
|
+
<h1>Dashboard</h1>
|
|
62
|
+
<p>Real-time memory visualization & management</p>
|
|
63
|
+
</div>
|
|
64
|
+
|
|
65
|
+
<div class="header-actions">
|
|
66
|
+
<div class="search-wrapper">
|
|
67
|
+
<i class="ri-search-line"></i>
|
|
68
|
+
<input type="text" id="search-input" class="search-input" placeholder="Search memories...">
|
|
69
|
+
</div>
|
|
70
|
+
|
|
71
|
+
<button id="refresh-btn" class="btn btn-secondary">
|
|
72
|
+
<i class="ri-refresh-line"></i>
|
|
73
|
+
<span>Refresh</span>
|
|
74
|
+
</button>
|
|
75
|
+
</div>
|
|
76
|
+
</header>
|
|
77
|
+
|
|
78
|
+
<!-- Stats Grid -->
|
|
79
|
+
<div class="stats-grid">
|
|
80
|
+
<div class="stat-card">
|
|
81
|
+
<div class="stat-value" id="stat-events">0</div>
|
|
82
|
+
<div class="stat-label">
|
|
83
|
+
<i class="ri-file-list-3-line"></i> Total Events
|
|
84
|
+
</div>
|
|
85
|
+
</div>
|
|
86
|
+
<div class="stat-card">
|
|
87
|
+
<div class="stat-value" id="stat-sessions">0</div>
|
|
88
|
+
<div class="stat-label">
|
|
89
|
+
<i class="ri-discuss-line"></i> Active Sessions
|
|
90
|
+
</div>
|
|
91
|
+
</div>
|
|
92
|
+
<div class="stat-card">
|
|
93
|
+
<div class="stat-value" id="stat-shared">0</div>
|
|
94
|
+
<div class="stat-label">
|
|
95
|
+
<i class="ri-share-forward-line"></i> Shared Items
|
|
96
|
+
</div>
|
|
97
|
+
</div>
|
|
98
|
+
<div class="stat-card">
|
|
99
|
+
<div class="stat-value" id="stat-vectors">0</div>
|
|
100
|
+
<div class="stat-label">
|
|
101
|
+
<i class="ri-node-tree"></i> Vector Nodes
|
|
102
|
+
</div>
|
|
103
|
+
</div>
|
|
418
104
|
</div>
|
|
419
105
|
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
<
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
<span>Best Practices</span>
|
|
106
|
+
<!-- Main Grid -->
|
|
107
|
+
<div class="dashboard-grid">
|
|
108
|
+
|
|
109
|
+
<!-- Left Column -->
|
|
110
|
+
<div class="left-col">
|
|
111
|
+
|
|
112
|
+
<!-- Memory Pipeline -->
|
|
113
|
+
<div class="card">
|
|
114
|
+
<div class="card-header">
|
|
115
|
+
<div class="card-title">
|
|
116
|
+
<i class="ri-flow-chart"></i>
|
|
117
|
+
<span>Memory Pipeline</span>
|
|
118
|
+
</div>
|
|
434
119
|
</div>
|
|
435
|
-
|
|
120
|
+
|
|
121
|
+
<!-- Pipeline Steps (Tabs) -->
|
|
122
|
+
<div class="pipeline-container">
|
|
123
|
+
<div class="pipeline-steps">
|
|
124
|
+
<div class="p-step active" data-level="L0">
|
|
125
|
+
<span class="p-step-name">Raw Events</span>
|
|
126
|
+
<span class="p-step-count">0</span>
|
|
127
|
+
</div>
|
|
128
|
+
<div class="p-step" data-level="L1">
|
|
129
|
+
<span class="p-step-name">Structured</span>
|
|
130
|
+
<span class="p-step-count">0</span>
|
|
131
|
+
</div>
|
|
132
|
+
<div class="p-step" data-level="L2">
|
|
133
|
+
<span class="p-step-name">Validated</span>
|
|
134
|
+
<span class="p-step-count">0</span>
|
|
135
|
+
</div>
|
|
136
|
+
<div class="p-step" data-level="L3">
|
|
137
|
+
<span class="p-step-name">Verified</span>
|
|
138
|
+
<span class="p-step-count">0</span>
|
|
139
|
+
</div>
|
|
140
|
+
<div class="p-step" data-level="L4">
|
|
141
|
+
<span class="p-step-name">Active</span>
|
|
142
|
+
<span class="p-step-count">0</span>
|
|
143
|
+
</div>
|
|
144
|
+
</div>
|
|
145
|
+
</div>
|
|
146
|
+
|
|
147
|
+
<!-- Events List -->
|
|
148
|
+
<div id="event-list-container" class="event-list">
|
|
149
|
+
<!-- Events injected by JS -->
|
|
150
|
+
<div class="event-item">
|
|
151
|
+
<div class="event-header">
|
|
152
|
+
<span class="event-type-badge type-system">System</span>
|
|
153
|
+
<span class="event-time">Just now</span>
|
|
154
|
+
</div>
|
|
155
|
+
<div class="event-content">Initializing dashboard interface...</div>
|
|
156
|
+
</div>
|
|
157
|
+
</div>
|
|
436
158
|
</div>
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
159
|
+
|
|
160
|
+
<!-- Activity Chart -->
|
|
161
|
+
<div class="card">
|
|
162
|
+
<div class="card-header">
|
|
163
|
+
<div class="card-title">
|
|
164
|
+
<i class="ri-bar-chart-grouped-line"></i>
|
|
165
|
+
<span>Activity Persistence</span>
|
|
166
|
+
</div>
|
|
441
167
|
</div>
|
|
442
|
-
<
|
|
168
|
+
<div id="activity-chart"></div>
|
|
443
169
|
</div>
|
|
170
|
+
|
|
444
171
|
</div>
|
|
445
172
|
|
|
446
|
-
|
|
447
|
-
<div
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
173
|
+
<!-- Right Column -->
|
|
174
|
+
<div class="right-col">
|
|
175
|
+
|
|
176
|
+
<!-- Endless Mode Status -->
|
|
177
|
+
<div class="card endless-card">
|
|
178
|
+
<div class="card-header">
|
|
179
|
+
<div class="card-title">
|
|
180
|
+
<i class="ri-infinite-loop-line"></i>
|
|
181
|
+
<span>Endless Mode</span>
|
|
182
|
+
</div>
|
|
183
|
+
</div>
|
|
184
|
+
<div style="display:flex; align-items:center; gap:12px; margin-top:10px;">
|
|
185
|
+
<div id="status-dot" class="status-dot"></div>
|
|
186
|
+
<span id="status-text" style="color:var(--text-muted); font-weight:500;">Idle</span>
|
|
187
|
+
</div>
|
|
451
188
|
</div>
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
189
|
+
|
|
190
|
+
<!-- Shared Knowledge -->
|
|
191
|
+
<div class="card">
|
|
192
|
+
<div class="card-header">
|
|
193
|
+
<div class="card-title">
|
|
194
|
+
<i class="ri-global-line"></i>
|
|
195
|
+
<span>Global Knowledge</span>
|
|
457
196
|
</div>
|
|
458
197
|
</div>
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
<
|
|
198
|
+
|
|
199
|
+
<div class="shared-list">
|
|
200
|
+
<div class="shared-item">
|
|
201
|
+
<div class="shared-info">
|
|
202
|
+
<div class="shared-icon">🔧</div>
|
|
203
|
+
<span>Troubleshooting</span>
|
|
204
|
+
</div>
|
|
205
|
+
<div class="shared-count" id="shared-troubleshooting">0</div>
|
|
206
|
+
</div>
|
|
207
|
+
<div class="shared-item">
|
|
208
|
+
<div class="shared-info">
|
|
209
|
+
<div class="shared-icon">✨</div>
|
|
210
|
+
<span>Best Practices</span>
|
|
211
|
+
</div>
|
|
212
|
+
<div class="shared-count" id="shared-best-practices">0</div>
|
|
213
|
+
</div>
|
|
214
|
+
<div class="shared-item">
|
|
215
|
+
<div class="shared-info">
|
|
216
|
+
<div class="shared-icon">⚠️</div>
|
|
217
|
+
<span>Common Errors</span>
|
|
218
|
+
</div>
|
|
219
|
+
<div class="shared-count" id="shared-errors">0</div>
|
|
220
|
+
</div>
|
|
462
221
|
</div>
|
|
463
222
|
</div>
|
|
223
|
+
|
|
464
224
|
</div>
|
|
465
|
-
</div>
|
|
466
|
-
</div>
|
|
467
225
|
|
|
468
|
-
<div class="timeline-container section" style="margin-top: 30px;">
|
|
469
|
-
<h2 class="section-title">📊 Activity Timeline (Last 7 Days)</h2>
|
|
470
|
-
<div class="timeline-bar" id="timeline-bar">
|
|
471
|
-
<!-- Filled by JS -->
|
|
472
226
|
</div>
|
|
473
|
-
|
|
474
|
-
<!-- Filled by JS -->
|
|
475
|
-
</div>
|
|
476
|
-
</div>
|
|
227
|
+
</main>
|
|
477
228
|
</div>
|
|
478
229
|
|
|
479
|
-
<script>
|
|
480
|
-
const API_BASE = '/api';
|
|
481
|
-
let refreshInterval = null;
|
|
482
|
-
|
|
483
|
-
async function fetchStats() {
|
|
484
|
-
try {
|
|
485
|
-
const response = await fetch(`${API_BASE}/stats`);
|
|
486
|
-
if (!response.ok) throw new Error('Failed to fetch stats');
|
|
487
|
-
return await response.json();
|
|
488
|
-
} catch (error) {
|
|
489
|
-
console.error('Stats fetch error:', error);
|
|
490
|
-
return null;
|
|
491
|
-
}
|
|
492
|
-
}
|
|
493
|
-
|
|
494
|
-
async function fetchSharedStats() {
|
|
495
|
-
try {
|
|
496
|
-
const response = await fetch(`${API_BASE}/stats/shared`);
|
|
497
|
-
if (!response.ok) throw new Error('Failed to fetch shared stats');
|
|
498
|
-
return await response.json();
|
|
499
|
-
} catch (error) {
|
|
500
|
-
console.error('Shared stats fetch error:', error);
|
|
501
|
-
return null;
|
|
502
|
-
}
|
|
503
|
-
}
|
|
504
|
-
|
|
505
|
-
async function fetchEndlessStatus() {
|
|
506
|
-
try {
|
|
507
|
-
const response = await fetch(`${API_BASE}/stats/endless`);
|
|
508
|
-
if (!response.ok) throw new Error('Failed to fetch endless status');
|
|
509
|
-
return await response.json();
|
|
510
|
-
} catch (error) {
|
|
511
|
-
console.error('Endless status fetch error:', error);
|
|
512
|
-
return null;
|
|
513
|
-
}
|
|
514
|
-
}
|
|
515
|
-
|
|
516
|
-
async function fetchSessions() {
|
|
517
|
-
try {
|
|
518
|
-
const response = await fetch(`${API_BASE}/sessions?limit=10`);
|
|
519
|
-
if (!response.ok) throw new Error('Failed to fetch sessions');
|
|
520
|
-
return await response.json();
|
|
521
|
-
} catch (error) {
|
|
522
|
-
console.error('Sessions fetch error:', error);
|
|
523
|
-
return null;
|
|
524
|
-
}
|
|
525
|
-
}
|
|
526
|
-
|
|
527
|
-
async function fetchTimeline() {
|
|
528
|
-
try {
|
|
529
|
-
const response = await fetch(`${API_BASE}/stats/timeline?days=7`);
|
|
530
|
-
if (!response.ok) throw new Error('Failed to fetch timeline');
|
|
531
|
-
return await response.json();
|
|
532
|
-
} catch (error) {
|
|
533
|
-
console.error('Timeline fetch error:', error);
|
|
534
|
-
return null;
|
|
535
|
-
}
|
|
536
|
-
}
|
|
537
|
-
|
|
538
|
-
async function searchMemories(query) {
|
|
539
|
-
try {
|
|
540
|
-
const response = await fetch(`${API_BASE}/search?q=${encodeURIComponent(query)}&limit=10`);
|
|
541
|
-
if (!response.ok) throw new Error('Failed to search');
|
|
542
|
-
return await response.json();
|
|
543
|
-
} catch (error) {
|
|
544
|
-
console.error('Search error:', error);
|
|
545
|
-
return null;
|
|
546
|
-
}
|
|
547
|
-
}
|
|
548
|
-
|
|
549
|
-
function formatNumber(num) {
|
|
550
|
-
if (num >= 1000) {
|
|
551
|
-
return (num / 1000).toFixed(1) + 'K';
|
|
552
|
-
}
|
|
553
|
-
return num.toString();
|
|
554
|
-
}
|
|
555
|
-
|
|
556
|
-
function formatTimeAgo(dateStr) {
|
|
557
|
-
const date = new Date(dateStr);
|
|
558
|
-
const now = new Date();
|
|
559
|
-
const diffMs = now - date;
|
|
560
|
-
const diffMins = Math.floor(diffMs / 60000);
|
|
561
|
-
const diffHours = Math.floor(diffMs / 3600000);
|
|
562
|
-
const diffDays = Math.floor(diffMs / 86400000);
|
|
563
|
-
|
|
564
|
-
if (diffMins < 1) return 'Just now';
|
|
565
|
-
if (diffMins < 60) return `${diffMins} min ago`;
|
|
566
|
-
if (diffHours < 24) return `${diffHours} hr ago`;
|
|
567
|
-
return `${diffDays} day${diffDays > 1 ? 's' : ''} ago`;
|
|
568
|
-
}
|
|
569
|
-
|
|
570
|
-
function updateStats(stats) {
|
|
571
|
-
if (!stats) return;
|
|
572
|
-
|
|
573
|
-
document.getElementById('stat-events').textContent = formatNumber(stats.storage?.eventCount || 0);
|
|
574
|
-
document.getElementById('stat-sessions').textContent = formatNumber(stats.sessions?.total || 0);
|
|
575
|
-
document.getElementById('stat-vectors').textContent = formatNumber(stats.storage?.vectorCount || 0);
|
|
576
|
-
}
|
|
577
|
-
|
|
578
|
-
function updateSharedStats(stats) {
|
|
579
|
-
if (!stats) {
|
|
580
|
-
document.getElementById('stat-shared').textContent = '0';
|
|
581
|
-
return;
|
|
582
|
-
}
|
|
583
|
-
|
|
584
|
-
const total = (stats.troubleshooting || 0) + (stats.bestPractices || 0) + (stats.commonErrors || 0);
|
|
585
|
-
document.getElementById('stat-shared').textContent = formatNumber(total);
|
|
586
|
-
document.getElementById('shared-troubleshooting').textContent = stats.troubleshooting || 0;
|
|
587
|
-
document.getElementById('shared-best-practices').textContent = stats.bestPractices || 0;
|
|
588
|
-
document.getElementById('shared-errors').textContent = stats.commonErrors || 0;
|
|
589
|
-
}
|
|
590
|
-
|
|
591
|
-
function updateEndlessStatus(status) {
|
|
592
|
-
const indicator = document.getElementById('endless-indicator');
|
|
593
|
-
const text = document.getElementById('endless-mode-text');
|
|
594
|
-
const details = document.getElementById('endless-details');
|
|
595
|
-
|
|
596
|
-
if (!status) {
|
|
597
|
-
text.textContent = 'Unable to load';
|
|
598
|
-
return;
|
|
599
|
-
}
|
|
600
|
-
|
|
601
|
-
if (status.mode === 'endless') {
|
|
602
|
-
indicator.className = 'status-indicator active';
|
|
603
|
-
text.textContent = 'Endless Mode Active';
|
|
604
|
-
details.style.display = 'block';
|
|
605
|
-
|
|
606
|
-
const continuityPercent = (status.continuityScore || 0) * 100;
|
|
607
|
-
document.getElementById('continuity-bar').style.width = `${continuityPercent}%`;
|
|
608
|
-
document.getElementById('working-set-size').textContent = status.workingSetSize || 0;
|
|
609
|
-
document.getElementById('consolidated-count').textContent = status.consolidatedCount || 0;
|
|
610
|
-
} else {
|
|
611
|
-
indicator.className = 'status-indicator inactive';
|
|
612
|
-
text.textContent = 'Session Mode (Endless Mode Disabled)';
|
|
613
|
-
details.style.display = 'none';
|
|
614
|
-
}
|
|
615
|
-
}
|
|
616
|
-
|
|
617
|
-
function updateSessions(sessions) {
|
|
618
|
-
const container = document.getElementById('session-list');
|
|
619
|
-
|
|
620
|
-
if (!sessions || sessions.length === 0) {
|
|
621
|
-
container.innerHTML = '<li class="empty-state">No sessions found</li>';
|
|
622
|
-
return;
|
|
623
|
-
}
|
|
624
|
-
|
|
625
|
-
container.innerHTML = sessions.map(session => `
|
|
626
|
-
<li class="session-item">
|
|
627
|
-
<div class="session-header">
|
|
628
|
-
<span class="session-id">${session.sessionId.slice(0, 16)}...</span>
|
|
629
|
-
<span class="session-time">${formatTimeAgo(session.lastActivity || session.startTime)}</span>
|
|
630
|
-
</div>
|
|
631
|
-
<div class="session-meta">
|
|
632
|
-
<span>📝 ${session.eventCount || 0} events</span>
|
|
633
|
-
<span>👤 ${session.promptCount || 0} prompts</span>
|
|
634
|
-
<span>🤖 ${session.responseCount || 0} responses</span>
|
|
635
|
-
</div>
|
|
636
|
-
</li>
|
|
637
|
-
`).join('');
|
|
638
|
-
}
|
|
639
|
-
|
|
640
|
-
function updateTimeline(timeline) {
|
|
641
|
-
const bar = document.getElementById('timeline-bar');
|
|
642
|
-
const labels = document.getElementById('timeline-labels');
|
|
643
|
-
|
|
644
|
-
if (!timeline || !timeline.daily || timeline.daily.length === 0) {
|
|
645
|
-
bar.innerHTML = '<div class="empty-state">No activity data</div>';
|
|
646
|
-
labels.innerHTML = '';
|
|
647
|
-
return;
|
|
648
|
-
}
|
|
649
|
-
|
|
650
|
-
const maxTotal = Math.max(...timeline.daily.map(d => d.total), 1);
|
|
651
|
-
|
|
652
|
-
bar.innerHTML = timeline.daily.map(day => {
|
|
653
|
-
const height = Math.max(4, (day.total / maxTotal) * 100);
|
|
654
|
-
const date = new Date(day.date).toLocaleDateString('en-US', { weekday: 'short' });
|
|
655
|
-
return `
|
|
656
|
-
<div
|
|
657
|
-
class="timeline-day"
|
|
658
|
-
style="height: ${height}%"
|
|
659
|
-
data-tooltip="${date}: ${day.total} events"
|
|
660
|
-
></div>
|
|
661
|
-
`;
|
|
662
|
-
}).join('');
|
|
663
|
-
|
|
664
|
-
const firstDate = new Date(timeline.daily[0].date).toLocaleDateString('en-US', { month: 'short', day: 'numeric' });
|
|
665
|
-
const lastDate = new Date(timeline.daily[timeline.daily.length - 1].date).toLocaleDateString('en-US', { month: 'short', day: 'numeric' });
|
|
666
|
-
labels.innerHTML = `<span>${firstDate}</span><span>${lastDate}</span>`;
|
|
667
|
-
}
|
|
668
|
-
|
|
669
|
-
function showSearchResults(results) {
|
|
670
|
-
const container = document.getElementById('search-results');
|
|
671
|
-
const content = document.getElementById('search-results-content');
|
|
672
|
-
|
|
673
|
-
if (!results || results.length === 0) {
|
|
674
|
-
content.innerHTML = '<div class="empty-state">No results found</div>';
|
|
675
|
-
container.style.display = 'block';
|
|
676
|
-
return;
|
|
677
|
-
}
|
|
678
|
-
|
|
679
|
-
content.innerHTML = results.map(result => `
|
|
680
|
-
<div class="session-item">
|
|
681
|
-
<div class="session-header">
|
|
682
|
-
<span class="session-id">${result.eventType || 'memory'}</span>
|
|
683
|
-
<span class="session-time">Score: ${(result.score * 100).toFixed(0)}%</span>
|
|
684
|
-
</div>
|
|
685
|
-
<p style="color: var(--text-secondary); margin-top: 8px;">
|
|
686
|
-
${result.content?.slice(0, 200) || 'No content'}${result.content?.length > 200 ? '...' : ''}
|
|
687
|
-
</p>
|
|
688
|
-
</div>
|
|
689
|
-
`).join('');
|
|
690
|
-
|
|
691
|
-
container.style.display = 'block';
|
|
692
|
-
}
|
|
693
|
-
|
|
694
|
-
let searchTimeout = null;
|
|
695
|
-
function handleSearch(event) {
|
|
696
|
-
const query = event.target.value.trim();
|
|
697
|
-
|
|
698
|
-
if (searchTimeout) clearTimeout(searchTimeout);
|
|
699
|
-
|
|
700
|
-
if (!query) {
|
|
701
|
-
document.getElementById('search-results').style.display = 'none';
|
|
702
|
-
return;
|
|
703
|
-
}
|
|
704
|
-
|
|
705
|
-
searchTimeout = setTimeout(async () => {
|
|
706
|
-
const results = await searchMemories(query);
|
|
707
|
-
showSearchResults(results?.results || results || []);
|
|
708
|
-
}, 300);
|
|
709
|
-
}
|
|
710
|
-
|
|
711
|
-
async function refreshData() {
|
|
712
|
-
const btn = document.querySelector('.refresh-btn');
|
|
713
|
-
btn.classList.add('loading');
|
|
714
|
-
document.getElementById('refresh-icon').textContent = '⏳';
|
|
715
|
-
|
|
716
|
-
try {
|
|
717
|
-
const [stats, sharedStats, endlessStatus, sessions, timeline] = await Promise.all([
|
|
718
|
-
fetchStats(),
|
|
719
|
-
fetchSharedStats(),
|
|
720
|
-
fetchEndlessStatus(),
|
|
721
|
-
fetchSessions(),
|
|
722
|
-
fetchTimeline()
|
|
723
|
-
]);
|
|
724
|
-
|
|
725
|
-
updateStats(stats);
|
|
726
|
-
updateSharedStats(sharedStats);
|
|
727
|
-
updateEndlessStatus(endlessStatus);
|
|
728
|
-
updateSessions(sessions?.sessions || sessions || []);
|
|
729
|
-
updateTimeline(timeline);
|
|
730
|
-
} catch (error) {
|
|
731
|
-
console.error('Refresh error:', error);
|
|
732
|
-
} finally {
|
|
733
|
-
btn.classList.remove('loading');
|
|
734
|
-
document.getElementById('refresh-icon').textContent = '🔄';
|
|
735
|
-
}
|
|
736
|
-
}
|
|
737
|
-
|
|
738
|
-
// Initial load
|
|
739
|
-
refreshData();
|
|
740
|
-
|
|
741
|
-
// Auto-refresh every 30 seconds
|
|
742
|
-
refreshInterval = setInterval(refreshData, 30000);
|
|
743
|
-
</script>
|
|
230
|
+
<script src="app.js"></script>
|
|
744
231
|
</body>
|
|
745
232
|
</html>
|