antri_cli 1.26.1 → 1.27.0
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/cli/promptToolkit.d.ts.map +1 -1
- package/dist/cli/promptToolkit.js +1 -0
- package/dist/cli/promptToolkit.js.map +1 -1
- package/dist/cli/shortcuts.d.ts.map +1 -1
- package/dist/cli/shortcuts.js +7 -0
- package/dist/cli/shortcuts.js.map +1 -1
- package/dist/core/config.js +1 -1
- package/dist/core/config.js.map +1 -1
- package/dist/core/updater.d.ts +1 -1
- package/dist/core/updater.js +1 -1
- package/dist/desktop/public/app.js +459 -0
- package/dist/desktop/public/index.html +318 -0
- package/dist/desktop/public/style.css +812 -0
- package/dist/desktop/server.d.ts +11 -0
- package/dist/desktop/server.d.ts.map +1 -0
- package/dist/desktop/server.js +315 -0
- package/dist/desktop/server.js.map +1 -0
- package/dist/index.js +16 -2
- package/dist/index.js.map +1 -1
- package/dist/memory/episodic.d.ts +1 -0
- package/dist/memory/episodic.d.ts.map +1 -1
- package/dist/memory/episodic.js +3 -0
- package/dist/memory/episodic.js.map +1 -1
- package/dist/memory/manager.d.ts +6 -0
- package/dist/memory/manager.d.ts.map +1 -1
- package/dist/memory/manager.js +8 -0
- package/dist/memory/manager.js.map +1 -1
- package/package.json +4 -2
|
@@ -0,0 +1,459 @@
|
|
|
1
|
+
// ANTRI Desktop Control Plane Client Engine
|
|
2
|
+
|
|
3
|
+
let currentConfig = null;
|
|
4
|
+
let activeTab = 'chat';
|
|
5
|
+
|
|
6
|
+
// Initialize on Load
|
|
7
|
+
document.addEventListener('DOMContentLoaded', async () => {
|
|
8
|
+
await loadStatus();
|
|
9
|
+
await loadProfiles();
|
|
10
|
+
await loadSkills();
|
|
11
|
+
await loadMemory();
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
// Load System Status & Config
|
|
15
|
+
async function loadStatus() {
|
|
16
|
+
try {
|
|
17
|
+
const res = await fetch('/api/status');
|
|
18
|
+
const data = await res.json();
|
|
19
|
+
currentConfig = data.config;
|
|
20
|
+
|
|
21
|
+
// Update Mode buttons
|
|
22
|
+
switchMode(currentConfig.mode || 'vibe', false);
|
|
23
|
+
|
|
24
|
+
// Update Perms badge
|
|
25
|
+
updatePermsBadge(currentConfig.alwaysAllow);
|
|
26
|
+
|
|
27
|
+
// Update Provider selector
|
|
28
|
+
const provSelect = document.getElementById('select-provider');
|
|
29
|
+
if (provSelect) provSelect.value = currentConfig.provider;
|
|
30
|
+
|
|
31
|
+
await loadModels();
|
|
32
|
+
} catch (err) {
|
|
33
|
+
console.error('Failed to load status:', err);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// Load Models for Provider
|
|
38
|
+
async function loadModels() {
|
|
39
|
+
try {
|
|
40
|
+
const res = await fetch('/api/models');
|
|
41
|
+
const data = await res.json();
|
|
42
|
+
const modelSelect = document.getElementById('select-model');
|
|
43
|
+
modelSelect.innerHTML = '';
|
|
44
|
+
|
|
45
|
+
data.models.forEach((m) => {
|
|
46
|
+
const opt = document.createElement('option');
|
|
47
|
+
opt.value = m.id;
|
|
48
|
+
opt.textContent = `${m.name} (${m.category})`;
|
|
49
|
+
if (m.id === currentConfig.model) opt.selected = true;
|
|
50
|
+
modelSelect.appendChild(opt);
|
|
51
|
+
});
|
|
52
|
+
} catch (err) {
|
|
53
|
+
console.error('Failed to load models:', err);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// Switch Mode (Plan / Vibe)
|
|
58
|
+
async function switchMode(mode, triggerSave = true) {
|
|
59
|
+
const btnVibe = document.getElementById('btn-mode-vibe');
|
|
60
|
+
const btnPlan = document.getElementById('btn-mode-plan');
|
|
61
|
+
|
|
62
|
+
if (mode === 'plan') {
|
|
63
|
+
btnPlan.classList.add('active');
|
|
64
|
+
btnVibe.classList.remove('active');
|
|
65
|
+
} else {
|
|
66
|
+
btnVibe.classList.add('active');
|
|
67
|
+
btnPlan.classList.remove('active');
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
if (triggerSave && currentConfig) {
|
|
71
|
+
currentConfig.mode = mode;
|
|
72
|
+
await fetch('/api/config', {
|
|
73
|
+
method: 'POST',
|
|
74
|
+
headers: { 'Content-Type': 'application/json' },
|
|
75
|
+
body: JSON.stringify({ mode }),
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// Toggle Always-Allow Permissions
|
|
81
|
+
async function toggleAlwaysAllow() {
|
|
82
|
+
if (!currentConfig) return;
|
|
83
|
+
const next = !currentConfig.alwaysAllow;
|
|
84
|
+
currentConfig.alwaysAllow = next;
|
|
85
|
+
updatePermsBadge(next);
|
|
86
|
+
|
|
87
|
+
await fetch('/api/config', {
|
|
88
|
+
method: 'POST',
|
|
89
|
+
headers: { 'Content-Type': 'application/json' },
|
|
90
|
+
body: JSON.stringify({ alwaysAllow: next }),
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
function updatePermsBadge(alwaysAllow) {
|
|
95
|
+
const badge = document.getElementById('perms-text');
|
|
96
|
+
if (alwaysAllow) {
|
|
97
|
+
badge.textContent = 'Always-Allow';
|
|
98
|
+
} else {
|
|
99
|
+
badge.textContent = 'Ask-First';
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// Provider & Model Handlers
|
|
104
|
+
async function onProviderChange(provider) {
|
|
105
|
+
await fetch('/api/config', {
|
|
106
|
+
method: 'POST',
|
|
107
|
+
headers: { 'Content-Type': 'application/json' },
|
|
108
|
+
body: JSON.stringify({ provider }),
|
|
109
|
+
});
|
|
110
|
+
await loadStatus();
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
async function onModelChange(model) {
|
|
114
|
+
await fetch('/api/config', {
|
|
115
|
+
method: 'POST',
|
|
116
|
+
headers: { 'Content-Type': 'application/json' },
|
|
117
|
+
body: JSON.stringify({ model }),
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// Tab Switching
|
|
122
|
+
function showTab(tabName) {
|
|
123
|
+
activeTab = tabName;
|
|
124
|
+
document.querySelectorAll('.tab-panel').forEach((el) => el.classList.remove('active'));
|
|
125
|
+
document.querySelectorAll('.nav-item').forEach((el) => el.classList.remove('active'));
|
|
126
|
+
|
|
127
|
+
const targetPanel = document.getElementById(`tab-${tabName}`);
|
|
128
|
+
if (targetPanel) targetPanel.classList.add('active');
|
|
129
|
+
|
|
130
|
+
const navIndex = ['chat', 'dialectic', 'goal', 'profiles', 'skills', 'memory'].indexOf(tabName);
|
|
131
|
+
const navButtons = document.querySelectorAll('.nav-item');
|
|
132
|
+
if (navButtons[navIndex]) navButtons[navIndex].classList.add('active');
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// Chat Prompt Submission with SSE Streaming
|
|
136
|
+
async function submitPrompt() {
|
|
137
|
+
const input = document.getElementById('prompt-input');
|
|
138
|
+
const prompt = input.value.trim();
|
|
139
|
+
if (!prompt) return;
|
|
140
|
+
|
|
141
|
+
input.value = '';
|
|
142
|
+
|
|
143
|
+
// Intercept /debate or /goal inside chat
|
|
144
|
+
if (prompt.startsWith('/debate')) {
|
|
145
|
+
showTab('dialectic');
|
|
146
|
+
document.getElementById('debate-query-input').value = prompt.replace('/debate', '').trim();
|
|
147
|
+
startDebate();
|
|
148
|
+
return;
|
|
149
|
+
}
|
|
150
|
+
if (prompt.startsWith('/goal') || prompt.startsWith('/loop')) {
|
|
151
|
+
showTab('goal');
|
|
152
|
+
document.getElementById('goal-objective-input').value = prompt.replace(/^\/(goal|loop)/, '').trim();
|
|
153
|
+
startGoalLoop();
|
|
154
|
+
return;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
appendMessage('user', prompt);
|
|
158
|
+
|
|
159
|
+
const assistantMsgEl = appendMessage('assistant', '');
|
|
160
|
+
const contentEl = assistantMsgEl.querySelector('.msg-content');
|
|
161
|
+
|
|
162
|
+
const sendBtn = document.getElementById('send-btn');
|
|
163
|
+
sendBtn.disabled = true;
|
|
164
|
+
sendBtn.textContent = 'Thinking...';
|
|
165
|
+
|
|
166
|
+
try {
|
|
167
|
+
const response = await fetch('/api/chat', {
|
|
168
|
+
method: 'POST',
|
|
169
|
+
headers: { 'Content-Type': 'application/json' },
|
|
170
|
+
body: JSON.stringify({ prompt }),
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
const reader = response.body.getReader();
|
|
174
|
+
const decoder = new TextDecoder();
|
|
175
|
+
let accumulated = '';
|
|
176
|
+
|
|
177
|
+
while (true) {
|
|
178
|
+
const { done, value } = await reader.read();
|
|
179
|
+
if (done) break;
|
|
180
|
+
|
|
181
|
+
const chunk = decoder.decode(value);
|
|
182
|
+
const lines = chunk.split('\n');
|
|
183
|
+
|
|
184
|
+
for (const line of lines) {
|
|
185
|
+
if (line.startsWith('data: ')) {
|
|
186
|
+
try {
|
|
187
|
+
const data = JSON.parse(line.slice(6));
|
|
188
|
+
if (data.token) {
|
|
189
|
+
accumulated += data.token;
|
|
190
|
+
contentEl.textContent = accumulated;
|
|
191
|
+
scrollToBottom();
|
|
192
|
+
} else if (data.name && data.arguments) {
|
|
193
|
+
// Tool call badge
|
|
194
|
+
const toolBadge = document.createElement('div');
|
|
195
|
+
toolBadge.className = 'tool-badge-pill';
|
|
196
|
+
toolBadge.textContent = `Tool: ${data.name}`;
|
|
197
|
+
assistantMsgEl.insertBefore(toolBadge, contentEl);
|
|
198
|
+
}
|
|
199
|
+
} catch (e) {}
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
} catch (err) {
|
|
204
|
+
contentEl.textContent = `Error: ${err.message}`;
|
|
205
|
+
} finally {
|
|
206
|
+
sendBtn.disabled = false;
|
|
207
|
+
sendBtn.innerHTML = '<span>Send</span>';
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
function appendMessage(role, text) {
|
|
212
|
+
const container = document.getElementById('chat-messages');
|
|
213
|
+
const row = document.createElement('div');
|
|
214
|
+
row.className = `msg-row ${role}`;
|
|
215
|
+
const content = document.createElement('div');
|
|
216
|
+
content.className = 'msg-content';
|
|
217
|
+
content.textContent = text;
|
|
218
|
+
row.appendChild(content);
|
|
219
|
+
container.appendChild(row);
|
|
220
|
+
scrollToBottom();
|
|
221
|
+
return row;
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
function scrollToBottom() {
|
|
225
|
+
const container = document.getElementById('chat-messages');
|
|
226
|
+
container.scrollTop = container.scrollHeight;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
function setPrompt(text) {
|
|
230
|
+
const input = document.getElementById('prompt-input');
|
|
231
|
+
input.value = text;
|
|
232
|
+
input.focus();
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
function handleInputKey(event) {
|
|
236
|
+
if (event.key === 'Enter' && (event.ctrlKey || event.metaKey)) {
|
|
237
|
+
event.preventDefault();
|
|
238
|
+
submitPrompt();
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
// Dialectic Debate Runner
|
|
243
|
+
async function startDebate() {
|
|
244
|
+
const input = document.getElementById('debate-query-input');
|
|
245
|
+
const query = input.value.trim();
|
|
246
|
+
if (!query) return;
|
|
247
|
+
|
|
248
|
+
const depth = document.getElementById('debate-depth-select').value;
|
|
249
|
+
|
|
250
|
+
document.getElementById('dialectic-thesis').textContent = 'Generating initial thesis & hypothesis...';
|
|
251
|
+
document.getElementById('dialectic-antithesis').textContent = 'Awaiting thesis to challenge assumptions...';
|
|
252
|
+
document.getElementById('dialectic-verification').textContent = 'Researcher standby for fact-checking...';
|
|
253
|
+
document.getElementById('dialectic-synthesis').textContent = 'Synthesizer awaiting debate completion...';
|
|
254
|
+
|
|
255
|
+
try {
|
|
256
|
+
const res = await fetch('/api/debate', {
|
|
257
|
+
method: 'POST',
|
|
258
|
+
headers: { 'Content-Type': 'application/json' },
|
|
259
|
+
body: JSON.stringify({ query, depth }),
|
|
260
|
+
});
|
|
261
|
+
|
|
262
|
+
const reader = res.body.getReader();
|
|
263
|
+
const decoder = new TextDecoder();
|
|
264
|
+
|
|
265
|
+
while (true) {
|
|
266
|
+
const { done, value } = await reader.read();
|
|
267
|
+
if (done) break;
|
|
268
|
+
|
|
269
|
+
const chunk = decoder.decode(value);
|
|
270
|
+
const lines = chunk.split('\n');
|
|
271
|
+
|
|
272
|
+
for (const line of lines) {
|
|
273
|
+
if (line.startsWith('data: ')) {
|
|
274
|
+
try {
|
|
275
|
+
const data = JSON.parse(line.slice(6));
|
|
276
|
+
if (data.thesis) document.getElementById('dialectic-thesis').textContent = data.thesis;
|
|
277
|
+
if (data.antithesis) document.getElementById('dialectic-antithesis').textContent = data.antithesis;
|
|
278
|
+
if (data.verification) document.getElementById('dialectic-verification').textContent = data.verification;
|
|
279
|
+
if (data.synthesis) document.getElementById('dialectic-synthesis').textContent = data.synthesis;
|
|
280
|
+
} catch (e) {}
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
} catch (err) {
|
|
285
|
+
document.getElementById('dialectic-synthesis').textContent = `Debate error: ${err.message}`;
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
// Goal Loop Runner
|
|
290
|
+
async function startGoalLoop() {
|
|
291
|
+
const input = document.getElementById('goal-objective-input');
|
|
292
|
+
const objective = input.value.trim();
|
|
293
|
+
if (!objective) return;
|
|
294
|
+
|
|
295
|
+
document.getElementById('goal-stage-1-content').textContent = 'Stage 1 executing: Drafting initial plan and solution...';
|
|
296
|
+
document.getElementById('goal-stage-2-content').textContent = 'Stage 2 standby: Awaiting draft for adversarial critique...';
|
|
297
|
+
document.getElementById('goal-stage-3-content').textContent = 'Stage 3 standby: Awaiting synthesis for hardened output...';
|
|
298
|
+
|
|
299
|
+
try {
|
|
300
|
+
const res = await fetch('/api/goal', {
|
|
301
|
+
method: 'POST',
|
|
302
|
+
headers: { 'Content-Type': 'application/json' },
|
|
303
|
+
body: JSON.stringify({ objective }),
|
|
304
|
+
});
|
|
305
|
+
|
|
306
|
+
const reader = res.body.getReader();
|
|
307
|
+
const decoder = new TextDecoder();
|
|
308
|
+
|
|
309
|
+
while (true) {
|
|
310
|
+
const { done, value } = await reader.read();
|
|
311
|
+
if (done) break;
|
|
312
|
+
|
|
313
|
+
const chunk = decoder.decode(value);
|
|
314
|
+
const lines = chunk.split('\n');
|
|
315
|
+
|
|
316
|
+
for (const line of lines) {
|
|
317
|
+
if (line.startsWith('data: ')) {
|
|
318
|
+
try {
|
|
319
|
+
const data = JSON.parse(line.slice(6));
|
|
320
|
+
if (data.draft) document.getElementById('goal-stage-1-content').textContent = data.draft;
|
|
321
|
+
if (data.critique) document.getElementById('goal-stage-2-content').textContent = data.critique;
|
|
322
|
+
if (data.finalOutput) document.getElementById('goal-stage-3-content').textContent = data.finalOutput;
|
|
323
|
+
} catch (e) {}
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
} catch (err) {
|
|
328
|
+
document.getElementById('goal-stage-3-content').textContent = `Goal execution error: ${err.message}`;
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
// Profile Management
|
|
333
|
+
async function loadProfiles() {
|
|
334
|
+
try {
|
|
335
|
+
const res = await fetch('/api/profiles');
|
|
336
|
+
const data = await res.json();
|
|
337
|
+
|
|
338
|
+
const select = document.getElementById('select-profile');
|
|
339
|
+
const list = document.getElementById('profile-items-list');
|
|
340
|
+
select.innerHTML = '';
|
|
341
|
+
list.innerHTML = '';
|
|
342
|
+
|
|
343
|
+
data.profiles.forEach((p) => {
|
|
344
|
+
// Dropdown option
|
|
345
|
+
const opt = document.createElement('option');
|
|
346
|
+
opt.value = p.name;
|
|
347
|
+
opt.textContent = p.name;
|
|
348
|
+
if (p.name === data.activeName) opt.selected = true;
|
|
349
|
+
select.appendChild(opt);
|
|
350
|
+
|
|
351
|
+
// List button
|
|
352
|
+
const btn = document.createElement('button');
|
|
353
|
+
btn.className = `profile-item-btn ${p.name === data.activeName ? 'active' : ''}`;
|
|
354
|
+
btn.textContent = `${p.name}.md`;
|
|
355
|
+
btn.onclick = () => selectProfile(p.name);
|
|
356
|
+
list.appendChild(btn);
|
|
357
|
+
});
|
|
358
|
+
|
|
359
|
+
document.getElementById('active-profile-title').textContent = `${data.activeName}.md`;
|
|
360
|
+
document.getElementById('profile-editor').value = data.activeContent || '';
|
|
361
|
+
} catch (err) {
|
|
362
|
+
console.error('Failed to load profiles:', err);
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
async function selectProfile(name) {
|
|
367
|
+
await fetch('/api/profile/select', {
|
|
368
|
+
method: 'POST',
|
|
369
|
+
headers: { 'Content-Type': 'application/json' },
|
|
370
|
+
body: JSON.stringify({ name }),
|
|
371
|
+
});
|
|
372
|
+
await loadProfiles();
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
async function createProfile() {
|
|
376
|
+
const input = document.getElementById('new-profile-name');
|
|
377
|
+
const name = input.value.trim();
|
|
378
|
+
if (!name) return;
|
|
379
|
+
|
|
380
|
+
await fetch('/api/profile/create', {
|
|
381
|
+
method: 'POST',
|
|
382
|
+
headers: { 'Content-Type': 'application/json' },
|
|
383
|
+
body: JSON.stringify({ name }),
|
|
384
|
+
});
|
|
385
|
+
|
|
386
|
+
input.value = '';
|
|
387
|
+
await loadProfiles();
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
async function saveActiveProfile() {
|
|
391
|
+
const content = document.getElementById('profile-editor').value;
|
|
392
|
+
await fetch('/api/profile/save', {
|
|
393
|
+
method: 'POST',
|
|
394
|
+
headers: { 'Content-Type': 'application/json' },
|
|
395
|
+
body: JSON.stringify({ content }),
|
|
396
|
+
});
|
|
397
|
+
alert('Profile saved.');
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
async function onProfileChange(name) {
|
|
401
|
+
await selectProfile(name);
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
// Skills & Memory Loaders
|
|
405
|
+
async function loadSkills() {
|
|
406
|
+
try {
|
|
407
|
+
const res = await fetch('/api/skills');
|
|
408
|
+
const data = await res.json();
|
|
409
|
+
const grid = document.getElementById('skills-catalog-grid');
|
|
410
|
+
grid.innerHTML = '';
|
|
411
|
+
|
|
412
|
+
data.allTools.forEach((t) => {
|
|
413
|
+
const card = document.createElement('div');
|
|
414
|
+
card.className = 'skill-card';
|
|
415
|
+
card.innerHTML = `
|
|
416
|
+
<h4>${t.name}</h4>
|
|
417
|
+
<p>${t.description}</p>
|
|
418
|
+
<button class="skill-btn" onclick="testSkill('${t.name}')">Dry-Run Skill</button>
|
|
419
|
+
`;
|
|
420
|
+
grid.appendChild(card);
|
|
421
|
+
});
|
|
422
|
+
} catch (err) {
|
|
423
|
+
console.error('Failed to load skills:', err);
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
async function testSkill(skillName) {
|
|
428
|
+
try {
|
|
429
|
+
const res = await fetch('/api/skill/test', {
|
|
430
|
+
method: 'POST',
|
|
431
|
+
headers: { 'Content-Type': 'application/json' },
|
|
432
|
+
body: JSON.stringify({ skillName, args: {} }),
|
|
433
|
+
});
|
|
434
|
+
const data = await res.json();
|
|
435
|
+
alert(`Skill '${skillName}' Output:\n${data.output || 'Execution complete'}`);
|
|
436
|
+
} catch (err) {
|
|
437
|
+
alert(`Execution failed: ${err.message}`);
|
|
438
|
+
}
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
async function loadMemory() {
|
|
442
|
+
try {
|
|
443
|
+
const res = await fetch('/api/memory');
|
|
444
|
+
const data = await res.json();
|
|
445
|
+
|
|
446
|
+
const semanticList = document.getElementById('semantic-memory-list');
|
|
447
|
+
const episodicList = document.getElementById('episodic-memory-list');
|
|
448
|
+
|
|
449
|
+
semanticList.innerHTML = (data.semanticItems || [])
|
|
450
|
+
.map((item) => `<div style="margin-bottom: 0.75rem; border-bottom: 1px solid var(--border-light); padding-bottom: 0.5rem;"><b>[${item.category}]</b> ${item.text}</div>`)
|
|
451
|
+
.join('') || '<div>No semantic vectors stored yet.</div>';
|
|
452
|
+
|
|
453
|
+
episodicList.innerHTML = (data.episodes || [])
|
|
454
|
+
.map((ep) => `<div style="margin-bottom: 0.75rem; border-bottom: 1px solid var(--border-light); padding-bottom: 0.5rem;"><b>Query:</b> ${ep.query}<br/><span style="color: var(--text-tertiary);">${ep.response.slice(0, 100)}...</span></div>`)
|
|
455
|
+
.join('') || '<div>No episodes recorded in current session.</div>';
|
|
456
|
+
} catch (err) {
|
|
457
|
+
console.error('Failed to load memory:', err);
|
|
458
|
+
}
|
|
459
|
+
}
|