claudeck 1.0.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 (157) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +233 -0
  3. package/cli.js +2 -0
  4. package/config/agent-chains.json +16 -0
  5. package/config/agent-dags.json +16 -0
  6. package/config/agents.json +46 -0
  7. package/config/bot-prompt.json +3 -0
  8. package/config/folders.json +66 -0
  9. package/config/prompts.json +92 -0
  10. package/config/repos.json +86 -0
  11. package/config/telegram-config.json +17 -0
  12. package/config/workflows.json +90 -0
  13. package/db.js +1198 -0
  14. package/package.json +55 -0
  15. package/plugins/claude-editor/client.css +171 -0
  16. package/plugins/claude-editor/client.js +183 -0
  17. package/plugins/event-stream/client.css +207 -0
  18. package/plugins/event-stream/client.js +271 -0
  19. package/plugins/linear/client.css +345 -0
  20. package/plugins/linear/client.js +380 -0
  21. package/plugins/linear/config.json +5 -0
  22. package/plugins/linear/server.js +312 -0
  23. package/plugins/repos/client.css +549 -0
  24. package/plugins/repos/client.js +663 -0
  25. package/plugins/repos/server.js +232 -0
  26. package/plugins/sudoku/client.css +196 -0
  27. package/plugins/sudoku/client.js +329 -0
  28. package/plugins/tasks/client.css +414 -0
  29. package/plugins/tasks/client.js +394 -0
  30. package/plugins/tasks/server.js +116 -0
  31. package/plugins/tic-tac-toe/client.css +167 -0
  32. package/plugins/tic-tac-toe/client.js +241 -0
  33. package/public/css/core/components.css +232 -0
  34. package/public/css/core/layout.css +330 -0
  35. package/public/css/core/print.css +18 -0
  36. package/public/css/core/reset.css +36 -0
  37. package/public/css/core/responsive.css +378 -0
  38. package/public/css/core/theme.css +116 -0
  39. package/public/css/core/variables.css +93 -0
  40. package/public/css/features/agent-monitor.css +297 -0
  41. package/public/css/features/agent-sidebar.css +525 -0
  42. package/public/css/features/agents.css +996 -0
  43. package/public/css/features/analytics.css +181 -0
  44. package/public/css/features/background-sessions.css +321 -0
  45. package/public/css/features/cost-dashboard.css +168 -0
  46. package/public/css/features/home.css +313 -0
  47. package/public/css/features/retro-terminal.css +88 -0
  48. package/public/css/features/telegram.css +127 -0
  49. package/public/css/features/tour.css +148 -0
  50. package/public/css/features/voice-input.css +60 -0
  51. package/public/css/features/welcome.css +241 -0
  52. package/public/css/panels/assistant-bot.css +442 -0
  53. package/public/css/panels/dev-docs.css +292 -0
  54. package/public/css/panels/file-explorer.css +322 -0
  55. package/public/css/panels/git-panel.css +221 -0
  56. package/public/css/panels/mcp-manager.css +199 -0
  57. package/public/css/panels/tips-feed.css +353 -0
  58. package/public/css/ui/commands.css +273 -0
  59. package/public/css/ui/context-gauge.css +76 -0
  60. package/public/css/ui/file-picker.css +69 -0
  61. package/public/css/ui/image-attachments.css +106 -0
  62. package/public/css/ui/messages.css +884 -0
  63. package/public/css/ui/modals.css +122 -0
  64. package/public/css/ui/parallel.css +217 -0
  65. package/public/css/ui/permissions.css +110 -0
  66. package/public/css/ui/right-panel.css +481 -0
  67. package/public/css/ui/sessions.css +689 -0
  68. package/public/css/ui/status-bar.css +425 -0
  69. package/public/css/ui/toolbox.css +206 -0
  70. package/public/data/tips.json +218 -0
  71. package/public/icons/favicon.png +0 -0
  72. package/public/icons/icon-192.png +0 -0
  73. package/public/icons/icon-512.png +0 -0
  74. package/public/icons/whaly.png +0 -0
  75. package/public/index.html +1140 -0
  76. package/public/js/core/api.js +591 -0
  77. package/public/js/core/constants.js +3 -0
  78. package/public/js/core/dom.js +270 -0
  79. package/public/js/core/events.js +10 -0
  80. package/public/js/core/plugin-loader.js +153 -0
  81. package/public/js/core/store.js +39 -0
  82. package/public/js/core/utils.js +25 -0
  83. package/public/js/core/ws.js +64 -0
  84. package/public/js/features/agent-monitor.js +222 -0
  85. package/public/js/features/agents.js +1209 -0
  86. package/public/js/features/analytics.js +397 -0
  87. package/public/js/features/attachments.js +251 -0
  88. package/public/js/features/background-sessions.js +475 -0
  89. package/public/js/features/chat.js +589 -0
  90. package/public/js/features/cost-dashboard.js +152 -0
  91. package/public/js/features/dag-editor.js +399 -0
  92. package/public/js/features/easter-egg.js +46 -0
  93. package/public/js/features/home.js +270 -0
  94. package/public/js/features/projects.js +372 -0
  95. package/public/js/features/prompts.js +228 -0
  96. package/public/js/features/sessions.js +332 -0
  97. package/public/js/features/telegram.js +131 -0
  98. package/public/js/features/tour.js +210 -0
  99. package/public/js/features/voice-input.js +185 -0
  100. package/public/js/features/welcome.js +43 -0
  101. package/public/js/features/workflows.js +277 -0
  102. package/public/js/main.js +51 -0
  103. package/public/js/panels/assistant-bot.js +445 -0
  104. package/public/js/panels/dev-docs.js +380 -0
  105. package/public/js/panels/file-explorer.js +486 -0
  106. package/public/js/panels/git-panel.js +285 -0
  107. package/public/js/panels/mcp-manager.js +311 -0
  108. package/public/js/panels/tips-feed.js +303 -0
  109. package/public/js/ui/commands.js +114 -0
  110. package/public/js/ui/context-gauge.js +100 -0
  111. package/public/js/ui/diff.js +124 -0
  112. package/public/js/ui/disabled-tools.js +36 -0
  113. package/public/js/ui/export.js +74 -0
  114. package/public/js/ui/formatting.js +206 -0
  115. package/public/js/ui/header-dropdowns.js +72 -0
  116. package/public/js/ui/input-meta.js +71 -0
  117. package/public/js/ui/max-turns.js +21 -0
  118. package/public/js/ui/messages.js +387 -0
  119. package/public/js/ui/model-selector.js +20 -0
  120. package/public/js/ui/notifications.js +232 -0
  121. package/public/js/ui/parallel.js +176 -0
  122. package/public/js/ui/permissions.js +168 -0
  123. package/public/js/ui/right-panel.js +173 -0
  124. package/public/js/ui/shortcuts.js +143 -0
  125. package/public/js/ui/sidebar-toggle.js +29 -0
  126. package/public/js/ui/status-bar.js +172 -0
  127. package/public/js/ui/tab-sdk.js +623 -0
  128. package/public/js/ui/theme.js +38 -0
  129. package/public/manifest.json +13 -0
  130. package/public/offline.html +190 -0
  131. package/public/style.css +42 -0
  132. package/public/sw.js +91 -0
  133. package/server/agent-loop.js +385 -0
  134. package/server/dag-executor.js +265 -0
  135. package/server/orchestrator.js +514 -0
  136. package/server/paths.js +61 -0
  137. package/server/plugin-mount.js +56 -0
  138. package/server/push-sender.js +31 -0
  139. package/server/routes/agents.js +294 -0
  140. package/server/routes/bot.js +45 -0
  141. package/server/routes/exec.js +35 -0
  142. package/server/routes/files.js +218 -0
  143. package/server/routes/mcp.js +82 -0
  144. package/server/routes/messages.js +36 -0
  145. package/server/routes/notifications.js +37 -0
  146. package/server/routes/projects.js +207 -0
  147. package/server/routes/prompts.js +53 -0
  148. package/server/routes/sessions.js +103 -0
  149. package/server/routes/stats.js +143 -0
  150. package/server/routes/telegram.js +71 -0
  151. package/server/routes/tips.js +135 -0
  152. package/server/routes/workflows.js +81 -0
  153. package/server/summarizer.js +55 -0
  154. package/server/telegram-poller.js +205 -0
  155. package/server/telegram-sender.js +304 -0
  156. package/server/ws-handler.js +926 -0
  157. package/server.js +179 -0
