@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.
Files changed (106) hide show
  1. package/CHANGELOG.md +10 -0
  2. package/dist/agent/agent-loop.d.ts +1 -8
  3. package/dist/agent/agent-loop.d.ts.map +1 -1
  4. package/dist/agent/agent-loop.js +44 -159
  5. package/dist/agent/agent-loop.js.map +1 -1
  6. package/dist/agent/claude-cli-wrapper.d.ts +6 -0
  7. package/dist/agent/claude-cli-wrapper.d.ts.map +1 -1
  8. package/dist/agent/claude-cli-wrapper.js +6 -0
  9. package/dist/agent/claude-cli-wrapper.js.map +1 -1
  10. package/dist/agent/codex-mcp-process.d.ts +85 -0
  11. package/dist/agent/codex-mcp-process.d.ts.map +1 -0
  12. package/dist/agent/codex-mcp-process.js +357 -0
  13. package/dist/agent/codex-mcp-process.js.map +1 -0
  14. package/dist/agent/session-pool.d.ts +17 -2
  15. package/dist/agent/session-pool.d.ts.map +1 -1
  16. package/dist/agent/session-pool.js +51 -26
  17. package/dist/agent/session-pool.js.map +1 -1
  18. package/dist/agent/types.d.ts +9 -24
  19. package/dist/agent/types.d.ts.map +1 -1
  20. package/dist/agent/types.js.map +1 -1
  21. package/dist/api/graph-api.d.ts.map +1 -1
  22. package/dist/api/graph-api.js +133 -45
  23. package/dist/api/graph-api.js.map +1 -1
  24. package/dist/cli/commands/init.d.ts +1 -1
  25. package/dist/cli/commands/init.d.ts.map +1 -1
  26. package/dist/cli/commands/init.js +14 -25
  27. package/dist/cli/commands/init.js.map +1 -1
  28. package/dist/cli/commands/run.d.ts.map +1 -1
  29. package/dist/cli/commands/run.js +3 -10
  30. package/dist/cli/commands/run.js.map +1 -1
  31. package/dist/cli/commands/start.d.ts.map +1 -1
  32. package/dist/cli/commands/start.js +143 -54
  33. package/dist/cli/commands/start.js.map +1 -1
  34. package/dist/cli/commands/status.d.ts.map +1 -1
  35. package/dist/cli/commands/status.js +2 -7
  36. package/dist/cli/commands/status.js.map +1 -1
  37. package/dist/cli/config/config-manager.d.ts.map +1 -1
  38. package/dist/cli/config/config-manager.js +9 -17
  39. package/dist/cli/config/config-manager.js.map +1 -1
  40. package/dist/cli/config/types.d.ts +19 -25
  41. package/dist/cli/config/types.d.ts.map +1 -1
  42. package/dist/cli/config/types.js.map +1 -1
  43. package/dist/cli/index.js +2 -2
  44. package/dist/cli/index.js.map +1 -1
  45. package/dist/gateways/context-injector.d.ts.map +1 -1
  46. package/dist/gateways/context-injector.js +6 -3
  47. package/dist/gateways/context-injector.js.map +1 -1
  48. package/dist/gateways/discord.d.ts +4 -0
  49. package/dist/gateways/discord.d.ts.map +1 -1
  50. package/dist/gateways/discord.js +39 -16
  51. package/dist/gateways/discord.js.map +1 -1
  52. package/dist/gateways/message-router.d.ts +6 -1
  53. package/dist/gateways/message-router.d.ts.map +1 -1
  54. package/dist/gateways/message-router.js +92 -7
  55. package/dist/gateways/message-router.js.map +1 -1
  56. package/dist/multi-agent/agent-process-manager.d.ts.map +1 -1
  57. package/dist/multi-agent/agent-process-manager.js +36 -9
  58. package/dist/multi-agent/agent-process-manager.js.map +1 -1
  59. package/dist/multi-agent/runtime-process.d.ts +4 -4
  60. package/dist/multi-agent/runtime-process.d.ts.map +1 -1
  61. package/dist/multi-agent/runtime-process.js +9 -20
  62. package/dist/multi-agent/runtime-process.js.map +1 -1
  63. package/dist/multi-agent/types.d.ts +13 -8
  64. package/dist/multi-agent/types.d.ts.map +1 -1
  65. package/dist/multi-agent/types.js.map +1 -1
  66. package/dist/setup/setup-prompt.d.ts +1 -1
  67. package/dist/setup/setup-prompt.d.ts.map +1 -1
  68. package/dist/setup/setup-prompt.js +19 -0
  69. package/dist/setup/setup-prompt.js.map +1 -1
  70. package/dist/setup/setup-server.d.ts.map +1 -1
  71. package/dist/setup/setup-server.js +39 -16
  72. package/dist/setup/setup-server.js.map +1 -1
  73. package/dist/skills/skill-registry.d.ts.map +1 -1
  74. package/dist/skills/skill-registry.js +5 -2
  75. package/dist/skills/skill-registry.js.map +1 -1
  76. package/package.json +5 -3
  77. package/public/setup.html +12 -1
  78. package/public/viewer/js/modules/chat.js +1760 -1976
  79. package/public/viewer/js/modules/dashboard.js +613 -695
  80. package/public/viewer/js/modules/graph.js +857 -970
  81. package/public/viewer/js/modules/memory.js +357 -312
  82. package/public/viewer/js/modules/settings.js +1009 -1026
  83. package/public/viewer/js/modules/skills.js +336 -355
  84. package/public/viewer/js/utils/api.js +255 -255
  85. package/public/viewer/js/utils/debug-logger.js +20 -26
  86. package/public/viewer/js/utils/dom.js +73 -60
  87. package/public/viewer/js/utils/format.js +182 -228
  88. package/public/viewer/js/utils/markdown.js +40 -0
  89. package/public/viewer/src/modules/chat.ts +2258 -0
  90. package/public/viewer/src/modules/dashboard.ts +1052 -0
  91. package/public/viewer/src/modules/graph.ts +1080 -0
  92. package/public/viewer/src/modules/memory.ts +453 -0
  93. package/public/viewer/src/modules/settings.ts +1398 -0
  94. package/public/viewer/src/modules/skills.ts +457 -0
  95. package/public/viewer/src/types/global.d.ts +168 -0
  96. package/public/viewer/src/utils/api.ts +650 -0
  97. package/public/viewer/src/utils/debug-logger.ts +36 -0
  98. package/public/viewer/src/utils/dom.ts +138 -0
  99. package/public/viewer/src/utils/format.ts +331 -0
  100. package/public/viewer/src/utils/markdown.ts +46 -0
  101. package/public/viewer/tsconfig.viewer.json +18 -0
  102. package/public/viewer/viewer.html +214 -311
  103. package/dist/agent/codex-cli-wrapper.d.ts +0 -85
  104. package/dist/agent/codex-cli-wrapper.d.ts.map +0 -1
  105. package/dist/agent/codex-cli-wrapper.js +0 -295
  106. 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
+ }