@newsails/veil-cli 1.0.1

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 (199) hide show
  1. package/.veil/agents/analyst/AGENT.md +21 -0
  2. package/.veil/agents/analyst/agent.json +23 -0
  3. package/.veil/agents/assistant/AGENT.md +15 -0
  4. package/.veil/agents/assistant/agent.json +19 -0
  5. package/.veil/agents/coder/AGENT.md +18 -0
  6. package/.veil/agents/coder/agent.json +19 -0
  7. package/.veil/agents/hello/AGENT.md +5 -0
  8. package/.veil/agents/hello/agent.json +13 -0
  9. package/.veil/agents/writer/AGENT.md +12 -0
  10. package/.veil/agents/writer/agent.json +17 -0
  11. package/.veil/memory/MEMORY.md +343 -0
  12. package/.veil/memory/agents/analyst/MEMORY.md +55 -0
  13. package/.veil/memory/agents/hello/MEMORY.md +12 -0
  14. package/.veil/runtime.pid +1 -0
  15. package/.veil/settings.json +10 -0
  16. package/.veil-studio/studio.db +0 -0
  17. package/.veil-studio/studio.db-shm +0 -0
  18. package/.veil-studio/studio.db-wal +0 -0
  19. package/PLAN/01-vision.md +26 -0
  20. package/PLAN/02-tech-stack.md +94 -0
  21. package/PLAN/03-agents.md +232 -0
  22. package/PLAN/04-runtime.md +171 -0
  23. package/PLAN/05-tools.md +211 -0
  24. package/PLAN/06-communication.md +243 -0
  25. package/PLAN/07-storage.md +218 -0
  26. package/PLAN/08-api-cli.md +153 -0
  27. package/PLAN/09-permissions.md +108 -0
  28. package/PLAN/10-ably.md +105 -0
  29. package/PLAN/11-file-formats.md +442 -0
  30. package/PLAN/12-folder-structure.md +205 -0
  31. package/PLAN/13-operations.md +212 -0
  32. package/PLAN/README.md +23 -0
  33. package/README.md +128 -0
  34. package/REPORT.md +174 -0
  35. package/TODO.md +45 -0
  36. package/ai-tests/FRONTEND_PROMPT.md +220 -0
  37. package/ai-tests/Research & Planning.md +814 -0
  38. package/ai-tests/prompt-001-basic-api.md +230 -0
  39. package/ai-tests/prompt-002-basic-flows.md +230 -0
  40. package/ai-tests/prompt-003-agent-behaviors.md +220 -0
  41. package/api/middleware.js +60 -0
  42. package/api/routes/agents.js +193 -0
  43. package/api/routes/chat.js +93 -0
  44. package/api/routes/completions.js +122 -0
  45. package/api/routes/daemons.js +80 -0
  46. package/api/routes/memory.js +169 -0
  47. package/api/routes/models.js +40 -0
  48. package/api/routes/remote-methods.js +74 -0
  49. package/api/routes/sessions.js +208 -0
  50. package/api/routes/settings.js +108 -0
  51. package/api/routes/system.js +50 -0
  52. package/api/routes/tasks.js +270 -0
  53. package/api/server.js +120 -0
  54. package/cli/formatter.js +70 -0
  55. package/cli/index.js +443 -0
  56. package/cli/parser.js +113 -0
  57. package/config/config.json +10 -0
  58. package/config/models.json +6826 -0
  59. package/core/agent.js +329 -0
  60. package/core/cancel.js +38 -0
  61. package/core/compaction.js +176 -0
  62. package/core/events.js +13 -0
  63. package/core/loop.js +564 -0
  64. package/core/memory.js +51 -0
  65. package/core/prompt.js +185 -0
  66. package/core/queue.js +96 -0
  67. package/core/registry.js +291 -0
  68. package/core/remote-methods.js +124 -0
  69. package/core/router.js +386 -0
  70. package/core/running-sessions.js +18 -0
  71. package/docs/api/01-system.md +84 -0
  72. package/docs/api/02-agents.md +374 -0
  73. package/docs/api/03-chat.md +269 -0
  74. package/docs/api/04-tasks.md +470 -0
  75. package/docs/api/05-sessions.md +444 -0
  76. package/docs/api/06-daemons.md +142 -0
  77. package/docs/api/07-memory.md +186 -0
  78. package/docs/api/08-settings.md +133 -0
  79. package/docs/api/09-models.md +119 -0
  80. package/docs/api/09-websocket.md +350 -0
  81. package/docs/api/10-completions.md +134 -0
  82. package/docs/api/README.md +116 -0
  83. package/docs/guide/01-quickstart.md +220 -0
  84. package/docs/guide/02-folder-structure.md +185 -0
  85. package/docs/guide/03-configuration.md +252 -0
  86. package/docs/guide/04-agents.md +267 -0
  87. package/docs/guide/05-cli.md +290 -0
  88. package/docs/guide/06-tools.md +643 -0
  89. package/docs/guide/07-permissions.md +236 -0
  90. package/docs/guide/08-memory.md +139 -0
  91. package/docs/guide/09-multi-agent.md +271 -0
  92. package/docs/guide/10-daemons.md +226 -0
  93. package/docs/guide/README.md +53 -0
  94. package/docs/index.html +623 -0
  95. package/examples/README.md +151 -0
  96. package/examples/agents/assistant/AGENT.md +31 -0
  97. package/examples/agents/assistant/SOUL.md +9 -0
  98. package/examples/agents/assistant/agent.json +74 -0
  99. package/examples/agents/hello/AGENT.md +15 -0
  100. package/examples/agents/hello/agent.json +14 -0
  101. package/examples/agents/monitor/AGENT.md +51 -0
  102. package/examples/agents/monitor/agent.json +33 -0
  103. package/examples/agents/monitor/heartbeats/monitor.md +24 -0
  104. package/examples/agents/orchestrator/AGENT.md +70 -0
  105. package/examples/agents/orchestrator/agent.json +30 -0
  106. package/examples/agents/researcher/AGENT.md +52 -0
  107. package/examples/agents/researcher/agent.json +49 -0
  108. package/examples/agents/researcher/skills/web-research.md +28 -0
  109. package/examples/skills/code-review.md +72 -0
  110. package/examples/skills/summarise.md +59 -0
  111. package/examples/skills/web-research.md +42 -0
  112. package/examples/tools/word-count/index.js +27 -0
  113. package/examples/tools/word-count/tool.json +18 -0
  114. package/infrastructure/database.js +563 -0
  115. package/infrastructure/scheduler.js +122 -0
  116. package/llm/client.js +206 -0
  117. package/migrations/001-initial.sql +121 -0
  118. package/migrations/002-debuggability.sql +13 -0
  119. package/migrations/003-drop-orphaned-columns.sql +72 -0
  120. package/migrations/004-session-message-token-fields.sql +78 -0
  121. package/migrations/005-session-thinking.sql +5 -0
  122. package/package.json +30 -0
  123. package/schemas/agent.json +143 -0
  124. package/schemas/settings.json +111 -0
  125. package/scripts/fetch-models.js +93 -0
  126. package/session-debug-scenario.md +248 -0
  127. package/settings/fields.js +52 -0
  128. package/system-prompts/base-core.md +7 -0
  129. package/system-prompts/environment.md +13 -0
  130. package/system-prompts/reminders/anti-drift.md +6 -0
  131. package/system-prompts/reminders/stall-recovery.md +10 -0
  132. package/system-prompts/safety-rules.md +25 -0
  133. package/system-prompts/task-heuristics.md +27 -0
  134. package/test/client.js +71 -0
  135. package/test/integration/01-health.test.js +25 -0
  136. package/test/integration/02-agents.test.js +80 -0
  137. package/test/integration/03-chat-hello.test.js +48 -0
  138. package/test/integration/04-chat-multiturn.test.js +61 -0
  139. package/test/integration/05-chat-writer.test.js +48 -0
  140. package/test/integration/06-task-basic.test.js +68 -0
  141. package/test/integration/07-task-tools.test.js +74 -0
  142. package/test/integration/08-task-code-analysis.test.js +69 -0
  143. package/test/integration/09-memory-analyst.test.js +63 -0
  144. package/test/integration/10-task-advanced.test.js +85 -0
  145. package/test/integration/11-sessions-advanced.test.js +84 -0
  146. package/test/integration/12-assistant-chat-tools.test.js +75 -0
  147. package/test/integration/13-edge-cases.test.js +99 -0
  148. package/test/integration/14-cancel.test.js +62 -0
  149. package/test/integration/15-debug.test.js +106 -0
  150. package/test/integration/16-memory-api.test.js +83 -0
  151. package/test/integration/17-settings-api.test.js +41 -0
  152. package/test/integration/18-tool-search-activation.test.js +119 -0
  153. package/test/results/.gitkeep +0 -0
  154. package/test/runner.js +206 -0
  155. package/test/smoke.js +216 -0
  156. package/tools/agent_message.js +85 -0
  157. package/tools/agent_send.js +80 -0
  158. package/tools/agent_spawn.js +44 -0
  159. package/tools/bash.js +49 -0
  160. package/tools/edit_file.js +41 -0
  161. package/tools/glob.js +64 -0
  162. package/tools/grep.js +82 -0
  163. package/tools/list_dir.js +63 -0
  164. package/tools/log_write.js +31 -0
  165. package/tools/memory_read.js +38 -0
  166. package/tools/memory_search.js +65 -0
  167. package/tools/memory_write.js +42 -0
  168. package/tools/read_file.js +48 -0
  169. package/tools/sleep.js +22 -0
  170. package/tools/task_create.js +41 -0
  171. package/tools/task_respond.js +37 -0
  172. package/tools/task_spawn.js +64 -0
  173. package/tools/task_status.js +39 -0
  174. package/tools/task_subscribe.js +37 -0
  175. package/tools/todo_read.js +26 -0
  176. package/tools/todo_write.js +38 -0
  177. package/tools/tool_activate.js +24 -0
  178. package/tools/tool_search.js +24 -0
  179. package/tools/web_fetch.js +50 -0
  180. package/tools/web_search.js +52 -0
  181. package/tools/write_file.js +28 -0
  182. package/ui/api.js +190 -0
  183. package/ui/app.js +281 -0
  184. package/ui/index.html +382 -0
  185. package/ui/views/agents.js +377 -0
  186. package/ui/views/chat.js +610 -0
  187. package/ui/views/connection.js +96 -0
  188. package/ui/views/daemons.js +129 -0
  189. package/ui/views/feed.js +194 -0
  190. package/ui/views/memory.js +263 -0
  191. package/ui/views/models.js +146 -0
  192. package/ui/views/sessions.js +314 -0
  193. package/ui/views/settings.js +142 -0
  194. package/ui/views/tasks.js +415 -0
  195. package/utils/context.js +49 -0
  196. package/utils/id.js +16 -0
  197. package/utils/models.js +88 -0
  198. package/utils/paths.js +213 -0
  199. package/utils/settings.js +172 -0
