@jungjaehoon/mama-os 0.33.0 → 0.34.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 (60) hide show
  1. package/README.md +15 -17
  2. package/dist/agent/os-agent-capabilities.md +2 -2
  3. package/dist/api/graph-api.d.ts +4 -4
  4. package/dist/api/graph-api.d.ts.map +1 -1
  5. package/dist/api/graph-api.js +47 -78
  6. package/dist/api/graph-api.js.map +1 -1
  7. package/dist/api/index.d.ts +10 -0
  8. package/dist/api/index.d.ts.map +1 -1
  9. package/dist/api/index.js +9 -2
  10. package/dist/api/index.js.map +1 -1
  11. package/dist/api/operator-handler.d.ts +1 -1
  12. package/dist/api/operator-handler.js +1 -1
  13. package/dist/api/runtime-status-handler.d.ts +41 -0
  14. package/dist/api/runtime-status-handler.d.ts.map +1 -0
  15. package/dist/api/runtime-status-handler.js +55 -0
  16. package/dist/api/runtime-status-handler.js.map +1 -0
  17. package/dist/cli/commands/start.js +1 -1
  18. package/dist/cli/commands/start.js.map +1 -1
  19. package/dist/cli/runtime/api-routes-init.js +1 -1
  20. package/dist/cli/runtime/api-routes-init.js.map +1 -1
  21. package/dist/cli/runtime/api-server-init.d.ts +12 -1
  22. package/dist/cli/runtime/api-server-init.d.ts.map +1 -1
  23. package/dist/cli/runtime/api-server-init.js +54 -0
  24. package/dist/cli/runtime/api-server-init.js.map +1 -1
  25. package/dist/cli/runtime/utilities.d.ts +1 -1
  26. package/dist/cli/runtime/utilities.d.ts.map +1 -1
  27. package/dist/cli/runtime/utilities.js +3 -1
  28. package/dist/cli/runtime/utilities.js.map +1 -1
  29. package/dist/multi-agent/dashboard-agent-persona.js +2 -2
  30. package/dist/operator/board-slot-instructions.d.ts +1 -1
  31. package/dist/operator/board-slot-instructions.js +1 -1
  32. package/package.json +1 -2
  33. package/public/viewer/js/modules/system.js +183 -0
  34. package/public/viewer/js/utils/ui-commands.js +4 -9
  35. package/public/viewer/log-viewer.html +2 -192
  36. package/public/viewer/operator/operator.css +1 -0
  37. package/public/viewer/operator/operator.js +14830 -0
  38. package/public/viewer/src/modules/system.ts +242 -0
  39. package/public/viewer/src/utils/ui-commands.ts +4 -8
  40. package/public/viewer/sw.js +44 -10
  41. package/public/viewer/viewer.css +338 -331
  42. package/public/viewer/viewer.html +388 -1472
  43. package/public/ui/assets/index-BDXDGEV6.js +0 -62
  44. package/public/ui/assets/index-po2tN0xq.css +0 -1
  45. package/public/ui/index.html +0 -14
  46. package/public/ui/theme-init.js +0 -20
  47. package/public/viewer/js/modules/agents.js +0 -1153
  48. package/public/viewer/js/modules/chat.js +0 -1993
  49. package/public/viewer/js/modules/connector-feed.js +0 -352
  50. package/public/viewer/js/modules/dashboard.js +0 -456
  51. package/public/viewer/js/modules/entity-audit.js +0 -106
  52. package/public/viewer/js/modules/entity-review.js +0 -66
  53. package/public/viewer/js/modules/settings.js +0 -1257
  54. package/public/viewer/src/modules/agents.ts +0 -1302
  55. package/public/viewer/src/modules/chat.ts +0 -2426
  56. package/public/viewer/src/modules/connector-feed.ts +0 -391
  57. package/public/viewer/src/modules/dashboard.ts +0 -498
  58. package/public/viewer/src/modules/entity-audit.ts +0 -154
  59. package/public/viewer/src/modules/entity-review.ts +0 -175
  60. package/public/viewer/src/modules/settings.ts +0 -1547
