@jungjaehoon/mama-os 0.8.3 → 0.9.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/CHANGELOG.md +10 -0
- package/dist/agent/agent-loop.d.ts +1 -8
- package/dist/agent/agent-loop.d.ts.map +1 -1
- package/dist/agent/agent-loop.js +44 -159
- package/dist/agent/agent-loop.js.map +1 -1
- package/dist/agent/claude-cli-wrapper.d.ts +6 -0
- package/dist/agent/claude-cli-wrapper.d.ts.map +1 -1
- package/dist/agent/claude-cli-wrapper.js +6 -0
- package/dist/agent/claude-cli-wrapper.js.map +1 -1
- package/dist/agent/codex-mcp-process.d.ts +85 -0
- package/dist/agent/codex-mcp-process.d.ts.map +1 -0
- package/dist/agent/codex-mcp-process.js +357 -0
- package/dist/agent/codex-mcp-process.js.map +1 -0
- package/dist/agent/session-pool.d.ts +17 -2
- package/dist/agent/session-pool.d.ts.map +1 -1
- package/dist/agent/session-pool.js +51 -26
- package/dist/agent/session-pool.js.map +1 -1
- package/dist/agent/types.d.ts +9 -24
- package/dist/agent/types.d.ts.map +1 -1
- package/dist/agent/types.js.map +1 -1
- package/dist/api/graph-api.d.ts.map +1 -1
- package/dist/api/graph-api.js +133 -45
- package/dist/api/graph-api.js.map +1 -1
- package/dist/cli/commands/init.d.ts +1 -1
- package/dist/cli/commands/init.d.ts.map +1 -1
- package/dist/cli/commands/init.js +14 -25
- package/dist/cli/commands/init.js.map +1 -1
- package/dist/cli/commands/run.d.ts.map +1 -1
- package/dist/cli/commands/run.js +3 -10
- package/dist/cli/commands/run.js.map +1 -1
- package/dist/cli/commands/start.d.ts.map +1 -1
- package/dist/cli/commands/start.js +143 -54
- package/dist/cli/commands/start.js.map +1 -1
- package/dist/cli/commands/status.d.ts.map +1 -1
- package/dist/cli/commands/status.js +2 -7
- package/dist/cli/commands/status.js.map +1 -1
- package/dist/cli/config/config-manager.d.ts.map +1 -1
- package/dist/cli/config/config-manager.js +9 -17
- package/dist/cli/config/config-manager.js.map +1 -1
- package/dist/cli/config/types.d.ts +19 -25
- package/dist/cli/config/types.d.ts.map +1 -1
- package/dist/cli/config/types.js.map +1 -1
- package/dist/cli/index.js +2 -2
- package/dist/cli/index.js.map +1 -1
- package/dist/gateways/context-injector.d.ts.map +1 -1
- package/dist/gateways/context-injector.js +6 -3
- package/dist/gateways/context-injector.js.map +1 -1
- package/dist/gateways/discord.d.ts +4 -0
- package/dist/gateways/discord.d.ts.map +1 -1
- package/dist/gateways/discord.js +39 -16
- package/dist/gateways/discord.js.map +1 -1
- package/dist/gateways/message-router.d.ts +6 -1
- package/dist/gateways/message-router.d.ts.map +1 -1
- package/dist/gateways/message-router.js +92 -7
- package/dist/gateways/message-router.js.map +1 -1
- package/dist/multi-agent/agent-process-manager.d.ts.map +1 -1
- package/dist/multi-agent/agent-process-manager.js +36 -9
- package/dist/multi-agent/agent-process-manager.js.map +1 -1
- package/dist/multi-agent/runtime-process.d.ts +4 -4
- package/dist/multi-agent/runtime-process.d.ts.map +1 -1
- package/dist/multi-agent/runtime-process.js +9 -20
- package/dist/multi-agent/runtime-process.js.map +1 -1
- package/dist/multi-agent/types.d.ts +13 -8
- package/dist/multi-agent/types.d.ts.map +1 -1
- package/dist/multi-agent/types.js.map +1 -1
- package/dist/setup/setup-prompt.d.ts +1 -1
- package/dist/setup/setup-prompt.d.ts.map +1 -1
- package/dist/setup/setup-prompt.js +19 -0
- package/dist/setup/setup-prompt.js.map +1 -1
- package/dist/setup/setup-server.d.ts.map +1 -1
- package/dist/setup/setup-server.js +39 -16
- package/dist/setup/setup-server.js.map +1 -1
- package/dist/skills/skill-registry.d.ts.map +1 -1
- package/dist/skills/skill-registry.js +5 -2
- package/dist/skills/skill-registry.js.map +1 -1
- package/package.json +5 -3
- package/public/setup.html +12 -1
- package/public/viewer/js/modules/chat.js +1760 -1976
- package/public/viewer/js/modules/dashboard.js +613 -695
- package/public/viewer/js/modules/graph.js +857 -970
- package/public/viewer/js/modules/memory.js +357 -312
- package/public/viewer/js/modules/settings.js +1009 -1026
- package/public/viewer/js/modules/skills.js +336 -355
- package/public/viewer/js/utils/api.js +255 -255
- package/public/viewer/js/utils/debug-logger.js +20 -26
- package/public/viewer/js/utils/dom.js +73 -60
- package/public/viewer/js/utils/format.js +182 -228
- package/public/viewer/js/utils/markdown.js +40 -0
- package/public/viewer/src/modules/chat.ts +2258 -0
- package/public/viewer/src/modules/dashboard.ts +1052 -0
- package/public/viewer/src/modules/graph.ts +1080 -0
- package/public/viewer/src/modules/memory.ts +453 -0
- package/public/viewer/src/modules/settings.ts +1398 -0
- package/public/viewer/src/modules/skills.ts +457 -0
- package/public/viewer/src/types/global.d.ts +168 -0
- package/public/viewer/src/utils/api.ts +650 -0
- package/public/viewer/src/utils/debug-logger.ts +36 -0
- package/public/viewer/src/utils/dom.ts +138 -0
- package/public/viewer/src/utils/format.ts +331 -0
- package/public/viewer/src/utils/markdown.ts +46 -0
- package/public/viewer/tsconfig.viewer.json +18 -0
- package/public/viewer/viewer.html +214 -311
- package/dist/agent/codex-cli-wrapper.d.ts +0 -85
- package/dist/agent/codex-cli-wrapper.d.ts.map +0 -1
- package/dist/agent/codex-cli-wrapper.js +0 -295
- package/dist/agent/codex-cli-wrapper.js.map +0 -1
|
@@ -0,0 +1,453 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Memory Module - MAMA Memory Integration
|
|
3
|
+
* @module modules/memory
|
|
4
|
+
* @version 1.0.0
|
|
5
|
+
*
|
|
6
|
+
* Handles Memory tab functionality including:
|
|
7
|
+
* - Semantic search of MAMA decisions
|
|
8
|
+
* - Related decision suggestions for chat messages
|
|
9
|
+
* - Save decision form modal
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
/* eslint-env browser */
|
|
13
|
+
/* global lucide */
|
|
14
|
+
|
|
15
|
+
import { escapeHtml, debounce, showToast, getElementByIdOrNull } from '../utils/dom.js';
|
|
16
|
+
import { formatRelativeTime, truncateText } from '../utils/format.js';
|
|
17
|
+
import { API, type MemorySearchItem } from '../utils/api.js';
|
|
18
|
+
import { DebugLogger } from '../utils/debug-logger.js';
|
|
19
|
+
|
|
20
|
+
const logger = new DebugLogger('Memory');
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Memory Module Class
|
|
24
|
+
*/
|
|
25
|
+
export class MemoryModule {
|
|
26
|
+
searchData: MemorySearchItem[] = [];
|
|
27
|
+
debouncedSearch = debounce(() => this.performSearch(), 300);
|
|
28
|
+
currentQuery = '';
|
|
29
|
+
|
|
30
|
+
constructor() {
|
|
31
|
+
// Initialize event listeners
|
|
32
|
+
this.initEventListeners();
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Initialize all event listeners
|
|
37
|
+
*/
|
|
38
|
+
initEventListeners(): void {
|
|
39
|
+
// Memory card click handler (event delegation)
|
|
40
|
+
const resultsContainer = getElementByIdOrNull<HTMLElement>('memory-results');
|
|
41
|
+
if (resultsContainer) {
|
|
42
|
+
resultsContainer.addEventListener('click', (e: MouseEvent) => {
|
|
43
|
+
const card = (e.target as HTMLElement | null)?.closest<HTMLElement>('[data-memory-card]');
|
|
44
|
+
if (card) {
|
|
45
|
+
const idx = parseInt(card.dataset.memoryCard || '', 10);
|
|
46
|
+
if (!Number.isNaN(idx)) {
|
|
47
|
+
this.toggleCard(idx);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// Modal click outside to close
|
|
54
|
+
document.addEventListener('click', (e: MouseEvent) => {
|
|
55
|
+
const modal = getElementByIdOrNull<HTMLDivElement>('save-decision-modal');
|
|
56
|
+
if (modal && e.target === modal) {
|
|
57
|
+
this.hideSaveForm();
|
|
58
|
+
}
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
// Escape key to close modal
|
|
62
|
+
document.addEventListener('keydown', (e: KeyboardEvent) => {
|
|
63
|
+
if (e.key === 'Escape') {
|
|
64
|
+
const modal = getElementByIdOrNull<HTMLDivElement>('save-decision-modal');
|
|
65
|
+
if (modal && modal.classList.contains('visible')) {
|
|
66
|
+
this.hideSaveForm();
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Handle memory search input event
|
|
74
|
+
* @param {KeyboardEvent} event - Keyboard event
|
|
75
|
+
*/
|
|
76
|
+
handleSearchInput(event: KeyboardEvent): void {
|
|
77
|
+
if (event.key === 'Enter') {
|
|
78
|
+
this.search();
|
|
79
|
+
} else {
|
|
80
|
+
this.debouncedSearch();
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Perform search (internal, debounced)
|
|
86
|
+
*/
|
|
87
|
+
async performSearch(): Promise<void> {
|
|
88
|
+
const input = getElementByIdOrNull<HTMLInputElement>('memory-search-input');
|
|
89
|
+
if (!input) {
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
const query = input.value.trim();
|
|
93
|
+
|
|
94
|
+
if (!query) {
|
|
95
|
+
this.showPlaceholder();
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
await this.search();
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Search memory decisions via API
|
|
104
|
+
*/
|
|
105
|
+
async search(): Promise<void> {
|
|
106
|
+
const input = getElementByIdOrNull<HTMLInputElement>('memory-search-input');
|
|
107
|
+
if (!input) {
|
|
108
|
+
return;
|
|
109
|
+
}
|
|
110
|
+
const query = input.value.trim();
|
|
111
|
+
|
|
112
|
+
if (!query) {
|
|
113
|
+
this.showPlaceholder();
|
|
114
|
+
return;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
this.setStatus('Searching...', 'loading');
|
|
118
|
+
|
|
119
|
+
try {
|
|
120
|
+
const data = await API.searchMemory(query, 10);
|
|
121
|
+
this.searchData = data.results || [];
|
|
122
|
+
this.renderResults(this.searchData, query);
|
|
123
|
+
this.setStatus(`Found ${this.searchData.length} decision(s)`, '');
|
|
124
|
+
} catch (error) {
|
|
125
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
126
|
+
logger.error('Search error:', message);
|
|
127
|
+
this.setStatus(`Error: ${message}`, 'error');
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* Search for related decisions (called automatically for chat messages)
|
|
133
|
+
* @param {string} message - Chat message text
|
|
134
|
+
* @returns {Promise<Array>} Related decisions
|
|
135
|
+
*/
|
|
136
|
+
async searchRelated(message: string): Promise<MemorySearchItem[]> {
|
|
137
|
+
if (!message || message.length < 3) {
|
|
138
|
+
return [];
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
try {
|
|
142
|
+
const data = await API.searchMemory(message, 5);
|
|
143
|
+
return data.results || [];
|
|
144
|
+
} catch (error) {
|
|
145
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
146
|
+
logger.error('Related search error:', message);
|
|
147
|
+
return [];
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* Show related decisions for a chat message
|
|
153
|
+
* @param {string} message - Chat message text
|
|
154
|
+
*/
|
|
155
|
+
async showRelatedForMessage(message: string): Promise<void> {
|
|
156
|
+
const results = await this.searchRelated(message);
|
|
157
|
+
|
|
158
|
+
if (results.length > 0) {
|
|
159
|
+
this.searchData = results;
|
|
160
|
+
|
|
161
|
+
// Update search input with the query (if element exists)
|
|
162
|
+
const input = getElementByIdOrNull<HTMLInputElement>('memory-search-input');
|
|
163
|
+
if (input) {
|
|
164
|
+
input.value = message.substring(0, 50) + (message.length > 50 ? '...' : '');
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
// Render results
|
|
168
|
+
this.renderResults(results, message);
|
|
169
|
+
this.setStatus(`${results.length} related decision(s) found`, '');
|
|
170
|
+
|
|
171
|
+
// Show notification
|
|
172
|
+
showToast(`🧠 ${results.length} related MAMA decision(s) found`);
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
/**
|
|
177
|
+
* Render search results
|
|
178
|
+
* @param {Array} results - Search results
|
|
179
|
+
* @param {string} query - Search query
|
|
180
|
+
*/
|
|
181
|
+
renderResults(results: MemorySearchItem[], query: string): void {
|
|
182
|
+
const container = getElementByIdOrNull<HTMLElement>('memory-results');
|
|
183
|
+
|
|
184
|
+
// Guard: element may not exist if not on Memory tab
|
|
185
|
+
if (!container) {
|
|
186
|
+
return;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
if (!results || results.length === 0) {
|
|
190
|
+
container.innerHTML = `
|
|
191
|
+
<div class="memory-placeholder">
|
|
192
|
+
<p>No decisions found for "${escapeHtml(query)}"</p>
|
|
193
|
+
<p class="memory-hint">Try different keywords or check if you have saved decisions</p>
|
|
194
|
+
</div>
|
|
195
|
+
`;
|
|
196
|
+
return;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
const html = results
|
|
200
|
+
.map((item, idx) => {
|
|
201
|
+
const rawOutcome = String(item.outcome || 'PENDING');
|
|
202
|
+
const normalizedOutcome = rawOutcome.toLowerCase();
|
|
203
|
+
const outcomeClass = ['success', 'failed', 'partial', 'pending'].includes(normalizedOutcome)
|
|
204
|
+
? normalizedOutcome
|
|
205
|
+
: 'pending';
|
|
206
|
+
return `
|
|
207
|
+
<div class="memory-card" data-memory-card="${idx}">
|
|
208
|
+
<div class="memory-card-header">
|
|
209
|
+
<span class="memory-card-topic">${escapeHtml(item.topic || 'Unknown')}</span>
|
|
210
|
+
${item.similarity ? `<span class="memory-card-score">${Math.round(item.similarity * 100)}%</span>` : ''}
|
|
211
|
+
</div>
|
|
212
|
+
<div class="memory-card-decision">${escapeHtml(truncateText(item.decision, 150))}</div>
|
|
213
|
+
<div class="memory-card-meta">
|
|
214
|
+
<span class="memory-card-outcome ${outcomeClass}">${escapeHtml(rawOutcome)}</span>
|
|
215
|
+
<span>${formatRelativeTime(item.created_at)}</span>
|
|
216
|
+
</div>
|
|
217
|
+
<div class="memory-card-reasoning">${escapeHtml(item.reasoning || 'No reasoning provided')}</div>
|
|
218
|
+
</div>
|
|
219
|
+
`;
|
|
220
|
+
})
|
|
221
|
+
.join('');
|
|
222
|
+
|
|
223
|
+
container.innerHTML = html;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
/**
|
|
227
|
+
* Toggle memory card expand/collapse
|
|
228
|
+
* @param {number} idx - Card index
|
|
229
|
+
*/
|
|
230
|
+
toggleCard(idx: number): void {
|
|
231
|
+
const cards = document.querySelectorAll<HTMLElement>('.memory-card');
|
|
232
|
+
cards.forEach((card, i) => {
|
|
233
|
+
if (i === idx) {
|
|
234
|
+
card.classList.toggle('expanded');
|
|
235
|
+
} else {
|
|
236
|
+
card.classList.remove('expanded');
|
|
237
|
+
}
|
|
238
|
+
});
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
/**
|
|
242
|
+
* Show memory placeholder
|
|
243
|
+
*/
|
|
244
|
+
showPlaceholder(): void {
|
|
245
|
+
const container = getElementByIdOrNull<HTMLElement>('memory-results');
|
|
246
|
+
if (!container) {
|
|
247
|
+
return;
|
|
248
|
+
}
|
|
249
|
+
container.innerHTML = `
|
|
250
|
+
<div class="memory-placeholder">
|
|
251
|
+
<p><i data-lucide="brain"></i> Search your MAMA decisions</p>
|
|
252
|
+
<p class="memory-hint">Type a keyword or send a chat message to see related decisions</p>
|
|
253
|
+
</div>
|
|
254
|
+
`;
|
|
255
|
+
this.setStatus('', '');
|
|
256
|
+
// Reinitialize Lucide icons for dynamic content
|
|
257
|
+
if (typeof lucide !== 'undefined' && typeof window.lucideConfig !== 'undefined') {
|
|
258
|
+
lucide.createIcons(window.lucideConfig);
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
/**
|
|
263
|
+
* Set memory status message
|
|
264
|
+
* @param {string} message - Status message
|
|
265
|
+
* @param {string} type - Status type (loading, error, success, '')
|
|
266
|
+
*/
|
|
267
|
+
setStatus(message: string, type: 'loading' | 'error' | 'success' | '' = ''): void {
|
|
268
|
+
const status = getElementByIdOrNull<HTMLElement>('memory-status');
|
|
269
|
+
if (!status) {
|
|
270
|
+
return;
|
|
271
|
+
} // Guard: element may not exist if not on Memory tab
|
|
272
|
+
status.textContent = message;
|
|
273
|
+
status.className = 'memory-status ' + (type || '');
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
/**
|
|
277
|
+
* Show save decision form modal
|
|
278
|
+
*/
|
|
279
|
+
showSaveForm(): void {
|
|
280
|
+
const modal = getElementByIdOrNull<HTMLDivElement>('save-decision-modal');
|
|
281
|
+
if (!modal) {
|
|
282
|
+
return;
|
|
283
|
+
}
|
|
284
|
+
modal.classList.add('visible');
|
|
285
|
+
|
|
286
|
+
// Clear form
|
|
287
|
+
const topicInput = getElementByIdOrNull<HTMLInputElement>('save-topic');
|
|
288
|
+
const decisionInput = getElementByIdOrNull<HTMLTextAreaElement>('save-decision');
|
|
289
|
+
const reasoningInput = getElementByIdOrNull<HTMLTextAreaElement>('save-reasoning');
|
|
290
|
+
const confidenceInput = getElementByIdOrNull<HTMLInputElement>('save-confidence');
|
|
291
|
+
const statusEl = getElementByIdOrNull<HTMLElement>('save-form-status');
|
|
292
|
+
if (topicInput) {
|
|
293
|
+
topicInput.value = '';
|
|
294
|
+
}
|
|
295
|
+
if (decisionInput) {
|
|
296
|
+
decisionInput.value = '';
|
|
297
|
+
}
|
|
298
|
+
if (reasoningInput) {
|
|
299
|
+
reasoningInput.value = '';
|
|
300
|
+
}
|
|
301
|
+
if (confidenceInput) {
|
|
302
|
+
confidenceInput.value = '0.8';
|
|
303
|
+
}
|
|
304
|
+
if (statusEl) {
|
|
305
|
+
statusEl.textContent = '';
|
|
306
|
+
statusEl.className = 'save-form-status';
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
// Focus on topic field
|
|
310
|
+
setTimeout(() => {
|
|
311
|
+
const focusTarget = getElementByIdOrNull<HTMLInputElement>('save-topic');
|
|
312
|
+
focusTarget?.focus();
|
|
313
|
+
}, 100);
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
/**
|
|
317
|
+
* Show save form with pre-filled text (for /save command)
|
|
318
|
+
*/
|
|
319
|
+
showSaveFormWithText(text: string): void {
|
|
320
|
+
const modal = getElementByIdOrNull<HTMLDivElement>('save-decision-modal');
|
|
321
|
+
if (!modal) {
|
|
322
|
+
return;
|
|
323
|
+
}
|
|
324
|
+
modal.classList.add('visible');
|
|
325
|
+
|
|
326
|
+
// Pre-fill decision field
|
|
327
|
+
const topicInput = getElementByIdOrNull<HTMLInputElement>('save-topic');
|
|
328
|
+
const decisionInput = getElementByIdOrNull<HTMLTextAreaElement>('save-decision');
|
|
329
|
+
const reasoningInput = getElementByIdOrNull<HTMLTextAreaElement>('save-reasoning');
|
|
330
|
+
const confidenceInput = getElementByIdOrNull<HTMLInputElement>('save-confidence');
|
|
331
|
+
const statusEl = getElementByIdOrNull<HTMLElement>('save-form-status');
|
|
332
|
+
if (topicInput) {
|
|
333
|
+
topicInput.value = '';
|
|
334
|
+
}
|
|
335
|
+
if (decisionInput) {
|
|
336
|
+
decisionInput.value = text;
|
|
337
|
+
}
|
|
338
|
+
if (reasoningInput) {
|
|
339
|
+
reasoningInput.value = '';
|
|
340
|
+
}
|
|
341
|
+
if (confidenceInput) {
|
|
342
|
+
confidenceInput.value = '0.8';
|
|
343
|
+
}
|
|
344
|
+
if (statusEl) {
|
|
345
|
+
statusEl.textContent = '';
|
|
346
|
+
statusEl.className = 'save-form-status';
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
// Focus on topic field
|
|
350
|
+
setTimeout(() => {
|
|
351
|
+
const focusTarget = getElementByIdOrNull<HTMLInputElement>('save-topic');
|
|
352
|
+
focusTarget?.focus();
|
|
353
|
+
}, 100);
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
/**
|
|
357
|
+
* Hide save decision form modal
|
|
358
|
+
*/
|
|
359
|
+
hideSaveForm(): void {
|
|
360
|
+
const modal = getElementByIdOrNull<HTMLDivElement>('save-decision-modal');
|
|
361
|
+
if (!modal) {
|
|
362
|
+
return;
|
|
363
|
+
}
|
|
364
|
+
modal.classList.remove('visible');
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
/**
|
|
368
|
+
* Execute search with query (for /search command)
|
|
369
|
+
*/
|
|
370
|
+
async searchWithQuery(query: string): Promise<void> {
|
|
371
|
+
const searchInput = getElementByIdOrNull<HTMLInputElement>('memory-search-input');
|
|
372
|
+
if (searchInput) {
|
|
373
|
+
searchInput.value = query;
|
|
374
|
+
}
|
|
375
|
+
this.currentQuery = query;
|
|
376
|
+
await this.search();
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
/**
|
|
380
|
+
* Submit save decision form
|
|
381
|
+
*/
|
|
382
|
+
async submitSaveForm(): Promise<void> {
|
|
383
|
+
const topicInput = getElementByIdOrNull<HTMLInputElement>('save-topic');
|
|
384
|
+
const decisionInput = getElementByIdOrNull<HTMLTextAreaElement>('save-decision');
|
|
385
|
+
const reasoningInput = getElementByIdOrNull<HTMLTextAreaElement>('save-reasoning');
|
|
386
|
+
const confidenceInput = getElementByIdOrNull<HTMLInputElement>('save-confidence');
|
|
387
|
+
const statusEl = getElementByIdOrNull<HTMLElement>('save-form-status');
|
|
388
|
+
// Select the Save button (last button in the modal actions)
|
|
389
|
+
const submitBtn = getElementByIdOrNull<HTMLButtonElement>('save-form-submit');
|
|
390
|
+
if (
|
|
391
|
+
!topicInput ||
|
|
392
|
+
!decisionInput ||
|
|
393
|
+
!reasoningInput ||
|
|
394
|
+
!confidenceInput ||
|
|
395
|
+
!statusEl ||
|
|
396
|
+
!submitBtn
|
|
397
|
+
) {
|
|
398
|
+
return;
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
const topic = topicInput.value.trim();
|
|
402
|
+
const decision = decisionInput.value.trim();
|
|
403
|
+
const reasoning = reasoningInput.value.trim();
|
|
404
|
+
const confidence = parseFloat(confidenceInput.value);
|
|
405
|
+
|
|
406
|
+
// Validation
|
|
407
|
+
if (!topic || !decision || !reasoning) {
|
|
408
|
+
statusEl.textContent = 'Please fill in all required fields';
|
|
409
|
+
statusEl.className = 'save-form-status error';
|
|
410
|
+
return;
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
if (isNaN(confidence) || confidence < 0 || confidence > 1) {
|
|
414
|
+
statusEl.textContent = 'Confidence must be between 0.0 and 1.0';
|
|
415
|
+
statusEl.className = 'save-form-status error';
|
|
416
|
+
return;
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
// Disable submit button
|
|
420
|
+
submitBtn.disabled = true;
|
|
421
|
+
statusEl.textContent = 'Saving...';
|
|
422
|
+
statusEl.className = 'save-form-status';
|
|
423
|
+
|
|
424
|
+
try {
|
|
425
|
+
await API.saveDecision({ topic, decision, reasoning, confidence });
|
|
426
|
+
|
|
427
|
+
// Success
|
|
428
|
+
statusEl.textContent = '✓ Decision saved successfully!';
|
|
429
|
+
statusEl.className = 'save-form-status success';
|
|
430
|
+
|
|
431
|
+
// Show toast notification
|
|
432
|
+
showToast('✓ Decision saved to MAMA memory');
|
|
433
|
+
|
|
434
|
+
// Close modal after 1.5 seconds
|
|
435
|
+
setTimeout(() => {
|
|
436
|
+
this.hideSaveForm();
|
|
437
|
+
|
|
438
|
+
// Refresh memory search if there's a query
|
|
439
|
+
const searchInput = getElementByIdOrNull<HTMLInputElement>('memory-search-input');
|
|
440
|
+
if (searchInput?.value.trim()) {
|
|
441
|
+
this.search();
|
|
442
|
+
}
|
|
443
|
+
}, 1500);
|
|
444
|
+
} catch (error) {
|
|
445
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
446
|
+
logger.error('Save error:', message);
|
|
447
|
+
statusEl.textContent = `Error: ${message}`;
|
|
448
|
+
statusEl.className = 'save-form-status error';
|
|
449
|
+
} finally {
|
|
450
|
+
submitBtn.disabled = false;
|
|
451
|
+
}
|
|
452
|
+
}
|
|
453
|
+
}
|