package/ui/app.js ADDED
@@ -0,0 +1,281 @@
1
+ 'use strict';
2
+
3
+ window.Veil = window.Veil || {};
4
+ const KB = window.Veil;
5
+
6
+ KB.state = {
7
+ connected: false,
8
+ serverUrl: '',
9
+ secret: '',
10
+ api: null,
11
+ currentAgent: null,
12
+ };
13
+
14
+ /* ── Utilities ──────────────────────────────────────────────────────── */
15
+ KB.utils = {
16
+ esc(s) {
17
+ if (s == null) return '';
18
+ return String(s)
19
+ .replace(/&/g,'&amp;').replace(/</g,'&lt;')
20
+ .replace(/>/g,'&gt;').replace(/"/g,'&quot;');
21
+ },
22
+
23
+ relTime(dateStr) {
24
+ if (!dateStr) return '—';
25
+ const diff = Date.now() - new Date(dateStr).getTime();
26
+ if (isNaN(diff)) return dateStr;
27
+ const s = Math.floor(diff / 1000);
28
+ if (s < 5) return 'just now';
29
+ if (s < 60) return s + 's ago';
30
+ const m = Math.floor(s / 60);
31
+ if (m < 60) return m + 'm ago';
32
+ const h = Math.floor(m / 60);
33
+ if (h < 24) return h + 'h ago';
34
+ const d = Math.floor(h / 24);
35
+ return d + 'd ago';
36
+ },
37
+
38
+ formatMs(ms) {
39
+ if (ms == null || ms < 0) return '—';
40
+ if (ms < 1000) return ms + 'ms';
41
+ if (ms < 60000) return (ms / 1000).toFixed(1) + 's';
42
+ return Math.floor(ms / 60000) + 'm ' + Math.floor((ms % 60000) / 1000) + 's';
43
+ },
44
+
45
+ formatCost(cost) {
46
+ if (cost == null || cost === 0) return '';
47
+ if (cost < 0.001) return '$' + cost.toFixed(6);
48
+ return '$' + cost.toFixed(4);
49
+ },
50
+
51
+ badge(status) {
52
+ return `<span class="badge badge-${KB.utils.esc(status)}">${KB.utils.esc(status)}</span>`;
53
+ },
54
+
55
+ spinner() {
56
+ return `<span class="spin" style="display:inline-block;width:13px;height:13px;border:2px solid var(--border2);border-top-color:var(--accent);border-radius:50%;"></span>`;
57
+ },
58
+
59
+ shortId(id) {
60
+ if (!id) return '—';
61
+ return id.length > 18 ? id.slice(0, 18) + '…' : id;
62
+ },
63
+
64
+ truncate(s, n) {
65
+ if (!s) return '';
66
+ s = String(s);
67
+ return s.length > n ? s.slice(0, n) + '…' : s;
68
+ },
69
+
70
+ toast(msg, type) {
71
+ const c = document.getElementById('toast-container');
72
+ if (!c) return;
73
+ const el = document.createElement('div');
74
+ const [bg, col, bc] =
75
+ type === 'error' ? ['var(--surface)', 'var(--red)', 'rgba(239,68,68,0.3)'] :
76
+ type === 'warn' ? ['var(--surface)', 'var(--yellow)', 'rgba(234,179,8,0.3)'] :
77
+ ['var(--surface)', 'var(--green)', 'rgba(34,197,94,0.3)'];
78
+ el.style.cssText = `background:${bg};color:${col};border:1px solid ${bc};padding:10px 16px;border-radius:10px;font-size:13px;font-weight:500;pointer-events:auto;box-shadow:0 8px 24px rgba(0,0,0,0.5);animation:fadeIn 0.18s ease both;max-width:320px;`;
79
+ el.textContent = msg;
80
+ c.appendChild(el);
81
+ setTimeout(() => { if (el.parentNode) el.parentNode.removeChild(el); }, 3500);
82
+ },
83
+
84
+ fmtDate(d) {
85
+ if (!d) return '—';
86
+ return new Date(d).toLocaleString();
87
+ },
88
+
89
+ fmtTokens(inp, out, cost) {
90
+ const parts = [];
91
+ if (inp) parts.push(inp.toLocaleString() + ' in');
92
+ if (out) parts.push(out.toLocaleString() + ' out');
93
+ if (cost) parts.push(KB.utils.formatCost(cost));
94
+ return parts.join(' / ');
95
+ },
96
+
97
+ /* SVG circular context-window ring.
98
+ size = current context tokens, limit = model max (0/null = unknown).
99
+ tooltipLines = array of {label, value} for the hover tooltip. */
100
+ contextRing(size, limit, tooltipLines) {
101
+ if (!size) return '';
102
+ const R = 14, STROKE = 3, C = 2 * Math.PI * R;
103
+ const pct = limit ? Math.min(size / limit, 1) : 0;
104
+ const color = pct > 0.85 ? 'var(--red)' : pct > 0.60 ? 'var(--yellow)' : 'var(--green)';
105
+ const used = limit ? Math.round(pct * 100) + '%' : '?';
106
+ const dash = C * (1 - pct);
107
+ const dim = (R + STROKE) * 2;
108
+ const lines = (tooltipLines || []).map(l =>
109
+ `<div><strong>${KB.utils.esc(l.label)}:</strong> ${KB.utils.esc(String(l.value))}</div>`
110
+ ).join('');
111
+ return `<span class="ctx-ring-wrap">
112
+ <svg width="${dim}" height="${dim}" viewBox="0 0 ${dim} ${dim}" style="transform:rotate(-90deg);">
113
+ <circle cx="${R+STROKE}" cy="${R+STROKE}" r="${R}" fill="none"
114
+ stroke="var(--border2)" stroke-width="${STROKE}"/>
115
+ <circle cx="${R+STROKE}" cy="${R+STROKE}" r="${R}" fill="none"
116
+ stroke="${color}" stroke-width="${STROKE}"
117
+ stroke-dasharray="${C}" stroke-dashoffset="${dash}"
118
+ stroke-linecap="round" style="transition:stroke-dashoffset 0.4s ease,stroke 0.4s ease;"/>
119
+ </svg>
120
+ <span style="position:absolute;font-size:8px;font-weight:700;color:${color};pointer-events:none;">${used}</span>
121
+ <span class="ctx-tooltip">${lines || '<div>No data</div>'}</span>
122
+ </span>`;
123
+ },
124
+ };
125
+
126
+ /* ── Modal ──────────────────────────────────────────────────────────── */
127
+ KB.modal = {
128
+ show(html) {
129
+ document.getElementById('modal-box').innerHTML = html;
130
+ const o = document.getElementById('modal-overlay');
131
+ o.style.display = 'flex';
132
+ },
133
+ hide() {
134
+ document.getElementById('modal-overlay').style.display = 'none';
135
+ document.getElementById('modal-box').innerHTML = '';
136
+ },
137
+ onOverlayClick(e) {
138
+ if (e.target === document.getElementById('modal-overlay')) KB.modal.hide();
139
+ },
140
+ };
141
+
142
+ /* ── Connection management ──────────────────────────────────────────── */
143
+ KB.loadConnection = function() {
144
+ KB.state.serverUrl = window.__VEIL_API__ || localStorage.getItem('veil_url') || '';
145
+ KB.state.secret = window.__VEIL_SECRET__ || localStorage.getItem('veil_secret') || '';
146
+ };
147
+
148
+ KB.saveConnection = function(url, secret) {
149
+ KB.state.serverUrl = url;
150
+ KB.state.secret = secret;
151
+ localStorage.setItem('veil_url', url);
152
+ localStorage.setItem('veil_secret', secret);
153
+ };
154
+
155
+ KB.clearConnection = function() {
156
+ KB.state.connected = false;
157
+ KB.state.serverUrl = '';
158
+ KB.state.secret = '';
159
+ KB.state.api = null;
160
+ KB.state.currentAgent = null;
161
+ localStorage.removeItem('veil_url');
162
+ localStorage.removeItem('veil_secret');
163
+ KB._updateIndicator();
164
+ document.getElementById('sidebar').style.display = 'none';
165
+ KB.navigate('connection');
166
+ };
167
+
168
+ KB._updateIndicator = function() {
169
+ const dot = document.getElementById('conn-dot');
170
+ const text = document.getElementById('conn-text');
171
+ if (!dot || !text) return;
172
+ if (KB.state.connected) {
173
+ dot.style.background = 'var(--green)';
174
+ dot.style.boxShadow = '0 0 6px var(--green)';
175
+ const short = KB.state.serverUrl.replace(/^https?:\/\//, '');
176
+ text.style.display = 'flex';
177
+ text.style.alignItems = 'center';
178
+ text.style.gap = '4px';
179
+ text.innerHTML = `<span style="color:var(--text2);font-size:11.5px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;flex:1;">${KB.utils.esc(short)}</span>`
180
+ + `<button onclick="window.Veil.clearConnection()" style="flex-shrink:0;background:none;border:none;cursor:pointer;color:var(--text3);font-size:14px;line-height:1;padding:0 2px;font-family:inherit;" title="Disconnect" onmouseover="this.style.color='var(--red)'" onmouseout="this.style.color='var(--text3)'">⎋</button>`;
181
+ } else {
182
+ dot.style.background = 'var(--border2)';
183
+ dot.style.boxShadow = 'none';
184
+ text.style.display = '';
185
+ text.textContent = 'Not connected';
186
+ }
187
+ };
188
+
189
+ /* ── Router ─────────────────────────────────────────────────────────── */
190
+ let _currentView = null;
191
+
192
+ KB.navigate = function(route, params) {
193
+ params = params || {};
194
+ if (_currentView && _currentView.unmount) {
195
+ try { _currentView.unmount(); } catch {}
196
+ }
197
+ const view = KB.views[route];
198
+ if (!view) {
199
+ KB.navigate('connection');
200
+ return;
201
+ }
202
+ /* update nav highlight */
203
+ document.querySelectorAll('.nav-link').forEach(el => {
204
+ if (el.getAttribute('data-route') === route) {
205
+ el.classList.add('nav-active');
206
+ } else {
207
+ el.classList.remove('nav-active');
208
+ }
209
+ });
210
+ const main = document.getElementById('main-content');
211
+ main.innerHTML = view.render(params);
212
+ if (view.mount) view.mount(params);
213
+ _currentView = view;
214
+ /* update hash without triggering another hashchange */
215
+ if (route !== 'connection') {
216
+ history.replaceState(null, '', '#' + route);
217
+ }
218
+ };
219
+
220
+ window.addEventListener('hashchange', () => {
221
+ if (!KB.state.connected) return;
222
+ const route = location.hash.slice(1) || 'agents';
223
+ if (KB.views[route]) KB.navigate(route);
224
+ });
225
+
226
+ /* ── Bootstrap ──────────────────────────────────────────────────────── */
227
+ KB.loadConnection();
228
+
229
+ if (KB.state.serverUrl) {
230
+ KB.state.api = new KB.API(KB.state.serverUrl, KB.state.secret);
231
+ KB.state.api.health()
232
+ .then(() => {
233
+ KB.state.connected = true;
234
+ KB._updateIndicator();
235
+ document.getElementById('sidebar').style.display = 'flex';
236
+ const route = location.hash.slice(1) || 'agents';
237
+ KB.navigate(KB.views[route] ? route : 'agents');
238
+ })
239
+ .catch(() => {
240
+ KB.state.connected = false;
241
+ KB.navigate('connection');
242
+ });
243
+ } else {
244
+ KB.navigate('connection');
245
+ }
246
+
247
+ /* ── Electron embedded titlebar ─────────────────────────────────────── */
248
+ (function () {
249
+ if (typeof window === 'undefined' || !window.electronWin) return;
250
+
251
+ const bar = document.getElementById('electron-titlebar');
252
+ if (!bar) return;
253
+
254
+ bar.style.display = 'flex';
255
+ document.body.style.paddingTop = '34px';
256
+
257
+ const logo = document.getElementById('sidebar-logo');
258
+ if (logo) logo.style.display = 'none';
259
+
260
+ const titleEl = document.getElementById('tb-title');
261
+ const apiUrl = window.__VEIL_API__ || KB.state.serverUrl;
262
+ if (titleEl && apiUrl) {
263
+ const secret = window.__VEIL_SECRET__ || KB.state.secret || '';
264
+ fetch(apiUrl.replace(/\/$/, '') + '/status', {
265
+ headers: secret ? { 'X-Veil-Secret': secret } : {},
266
+ })
267
+ .then(r => r.json())
268
+ .then(data => {
269
+ if (data.cwd) {
270
+ const parts = data.cwd.replace(/\\/g, '/').split('/').filter(Boolean);
271
+ const name = parts[parts.length - 1] || data.cwd;
272
+ titleEl.textContent = 'VeilCLI\u2002|\u2002' + name;
273
+ }
274
+ })
275
+ .catch(() => {});
276
+ }
277
+
278
+ document.getElementById('tb-min').onclick = () => window.electronWin.minimize();
279
+ document.getElementById('tb-max').onclick = () => window.electronWin.maximize();
280
+ document.getElementById('tb-close').onclick = () => window.electronWin.close();
281
+ }());
package/ui/index.html ADDED
@@ -0,0 +1,382 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>VeilCLI</title>
7
+ <link rel="preconnect" href="https://fonts.googleapis.com">
8
+ <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
9
+ <link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&family=JetBrains+Mono:wght@400;500&display=swap" rel="stylesheet">
10
+ <script src="https://cdn.jsdelivr.net/npm/marked@9/marked.min.js"></script>
11
+ <script src="https://cdn.jsdelivr.net/npm/dompurify@3/dist/purify.min.js"></script>
12
+ <style>
13
+ :root {
14
+ --bg: #0C0C10;
15
+ --surface: #13131A;
16
+ --surface2: #1A1A23;
17
+ --surface3: #22222E;
18
+ --border: #24242F;
19
+ --border2: #2E2E3C;
20
+ --text: #E2E2EE;
21
+ --text2: #8080A0;
22
+ --text3: #44445A;
23
+ --accent: #7C6AF0;
24
+ --accent-h: #6B5ADF;
25
+ --accent-glow: rgba(124,106,240,0.18);
26
+ --green: #22C55E;
27
+ --green-dim: rgba(34,197,94,0.12);
28
+ --red: #EF4444;
29
+ --red-dim: rgba(239,68,68,0.12);
30
+ --yellow: #EAB308;
31
+ --yellow-dim: rgba(234,179,8,0.12);
32
+ --blue: #3B82F6;
33
+ --blue-dim: rgba(59,130,246,0.12);
34
+ --radius: 10px;
35
+ --radius-sm: 7px;
36
+ --radius-lg: 14px;
37
+ --bg-alt: #09090D;
38
+ --bg-task: rgba(59,130,246,0.08);
39
+ --bg-chat: rgba(34,197,94,0.08);
40
+ --bg-daemon: rgba(234,179,8,0.08);
41
+ }
42
+
43
+ *, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
44
+
45
+ body {
46
+ font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
47
+ background: var(--bg);
48
+ color: var(--text);
49
+ font-size: 13.5px;
50
+ line-height: 1.55;
51
+ -webkit-font-smoothing: antialiased;
52
+ }
53
+
54
+ /* ── Scrollbars ───────────────────────────────────── */
55
+ ::-webkit-scrollbar { width: 4px; height: 4px; }
56
+ ::-webkit-scrollbar-track { background: transparent; }
57
+ ::-webkit-scrollbar-thumb { background: var(--border2); border-radius: 4px; }
58
+ ::-webkit-scrollbar-thumb:hover { background: var(--text3); }
59
+
60
+ /* ── Streaming cursor ─────────────────────────────── */
61
+ .streaming::after {
62
+ content: '│';
63
+ animation: cur-blink 1s step-start infinite;
64
+ color: var(--accent);
65
+ font-weight: 300;
66
+ }
67
+ @keyframes cur-blink { 50% { opacity: 0; } }
68
+
69
+ /* ── Nav ──────────────────────────────────────────── */
70
+ .nav-link {
71
+ display: flex; align-items: center; gap: 9px;
72
+ padding: 8px 10px; border-radius: var(--radius-sm);
73
+ font-size: 13px; font-weight: 450; color: var(--text2);
74
+ text-decoration: none; cursor: pointer;
75
+ transition: background 0.12s, color 0.12s;
76
+ user-select: none; margin: 1px 0;
77
+ }
78
+ .nav-link:hover { background: var(--surface2); color: var(--text); }
79
+ .nav-active { background: var(--accent-glow) !important; color: var(--text) !important; }
80
+ .nav-active .ni { color: var(--accent); }
81
+
82
+ /* ── Buttons ──────────────────────────────────────── */
83
+ .btn {
84
+ display: inline-flex; align-items: center; justify-content: center; gap: 6px;
85
+ padding: 7px 14px; border-radius: var(--radius-sm);
86
+ font-size: 13px; font-weight: 500; font-family: inherit;
87
+ cursor: pointer; border: 1px solid transparent;
88
+ transition: background 0.12s, border-color 0.12s, opacity 0.12s, box-shadow 0.12s;
89
+ white-space: nowrap; user-select: none; outline: none;
90
+ }
91
+ .btn:disabled { opacity: 0.38; cursor: not-allowed; pointer-events: none; }
92
+ .btn-primary { background: var(--accent); color: #fff; border-color: var(--accent); }
93
+ .btn-primary:hover { background: var(--accent-h); border-color: var(--accent-h); box-shadow: 0 0 0 3px var(--accent-glow); }
94
+ .btn-secondary { background: var(--surface2); color: var(--text); border-color: var(--border2); }
95
+ .btn-secondary:hover { background: var(--surface3); border-color: var(--border2); }
96
+ .btn-ghost { background: transparent; color: var(--text2); border-color: transparent; }
97
+ .btn-ghost:hover { background: var(--surface2); color: var(--text); }
98
+ .btn-danger { background: var(--red-dim); color: var(--red); border-color: rgba(239,68,68,0.25); }
99
+ .btn-danger:hover { background: rgba(239,68,68,0.2); }
100
+ .btn-success { background: var(--green-dim); color: var(--green); border-color: rgba(34,197,94,0.25); }
101
+ .btn-success:hover { background: rgba(34,197,94,0.2); }
102
+ .btn-sm { padding: 4px 10px; font-size: 12px; border-radius: 6px; }
103
+ .btn-xs { padding: 2px 7px; font-size: 11px; border-radius: 5px; }
104
+
105
+ /* ── Inputs ───────────────────────────────────────── */
106
+ .input {
107
+ background: var(--surface2); border: 1px solid var(--border2);
108
+ border-radius: var(--radius-sm); padding: 8px 12px;
109
+ font-size: 13.5px; color: var(--text); font-family: inherit;
110
+ width: 100%; outline: none;
111
+ transition: border-color 0.15s, box-shadow 0.15s;
112
+ }
113
+ .input:focus { border-color: var(--accent); box-shadow: 0 0 0 3px var(--accent-glow); }
114
+ .input::placeholder { color: var(--text3); }
115
+ select.input { cursor: pointer; }
116
+ textarea.input { resize: vertical; }
117
+ textarea.mono { font-family: 'JetBrains Mono', 'Courier New', monospace; font-size: 12.5px; line-height: 1.6; }
118
+
119
+ /* ── Cards ────────────────────────────────────────── */
120
+ .card { background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius-lg); overflow: hidden; }
121
+ .card2 { background: var(--surface2); border: 1px solid var(--border2); border-radius: var(--radius); }
122
+
123
+ /* ── Badges ───────────────────────────────────────── */
124
+ .badge {
125
+ display: inline-flex; align-items: center;
126
+ padding: 2px 8px; border-radius: 999px;
127
+ font-size: 11px; font-weight: 600; letter-spacing: 0.02em;
128
+ }
129
+ .badge-pending { background: rgba(128,128,160,0.14); color: #9090B8; }
130
+ .badge-processing { background: var(--blue-dim); color: #60A5FA; }
131
+ .badge-finished { background: var(--green-dim); color: #4ADE80; }
132
+ .badge-failed { background: var(--red-dim); color: #F87171; }
133
+ .badge-waiting { background: var(--yellow-dim); color: #FCD34D; }
134
+ .badge-canceled { background: rgba(68,68,90,0.18); color: #6868A0; }
135
+ .badge-active { background: var(--green-dim); color: #4ADE80; }
136
+ .badge-closed { background: rgba(68,68,90,0.18); color: #6868A0; }
137
+
138
+ /* ── Messages ─────────────────────────────────────── */
139
+ .msg-user { background: var(--surface3); border-radius: 16px 16px 4px 16px; }
140
+ .msg-system { background: rgba(68,68,90,0.12); border: 1px solid var(--border); border-radius: 8px; color: var(--text2); font-size: 12px; }
141
+ .msg-tool { background: var(--bg); border: 1px solid var(--border2); border-radius: 8px; font-family: 'JetBrains Mono', monospace; font-size: 12px; }
142
+
143
+ /* ── Tool block ───────────────────────────────────── */
144
+ .tool-block { background: var(--bg); border: 1px solid var(--border2); border-radius: 8px; overflow: hidden; }
145
+
146
+ /* ── List items ───────────────────────────────────── */
147
+ .list-item { padding: 9px 12px; border-radius: var(--radius-sm); margin: 1px 4px; cursor: pointer; transition: background 0.1s; }
148
+ .list-item:hover { background: var(--surface2); }
149
+ .list-item.selected { background: var(--accent-glow); }
150
+
151
+ /* ── Panel header ─────────────────────────────────── */
152
+ .panel-header { padding: 10px 16px; border-bottom: 1px solid var(--border); font-size: 11px; font-weight: 600; letter-spacing: 0.06em; text-transform: uppercase; color: var(--text3); }
153
+
154
+ /* ── Timeline dot ──────────────────────────────────── */
155
+ .timeline-dot { width: 7px; height: 7px; border-radius: 50%; flex-shrink: 0; margin-top: 6px; }
156
+
157
+ /* ── Divider ──────────────────────────────────────── */
158
+ hr, .divider { border: none; border-top: 1px solid var(--border); }
159
+
160
+ /* ── Spinner ──────────────────────────────────────── */
161
+ .spin { animation: spin 0.7s linear infinite; display: inline-block; }
162
+ @keyframes spin { to { transform: rotate(360deg); } }
163
+
164
+ /* ── Animations ───────────────────────────────────── */
165
+ .fade-in { animation: fadeIn 0.18s ease both; }
166
+ @keyframes fadeIn { from { opacity: 0; transform: translateY(5px); } to { opacity: 1; } }
167
+
168
+ /* ── Code / pre ───────────────────────────────────── */
169
+ pre { white-space: pre-wrap; word-break: break-all; font-family: 'JetBrains Mono', monospace; font-size: 12px; }
170
+
171
+ /* ── Markdown prose ────────────────────────────────── */
172
+ .msg-md { font-size: 14px; line-height: 1.65; color: var(--text); word-break: break-word; }
173
+ .msg-md p { margin: 0 0 10px; }
174
+ .msg-md p:last-child { margin-bottom: 0; }
175
+ .msg-md h1,.msg-md h2,.msg-md h3,.msg-md h4 { font-weight: 700; margin: 16px 0 8px; color: var(--text); line-height: 1.3; }
176
+ .msg-md h1 { font-size: 18px; } .msg-md h2 { font-size: 16px; } .msg-md h3 { font-size: 14.5px; } .msg-md h4 { font-size: 13.5px; }
177
+ .msg-md ul,.msg-md ol { margin: 6px 0 10px 20px; padding: 0; }
178
+ .msg-md li { margin: 3px 0; }
179
+ .msg-md code { font-family: 'JetBrains Mono', monospace; font-size: 12.5px; background: var(--surface2); border: 1px solid var(--border2); border-radius: 4px; padding: 1px 5px; color: var(--accent); }
180
+ .msg-md pre { background: var(--bg); border: 1px solid var(--border2); border-radius: 8px; padding: 12px 14px; overflow-x: auto; margin: 8px 0; }
181
+ .msg-md pre code { background: none; border: none; padding: 0; color: var(--text2); font-size: 12.5px; }
182
+ .msg-md blockquote { border-left: 3px solid var(--border2); margin: 8px 0; padding: 4px 12px; color: var(--text2); }
183
+ .msg-md table { border-collapse: collapse; width: 100%; margin: 8px 0; font-size: 13px; }
184
+ .msg-md th { background: var(--surface2); font-weight: 600; }
185
+ .msg-md th, .msg-md td { border: 1px solid var(--border); padding: 6px 10px; text-align: left; }
186
+ .msg-md a { color: var(--accent); text-decoration: none; } .msg-md a:hover { text-decoration: underline; }
187
+ .msg-md hr { border: none; border-top: 1px solid var(--border); margin: 12px 0; }
188
+ .msg-md strong { color: var(--text); font-weight: 600; }
189
+
190
+ /* ── Copy button ────────────────────────────────────── */
191
+ .msg-bubble-wrap { position: relative; }
192
+ .msg-copy-btn {
193
+ position: absolute; bottom: -10px; left: 65px;
194
+ opacity: 0; transition: opacity 0.15s;
195
+ background: var(--surface2); border: 1px solid var(--border2);
196
+ color: var(--text3); border-radius: 6px;
197
+ padding: 3px 8px; font-size: 11.5px; cursor: pointer;
198
+ font-family: inherit;
199
+ }
200
+ .msg-bubble-wrap:hover .msg-copy-btn { opacity: 1; }
201
+ .msg-copy-btn:hover { background: var(--surface3); color: var(--text); }
202
+
203
+ /* ── Context ring ───────────────────────────────────── */
204
+ .ctx-ring-wrap {
205
+ position: relative; display: inline-flex; align-items: center; justify-content: center;
206
+ cursor: default; flex-shrink: 0;
207
+ }
208
+ .ctx-ring-wrap svg { display: block; }
209
+ .ctx-tooltip {
210
+ display: none; position: absolute; top: calc(100% + 8px); right: 0;
211
+ background: var(--surface2); border: 1px solid var(--border2);
212
+ border-radius: 8px; padding: 10px 13px; min-width: 200px;
213
+ font-size: 12px; color: var(--text2); line-height: 1.7;
214
+ box-shadow: 0 8px 24px rgba(0,0,0,0.45); z-index: 200; white-space: nowrap;
215
+ }
216
+ .ctx-tooltip strong { color: var(--text); }
217
+ .ctx-ring-wrap:hover .ctx-tooltip { display: block; }
218
+
219
+ /* ── Thinking dots ──────────────────────────────────── */
220
+ .thinking-dots { color: var(--text3); font-size: 13.5px; font-style: italic; }
221
+ .thinking-dots::after {
222
+ content: '';
223
+ animation: thinkDots 1.2s steps(4, end) infinite;
224
+ }
225
+ @keyframes thinkDots {
226
+ 0% { content: ''; }
227
+ 25% { content: '.'; }
228
+ 50% { content: '..'; }
229
+ 75% { content: '...'; }
230
+ 100% { content: ''; }
231
+ }
232
+
233
+ /* ── Avatar ───────────────────────────────────────── */
234
+ .avatar {
235
+ width: 28px; height: 28px; border-radius: 50%;
236
+ display: flex; align-items: center; justify-content: center;
237
+ font-size: 11px; font-weight: 700; flex-shrink: 0;
238
+ text-transform: uppercase; letter-spacing: 0;
239
+ }
240
+ .avatar-accent { background: var(--accent-glow); color: var(--accent); }
241
+ .avatar-surface { background: var(--surface3); color: var(--text2); }
242
+
243
+ /* ── Info row ─────────────────────────────────────── */
244
+ .info-label { font-size: 10.5px; font-weight: 600; letter-spacing: 0.06em; text-transform: uppercase; color: var(--text3); margin-bottom: 3px; }
245
+ .info-value { font-size: 13.5px; color: var(--text); }
246
+
247
+ /* ── Electron embedded titlebar ─────────────────────── */
248
+ #electron-titlebar {
249
+ display: none;
250
+ position: fixed;
251
+ top: 0; left: 0; right: 0;
252
+ height: 34px;
253
+ background: var(--surface);
254
+ border-bottom: 1px solid var(--border);
255
+ -webkit-app-region: drag;
256
+ user-select: none;
257
+ z-index: 10000;
258
+ align-items: center;
259
+ padding: 0 6px 0 0;
260
+ }
261
+ .tb-win-btn {
262
+ -webkit-app-region: no-drag;
263
+ width: 28px; height: 28px;
264
+ border-radius: 6px;
265
+ border: none; cursor: pointer;
266
+ font-size: 14px; line-height: 1;
267
+ display: flex; align-items: center; justify-content: center;
268
+ background: transparent; color: var(--text3);
269
+ transition: background 0.12s, color 0.12s;
270
+ font-family: inherit;
271
+ }
272
+ .tb-win-btn:hover { background: var(--surface2); color: var(--text); }
273
+ #tb-close:hover { background: var(--red-dim); color: var(--red); }
274
+
275
+ /* ── Tab bar ──────────────────────────────────────── */
276
+ .tab-btn {
277
+ padding: 8px 14px; font-size: 13px; font-weight: 500;
278
+ color: var(--text2); background: none; border: none;
279
+ border-bottom: 2px solid transparent; cursor: pointer;
280
+ transition: color 0.12s, border-color 0.12s; font-family: inherit;
281
+ }
282
+ .tab-btn:hover { color: var(--text); }
283
+ .tab-active { color: var(--text) !important; border-bottom-color: var(--accent) !important; }
284
+ </style>
285
+ </head>
286
+ <body style="height:100vh;overflow:hidden;display:flex;">
287
+
288
+ <!-- Electron embedded titlebar — hidden in browser, shown when inside Veil Desktop -->
289
+ <div id="electron-titlebar">
290
+ <div style="flex:1;padding-left:10px;display:flex;align-items:center;gap:7px;-webkit-app-region:drag;pointer-events:none;">
291
+ <div style="width:20px;height:20px;background:linear-gradient(135deg,var(--accent),#5B8AF0);border-radius:5px;display:flex;align-items:center;justify-content:center;flex-shrink:0;">
292
+ <svg width="11" height="11" viewBox="0 0 24 24" fill="none" stroke="#fff" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><polygon points="13 2 3 14 12 14 11 22 21 10 12 10 13 2"></polygon></svg>
293
+ </div>
294
+ <span id="tb-title" style="font-size:13px;font-weight:600;color:var(--text);letter-spacing:-0.01em;">VeilCLI</span>
295
+ </div>
296
+ <div style="-webkit-app-region:no-drag;display:flex;gap:2px;align-items:center;">
297
+ <button id="tb-min" class="tb-win-btn" title="Minimize">&#x2013;</button>
298
+ <button id="tb-max" class="tb-win-btn" title="Maximize">&#x25a1;</button>
299
+ <button id="tb-close" class="tb-win-btn" title="Close">&#x2715;</button>
300
+ </div>
301
+ </div>
302
+
303
+ <!-- Sidebar -->
304
+ <nav id="sidebar" style="display:none;width:210px;background:var(--surface);border-right:1px solid var(--border);flex-shrink:0;flex-direction:column;overflow:hidden;">
305
+ <!-- Logo -->
306
+ <div id="sidebar-logo" style="padding:13px 12px 11px;border-bottom:1px solid var(--border);display:flex;align-items:center;gap:9px;flex-shrink:0;">
307
+ <div style="width:28px;height:28px;background:linear-gradient(135deg,var(--accent),#5B8AF0);border-radius:8px;display:flex;align-items:center;justify-content:center;flex-shrink:0;box-shadow:0 2px 8px var(--accent-glow);">
308
+ <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="#fff" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><polygon points="13 2 3 14 12 14 11 22 21 10 12 10 13 2"></polygon></svg>
309
+ </div>
310
+ <span style="font-weight:700;font-size:14px;letter-spacing:-0.02em;">Veil DashBoard</span>
311
+ </div>
312
+ <!-- Nav links -->
313
+ <div style="flex:1;overflow-y:auto;padding:8px;">
314
+ <a href="#chat" data-route="chat" class="nav-link">
315
+ <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z"></path></svg>
316
+ Chat
317
+ </a>
318
+ <a href="#sessions" data-route="sessions" class="nav-link">
319
+ <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"></path><polyline points="14 2 14 8 20 8"></polyline><line x1="16" y1="13" x2="8" y2="13"></line><line x1="16" y1="17" x2="8" y2="17"></line><polyline points="10 9 9 9 8 9"></polyline></svg>
320
+ Sessions
321
+ </a>
322
+ <a href="#agents" data-route="agents" class="nav-link">
323
+ <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="11" width="18" height="10" rx="2"></rect><circle cx="12" cy="5" r="2"></circle><path d="M12 7v4"></path><line x1="8" y1="16" x2="8" y2="16"></line><line x1="16" y1="16" x2="16" y2="16"></line></svg>
324
+ Agents
325
+ </a>
326
+ <a href="#tasks" data-route="tasks" class="nav-link">
327
+ <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M9 11l3 3L22 4"></path><path d="M21 12v7a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h11"></path></svg>
328
+ Tasks
329
+ </a>
330
+ <a href="#daemons" data-route="daemons" class="nav-link">
331
+ <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"></circle><polyline points="12 6 12 12 16 14"></polyline></svg>
332
+ Daemons
333
+ </a>
334
+ <a href="#memory" data-route="memory" class="nav-link">
335
+ <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="4" y="4" width="16" height="16" rx="2"></rect><line x1="4" y1="9" x2="20" y2="9"></line><line x1="4" y1="14" x2="20" y2="14"></line><line x1="9" y1="4" x2="9" y2="20"></line><line x1="15" y1="4" x2="15" y2="20"></line></svg>
336
+ Memory
337
+ </a>
338
+ <a href="#settings" data-route="settings" class="nav-link">
339
+ <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="3"></circle><path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1 0 2.83 2 2 0 0 1-2.83 0l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-2 2 2 2 0 0 1-2-2v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 0 1-2.83 0 2 2 0 0 1 0-2.83l.06-.06a1.65 1.65 0 0 0 .33-1.82 1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1-2-2 2 2 0 0 1 2-2h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 0 1 0-2.83 2 2 0 0 1 2.83 0l.06.06a1.65 1.65 0 0 0 1.82.33H9a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 2-2 2 2 0 0 1 2 2v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 0 2 2 0 0 1 0 2.83l-.06.06a1.65 1.65 0 0 0-.33 1.82V9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 2 2 2 2 0 0 1-2 2h-.09a1.65 1.65 0 0 0-1.51 1z"></path></svg>
340
+ Settings
341
+ </a>
342
+ <a href="#models" data-route="models" class="nav-link">
343
+ <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polygon points="12 2 2 7 12 12 22 7 12 2"></polygon><polyline points="2 17 12 22 22 17"></polyline><polyline points="2 12 12 17 22 12"></polyline></svg>
344
+ Models
345
+ </a>
346
+ <a href="#feed" data-route="feed" class="nav-link">
347
+ <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M4.9 19.1C1 15.2 1 8.8 4.9 4.9"></path><path d="M7.8 16.2c-2.3-2.3-2.3-6.1 0-8.5"></path><circle cx="12" cy="12" r="2"></circle><path d="M16.2 7.8c2.3 2.3 2.3 6.1 0 8.5"></path><path d="M19.1 4.9C23 8.8 23 15.1 19.1 19"></path></svg>
348
+ Live Feed
349
+ </a>
350
+ </div>
351
+ <!-- Connection status -->
352
+ <div id="connection-indicator" style="padding:10px 12px;border-top:1px solid var(--border);display:flex;align-items:center;gap:7px;flex-shrink:0;">
353
+ <span id="conn-dot" style="display:inline-block;width:7px;height:7px;border-radius:50%;background:var(--border2);transition:background 0.3s;flex-shrink:0;"></span>
354
+ <span id="conn-text" style="font-size:12px;color:var(--text3);flex:1;min-width:0;">Not connected</span>
355
+ </div>
356
+ </nav>
357
+
358
+ <!-- Main -->
359
+ <main id="main-content" style="flex:1;overflow:auto;background:var(--bg);"></main>
360
+
361
+ <!-- Toasts -->
362
+ <div id="toast-container" style="position:fixed;bottom:20px;right:20px;z-index:9999;display:flex;flex-direction:column;gap:8px;pointer-events:none;"></div>
363
+
364
+ <!-- Modal -->
365
+ <div id="modal-overlay" style="display:none;position:fixed;inset:0;z-index:9000;background:rgba(0,0,0,0.65);backdrop-filter:blur(6px);align-items:center;justify-content:center;" onclick="window.Veil.modal.onOverlayClick(event)">
366
+ <div id="modal-box" class="card fade-in" style="max-width:600px;width:90%;max-height:88vh;overflow:auto;padding:28px;border-radius:16px;border-color:var(--border2);"></div>
367
+ </div>
368
+
369
+ <script src="api.js"></script>
370
+ <script src="views/connection.js"></script>
371
+ <script src="views/agents.js"></script>
372
+ <script src="views/chat.js"></script>
373
+ <script src="views/tasks.js"></script>
374
+ <script src="views/sessions.js"></script>
375
+ <script src="views/daemons.js"></script>
376
+ <script src="views/memory.js"></script>
377
+ <script src="views/settings.js"></script>
378
+ <script src="views/models.js"></script>
379
+ <script src="views/feed.js"></script>
380
+ <script src="app.js"></script>
381
+ </body>
382
+ </html>