@@ -0,0 +1,183 @@
1
+ /**
2
+ * System views: Runtime and Connectors.
3
+ * @module modules/system
4
+ *
5
+ * Read-only truth, one source. Both views render `GET /api/runtime/status` and
6
+ * nothing else: no config read, no client-side model registry, no credential
7
+ * ever reaching the DOM. Editing daemon configuration from the browser is not a
8
+ * capability of this surface; setup is a CLI action.
9
+ */
10
+ /* eslint-env browser */
11
+ const RUNTIME_STATUS_ENDPOINT = '/api/runtime/status';
12
+ /** Shown when the daemon booted without a health service. Never a fake score. */
13
+ const HEALTH_UNAVAILABLE = 'unavailable';
14
+ export function escapeHtml(value) {
15
+ return String(value ?? '')
16
+ .replace(/&/g, '&')
17
+ .replace(/</g, '&lt;')
18
+ .replace(/>/g, '&gt;')
19
+ .replace(/"/g, '&quot;')
20
+ .replace(/'/g, '&#39;');
21
+ }
22
+ async function defaultFetchStatus() {
23
+ const response = await fetch(RUNTIME_STATUS_ENDPOINT);
24
+ if (!response.ok) {
25
+ throw new Error('HTTP ' + response.status);
26
+ }
27
+ return (await response.json());
28
+ }
29
+ function skeleton(rows) {
30
+ let html = '<div class="mama-system-skeleton" aria-busy="true" aria-live="polite">';
31
+ for (let i = 0; i < rows; i++) {
32
+ html += '<div class="mama-system-skeleton-row"></div>';
33
+ }
34
+ html += '</div>';
35
+ return html;
36
+ }
37
+ /**
38
+ * Failure names the endpoint that failed and offers a retry. A System view that
39
+ * silently shows nothing is indistinguishable from a healthy empty runtime.
40
+ */
41
+ function errorCard(error) {
42
+ const detail = error instanceof Error ? error.message : String(error ?? 'Unknown error');
43
+ return ('<div class="mama-system-error" role="alert">' +
44
+ '<p class="mama-system-error-title">Could not read ' +
45
+ escapeHtml(RUNTIME_STATUS_ENDPOINT) +
46
+ '</p>' +
47
+ '<p class="mama-system-error-detail">' +
48
+ escapeHtml(detail) +
49
+ '</p>' +
50
+ '<button type="button" class="mama-system-retry" data-retry>Retry</button>' +
51
+ '</div>');
52
+ }
53
+ function formatStartedAt(startedAt) {
54
+ if (!Number.isFinite(startedAt) || startedAt <= 0)
55
+ return 'unknown';
56
+ return new Date(startedAt).toLocaleString();
57
+ }
58
+ function formatUptime(startedAt) {
59
+ if (!Number.isFinite(startedAt) || startedAt <= 0)
60
+ return 'unknown';
61
+ const seconds = Math.max(0, Math.floor((Date.now() - startedAt) / 1000));
62
+ const days = Math.floor(seconds / 86400);
63
+ const hours = Math.floor((seconds % 86400) / 3600);
64
+ const minutes = Math.floor((seconds % 3600) / 60);
65
+ if (days > 0)
66
+ return days + 'd ' + hours + 'h';
67
+ if (hours > 0)
68
+ return hours + 'h ' + minutes + 'm';
69
+ return minutes + 'm';
70
+ }
71
+ function statTile(label, value, tone = 'neutral') {
72
+ return ('<div class="mama-system-tile" data-tone="' +
73
+ escapeHtml(tone) +
74
+ '">' +
75
+ '<span class="mama-system-tile-label">' +
76
+ escapeHtml(label) +
77
+ '</span>' +
78
+ '<span class="mama-system-tile-value">' +
79
+ escapeHtml(value) +
80
+ '</span>' +
81
+ '</div>');
82
+ }
83
+ export function renderRuntimeSnapshot(snapshot) {
84
+ const health = snapshot.health;
85
+ const healthValue = health ? health.status + ' - ' + health.score : HEALTH_UNAVAILABLE;
86
+ const healthTone = health
87
+ ? health.status === 'healthy'
88
+ ? 'ok'
89
+ : health.status === 'degraded'
90
+ ? 'warn'
91
+ : 'bad'
92
+ : 'muted';
93
+ return ('<section class="mama-system-panel">' +
94
+ '<h2 class="mama-system-heading">Runtime</h2>' +
95
+ '<p class="mama-system-note">Reported by the running daemon; some fields reflect boot-time state. Configuration is edited via the CLI or the config file.</p>' +
96
+ '<div class="mama-system-tiles">' +
97
+ statTile('Daemon', snapshot.running ? 'running' : 'stopped', snapshot.running ? 'ok' : 'bad') +
98
+ statTile('Backend', snapshot.backend) +
99
+ statTile('Model', snapshot.model) +
100
+ statTile('Health', healthValue, healthTone) +
101
+ statTile('Uptime', formatUptime(snapshot.startedAt)) +
102
+ statTile('Started', formatStartedAt(snapshot.startedAt)) +
103
+ '</div>' +
104
+ '</section>');
105
+ }
106
+ const CONNECTOR_STATE_TONE = {
107
+ connected: 'ok',
108
+ disconnected: 'muted',
109
+ unknown: 'warn',
110
+ };
111
+ /**
112
+ * What a connector state actually means. 'connected' is not a live probe: it
113
+ * says the connector registered when the daemon booted, so the chip carries the
114
+ * narrower claim rather than letting the word oversell itself.
115
+ */
116
+ const CONNECTOR_STATE_TITLE = {
117
+ connected: 'Registered at daemon boot',
118
+ disconnected: 'Disabled in config',
119
+ unknown: 'Enabled in config but not registered at boot',
120
+ };
121
+ export function renderConnectorsSnapshot(snapshot) {
122
+ const connectors = snapshot.connectors ?? [];
123
+ if (connectors.length === 0) {
124
+ return ('<section class="mama-system-panel">' +
125
+ '<h2 class="mama-system-heading">Connectors</h2>' +
126
+ '<div class="mama-system-empty">' +
127
+ '<p class="mama-system-empty-title">No connectors configured</p>' +
128
+ '<p class="mama-system-empty-detail">Add one from a terminal:</p>' +
129
+ '<code class="mama-system-code">mama connector add &lt;name&gt;</code>' +
130
+ '</div>' +
131
+ '</section>');
132
+ }
133
+ let rows = '';
134
+ for (const connector of connectors) {
135
+ rows +=
136
+ '<li class="mama-system-row" data-tone="' +
137
+ escapeHtml(CONNECTOR_STATE_TONE[connector.state] ?? 'muted') +
138
+ '">' +
139
+ '<span class="mama-system-dot"></span>' +
140
+ '<span class="mama-system-row-name">' +
141
+ escapeHtml(connector.name) +
142
+ '</span>' +
143
+ '<span class="mama-system-row-state" title="' +
144
+ escapeHtml(CONNECTOR_STATE_TITLE[connector.state] ?? 'State reported at daemon boot') +
145
+ '">' +
146
+ escapeHtml(connector.state) +
147
+ '</span>' +
148
+ (connector.enabled ? '' : '<span class="mama-system-badge">off</span>') +
149
+ '</li>';
150
+ }
151
+ return ('<section class="mama-system-panel">' +
152
+ '<h2 class="mama-system-heading">Connectors</h2>' +
153
+ '<p class="mama-system-note">Read-only. Add or remove connectors with <code>mama connector add</code>.</p>' +
154
+ '<ul class="mama-system-list">' +
155
+ rows +
156
+ '</ul>' +
157
+ '</section>');
158
+ }
159
+ async function renderView(container, options, toHtml, skeletonRows) {
160
+ if (!container)
161
+ return;
162
+ const fetchStatus = options.fetchStatus ?? defaultFetchStatus;
163
+ container.innerHTML = skeleton(skeletonRows);
164
+ try {
165
+ const snapshot = await fetchStatus();
166
+ container.innerHTML = toHtml(snapshot);
167
+ }
168
+ catch (error) {
169
+ container.innerHTML = errorCard(error);
170
+ const retry = container.querySelector('[data-retry]');
171
+ if (retry) {
172
+ retry.addEventListener('click', () => {
173
+ void renderView(container, options, toHtml, skeletonRows);
174
+ });
175
+ }
176
+ }
177
+ }
178
+ export function renderRuntimeView(container, options = {}) {
179
+ return renderView(container, options, renderRuntimeSnapshot, 3);
180
+ }
181
+ export function renderConnectorsView(container, options = {}) {
182
+ return renderView(container, options, renderConnectorsSnapshot, 5);
183
+ }
@@ -14,16 +14,11 @@ const VIEWER_FRONTDOOR_CHANNEL_ID = 'mama_os_main';
14
14
  const PAGE_CONTEXT_REPUBLISH_MS = 5000;
15
15
  let lastReportedPageContext = null;
16
16
  let lastPageContextPublishAt = 0;
17
+ // The Viewer always reports into the one frontdoor channel. The per-chat
18
+ // session override this used to read is gone with the Chat tab that wrote it:
19
+ // nothing writes `mama_chat_session_id` any more, so reading it could only ever
20
+ // return a stale value.
17
21
  function getViewerChannelId() {
18
- try {
19
- const sessionId = window.localStorage.getItem('mama_chat_session_id');
20
- if (sessionId && !sessionId.startsWith('session_')) {
21
- return sessionId;
22
- }
23
- }
24
- catch {
25
- /* ignore */
26
- }
27
22
  return VIEWER_FRONTDOOR_CHANNEL_ID;
28
23
  }
29
24
  export function startUICommandPolling(switchTab) {
@@ -14,7 +14,6 @@ body{font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',sans-serif;backgrou
14
14
  .status-dot.connected{background:var(--success);box-shadow:0 0 6px var(--success)}
15
15
  .status-dot.disconnected{background:var(--error)}
16
16
  .status-dot.connecting{background:var(--warn);animation:pulse 1s infinite}
17
- .status-dot.ws-connected{background:#39d353;box-shadow:0 0 8px #39d353}
18
17
  @keyframes pulse{0%,100%{opacity:1}50%{opacity:.3}}
19
18
  @keyframes fadeNew{from{background:rgba(59,185,80,.18)}to{background:transparent}}
20
19
  .status-text{font-size:11px;color:var(--muted);min-width:100px}
@@ -24,7 +23,6 @@ body{font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',sans-serif;backgrou
24
23
  .btn:hover{border-color:var(--accent);color:var(--accent)}
25
24
  .btn.active{background:var(--accent);color:#fff;border-color:var(--accent)}
26
25
  .btn.paused{background:var(--warn);border-color:var(--warn);color:#000}
27
- .btn.ws-mode{background:#39d353;border-color:#39d353;color:#000}
28
26
  .sel-ctrl{background:var(--bg);border:1px solid var(--border);border-radius:5px;padding:3px 5px;color:var(--text);font-size:11px;font-family:inherit;outline:none}
29
27
  .toolbar{display:flex;align-items:center;gap:6px;padding:5px 14px;background:var(--surface);border-bottom:1px solid var(--border);flex-shrink:0;flex-wrap:wrap}
30
28
  .search-box{flex:1;min-width:120px;display:flex;align-items:center;gap:5px;background:var(--bg);border:1px solid var(--border);border-radius:5px;padding:3px 7px}
@@ -104,8 +102,6 @@ body{font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',sans-serif;backgrou
104
102
  .selection-bar .sel-count{font-weight:600}
105
103
  .selection-bar button{padding:3px 10px;font-size:11px;border:1px solid rgba(255,255,255,.3);background:rgba(255,255,255,.1);color:#fff;border-radius:6px;cursor:pointer}
106
104
  .selection-bar button:hover{background:rgba(255,255,255,.25)}
107
- .selection-bar .send-btn{background:#fff;color:var(--accent);font-weight:600;border:none}
108
- .selection-bar .send-btn:hover{background:#e6edf3}
109
105
 
110
106
  /* Log area + minimap */
111
107
  .log-wrap{flex:1;overflow:hidden;display:flex;flex-direction:column}
@@ -147,26 +143,6 @@ body{font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',sans-serif;backgrou
147
143
  .empty .ico{font-size:36px;opacity:.25}
148
144
  .empty p{font-size:11px}
149
145
  .empty kbd{background:var(--surface);border:1px solid var(--border);border-radius:3px;padding:1px 5px;font-size:10px}
150
- .prompt-panel{border-top:1px solid var(--border);background:var(--surface);flex-shrink:0}
151
- .prompt-hdr{display:flex;align-items:center;justify-content:space-between;padding:6px 14px;cursor:pointer;user-select:none}
152
- .prompt-hdr h3{font-size:11px;color:var(--accent);font-weight:600}
153
- .prompt-hdr .arrow{font-size:10px;color:var(--muted);transition:transform .2s}
154
- .prompt-hdr .arrow.open{transform:rotate(180deg)}
155
- .prompt-body{padding:0 14px 8px;display:none}
156
- .prompt-body.open{display:block}
157
- .presets{display:flex;gap:3px;flex-wrap:wrap;margin-bottom:6px}
158
- .preset{padding:2px 8px;font-size:10px;background:var(--bg);border:1px solid var(--border);border-radius:10px;color:var(--muted);cursor:pointer;transition:all .12s;font-family:inherit}
159
- .preset:hover{border-color:var(--accent);color:var(--accent)}
160
- .prompt-row{display:flex;gap:6px}
161
- .prompt-input{flex:1;background:var(--bg);border:1px solid var(--border);border-radius:6px;padding:6px 10px;color:var(--text);font-size:11px;font-family:inherit;resize:none;outline:none;min-height:40px;max-height:80px}
162
- .prompt-input:focus{border-color:var(--accent)}
163
- .prompt-input::placeholder{color:var(--muted)}
164
- .prompt-actions{display:flex;flex-direction:column;gap:3px}
165
- .send-btn-prompt{padding:5px 12px;background:var(--accent);color:#fff;border:none;border-radius:5px;font-size:11px;font-weight:600;cursor:pointer;font-family:inherit}
166
- .send-btn-prompt:hover{opacity:.9}
167
- .copy-btn-prompt{padding:5px 12px;background:var(--bg);color:var(--muted);border:1px solid var(--border);border-radius:5px;font-size:11px;cursor:pointer;font-family:inherit}
168
- .copy-btn-prompt:hover{color:var(--text);border-color:var(--muted)}
169
-
170
146
  /* Export dropdown */
171
147
  .export-dropdown{position:relative;display:inline-block}
172
148
  .export-menu{display:none;position:absolute;top:100%;right:0;margin-top:4px;background:var(--surface);border:1px solid var(--border);border-radius:6px;padding:4px 0;z-index:100;min-width:160px;box-shadow:0 4px 12px rgba(0,0,0,.4)}
@@ -184,7 +160,6 @@ body{font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',sans-serif;backgrou
184
160
  <span class="rate-badge" id="rateBadge" title="Lines per second">0 l/s</span>
185
161
  <div class="hdr-right">
186
162
  <button class="btn" id="liveBtn" title="Toggle live (L)">&#9654; Live</button>
187
- <button class="btn" id="wsBtn" title="WebSocket mode (W)">WS</button>
188
163
  <select class="sel-ctrl" id="intervalSel" title="Poll interval">
189
164
  <option value="1000">1s</option>
190
165
  <option value="2000" selected>2s</option>
@@ -279,7 +254,6 @@ body{font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',sans-serif;backgrou
279
254
  <div style="flex:1"></div>
280
255
  <button id="selPin" title="Pin selected lines">&#128204; Pin</button>
281
256
  <button id="selCopy">Copy</button>
282
- <button class="send-btn" id="selSend">Send to Chat</button>
283
257
  <button id="selClear">Clear</button>
284
258
  </div>
285
259
  <div class="log-wrap">
@@ -288,7 +262,7 @@ body{font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',sans-serif;backgrou
288
262
  <div class="empty" id="emptyState">
289
263
  <div class="ico">&#128203;</div>
290
264
  <p>daemon.log</p>
291
- <p><kbd>L</kbd> Live <kbd>W</kbd> WebSocket <kbd>/</kbd> Search <kbd>A</kbd> Auto-scroll <kbd>P</kbd> Pause <kbd>D</kbd> Stats</p>
265
+ <p><kbd>L</kbd> Live <kbd>/</kbd> Search <kbd>A</kbd> Auto-scroll <kbd>P</kbd> Pause <kbd>D</kbd> Stats</p>
292
266
  </div>
293
267
  </div>
294
268
  <div class="minimap" id="minimap">
@@ -296,27 +270,6 @@ body{font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',sans-serif;backgrou
296
270
  </div>
297
271
  </div>
298
272
  </div>
299
- <div class="prompt-panel">
300
- <div class="prompt-hdr" id="promptHdr">
301
- <h3>&#128172; Send to Chat</h3>
302
- <span class="arrow open" id="promptArrow">&#9660;</span>
303
- </div>
304
- <div class="prompt-body open" id="promptBody">
305
- <div class="presets">
306
- <button class="preset" data-p="errors">&#128308; Errors</button>
307
- <button class="preset" data-p="status">&#128202; Status</button>
308
- <button class="preset" data-p="slow">&#128012; Slow</button>
309
- <button class="preset" data-p="recent">&#128221; Recent</button>
310
- </div>
311
- <div class="prompt-row">
312
- <textarea class="prompt-input" id="promptInput" placeholder="루나에게 요청..."></textarea>
313
- <div class="prompt-actions">
314
- <button class="send-btn-prompt" id="promptSend">&#128228; Send</button>
315
- <button class="copy-btn-prompt" id="promptCopy">&#128203; Copy</button>
316
- </div>
317
- </div>
318
- </div>
319
- </div>
320
273
  <script>
321
274
  (function(){
322
275
  'use strict';
@@ -328,16 +281,10 @@ const NEW_LINE_ANIM_MS = 900;
328
281
  const AUTO_SCROLL_THRESHOLD = 50;
329
282
  const MAX_INCREMENTAL_LINES = 200;
330
283
  const PINS_STORAGE_KEY = 'mama-log-viewer-pins';
331
- const WS_RECONNECT_DELAY = 3000;
332
- const WS_MAX_RETRIES = 5;
333
284
  const HISTOGRAM_BUCKETS = 30;
334
285
 
335
286
  // Use same-origin by default so custom host/IP + non-default port keeps working.
336
287
  const API_BASE = location.protocol === 'file:' ? 'http://localhost:3847' : '';
337
- const WS_URL = location.protocol === 'file:'
338
- ? 'ws://localhost:3847/ws'
339
- : (location.protocol === 'https:' ? 'wss://' : 'ws://') + location.host + '/ws';
340
- const POST_MSG_ORIGIN = location.protocol === 'file:' ? '*' : location.origin;
341
288
 
342
289
  const S = {
343
290
  logs: [], autoScroll: true, selected: new Set(), lastClickIdx: -1,
@@ -348,7 +295,6 @@ const S = {
348
295
  activeSources: null, excludedSources: new Set(),
349
296
  activeChannels: null, excludedChannels: new Set(), sourcePanelOpen: false,
350
297
  regexMode: false,
351
- wsMode: false, ws: null, wsRetries: 0,
352
298
  pins: new Set(),
353
299
  pinsPanelOpen: false,
354
300
  statsDashOpen: false,
@@ -630,8 +576,7 @@ function fetchLogs() {
630
576
  }).then((data) => {
631
577
  const hasTotal = Number.isFinite(data.total);
632
578
  const displayTotal = hasTotal ? data.total : (data.truncated ? '~' + data.lines.length : data.lines.length);
633
- const modeLabel = S.wsMode ? 'WS+Poll' : 'Polling';
634
- setStatus('connected', modeLabel + ' (' + (parseInt($('intervalSel').value) / 1000) + 's) \u2022 ' + displayTotal + ' total');
579
+ setStatus('connected', 'Polling (' + (parseInt($('intervalSel').value) / 1000) + 's) \u2022 ' + displayTotal + ' total');
635
580
  const bytes = data.totalBytes || data.fileSize;
636
581
  if (bytes) $('fileInfo').textContent = (bytes / 1024 / 1024).toFixed(1) + 'MB';
637
582
 
@@ -688,77 +633,6 @@ function stopPolling() {
688
633
  setStatus('disconnected', 'Stopped \u2014 ' + S.logs.length + ' lines');
689
634
  }
690
635
 
691
- /* ===== WebSocket ===== */
692
- function connectWS() {
693
- if (S.ws && (S.ws.readyState === WebSocket.OPEN || S.ws.readyState === WebSocket.CONNECTING)) return;
694
-
695
- S.wsMode = true;
696
- $('wsBtn').classList.add('ws-mode');
697
- setStatus('connecting', 'WS connecting...');
698
-
699
- try {
700
- S.ws = new WebSocket(WS_URL);
701
- } catch (e) {
702
- fallbackToPolling('WS init failed');
703
- return;
704
- }
705
-
706
- S.ws.onopen = () => {
707
- S.wsRetries = 0;
708
- setStatus('ws-connected', 'WebSocket connected');
709
- S.ws.send(JSON.stringify({ type: 'subscribe', channel: 'logs' }));
710
- };
711
-
712
- S.ws.onmessage = (evt) => {
713
- try {
714
- const data = JSON.parse(evt.data);
715
- if (data.type === 'log' && data.line) {
716
- if (emptyState && emptyState.parentNode) emptyState.remove();
717
- addLines([data.line], false);
718
- } else if (data.type === 'logs' && data.lines) {
719
- addLines(data.lines, !S.initialized);
720
- S.initialized = true;
721
- }
722
- } catch (e) {
723
- if (typeof evt.data === 'string' && evt.data.trim()) {
724
- addLines([evt.data], false);
725
- }
726
- }
727
- };
728
-
729
- S.ws.onclose = () => {
730
- if (!S.wsMode) return;
731
- S.wsRetries++;
732
- if (S.wsRetries <= WS_MAX_RETRIES) {
733
- setStatus('connecting', 'WS reconnecting (' + S.wsRetries + '/' + WS_MAX_RETRIES + ')...');
734
- setTimeout(connectWS, WS_RECONNECT_DELAY);
735
- } else {
736
- fallbackToPolling('WS max retries reached');
737
- }
738
- };
739
-
740
- S.ws.onerror = () => {
741
- // onclose will fire after onerror
742
- };
743
- }
744
-
745
- function disconnectWS() {
746
- S.wsMode = false;
747
- $('wsBtn').classList.remove('ws-mode');
748
- if (S.ws) {
749
- S.ws.onclose = null;
750
- S.ws.close();
751
- S.ws = null;
752
- }
753
- S.wsRetries = 0;
754
- }
755
-
756
- function fallbackToPolling(reason) {
757
- disconnectWS();
758
- setStatus('disconnected', 'WS failed: ' + reason + ' \u2014 switching to polling');
759
- if (!S.live) startPolling();
760
- }
761
-
762
636
  /* ===== Export ===== */
763
637
  function downloadFile(content, filename) {
764
638
  const blob = new Blob([content], { type: 'text/plain;charset=utf-8' });
@@ -1169,14 +1043,6 @@ $('selCopy').addEventListener('click', () => {
1169
1043
  $('selCopy').textContent = '\u2705'; setTimeout(() => { $('selCopy').textContent = 'Copy'; }, 1200);
1170
1044
  });
1171
1045
  });
1172
- $('selSend').addEventListener('click', () => {
1173
- const text = getSelectedText();
1174
- const msg = '[\uc120\ud0dd\ub41c \ub85c\uadf8 ' + S.selected.size + '\uc904]:\n```\n' + text + '\n```';
1175
- window.parent.postMessage({ type: 'viewer:sendToChat', message: msg }, POST_MSG_ORIGIN);
1176
- S.selected.clear();
1177
- logArea.querySelectorAll('.ll.selected').forEach((el) => { el.classList.remove('selected'); });
1178
- updateSelectionBar();
1179
- });
1180
1046
  $('selClear').addEventListener('click', () => {
1181
1047
  S.selected.clear();
1182
1048
  logArea.querySelectorAll('.ll.selected').forEach((el) => { el.classList.remove('selected'); });
@@ -1211,16 +1077,6 @@ $('clearBtn').addEventListener('click', () => {
1211
1077
  if (S.statsDashOpen) updateStatsDashboard();
1212
1078
  });
1213
1079
 
1214
- $('wsBtn').addEventListener('click', () => {
1215
- if (S.wsMode) {
1216
- disconnectWS();
1217
- setStatus(S.live ? 'connected' : 'disconnected', S.live ? 'Polling mode' : 'WS disconnected');
1218
- } else {
1219
- connectWS();
1220
- if (!S.live) startPolling();
1221
- }
1222
- });
1223
-
1224
1080
  document.querySelectorAll('.lvl').forEach((el) => {
1225
1081
  el.addEventListener('click', () => {
1226
1082
  const lv = el.getAttribute('data-l');
@@ -1256,7 +1112,6 @@ document.addEventListener('keydown', (e) => {
1256
1112
  if (e.ctrlKey && e.key === 'f') { e.preventDefault(); $('searchInput').focus(); return; }
1257
1113
  if (e.key === '/') { e.preventDefault(); $('searchInput').focus(); return; }
1258
1114
  if (e.key === 'l' || e.key === 'L') { S.live ? stopPolling() : startPolling(); }
1259
- if (e.key === 'w' || e.key === 'W') { $('wsBtn').click(); }
1260
1115
  if (e.key === 'a' && !e.ctrlKey && !e.metaKey) {
1261
1116
  S.autoScroll = !S.autoScroll;
1262
1117
  $('autoBtn').classList.toggle('active', S.autoScroll);
@@ -1288,20 +1143,6 @@ document.addEventListener('keydown', (e) => {
1288
1143
  }
1289
1144
  });
1290
1145
 
1291
- /* ===== Prompt panel ===== */
1292
- $('promptHdr').addEventListener('click', () => {
1293
- $('promptBody').classList.toggle('open');
1294
- $('promptArrow').classList.toggle('open');
1295
- });
1296
-
1297
- const PRESETS = {
1298
- errors: 'Analyze ERROR, FAIL, ECONNREFUSED logs from daemon.log and explain causes and fixes',
1299
- status: 'Check current MAMA OS status (processes, memory, uptime, connections)',
1300
- slow: 'Find slow operations (timeout, slow, 1000ms+) and analyze performance bottlenecks',
1301
- recent: 'Summarize the last 50 log lines \u2014 what happened, any errors?',
1302
- agents: 'Show only agent activity: Dashboard Agent, Wiki Agent, Conductor, delegate, audit results'
1303
- };
1304
-
1305
1146
  // Agent Activity patterns for filtering and audit timeline
1306
1147
  const AGENT_PATTERNS = /Dashboard Agent|Wiki Agent|Conductor|Audit|delegate|Briefing published|Compilation complete|No changes detected|agent-refresh|wiki\/compile|Tool\] delegate/;
1307
1148
 
@@ -1337,13 +1178,6 @@ $('auditPanelToggle').addEventListener('click', () => {
1337
1178
  panel.style.display = isVisible ? 'none' : 'block';
1338
1179
  if (!isVisible) buildAuditTimeline();
1339
1180
  });
1340
- document.querySelectorAll('.preset').forEach((el) => {
1341
- el.addEventListener('click', () => {
1342
- $('promptInput').value = PRESETS[el.getAttribute('data-p')];
1343
- $('promptInput').focus();
1344
- });
1345
- });
1346
-
1347
1181
  // Toolbar: Agent filter button
1348
1182
  $('agentFilterBtn').addEventListener('click', () => {
1349
1183
  const isActive = $('agentFilterBtn').classList.toggle('active');
@@ -1358,30 +1192,6 @@ $('agentFilterBtn').addEventListener('click', () => {
1358
1192
  applyFilters();
1359
1193
  });
1360
1194
 
1361
- $('promptSend').addEventListener('click', () => {
1362
- const text = $('promptInput').value.trim();
1363
- if (!text) return;
1364
- let msg = text;
1365
- if (S.selected.size > 0) {
1366
- const selLogs = [];
1367
- S.selected.forEach((idx) => { if (S.logs[idx]) selLogs.push(S.logs[idx].raw); });
1368
- msg += '\n\n[\uc120\ud0dd\ub41c \ub85c\uadf8 ' + selLogs.length + '\uc904]:\n```\n' + selLogs.join('\n') + '\n```';
1369
- }
1370
- window.parent.postMessage({ type: 'viewer:sendToChat', message: msg }, POST_MSG_ORIGIN);
1371
- $('promptInput').value = '';
1372
- });
1373
- $('promptCopy').addEventListener('click', () => {
1374
- let text = $('promptInput').value;
1375
- if (S.selected.size > 0) {
1376
- const selLogs = [];
1377
- S.selected.forEach((idx) => { if (S.logs[idx]) selLogs.push(S.logs[idx].raw); });
1378
- text += '\n\n```\n' + selLogs.join('\n') + '\n```';
1379
- }
1380
- navigator.clipboard.writeText(text).then(() => {
1381
- $('promptCopy').textContent = '\u2705'; setTimeout(() => { $('promptCopy').innerHTML = '&#128203; Copy'; }, 1200);
1382
- });
1383
- });
1384
-
1385
1195
  window.addEventListener('message', (e) => {
1386
1196
  if (e.data && e.data.type === 'mama:logs') {
1387
1197
  const lines = e.data.content.split('\n').filter((l) => l.trim());
@@ -0,0 +1 @@
1
+ /*! tailwindcss v4.3.2 | MIT License | https://tailwindcss.com */@layer properties{@supports (((-webkit-hyphens:none)) and (not (margin-trim:inline))) or ((-moz-orient:inline) and (not (color:rgb(from red r g b)))){*,:before,:after,::backdrop{--tw-space-y-reverse:0;--tw-border-style:solid;--tw-font-weight:initial;--tw-tracking:initial;--tw-shadow:0 0 #0000;--tw-shadow-color:initial;--tw-shadow-alpha:100%;--tw-inset-shadow:0 0 #0000;--tw-inset-shadow-color:initial;--tw-inset-shadow-alpha:100%;--tw-ring-color:initial;--tw-ring-shadow:0 0 #0000;--tw-inset-ring-color:initial;--tw-inset-ring-shadow:0 0 #0000;--tw-ring-inset:initial;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-offset-shadow:0 0 #0000;--tw-blur:initial;--tw-brightness:initial;--tw-contrast:initial;--tw-grayscale:initial;--tw-hue-rotate:initial;--tw-invert:initial;--tw-opacity:initial;--tw-saturate:initial;--tw-sepia:initial;--tw-drop-shadow:initial;--tw-drop-shadow-color:initial;--tw-drop-shadow-alpha:100%;--tw-drop-shadow-size:initial}}}@layer theme{:root,:host{--font-sans:ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";--spacing:.25rem;--container-sm:24rem;--container-md:28rem;--container-4xl:56rem;--container-6xl:72rem;--text-xs:.75rem;--text-xs--line-height:calc(1 / .75);--text-sm:.875rem;--text-sm--line-height:calc(1.25 / .875);--text-base:1rem;--text-base--line-height: 1.5 ;--text-lg:1.125rem;--text-lg--line-height:calc(1.75 / 1.125);--font-weight-medium:500;--font-weight-semibold:600;--tracking-wide:.025em;--radius-lg:.5rem;--radius-xl:.75rem;--shadow-xs:0 1px 2px #0000000d;--default-transition-duration:.15s;--default-transition-timing-function:cubic-bezier(.4, 0, .2, 1);--default-font-family:var(--font-sans);--color-bg:#f9f8f6;--color-surface:#fff;--color-surface-hover:#f5f3f0;--color-surface-selected:#efece7;--color-surface-secondary:#f7f5f3;--color-agent:#ffce00;--color-agent-hover:#e6b800;--color-agent-strong:#7d5b00;--color-agent-light:#eddbf7;--color-on-agent:#131313;--color-text:#131313;--color-text-secondary:#616161;--color-text-tertiary:#8a8a8a;--color-border:#e3e3e3;--color-success:#047b5d;--color-warning:#ffb800;--color-danger:#d72c0d;--color-success-soft:#e7f5f0;--color-success-text:#04664e;--color-warning-soft:#fff4d6;--color-warning-text:#805c00;--shadow-drawer:-12px 0 32px #0000002e}}@layer base{#operator-root{color-scheme:light;height:100%;font-family:var(--default-font-family,ui-sans-serif, system-ui, sans-serif);-webkit-text-size-adjust:100%;-moz-tab-size:4;tab-size:4;font-size:16px;line-height:1.5}#operator-root,#operator-root *,#operator-root :before,#operator-root :after{box-sizing:border-box;border:0 solid;margin:0;padding:0}#operator-root h1,#operator-root h2,#operator-root h3,#operator-root h4{font-size:inherit;font-weight:inherit}#operator-root ol,#operator-root ul{list-style:none}#operator-root button,#operator-root input,#operator-root select,#operator-root textarea{font:inherit;color:inherit;letter-spacing:inherit;background-color:#0000;border-radius:0}#operator-root button,#operator-root [role=button]{cursor:pointer}#operator-root button:disabled{cursor:default}#operator-root img,#operator-root svg,#operator-root video,#operator-root canvas{vertical-align:middle;display:block}#operator-root img,#operator-root video{max-width:100%;height:auto}#operator-root table{border-collapse:collapse;text-indent:0;border-color:inherit}#operator-root ::placeholder{color:currentColor}@supports (color:color-mix(in lab,red,red)){#operator-root ::placeholder{color:color-mix(in srgb,currentColor 50%,transparent)}}#operator-root :where(a[href],button,input,select,textarea,summary,[tabindex]:not([tabindex="-1"])):focus-visible{outline:2px solid var(--color-agent-strong);outline-offset:2px}}@layer components;@layer utilities{.fixed{position:fixed}.container{width:100%}@media(min-width:40rem){.container{max-width:40rem}}@media(min-width:48rem){.container{max-width:48rem}}@media(min-width:64rem){.container{max-width:64rem}}@media(min-width:80rem){.container{max-width:80rem}}@media(min-width:96rem){.container{max-width:96rem}}.mx-auto{margin-inline:auto}.mt-0\.5{margin-top:calc(var(--spacing) * .5)}.mt-1{margin-top:var(--spacing)}.mt-2{margin-top:calc(var(--spacing) * 2)}.mt-3{margin-top:calc(var(--spacing) * 3)}.mb-2{margin-bottom:calc(var(--spacing) * 2)}.mb-4{margin-bottom:calc(var(--spacing) * 4)}.block{display:block}.flex{display:flex}.grid{display:grid}.hidden{display:none}.h-4{height:calc(var(--spacing) * 4)}.h-full{height:100%}.min-h-0{min-height:0}.w-4{width:calc(var(--spacing) * 4)}.w-full{width:100%}.max-w-4xl{max-width:var(--container-4xl)}.max-w-6xl{max-width:var(--container-6xl)}.max-w-48{max-width:calc(var(--spacing) * 48)}.max-w-80{max-width:calc(var(--spacing) * 80)}.max-w-md{max-width:var(--container-md)}.min-w-0{min-width:0}.min-w-56{min-width:calc(var(--spacing) * 56)}.min-w-64{min-width:calc(var(--spacing) * 64)}.flex-1{flex:1}.flex-shrink-0,.shrink-0{flex-shrink:0}.rotate-90{rotate:90deg}.cursor-pointer{cursor:pointer}.scroll-mt-4{scroll-margin-top:calc(var(--spacing) * 4)}.list-decimal{list-style-type:decimal}.list-disc{list-style-type:disc}.grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.flex-col{flex-direction:column}.flex-wrap{flex-wrap:wrap}.items-center{align-items:center}.items-start{align-items:flex-start}.justify-between{justify-content:space-between}.justify-center{justify-content:center}.gap-2{gap:calc(var(--spacing) * 2)}.gap-3{gap:calc(var(--spacing) * 3)}.gap-4{gap:calc(var(--spacing) * 4)}:where(.space-y-1>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(var(--spacing) * var(--tw-space-y-reverse));margin-block-end:calc(var(--spacing) * calc(1 - var(--tw-space-y-reverse)))}:where(.space-y-3>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing) * 3) * var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing) * 3) * calc(1 - var(--tw-space-y-reverse)))}:where(.space-y-6>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing) * 6) * var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing) * 6) * calc(1 - var(--tw-space-y-reverse)))}.truncate{text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.overflow-hidden{overflow:hidden}.overflow-x-auto{overflow-x:auto}.overflow-y-auto{overflow-y:auto}.rounded{border-radius:.25rem}.rounded-full{border-radius:3.40282e38px}.rounded-lg{border-radius:var(--radius-lg)}.rounded-xl{border-radius:var(--radius-xl)}.border{border-style:var(--tw-border-style);border-width:1px}.border-0{border-style:var(--tw-border-style);border-width:0}.border-t{border-top-style:var(--tw-border-style);border-top-width:1px}.border-b{border-bottom-style:var(--tw-border-style);border-bottom-width:1px}.border-border{border-color:var(--color-border)}.bg-agent{background-color:var(--color-agent)}.bg-agent-light{background-color:var(--color-agent-light)}.bg-bg{background-color:var(--color-bg)}.bg-danger{background-color:var(--color-danger)}.bg-danger\/10{background-color:#d72c0d1a}@supports (color:color-mix(in lab,red,red)){.bg-danger\/10{background-color:color-mix(in oklab,var(--color-danger) 10%,transparent)}}.bg-success-soft{background-color:var(--color-success-soft)}.bg-surface{background-color:var(--color-surface)}.bg-surface-hover{background-color:var(--color-surface-hover)}.bg-surface-secondary{background-color:var(--color-surface-secondary)}.bg-surface-selected{background-color:var(--color-surface-selected)}.bg-warning-soft{background-color:var(--color-warning-soft)}.p-4{padding:calc(var(--spacing) * 4)}.px-2{padding-inline:calc(var(--spacing) * 2)}.px-2\.5{padding-inline:calc(var(--spacing) * 2.5)}.px-3{padding-inline:calc(var(--spacing) * 3)}.px-4{padding-inline:calc(var(--spacing) * 4)}.px-5{padding-inline:calc(var(--spacing) * 5)}.px-6{padding-inline:calc(var(--spacing) * 6)}.py-0\.5{padding-block:calc(var(--spacing) * .5)}.py-1{padding-block:var(--spacing)}.py-1\.5{padding-block:calc(var(--spacing) * 1.5)}.py-2{padding-block:calc(var(--spacing) * 2)}.py-3{padding-block:calc(var(--spacing) * 3)}.py-4{padding-block:calc(var(--spacing) * 4)}.py-5{padding-block:calc(var(--spacing) * 5)}.py-16{padding-block:calc(var(--spacing) * 16)}.pt-5{padding-top:calc(var(--spacing) * 5)}.pl-1{padding-left:var(--spacing)}.pl-5{padding-left:calc(var(--spacing) * 5)}.text-center{text-align:center}.text-left{text-align:left}.text-base{font-size:var(--text-base);line-height:var(--tw-leading,var(--text-base--line-height))}.text-lg{font-size:var(--text-lg);line-height:var(--tw-leading,var(--text-lg--line-height))}.text-sm{font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height))}.text-xs{font-size:var(--text-xs);line-height:var(--tw-leading,var(--text-xs--line-height))}.text-\[10px\]{font-size:10px}.text-\[11px\]{font-size:11px}.font-medium{--tw-font-weight:var(--font-weight-medium);font-weight:var(--font-weight-medium)}.font-semibold{--tw-font-weight:var(--font-weight-semibold);font-weight:var(--font-weight-semibold)}.tracking-wide{--tw-tracking:var(--tracking-wide);letter-spacing:var(--tracking-wide)}.break-words{overflow-wrap:break-word}.break-all{word-break:break-all}.whitespace-normal{white-space:normal}.whitespace-nowrap{white-space:nowrap}.whitespace-pre-wrap{white-space:pre-wrap}.text-agent-strong{color:var(--color-agent-strong)}.text-danger{color:var(--color-danger)}.text-on-agent{color:var(--color-on-agent)}.text-success{color:var(--color-success)}.text-success-text{color:var(--color-success-text)}.text-text{color:var(--color-text)}.text-text-secondary{color:var(--color-text-secondary)}.text-text-tertiary{color:var(--color-text-tertiary)}.text-warning-text{color:var(--color-warning-text)}.uppercase{text-transform:uppercase}.underline-offset-2{text-underline-offset:2px}.shadow-\[var\(--shadow-xs\)\]{--tw-shadow:var(--shadow-xs);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.filter{filter:var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,)}.transition-colors{transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.transition-transform{transition-property:transform,translate,scale,rotate;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.last\:border-0:last-child{border-style:var(--tw-border-style);border-width:0}@media(hover:hover){.hover\:bg-agent-hover:hover{background-color:var(--color-agent-hover)}.hover\:bg-surface-hover:hover{background-color:var(--color-surface-hover)}.hover\:bg-surface-selected:hover{background-color:var(--color-surface-selected)}.hover\:underline:hover{text-decoration-line:underline}.hover\:opacity-80:hover{opacity:.8}}.focus\:ring-2:focus{--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.focus\:ring-agent-strong:focus{--tw-ring-color:var(--color-agent-strong)}.disabled\:opacity-40:disabled{opacity:.4}.disabled\:opacity-50:disabled{opacity:.5}@media(min-width:40rem){.sm\:max-w-sm{max-width:var(--container-sm)}.sm\:flex-row{flex-direction:row}.sm\:items-end{align-items:flex-end}.sm\:justify-between{justify-content:space-between}}@media(min-width:48rem){.md\:grid-cols-4{grid-template-columns:repeat(4,minmax(0,1fr))}}}@media(prefers-reduced-motion:reduce){#operator-root *,#operator-root :before,#operator-root :after{scroll-behavior:auto!important;transition-duration:.01ms!important;animation-duration:.01ms!important;animation-iteration-count:1!important}}.trigger-drawer,.task-drawer{border:0;border-left:1px solid var(--color-border);background:var(--color-surface);width:min(100%,32rem);height:100dvh;max-height:100dvh;color:var(--color-text);box-shadow:var(--shadow-drawer);margin:0 0 0 auto;padding:0;overflow:hidden}.trigger-drawer::backdrop,.task-drawer::backdrop{background:#13131361}@supports (color:color-mix(in lab,red,red)){.trigger-drawer::backdrop,.task-drawer::backdrop{background:color-mix(in srgb,var(--color-text) 38%,transparent)}}.report-summary{margin-bottom:12px}.summary-title{color:var(--color-text);margin-bottom:4px;font-size:14px;font-weight:600}.summary-stats{color:var(--color-text-secondary);font-size:12px}.stat-highlight{color:var(--color-agent-strong);font-weight:700}.report-section-title{color:var(--color-text);margin:4px 0 10px;font-size:13px;font-weight:600}.report-card{border:1px solid var(--color-border);background:var(--color-surface);border-radius:10px;margin-bottom:8px;padding:10px 12px}.card-header{justify-content:space-between;align-items:center;gap:8px;display:flex}.card-title{color:var(--color-text);text-overflow:ellipsis;white-space:nowrap;min-width:0;font-size:13px;font-weight:600;overflow:hidden}.card-badge{border-radius:999px;flex-shrink:0;padding:2px 8px;font-size:11px;font-weight:500}.badge-danger{color:var(--color-danger);background:#d72c0d1f}@supports (color:color-mix(in lab,red,red)){.badge-danger{background:color-mix(in srgb,var(--color-danger) 12%,transparent)}}.badge-warning{color:var(--color-warning-text);background:#ffb80029}@supports (color:color-mix(in lab,red,red)){.badge-warning{background:color-mix(in srgb,var(--color-warning) 16%,transparent)}}.badge-info{color:var(--color-agent-strong);background:var(--color-agent-light)}.badge-success{color:var(--color-success);background:#047b5d1f}@supports (color:color-mix(in lab,red,red)){.badge-success{background:color-mix(in srgb,var(--color-success) 12%,transparent)}}.card-tags{flex-wrap:wrap;gap:4px;margin-top:4px;display:flex}.tag{background:var(--color-surface-secondary);color:var(--color-text-tertiary);border-radius:999px;padding:1px 6px;font-size:10px}.tag-channel{background:var(--color-agent-light);color:var(--color-agent-strong)}.card-action{color:var(--color-text-secondary);margin-top:6px;font-size:12px}.card-action:before{content:"→ ";color:var(--color-agent-strong)}.report-table{border-collapse:collapse;width:100%;min-width:640px;font-size:12px}.report-slot-content{overflow-wrap:anywhere;min-width:0;max-width:100%;overflow-x:auto}.report-table th{text-align:left;color:var(--color-text-tertiary);border-bottom:1px solid var(--color-border);padding:6px 8px;font-weight:600}.report-table td{border-bottom:1px solid var(--color-border);color:var(--color-text-secondary);padding:6px 8px}.report-table tr:last-child td{border-bottom:none}.task-table{border-collapse:collapse;width:100%;min-width:1040px}.task-table th{border-bottom:1px solid var(--color-border);background:var(--color-surface-secondary);color:var(--color-text-secondary);text-align:left;white-space:nowrap;padding:10px 12px;font-size:11px;font-weight:600}.task-reference-link{color:var(--color-agent-strong);text-underline-offset:2px;font-weight:600;text-decoration:underline}.task-reference-link:hover{color:var(--color-text)}@property --tw-space-y-reverse{syntax:"*";inherits:false;initial-value:0}@property --tw-border-style{syntax:"*";inherits:false;initial-value:solid}@property --tw-font-weight{syntax:"*";inherits:false}@property --tw-tracking{syntax:"*";inherits:false}@property --tw-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-shadow-color{syntax:"*";inherits:false}@property --tw-shadow-alpha{syntax:"<percentage>";inherits:false;initial-value:100%}@property --tw-inset-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-inset-shadow-color{syntax:"*";inherits:false}@property --tw-inset-shadow-alpha{syntax:"<percentage>";inherits:false;initial-value:100%}@property --tw-ring-color{syntax:"*";inherits:false}@property --tw-ring-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-inset-ring-color{syntax:"*";inherits:false}@property --tw-inset-ring-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-ring-inset{syntax:"*";inherits:false}@property --tw-ring-offset-width{syntax:"<length>";inherits:false;initial-value:0}@property --tw-ring-offset-color{syntax:"*";inherits:false;initial-value:#fff}@property --tw-ring-offset-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-blur{syntax:"*";inherits:false}@property --tw-brightness{syntax:"*";inherits:false}@property --tw-contrast{syntax:"*";inherits:false}@property --tw-grayscale{syntax:"*";inherits:false}@property --tw-hue-rotate{syntax:"*";inherits:false}@property --tw-invert{syntax:"*";inherits:false}@property --tw-opacity{syntax:"*";inherits:false}@property --tw-saturate{syntax:"*";inherits:false}@property --tw-sepia{syntax:"*";inherits:false}@property --tw-drop-shadow{syntax:"*";inherits:false}@property --tw-drop-shadow-color{syntax:"*";inherits:false}@property --tw-drop-shadow-alpha{syntax:"<percentage>";inherits:false;initial-value:100%}@property --tw-drop-shadow-size{syntax:"*";inherits:false}