agentgui 1.0.150 → 1.0.152
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.
- package/database.js +87 -66
- package/package.json +1 -1
- package/server.js +245 -119
- package/static/index.html +29 -12
- package/static/js/client.js +7 -6
- package/static/js/streaming-renderer.js +55 -18
- package/static/js/websocket-manager.js +16 -27
- package/static/styles.css +0 -1989
package/static/index.html
CHANGED
|
@@ -7,10 +7,14 @@
|
|
|
7
7
|
<title>AgentGUI</title>
|
|
8
8
|
<link rel="icon" type="image/svg+xml" href="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'%3E%3Crect width='100' height='100' rx='20' fill='%233b82f6'/%3E%3Ctext x='50' y='68' font-size='50' font-family='sans-serif' font-weight='bold' fill='white' text-anchor='middle'%3EG%3C/text%3E%3C/svg%3E">
|
|
9
9
|
|
|
10
|
+
<link rel="preconnect" href="https://cdn.jsdelivr.net" crossorigin>
|
|
11
|
+
<link rel="preconnect" href="https://cdnjs.cloudflare.com" crossorigin>
|
|
10
12
|
<link href="https://cdn.jsdelivr.net/npm/rippleui@1.12.1/dist/css/styles.css" rel="stylesheet">
|
|
11
|
-
<link href="https://cdn.jsdelivr.net/npm/prismjs@1.29.0/themes/prism-dark.css" rel=
|
|
12
|
-
<link href="https://
|
|
13
|
-
<
|
|
13
|
+
<link rel="preload" href="https://cdn.jsdelivr.net/npm/prismjs@1.29.0/themes/prism-dark.css" as="style" onload="this.onload=null;this.rel='stylesheet'">
|
|
14
|
+
<noscript><link href="https://cdn.jsdelivr.net/npm/prismjs@1.29.0/themes/prism-dark.css" rel="stylesheet"></noscript>
|
|
15
|
+
<link rel="preload" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.11.1/styles/github-dark.min.css" as="style" onload="this.onload=null;this.rel='stylesheet'">
|
|
16
|
+
<noscript><link href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.11.1/styles/github-dark.min.css" rel="stylesheet"></noscript>
|
|
17
|
+
<script defer src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.11.1/highlight.min.js"></script>
|
|
14
18
|
|
|
15
19
|
<style>
|
|
16
20
|
*, *::before, *::after { box-sizing: border-box; }
|
|
@@ -1158,6 +1162,19 @@
|
|
|
1158
1162
|
font-size: 0.9rem;
|
|
1159
1163
|
}
|
|
1160
1164
|
|
|
1165
|
+
.block-text + .block-text {
|
|
1166
|
+
margin-top: -0.75rem;
|
|
1167
|
+
padding-top: 0;
|
|
1168
|
+
border-top-left-radius: 0;
|
|
1169
|
+
border-top-right-radius: 0;
|
|
1170
|
+
}
|
|
1171
|
+
|
|
1172
|
+
.block-text:has(+ .block-text) {
|
|
1173
|
+
margin-bottom: 0;
|
|
1174
|
+
border-bottom-left-radius: 0;
|
|
1175
|
+
border-bottom-right-radius: 0;
|
|
1176
|
+
}
|
|
1177
|
+
|
|
1161
1178
|
.block-code {
|
|
1162
1179
|
margin-bottom: 0.75rem;
|
|
1163
1180
|
border-radius: 0.5rem;
|
|
@@ -2060,16 +2077,16 @@
|
|
|
2060
2077
|
</div>
|
|
2061
2078
|
</div>
|
|
2062
2079
|
|
|
2063
|
-
<script src="/gm/js/event-processor.js"></script>
|
|
2064
|
-
<script src="/gm/js/streaming-renderer.js"></script>
|
|
2065
|
-
<script src="/gm/js/websocket-manager.js"></script>
|
|
2066
|
-
<script src="/gm/js/event-filter.js"></script>
|
|
2067
|
-
<script src="/gm/js/syntax-highlighter.js"></script>
|
|
2068
|
-
<script src="/gm/js/ui-components.js"></script>
|
|
2069
|
-
<script src="/gm/js/conversations.js"></script>
|
|
2070
|
-
<script src="/gm/js/client.js"></script>
|
|
2080
|
+
<script defer src="/gm/js/event-processor.js"></script>
|
|
2081
|
+
<script defer src="/gm/js/streaming-renderer.js"></script>
|
|
2082
|
+
<script defer src="/gm/js/websocket-manager.js"></script>
|
|
2083
|
+
<script defer src="/gm/js/event-filter.js"></script>
|
|
2084
|
+
<script defer src="/gm/js/syntax-highlighter.js"></script>
|
|
2085
|
+
<script defer src="/gm/js/ui-components.js"></script>
|
|
2086
|
+
<script defer src="/gm/js/conversations.js"></script>
|
|
2087
|
+
<script defer src="/gm/js/client.js"></script>
|
|
2071
2088
|
<script type="module" src="/gm/js/voice.js"></script>
|
|
2072
|
-
<script src="/gm/js/features.js"></script>
|
|
2089
|
+
<script defer src="/gm/js/features.js"></script>
|
|
2073
2090
|
|
|
2074
2091
|
<script>
|
|
2075
2092
|
const savedTheme = localStorage.getItem('theme') || 'light';
|
package/static/js/client.js
CHANGED
|
@@ -544,12 +544,13 @@ class AgentGUIClient {
|
|
|
544
544
|
}
|
|
545
545
|
|
|
546
546
|
scrollToBottom() {
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
547
|
+
if (this._scrollRafPending) return;
|
|
548
|
+
this._scrollRafPending = true;
|
|
549
|
+
requestAnimationFrame(() => {
|
|
550
|
+
this._scrollRafPending = false;
|
|
551
|
+
const scrollContainer = document.getElementById('output-scroll');
|
|
552
|
+
if (scrollContainer) scrollContainer.scrollTop = scrollContainer.scrollHeight;
|
|
553
|
+
});
|
|
553
554
|
}
|
|
554
555
|
|
|
555
556
|
handleStreamingError(data) {
|
|
@@ -135,12 +135,17 @@ class StreamingRenderer {
|
|
|
135
135
|
const lastTime = this.dedupMap.get(key);
|
|
136
136
|
const now = Date.now();
|
|
137
137
|
|
|
138
|
-
// Deduplicate within 100ms window
|
|
139
138
|
if (lastTime && (now - lastTime) < 100) {
|
|
140
139
|
return true;
|
|
141
140
|
}
|
|
142
141
|
|
|
143
142
|
this.dedupMap.set(key, now);
|
|
143
|
+
if (this.dedupMap.size > 5000) {
|
|
144
|
+
const cutoff = now - 1000;
|
|
145
|
+
for (const [k, t] of this.dedupMap) {
|
|
146
|
+
if (t < cutoff) this.dedupMap.delete(k);
|
|
147
|
+
}
|
|
148
|
+
}
|
|
144
149
|
return false;
|
|
145
150
|
}
|
|
146
151
|
|
|
@@ -358,17 +363,25 @@ class StreamingRenderer {
|
|
|
358
363
|
* Render text block with semantic HTML
|
|
359
364
|
*/
|
|
360
365
|
renderBlockText(block, context) {
|
|
361
|
-
const div = document.createElement('div');
|
|
362
|
-
div.className = 'block-text';
|
|
363
|
-
|
|
364
366
|
const text = block.text || '';
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
367
|
+
const isHtml = this.containsHtmlTags(text);
|
|
368
|
+
const cached = this.renderCache.get(text);
|
|
369
|
+
const html = cached || (isHtml ? this.sanitizeHtml(text) : this.parseAndRenderMarkdown(text));
|
|
370
|
+
|
|
371
|
+
if (!cached && this.renderCache.size < 2000) {
|
|
372
|
+
this.renderCache.set(text, html);
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
const lastChild = this.outputContainer && this.outputContainer.lastElementChild;
|
|
376
|
+
if (lastChild && lastChild.classList.contains('block-text') && !isHtml && !lastChild.classList.contains('html-content')) {
|
|
377
|
+
lastChild.innerHTML += html;
|
|
378
|
+
return null;
|
|
370
379
|
}
|
|
371
380
|
|
|
381
|
+
const div = document.createElement('div');
|
|
382
|
+
div.className = 'block-text';
|
|
383
|
+
if (isHtml) div.classList.add('html-content');
|
|
384
|
+
div.innerHTML = html;
|
|
372
385
|
return div;
|
|
373
386
|
}
|
|
374
387
|
|
|
@@ -447,11 +460,9 @@ class StreamingRenderer {
|
|
|
447
460
|
|
|
448
461
|
const preStyle = "background:#1e293b;padding:1rem;border-radius:0 0 0.375rem 0.375rem;overflow-x:auto;font-family:'Monaco','Menlo','Ubuntu Mono',monospace;font-size:0.875rem;line-height:1.6;color:#e2e8f0;border:1px solid #334155;border-top:none;margin:0";
|
|
449
462
|
const codeContainer = document.createElement('div');
|
|
463
|
+
codeContainer.innerHTML = `<pre style="${preStyle}"><code>${this.escapeHtml(code)}</code></pre>`;
|
|
450
464
|
if (typeof hljs !== 'undefined') {
|
|
451
|
-
|
|
452
|
-
codeContainer.innerHTML = `<pre style="${preStyle}"><code class="hljs">${result.value}</code></pre>`;
|
|
453
|
-
} else {
|
|
454
|
-
codeContainer.innerHTML = `<pre style="${preStyle}"><code>${this.escapeHtml(code)}</code></pre>`;
|
|
465
|
+
this.lazyHighlight(codeContainer, code);
|
|
455
466
|
}
|
|
456
467
|
|
|
457
468
|
details.appendChild(summary);
|
|
@@ -1785,13 +1796,39 @@ class StreamingRenderer {
|
|
|
1785
1796
|
* Auto-scroll to bottom of container
|
|
1786
1797
|
*/
|
|
1787
1798
|
autoScroll() {
|
|
1788
|
-
if (this.
|
|
1789
|
-
|
|
1790
|
-
|
|
1791
|
-
|
|
1792
|
-
|
|
1799
|
+
if (this._scrollRafPending) return;
|
|
1800
|
+
this._scrollRafPending = true;
|
|
1801
|
+
requestAnimationFrame(() => {
|
|
1802
|
+
this._scrollRafPending = false;
|
|
1803
|
+
if (this.scrollContainer) {
|
|
1804
|
+
try { this.scrollContainer.scrollTop = this.scrollContainer.scrollHeight; } catch (_) {}
|
|
1793
1805
|
}
|
|
1806
|
+
});
|
|
1807
|
+
}
|
|
1808
|
+
|
|
1809
|
+
lazyHighlight(container, code) {
|
|
1810
|
+
if (!this._hlObserver) {
|
|
1811
|
+
this._hlObserver = new IntersectionObserver((entries) => {
|
|
1812
|
+
for (const entry of entries) {
|
|
1813
|
+
if (!entry.isIntersecting) continue;
|
|
1814
|
+
const el = entry.target;
|
|
1815
|
+
const raw = el._rawCode;
|
|
1816
|
+
if (!raw) continue;
|
|
1817
|
+
this._hlObserver.unobserve(el);
|
|
1818
|
+
try {
|
|
1819
|
+
const codeEl = el.querySelector('code');
|
|
1820
|
+
if (codeEl && typeof hljs !== 'undefined') {
|
|
1821
|
+
const result = hljs.highlightAuto(raw);
|
|
1822
|
+
codeEl.classList.add('hljs');
|
|
1823
|
+
codeEl.innerHTML = result.value;
|
|
1824
|
+
}
|
|
1825
|
+
} catch (_) {}
|
|
1826
|
+
delete el._rawCode;
|
|
1827
|
+
}
|
|
1828
|
+
}, { rootMargin: '200px' });
|
|
1794
1829
|
}
|
|
1830
|
+
container._rawCode = code;
|
|
1831
|
+
this._hlObserver.observe(container);
|
|
1795
1832
|
}
|
|
1796
1833
|
|
|
1797
1834
|
updateVirtualScroll() {
|
|
@@ -137,26 +137,23 @@ class WebSocketManager {
|
|
|
137
137
|
*/
|
|
138
138
|
onMessage(event) {
|
|
139
139
|
try {
|
|
140
|
-
const
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
140
|
+
const parsed = JSON.parse(event.data);
|
|
141
|
+
const messages = Array.isArray(parsed) ? parsed : [parsed];
|
|
142
|
+
this.stats.totalMessagesReceived += messages.length;
|
|
143
|
+
|
|
144
|
+
for (const data of messages) {
|
|
145
|
+
if (data.type === 'pong') {
|
|
146
|
+
const requestId = data.requestId;
|
|
147
|
+
if (requestId && this.requestMap.has(requestId)) {
|
|
148
|
+
const request = this.requestMap.get(requestId);
|
|
149
|
+
request.resolve({ latency: Date.now() - request.sentTime });
|
|
150
|
+
this.requestMap.delete(requestId);
|
|
151
|
+
}
|
|
152
|
+
continue;
|
|
150
153
|
}
|
|
151
|
-
return;
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
// Route message to listeners
|
|
155
|
-
this.emit('message', data);
|
|
156
154
|
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
this.emit(`message:${data.type}`, data);
|
|
155
|
+
this.emit('message', data);
|
|
156
|
+
if (data.type) this.emit(`message:${data.type}`, data);
|
|
160
157
|
}
|
|
161
158
|
} catch (error) {
|
|
162
159
|
console.error('WebSocket message parse error:', error);
|
|
@@ -234,15 +231,7 @@ class WebSocketManager {
|
|
|
234
231
|
* Start heartbeat/keepalive
|
|
235
232
|
*/
|
|
236
233
|
startHeartbeat() {
|
|
237
|
-
if (this.heartbeatTimer)
|
|
238
|
-
clearTimeout(this.heartbeatTimer);
|
|
239
|
-
}
|
|
240
|
-
|
|
241
|
-
this.heartbeatTimer = setInterval(() => {
|
|
242
|
-
if (this.isConnected) {
|
|
243
|
-
this.ping();
|
|
244
|
-
}
|
|
245
|
-
}, this.config.heartbeatInterval);
|
|
234
|
+
if (this.heartbeatTimer) clearTimeout(this.heartbeatTimer);
|
|
246
235
|
}
|
|
247
236
|
|
|
248
237
|
/**
|