@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,457 @@
1
+ /**
2
+ * Skills Marketplace Module
3
+ * @module modules/skills
4
+ * @version 1.0.0
5
+ *
6
+ * Manages skill browsing, installation, and configuration
7
+ * across MAMA, Cowork, and OpenClaw sources.
8
+ */
9
+
10
+ /* eslint-env browser */
11
+
12
+ import { API, type SkillItem } from '../utils/api.js';
13
+ import { DebugLogger } from '../utils/debug-logger.js';
14
+ import { getElementByIdOrNull } from '../utils/dom.js';
15
+ import { renderSafeMarkdown } from '../utils/markdown.js';
16
+
17
+ const logger = new DebugLogger('Skills');
18
+
19
+ /**
20
+ * Skills marketplace module
21
+ */
22
+ export const SkillsModule = {
23
+ /** All skills (installed + catalog) */
24
+ installed: [] as SkillItem[],
25
+ catalog: [] as SkillItem[],
26
+ /** Current filter */
27
+ currentFilter: 'all',
28
+ /** Search query */
29
+ searchQuery: '',
30
+ /** Whether initialized */
31
+ _initialized: false,
32
+ /** Debounce timer */
33
+ _searchTimer: null as ReturnType<typeof setTimeout> | null,
34
+
35
+ /**
36
+ * Initialize the skills tab
37
+ */
38
+ async init(): Promise<void> {
39
+ if (!this._initialized) {
40
+ this._bindEvents();
41
+ this._initialized = true;
42
+ }
43
+ await this.loadSkills();
44
+ this.render();
45
+ },
46
+
47
+ /**
48
+ * Load installed + catalog skills
49
+ */
50
+ async loadSkills(): Promise<void> {
51
+ try {
52
+ const [installedResponse, catalogResponse] = await Promise.all([
53
+ API.getSkills(),
54
+ API.getSkillCatalog('all'),
55
+ ]);
56
+
57
+ this.installed = installedResponse.skills || [];
58
+ const catalogItems = catalogResponse.skills || [];
59
+ const installedMap = new Set(
60
+ this.installed.map((item: SkillItem) => `${item.source}::${item.id}`)
61
+ );
62
+ this.catalog = catalogItems.filter(
63
+ (s: SkillItem) => !installedMap.has(`${s.source}::${s.id}`)
64
+ );
65
+ } catch (error) {
66
+ const message = error instanceof Error ? error.message : String(error);
67
+ logger.error('Unexpected error while loading skills:', message);
68
+ // Initialize with empty arrays on failure
69
+ this.installed = [];
70
+ this.catalog = [];
71
+ }
72
+ },
73
+
74
+ /**
75
+ * Bind UI events
76
+ */
77
+ _bindEvents(): void {
78
+ // Search input
79
+ const searchInput = getElementByIdOrNull<HTMLInputElement>('skills-search');
80
+ if (searchInput) {
81
+ searchInput.addEventListener('input', (e: Event) => {
82
+ clearTimeout(this._searchTimer);
83
+ this._searchTimer = setTimeout(() => {
84
+ const target = e.target;
85
+ if (!(target instanceof HTMLInputElement)) {
86
+ return;
87
+ }
88
+ this.searchQuery = target.value.trim();
89
+ this.render();
90
+ }, 300);
91
+ });
92
+ }
93
+
94
+ // URL install
95
+ const urlBtn = getElementByIdOrNull<HTMLButtonElement>('skills-url-install-btn');
96
+ if (urlBtn) {
97
+ urlBtn.addEventListener('click', () => {
98
+ const input = getElementByIdOrNull<HTMLInputElement>('skills-url-input');
99
+ const url = input?.value?.trim();
100
+ if (url) {
101
+ this.installFromUrl(url);
102
+ }
103
+ });
104
+ }
105
+ const urlInput = getElementByIdOrNull<HTMLInputElement>('skills-url-input');
106
+ if (urlInput) {
107
+ urlInput.addEventListener('keydown', (e: KeyboardEvent) => {
108
+ if (e.key === 'Enter') {
109
+ const target = e.target;
110
+ if (!(target instanceof HTMLInputElement)) {
111
+ return;
112
+ }
113
+ const url = target.value.trim();
114
+ if (url) {
115
+ this.installFromUrl(url);
116
+ }
117
+ }
118
+ });
119
+ }
120
+
121
+ // Filter buttons
122
+ const filterBar = getElementByIdOrNull<HTMLElement>('skills-filter-bar');
123
+ if (filterBar) {
124
+ filterBar.addEventListener('click', (e: MouseEvent) => {
125
+ const target = e.target;
126
+ const btn =
127
+ target instanceof HTMLElement ? target.closest<HTMLElement>('[data-filter]') : null;
128
+ if (!btn) {
129
+ return;
130
+ }
131
+ const filter = btn.dataset.filter;
132
+ if (!filter) {
133
+ return;
134
+ }
135
+ this.currentFilter = filter;
136
+ // Update active state
137
+ filterBar.querySelectorAll<HTMLElement>('[data-filter]').forEach((b) => {
138
+ b.classList.toggle('bg-yellow-400', b.dataset.filter === this.currentFilter);
139
+ b.classList.toggle('text-gray-900', b.dataset.filter === this.currentFilter);
140
+ b.classList.toggle('bg-gray-700', b.dataset.filter !== this.currentFilter);
141
+ b.classList.toggle('text-gray-300', b.dataset.filter !== this.currentFilter);
142
+ });
143
+ this.render();
144
+ });
145
+ }
146
+ },
147
+
148
+ /**
149
+ * Render the full skills view
150
+ */
151
+ render(): void {
152
+ const container = getElementByIdOrNull<HTMLElement>('skills-content');
153
+ if (!container) {
154
+ return;
155
+ }
156
+
157
+ const installed = this._filterSkills(this.installed);
158
+ const available = this._filterSkills(this.catalog);
159
+
160
+ container.innerHTML = `
161
+ ${
162
+ installed.length > 0
163
+ ? `
164
+ <div class="mb-6">
165
+ <h3 class="text-sm font-semibold text-gray-400 uppercase tracking-wider mb-3">
166
+ Installed (${installed.length})
167
+ </h3>
168
+ <div class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-3">
169
+ ${installed.map((s) => this._renderCard(s, true)).join('')}
170
+ </div>
171
+ </div>
172
+ `
173
+ : ''
174
+ }
175
+
176
+ <div class="mb-6">
177
+ <h3 class="text-sm font-semibold text-gray-400 uppercase tracking-wider mb-3">
178
+ Available (${available.length})
179
+ </h3>
180
+ ${
181
+ available.length > 0
182
+ ? `
183
+ <div class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-3">
184
+ ${available.map((s) => this._renderCard(s, false)).join('')}
185
+ </div>
186
+ `
187
+ : `
188
+ <p class="text-gray-500 text-sm">
189
+ ${this.searchQuery ? 'No skills match your search.' : 'Loading catalog...'}
190
+ </p>
191
+ `
192
+ }
193
+ </div>
194
+ `;
195
+
196
+ // Bind card actions
197
+ container.querySelectorAll<HTMLButtonElement>('[data-action]').forEach((btn) => {
198
+ btn.addEventListener('click', (e: MouseEvent) => {
199
+ e.stopPropagation();
200
+ const action = btn.dataset.action;
201
+ const id = btn.dataset.id;
202
+ const source = btn.dataset.source;
203
+ if (!action || !id || !source) {
204
+ return;
205
+ }
206
+ if (action === 'install') {
207
+ this.install(source, id);
208
+ } else if (action === 'uninstall') {
209
+ this.uninstall(source, id);
210
+ } else if (action === 'toggle') {
211
+ this.toggle(source, id, btn.dataset.enabled !== 'true');
212
+ }
213
+ });
214
+ });
215
+
216
+ // Bind card click for detail
217
+ container.querySelectorAll<HTMLElement>('[data-skill-card]').forEach((card) => {
218
+ card.addEventListener('click', () => {
219
+ const source = card.dataset.source;
220
+ const id = card.dataset.id;
221
+ if (!source || !id) {
222
+ return;
223
+ }
224
+ this.showDetail(source, id);
225
+ });
226
+ });
227
+ },
228
+
229
+ /**
230
+ * Filter skills by current filter + search query
231
+ */
232
+ _filterSkills(skills: SkillItem[]): SkillItem[] {
233
+ let filtered = skills;
234
+
235
+ // Source filter
236
+ if (this.currentFilter !== 'all' && this.currentFilter !== 'installed') {
237
+ filtered = filtered.filter((s) => s.source === this.currentFilter);
238
+ }
239
+
240
+ // Search filter
241
+ if (this.searchQuery) {
242
+ const q = this.searchQuery.toLowerCase();
243
+ filtered = filtered.filter(
244
+ (s) =>
245
+ s.name.toLowerCase().includes(q) ||
246
+ (s.description || '').toLowerCase().includes(q) ||
247
+ s.id.toLowerCase().includes(q)
248
+ );
249
+ }
250
+
251
+ return filtered;
252
+ },
253
+
254
+ /**
255
+ * Render a single skill card
256
+ */
257
+ _renderCard(skill: SkillItem, isInstalled: boolean): string {
258
+ const sourceColors = {
259
+ mama: 'bg-yellow-900/30 text-yellow-400',
260
+ cowork: 'bg-blue-900/30 text-blue-400',
261
+ external: 'bg-purple-900/30 text-purple-400',
262
+ };
263
+ const badgeClass = sourceColors[skill.source] || 'bg-gray-700 text-gray-400';
264
+ const enabledClass = skill.enabled !== false ? 'border-green-500/30' : 'border-gray-700';
265
+
266
+ return `
267
+ <div class="bg-gray-800 rounded-lg border ${enabledClass} p-3 cursor-pointer
268
+ hover:border-yellow-500/50 transition-colors"
269
+ data-skill-card data-id="${this._escapeHtml(skill.id)}" data-source="${this._escapeHtml(skill.source)}">
270
+ <div class="flex items-start justify-between mb-2">
271
+ <h4 class="font-medium text-sm text-white truncate flex-1">${this._escapeHtml(skill.name)}</h4>
272
+ <span class="text-xs px-1.5 py-0.5 rounded ${badgeClass} ml-2 whitespace-nowrap">
273
+ ${this._escapeHtml(skill.source)}
274
+ </span>
275
+ </div>
276
+ <p class="text-xs text-gray-400 line-clamp-2 mb-3">${this._escapeHtml(skill.description || '')}</p>
277
+ <div class="flex items-center justify-between">
278
+ ${
279
+ isInstalled
280
+ ? `
281
+ <button data-action="toggle" data-id="${this._escapeHtml(skill.id)}" data-source="${this._escapeHtml(skill.source)}"
282
+ data-enabled="${skill.enabled !== false}"
283
+ class="text-xs px-2 py-1 rounded ${skill.enabled !== false ? 'bg-green-900/30 text-green-400' : 'bg-gray-700 text-gray-400'}">
284
+ ${skill.enabled !== false ? 'Enabled' : 'Disabled'}
285
+ </button>
286
+ <button data-action="uninstall" data-id="${this._escapeHtml(skill.id)}" data-source="${this._escapeHtml(skill.source)}"
287
+ class="text-xs px-2 py-1 rounded bg-red-900/30 text-red-400 hover:bg-red-900/50">
288
+ Remove
289
+ </button>
290
+ `
291
+ : `
292
+ <span></span>
293
+ <button data-action="install" data-id="${this._escapeHtml(skill.id)}" data-source="${this._escapeHtml(skill.source)}"
294
+ class="text-xs px-2 py-1 rounded bg-yellow-900/30 text-yellow-400 hover:bg-yellow-900/50">
295
+ Install
296
+ </button>
297
+ `
298
+ }
299
+ </div>
300
+ </div>
301
+ `;
302
+ },
303
+
304
+ /**
305
+ * Install a skill
306
+ */
307
+ async install(source: string, name: string): Promise<void> {
308
+ try {
309
+ const btn = document.querySelector<HTMLButtonElement>(
310
+ `[data-action="install"][data-id="${CSS.escape(name)}"][data-source="${CSS.escape(source)}"]`
311
+ );
312
+ if (btn) {
313
+ btn.textContent = 'Installing...';
314
+ btn.disabled = true;
315
+ }
316
+ await API.installSkill(source, name);
317
+ await this.loadSkills();
318
+ this.render();
319
+ } catch (error) {
320
+ const message = error instanceof Error ? error.message : String(error);
321
+ logger.error('Install failed:', message);
322
+ alert(`Failed to install ${name}: ${message}`);
323
+ this.render();
324
+ }
325
+ },
326
+
327
+ /**
328
+ * Install from GitHub URL
329
+ */
330
+ async installFromUrl(url: string): Promise<void> {
331
+ const btn = getElementByIdOrNull<HTMLButtonElement>('skills-url-install-btn');
332
+ const input = getElementByIdOrNull<HTMLInputElement>('skills-url-input');
333
+ try {
334
+ if (btn) {
335
+ btn.textContent = 'Installing...';
336
+ btn.disabled = true;
337
+ }
338
+ await API.installSkillFromUrl(url);
339
+ if (input) {
340
+ input.value = '';
341
+ }
342
+ await this.loadSkills();
343
+ this.render();
344
+ } catch (error) {
345
+ const message = error instanceof Error ? error.message : String(error);
346
+ logger.error('URL install failed:', message);
347
+ alert(`Failed to install from URL: ${message}`);
348
+ } finally {
349
+ if (btn) {
350
+ btn.textContent = 'Install URL';
351
+ btn.disabled = false;
352
+ }
353
+ }
354
+ },
355
+
356
+ /**
357
+ * Uninstall a skill
358
+ */
359
+ async uninstall(source: string, name: string): Promise<void> {
360
+ if (!confirm(`Remove skill "${name}"?`)) {
361
+ return;
362
+ }
363
+
364
+ try {
365
+ await API.uninstallSkill(name, source);
366
+ await this.loadSkills();
367
+ this.render();
368
+ } catch (error) {
369
+ logger.error('Uninstall failed:', error instanceof Error ? error.message : String(error));
370
+ }
371
+ },
372
+
373
+ /**
374
+ * Toggle skill enabled/disabled
375
+ */
376
+ async toggle(source: string, name: string, enabled: boolean): Promise<void> {
377
+ try {
378
+ await API.toggleSkill(name, enabled, source);
379
+ // Update local state
380
+ const skill = this.installed.find((s) => s.id === name && s.source === source);
381
+ if (skill) {
382
+ skill.enabled = enabled;
383
+ }
384
+ this.render();
385
+ } catch (error) {
386
+ logger.error('Toggle failed:', error instanceof Error ? error.message : String(error));
387
+ }
388
+ },
389
+
390
+ /**
391
+ * Show skill detail modal
392
+ */
393
+ async showDetail(source: string, name: string): Promise<void> {
394
+ const modal = getElementByIdOrNull<HTMLElement>('skill-detail-modal');
395
+ const modalContent = getElementByIdOrNull<HTMLElement>('skill-detail-content');
396
+ if (!modal || !modalContent) {
397
+ return;
398
+ }
399
+
400
+ modalContent.innerHTML = '<p class="text-gray-400">Loading...</p>';
401
+ modal.classList.remove('hidden');
402
+
403
+ try {
404
+ const { content } = await API.getSkillContent(name, source);
405
+ modalContent.innerHTML = `
406
+ <div class="flex items-center justify-between mb-4">
407
+ <h2 class="text-lg font-bold text-white">${this._escapeHtml(name)}</h2>
408
+ <span class="text-xs px-2 py-1 rounded bg-gray-700 text-gray-400">${this._escapeHtml(source)}</span>
409
+ </div>
410
+ <div class="prose prose-invert prose-sm max-w-none">
411
+ ${this._renderMarkdown(content)}
412
+ </div>
413
+ `;
414
+ } catch (error) {
415
+ const message = error instanceof Error ? error.message : String(error);
416
+ modalContent.innerHTML = `<p class="text-red-400">Failed to load: ${this._escapeHtml(message)}</p>`;
417
+ }
418
+ },
419
+
420
+ /**
421
+ * Close detail modal
422
+ */
423
+ closeDetail(): void {
424
+ const modal = getElementByIdOrNull<HTMLElement>('skill-detail-modal');
425
+ if (modal) {
426
+ modal.classList.add('hidden');
427
+ }
428
+ },
429
+
430
+ /**
431
+ * Escape HTML special characters
432
+ */
433
+ _escapeHtml(unsafe: unknown): string {
434
+ if (!unsafe) {
435
+ return '';
436
+ }
437
+ return unsafe
438
+ .toString()
439
+ .replace(/&/g, '&amp;')
440
+ .replace(/</g, '&lt;')
441
+ .replace(/>/g, '&gt;')
442
+ .replace(/"/g, '&quot;')
443
+ .replace(/'/g, '&#039;');
444
+ },
445
+
446
+ /**
447
+ * Render markdown to HTML using marked.js, with sanitization
448
+ */
449
+ _renderMarkdown(md: string): string {
450
+ if (!md) {
451
+ return '';
452
+ }
453
+ // Remove frontmatter
454
+ md = md.replace(/^---\n[\s\S]*?\n---\n/, '');
455
+ return renderSafeMarkdown(md);
456
+ },
457
+ };
@@ -0,0 +1,168 @@
1
+ /* eslint-disable @typescript-eslint/no-explicit-any */
2
+ export {};
3
+
4
+ declare global {
5
+ interface SpeechRecognitionConstructor {
6
+ new (): SpeechRecognition;
7
+ }
8
+
9
+ interface SpeechRecognitionAlternative {
10
+ transcript: string;
11
+ confidence: number;
12
+ }
13
+
14
+ interface SpeechRecognitionResult {
15
+ length: number;
16
+ isFinal: boolean;
17
+ item(index: number): SpeechRecognitionAlternative;
18
+ [index: number]: SpeechRecognitionAlternative;
19
+ }
20
+
21
+ interface SpeechRecognitionResultList {
22
+ length: number;
23
+ item(index: number): SpeechRecognitionResult;
24
+ [index: number]: SpeechRecognitionResult;
25
+ }
26
+
27
+ interface SpeechRecognitionErrorEvent extends Event {
28
+ error: string;
29
+ }
30
+
31
+ interface SpeechRecognitionEvent extends Event {
32
+ resultIndex: number;
33
+ results: SpeechRecognitionResultList;
34
+ }
35
+
36
+ interface SpeechRecognition extends EventTarget {
37
+ lang: string;
38
+ continuous: boolean;
39
+ interimResults: boolean;
40
+ maxAlternatives: number;
41
+ onresult: ((event: SpeechRecognitionEvent) => void) | null;
42
+ onend: (() => void) | null;
43
+ onerror: ((event: SpeechRecognitionErrorEvent) => void) | null;
44
+ start(): void;
45
+ stop(): void;
46
+ }
47
+
48
+ interface VisNodeRecord {
49
+ id?: string | number;
50
+ from?: string | number;
51
+ to?: string | number;
52
+ font?: {
53
+ color?: string;
54
+ [key: string]: unknown;
55
+ };
56
+ color?: unknown;
57
+ label?: string;
58
+ title?: string;
59
+ size?: number;
60
+ borderWidth?: number;
61
+ data?: unknown;
62
+ hidden?: boolean;
63
+ opacity?: number;
64
+ [key: string]: unknown;
65
+ }
66
+
67
+ interface VisDataSet<T extends VisNodeRecord> {
68
+ get(): T[];
69
+ update(item: Partial<T> | Partial<T>[]): void;
70
+ add(items: T | T[]): void;
71
+ clear?(): void;
72
+ remove?(id: string | number | Array<string | number>): void;
73
+ }
74
+
75
+ interface VisNetworkDataContext {
76
+ nodes: VisDataSet<VisNodeRecord>;
77
+ edges: VisDataSet<VisNodeRecord>;
78
+ }
79
+
80
+ interface VisNetwork {
81
+ on(
82
+ event: 'click' | 'stabilized',
83
+ handler: (params: { nodes: Array<string | number> }) => void
84
+ ): void;
85
+ body: {
86
+ data: VisNetworkDataContext;
87
+ };
88
+ focus(nodeId: string | number, options?: unknown): void;
89
+ selectNodes(ids: Array<string | number>): void;
90
+ destroy?: () => void;
91
+ }
92
+
93
+ interface VisConstructor {
94
+ DataSet: new <T extends VisNodeRecord>(items?: T[]) => VisDataSet<T>;
95
+ DataSet<T extends VisNodeRecord>(items?: T[]): VisDataSet<T>;
96
+ Network: new (
97
+ container: HTMLElement,
98
+ data: Record<string, unknown>,
99
+ options: unknown
100
+ ) => VisNetwork;
101
+ }
102
+
103
+ const vis: VisConstructor;
104
+ const marked: {
105
+ parse(
106
+ markdown: string,
107
+ options?: {
108
+ mangle?: boolean;
109
+ headerIds?: boolean;
110
+ sanitize?: boolean;
111
+ }
112
+ ): string;
113
+ };
114
+ interface DOMPurifyConfig {
115
+ [key: string]: unknown;
116
+ ALLOWED_TAGS?: string[];
117
+ ALLOWED_ATTR?: string[];
118
+ ADD_TAGS?: string[];
119
+ ADD_ATTR?: string[];
120
+ }
121
+ const DOMPurify: {
122
+ sanitize(dirty: string, options?: DOMPurifyConfig): string;
123
+ };
124
+ const lucide: {
125
+ createIcons(config?: unknown): void;
126
+ };
127
+
128
+ interface Window {
129
+ switchTab?: (tab: string) => void;
130
+ chatModule?: {
131
+ toggleToolCard: (toolId: string) => void;
132
+ };
133
+ graphModule?: {
134
+ navigateToNode: (nodeId: string) => void;
135
+ };
136
+ memoryModule?: {
137
+ toggleCard: (index: number) => void;
138
+ searchWithQuery: (query: string) => Promise<void>;
139
+ showSaveFormWithText: (text: string) => void;
140
+ showSaveForm: () => void;
141
+ };
142
+ settingsModule?: {
143
+ init?: () => Promise<void>;
144
+ addCronJob?: () => Promise<void>;
145
+ resetForm?: () => void;
146
+ saveAndRestart?: () => Promise<void>;
147
+ toggleAgent: (agentId: string, enabled: boolean) => Promise<void>;
148
+ onAgentBackendChange: (agentId: string) => void;
149
+ saveAgentConfig: (agentId: string) => Promise<void>;
150
+ toggleAllGateway?: (checked: boolean) => void;
151
+ toggleAllMCP?: (checked: boolean) => void;
152
+ toggleCronJob?: (id: string, enabled: boolean) => Promise<void>;
153
+ deleteCronJob?: (id: string) => Promise<void>;
154
+ };
155
+ skillsModule?: {
156
+ closeDetail?: () => void;
157
+ install?: (source: string, name: string) => Promise<void>;
158
+ uninstall?: (source: string, name: string) => Promise<void>;
159
+ toggle?: (source: string, name: string, enabled: boolean) => Promise<void>;
160
+ init?: () => Promise<void>;
161
+ loadSkills?: () => Promise<void>;
162
+ render?: () => void;
163
+ };
164
+ SpeechRecognition?: SpeechRecognitionConstructor;
165
+ webkitSpeechRecognition?: SpeechRecognitionConstructor;
166
+ lucideConfig?: unknown;
167
+ }
168
+ }