@@ -0,0 +1,445 @@
1
+ // Floating assistant bot — independent chat bubble with custom system prompt
2
+ import { BOT_CHAT_ID } from '../core/constants.js';
3
+ import { on } from '../core/events.js';
4
+ import { getState } from '../core/store.js';
5
+ import { renderMarkdown, highlightCodeBlocks, addCopyButtons } from '../ui/formatting.js';
6
+ import * as api from '../core/api.js';
7
+ import { getSelectedModel } from '../ui/model-selector.js';
8
+ import { $ } from '../core/dom.js';
9
+
10
+ const SESSIONS_KEY = 'claudeck-bot-sessions';
11
+ let panel, messagesDiv, inputEl, sendBtn, stopBtn, settingsOverlay, promptTextarea;
12
+ let freeBotSessionId = null;
13
+ let isStreaming = false;
14
+ let currentAssistantEl = null;
15
+ let cachedSystemPrompt = null;
16
+
17
+ // ── Session management ──────────────────────────────────
18
+
19
+ function getBotSessions() {
20
+ try {
21
+ return JSON.parse(localStorage.getItem(SESSIONS_KEY) || '{}');
22
+ } catch { return {}; }
23
+ }
24
+
25
+ function setBotSession(projectPath, sessionId) {
26
+ const sessions = getBotSessions();
27
+ sessions[projectPath] = sessionId;
28
+ localStorage.setItem(SESSIONS_KEY, JSON.stringify(sessions));
29
+ }
30
+
31
+ function getCurrentProject() {
32
+ return $.projectSelect?.value || '';
33
+ }
34
+
35
+
36
+ // ── DOM creation ────────────────────────────────────────
37
+
38
+ function createBotDOM() {
39
+ // Bubble
40
+ const bubble = document.createElement('button');
41
+ bubble.className = 'bot-bubble';
42
+ bubble.innerHTML = '<img src="/icons/whaly.png" alt="Whaly" width="32" height="32">';
43
+ bubble.title = 'Assistant Bot';
44
+ bubble.addEventListener('click', togglePanel);
45
+
46
+ // Panel
47
+ panel = document.createElement('div');
48
+ panel.className = 'bot-panel';
49
+ panel.innerHTML = `
50
+ <div class="bot-header">
51
+ <span class="bot-header-title">Assistant Bot</span>
52
+ <button class="bot-header-btn bot-new-btn" title="New chat">&#x21bb;</button>
53
+ <button class="bot-header-btn bot-settings-btn" title="Settings">&#x2699;</button>
54
+ <button class="bot-header-btn bot-close-btn" title="Close">&times;</button>
55
+ </div>
56
+ <div class="bot-messages"></div>
57
+ <div class="bot-input-bar">
58
+ <textarea class="bot-input" placeholder="Ask the assistant..." rows="1"></textarea>
59
+ <button class="bot-send-btn">Send</button>
60
+ <button class="bot-stop-btn">Stop</button>
61
+ </div>
62
+ <div class="bot-settings-overlay">
63
+ <div class="bot-settings-header">
64
+ <span>System Prompt</span>
65
+ <button class="bot-header-btn bot-settings-close">&times;</button>
66
+ </div>
67
+ <div class="bot-settings-body">
68
+ <label>Customize the assistant's behavior:</label>
69
+ <textarea class="bot-prompt-textarea" placeholder="Enter system prompt..."></textarea>
70
+ </div>
71
+ <div class="bot-settings-actions">
72
+ <button class="bot-settings-cancel">Cancel</button>
73
+ <button class="bot-settings-save primary">Save</button>
74
+ </div>
75
+ </div>
76
+ `;
77
+
78
+ document.body.appendChild(bubble);
79
+ document.body.appendChild(panel);
80
+
81
+ // Cache DOM references
82
+ messagesDiv = panel.querySelector('.bot-messages');
83
+ inputEl = panel.querySelector('.bot-input');
84
+ sendBtn = panel.querySelector('.bot-send-btn');
85
+ stopBtn = panel.querySelector('.bot-stop-btn');
86
+ settingsOverlay = panel.querySelector('.bot-settings-overlay');
87
+ promptTextarea = panel.querySelector('.bot-prompt-textarea');
88
+ // Event listeners
89
+ panel.querySelector('.bot-close-btn').addEventListener('click', closePanel);
90
+ panel.querySelector('.bot-new-btn').addEventListener('click', newBotSession);
91
+ panel.querySelector('.bot-settings-btn').addEventListener('click', openSettings);
92
+ panel.querySelector('.bot-settings-close').addEventListener('click', closeSettings);
93
+ panel.querySelector('.bot-settings-cancel').addEventListener('click', closeSettings);
94
+ panel.querySelector('.bot-settings-save').addEventListener('click', saveSettings);
95
+
96
+ sendBtn.addEventListener('click', sendBotMessage);
97
+ stopBtn.addEventListener('click', stopBotGeneration);
98
+
99
+ inputEl.addEventListener('keydown', (e) => {
100
+ if (e.key === 'Enter' && !e.shiftKey) {
101
+ e.preventDefault();
102
+ sendBotMessage();
103
+ }
104
+ });
105
+
106
+ inputEl.addEventListener('input', () => {
107
+ inputEl.style.height = 'auto';
108
+ inputEl.style.height = Math.min(inputEl.scrollHeight, 100) + 'px';
109
+ });
110
+ }
111
+
112
+ // ── Panel toggle ────────────────────────────────────────
113
+
114
+ function togglePanel() {
115
+ if (panel.classList.contains('open')) {
116
+ closePanel();
117
+ } else {
118
+ openPanel();
119
+ }
120
+ }
121
+
122
+ function openPanel() {
123
+ panel.classList.add('open');
124
+ freeBotSessionId = getBotSessions()['__free__'] || null;
125
+ loadBotHistory();
126
+ inputEl.focus();
127
+ }
128
+
129
+ function closePanel() {
130
+ panel.classList.remove('open');
131
+ closeSettings();
132
+ }
133
+
134
+ // ── System prompt ───────────────────────────────────────
135
+
136
+ async function fetchSystemPrompt() {
137
+ try {
138
+ const data = await fetch('/api/bot/prompt').then(r => r.json());
139
+ cachedSystemPrompt = data.systemPrompt || '';
140
+ return cachedSystemPrompt;
141
+ } catch {
142
+ return cachedSystemPrompt || '';
143
+ }
144
+ }
145
+
146
+ async function openSettings() {
147
+ const prompt = await fetchSystemPrompt();
148
+ promptTextarea.value = prompt;
149
+ settingsOverlay.classList.add('open');
150
+ promptTextarea.focus();
151
+ }
152
+
153
+ function closeSettings() {
154
+ settingsOverlay.classList.remove('open');
155
+ }
156
+
157
+ async function saveSettings() {
158
+ const newPrompt = promptTextarea.value.trim();
159
+ try {
160
+ await fetch('/api/bot/prompt', {
161
+ method: 'PUT',
162
+ headers: { 'Content-Type': 'application/json' },
163
+ body: JSON.stringify({ systemPrompt: newPrompt }),
164
+ });
165
+ cachedSystemPrompt = newPrompt;
166
+ } catch (err) {
167
+ console.error('Failed to save bot prompt:', err);
168
+ }
169
+ closeSettings();
170
+ }
171
+
172
+ // ── Send message ────────────────────────────────────────
173
+
174
+ function getActiveBotSessionId() {
175
+ return freeBotSessionId;
176
+ }
177
+
178
+ async function sendBotMessage() {
179
+ const text = inputEl.value.trim();
180
+ if (!text || isStreaming) return;
181
+
182
+ const ws = getState('ws');
183
+ if (!ws || ws.readyState !== WebSocket.OPEN) return;
184
+
185
+ const cwd = getCurrentProject() || '/tmp';
186
+
187
+ // Ensure we have the system prompt
188
+ if (cachedSystemPrompt === null) {
189
+ await fetchSystemPrompt();
190
+ }
191
+
192
+ // Render user message
193
+ appendMessage('user', text);
194
+ inputEl.value = '';
195
+ inputEl.style.height = 'auto';
196
+
197
+ isStreaming = true;
198
+ sendBtn.style.display = 'none';
199
+ stopBtn.classList.add('visible');
200
+ currentAssistantEl = null;
201
+
202
+ const model = getSelectedModel();
203
+ const activeSid = getActiveBotSessionId();
204
+
205
+ const payload = {
206
+ type: 'chat',
207
+ message: text,
208
+ chatId: BOT_CHAT_ID,
209
+ systemPrompt: cachedSystemPrompt || undefined,
210
+ };
211
+
212
+ payload.cwd = cwd;
213
+ payload.sessionId = activeSid;
214
+ payload.projectName = 'Assistant Bot';
215
+ payload.permissionMode = 'bypass';
216
+
217
+ if (model) payload.model = model;
218
+
219
+ ws.send(JSON.stringify(payload));
220
+ showBotThinking('Thinking...');
221
+ }
222
+
223
+ function stopBotGeneration() {
224
+ const ws = getState('ws');
225
+ if (ws && ws.readyState === WebSocket.OPEN) {
226
+ ws.send(JSON.stringify({ type: 'abort', chatId: BOT_CHAT_ID }));
227
+ }
228
+ }
229
+
230
+ // ── Message rendering ───────────────────────────────────
231
+
232
+ // Merge adjacent <ol> tags split by <br> so list numbering is continuous
233
+ function mergeAdjacentLists(html) {
234
+ return html
235
+ .replace(/<\/ol>(?:\s*<br>\s*)*<ol class="md-list md-ol">/g, '')
236
+ .replace(/<\/ul>(?:\s*<br>\s*)*<ul class="md-list md-ul">/g, '');
237
+ }
238
+
239
+ function renderBotMarkdown(text) {
240
+ return mergeAdjacentLists(renderMarkdown(text));
241
+ }
242
+
243
+ function showBotWhaly() {
244
+ if (!messagesDiv) return;
245
+ removeBotWhaly();
246
+ const el = document.createElement('div');
247
+ el.className = 'whaly-placeholder';
248
+ el.innerHTML = `<img src="/icons/whaly.png" alt="Whaly" draggable="false"><div class="whaly-text">~ ask the assistant anything ~</div>`;
249
+ messagesDiv.appendChild(el);
250
+ }
251
+
252
+ function removeBotWhaly() {
253
+ if (!messagesDiv) return;
254
+ const el = messagesDiv.querySelector('.whaly-placeholder');
255
+ if (el) el.remove();
256
+ }
257
+
258
+ function appendMessage(role, content) {
259
+ removeBotWhaly();
260
+ const div = document.createElement('div');
261
+ div.className = `bot-msg ${role}`;
262
+
263
+ if (role === 'assistant') {
264
+ div.innerHTML = renderBotMarkdown(content);
265
+ highlightCodeBlocks(div);
266
+ addCopyButtons(div);
267
+ } else if (role === 'user') {
268
+ div.textContent = content;
269
+ } else if (role === 'error') {
270
+ div.textContent = content;
271
+ }
272
+
273
+ messagesDiv.appendChild(div);
274
+ messagesDiv.scrollTop = messagesDiv.scrollHeight;
275
+ return div;
276
+ }
277
+
278
+ function appendToolIndicator(name, input) {
279
+ const div = document.createElement('div');
280
+ div.className = 'bot-msg tool-indicator';
281
+ const detail = input?.file_path || input?.command?.slice(0, 60) || input?.pattern || '';
282
+ div.textContent = `${name}${detail ? ': ' + detail : ''}`;
283
+ messagesDiv.appendChild(div);
284
+ messagesDiv.scrollTop = messagesDiv.scrollHeight;
285
+ }
286
+
287
+ function appendToolResult(content, isError) {
288
+ const div = document.createElement('div');
289
+ div.className = `bot-msg tool-result-msg${isError ? ' error' : ''}`;
290
+ div.textContent = content?.slice(0, 200) || (isError ? 'Error' : 'Done');
291
+ messagesDiv.appendChild(div);
292
+ messagesDiv.scrollTop = messagesDiv.scrollHeight;
293
+ }
294
+
295
+ function showBotThinking(text) {
296
+ removeBotThinking();
297
+ const el = document.createElement('div');
298
+ el.className = 'bot-thinking';
299
+ el.textContent = text;
300
+ messagesDiv.appendChild(el);
301
+ messagesDiv.scrollTop = messagesDiv.scrollHeight;
302
+ }
303
+
304
+ function removeBotThinking() {
305
+ const t = messagesDiv?.querySelector('.bot-thinking');
306
+ if (t) t.remove();
307
+ }
308
+
309
+ function finishStreaming() {
310
+ isStreaming = false;
311
+ currentAssistantEl = null;
312
+ removeBotThinking();
313
+ sendBtn.style.display = '';
314
+ stopBtn.classList.remove('visible');
315
+ inputEl.focus();
316
+ }
317
+
318
+ // ── WS message handler ─────────────────────────────────
319
+
320
+ function handleBotWsMessage(msg) {
321
+ if (msg.chatId !== BOT_CHAT_ID) return;
322
+
323
+ removeBotThinking();
324
+
325
+ switch (msg.type) {
326
+ case 'session':
327
+ freeBotSessionId = msg.sessionId;
328
+ setBotSession('__free__', freeBotSessionId);
329
+ showBotThinking('Thinking...');
330
+ break;
331
+
332
+ case 'text':
333
+ if (!currentAssistantEl) {
334
+ currentAssistantEl = appendMessage('assistant', msg.text);
335
+ } else {
336
+ // Accumulate text — re-render markdown for the full content
337
+ const prev = currentAssistantEl.dataset.rawText || '';
338
+ const full = prev + msg.text;
339
+ currentAssistantEl.dataset.rawText = full;
340
+ currentAssistantEl.innerHTML = renderBotMarkdown(full);
341
+ highlightCodeBlocks(currentAssistantEl);
342
+ addCopyButtons(currentAssistantEl);
343
+ }
344
+ // Store raw text on first message too
345
+ if (!currentAssistantEl.dataset.rawText) {
346
+ currentAssistantEl.dataset.rawText = msg.text;
347
+ }
348
+ messagesDiv.scrollTop = messagesDiv.scrollHeight;
349
+ break;
350
+
351
+ case 'tool':
352
+ appendToolIndicator(msg.name, msg.input);
353
+ showBotThinking(`Running ${msg.name}...`);
354
+ break;
355
+
356
+ case 'tool_result':
357
+ appendToolResult(msg.content, msg.isError);
358
+ showBotThinking('Thinking...');
359
+ break;
360
+
361
+ case 'result':
362
+ removeBotThinking();
363
+ break;
364
+
365
+ case 'done':
366
+ finishStreaming();
367
+ break;
368
+
369
+ case 'aborted':
370
+ finishStreaming();
371
+ break;
372
+
373
+ case 'error':
374
+ finishStreaming();
375
+ appendMessage('error', msg.error || 'Unknown error');
376
+ break;
377
+
378
+ case 'permission_request':
379
+ // Bot permission requests are handled by the main permission system
380
+ break;
381
+ }
382
+ }
383
+
384
+ // ── Load history ────────────────────────────────────────
385
+
386
+ async function loadBotHistory() {
387
+ const sid = getActiveBotSessionId();
388
+ if (!sid) {
389
+ messagesDiv.innerHTML = '';
390
+ showBotWhaly();
391
+ return;
392
+ }
393
+
394
+ try {
395
+ const messages = await api.fetchMessagesByChatId(sid, BOT_CHAT_ID);
396
+ messagesDiv.innerHTML = '';
397
+
398
+ for (const msg of messages) {
399
+ let data;
400
+ try { data = JSON.parse(msg.content); } catch { continue; }
401
+
402
+ if (msg.role === 'user') {
403
+ appendMessage('user', data.text || '');
404
+ } else if (msg.role === 'assistant') {
405
+ appendMessage('assistant', data.text || '');
406
+ } else if (msg.role === 'tool') {
407
+ appendToolIndicator(data.name, data.input);
408
+ } else if (msg.role === 'tool_result') {
409
+ appendToolResult(data.content, data.isError);
410
+ } else if (msg.role === 'error') {
411
+ appendMessage('error', data.error || 'Error');
412
+ }
413
+ }
414
+ } catch (err) {
415
+ console.error('Failed to load bot history:', err);
416
+ }
417
+ }
418
+
419
+ // ── New session ─────────────────────────────────────────
420
+
421
+ function newBotSession() {
422
+ if (isStreaming) {
423
+ stopBotGeneration();
424
+ finishStreaming();
425
+ }
426
+
427
+ const sessions = getBotSessions();
428
+ freeBotSessionId = null;
429
+ delete sessions['__free__'];
430
+ localStorage.setItem(SESSIONS_KEY, JSON.stringify(sessions));
431
+ messagesDiv.innerHTML = '';
432
+ currentAssistantEl = null;
433
+ showBotWhaly();
434
+ }
435
+
436
+ // ── Init ────────────────────────────────────────────────
437
+
438
+ function init() {
439
+ createBotDOM();
440
+ on('ws:message', handleBotWsMessage);
441
+ fetchSystemPrompt();
442
+ }
443
+
444
+ // Run on import
445
+ init();