agentgui 1.0.742 → 1.0.743
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/lib/ws-handlers-conv.js +18 -0
- package/package.json +1 -1
- package/server.js +50 -0
- package/static/js/client.js +27 -0
- package/static/js/streaming-renderer.js +11 -0
package/lib/ws-handlers-conv.js
CHANGED
|
@@ -117,6 +117,24 @@ export function register(router, deps) {
|
|
|
117
117
|
return { ok: true, chunks: result.chunks, total: result.total, hasMore: result.hasMore, limit: result.limit };
|
|
118
118
|
});
|
|
119
119
|
|
|
120
|
+
router.handle('conv.export', (p) => {
|
|
121
|
+
const conv = queries.getConversation(p.id);
|
|
122
|
+
if (!conv) notFound();
|
|
123
|
+
const msgs = queries.getConversationMessages(p.id);
|
|
124
|
+
const format = p.format || 'markdown';
|
|
125
|
+
if (format === 'json') return { conversation: conv, messages: msgs };
|
|
126
|
+
let md = `# ${conv.title || 'Conversation'}\n\n`;
|
|
127
|
+
md += `Agent: ${conv.agentType || 'unknown'} | Created: ${new Date(conv.created_at).toISOString()}\n\n---\n\n`;
|
|
128
|
+
for (const m of msgs) {
|
|
129
|
+
const role = m.role === 'user' ? 'User' : 'Assistant';
|
|
130
|
+
md += `## ${role}\n\n`;
|
|
131
|
+
let content = m.content;
|
|
132
|
+
try { const parsed = JSON.parse(content); if (Array.isArray(parsed)) { content = parsed.map(b => b.text || b.content || JSON.stringify(b)).join('\n'); } } catch {}
|
|
133
|
+
md += content + '\n\n---\n\n';
|
|
134
|
+
}
|
|
135
|
+
return { markdown: md, title: conv.title };
|
|
136
|
+
});
|
|
137
|
+
|
|
120
138
|
router.handle('conv.cancel', (p) => {
|
|
121
139
|
if (!execMachine.isActive(p.id)) notFound('No active execution to cancel');
|
|
122
140
|
const ctx = execMachine.getContext(p.id);
|
package/package.json
CHANGED
package/server.js
CHANGED
|
@@ -3684,6 +3684,41 @@ const _assetCache = new LRUCache({ max: 200 });
|
|
|
3684
3684
|
let _htmlCache = null;
|
|
3685
3685
|
let _htmlCacheEtag = null;
|
|
3686
3686
|
|
|
3687
|
+
function warmAssetCache() {
|
|
3688
|
+
const dirs = ['js', 'css', 'lib', 'vendor'];
|
|
3689
|
+
let count = 0;
|
|
3690
|
+
for (const dir of dirs) {
|
|
3691
|
+
const full = path.join(staticDir, dir);
|
|
3692
|
+
if (!fs.existsSync(full)) continue;
|
|
3693
|
+
for (const file of fs.readdirSync(full)) {
|
|
3694
|
+
const filePath = path.join(full, file);
|
|
3695
|
+
try {
|
|
3696
|
+
const stats = fs.statSync(filePath);
|
|
3697
|
+
if (!stats.isFile()) continue;
|
|
3698
|
+
const etag = generateETag(stats);
|
|
3699
|
+
if (_assetCache.has(etag)) continue;
|
|
3700
|
+
const raw = fs.readFileSync(filePath);
|
|
3701
|
+
const entry = raw.length < 860 ? { raw, gz: null } : { raw, gz: zlib.gzipSync(raw, { level: 6 }) };
|
|
3702
|
+
_assetCache.set(etag, entry);
|
|
3703
|
+
count++;
|
|
3704
|
+
} catch (_) {}
|
|
3705
|
+
}
|
|
3706
|
+
}
|
|
3707
|
+
for (const file of ['app.js', 'theme.js']) {
|
|
3708
|
+
const filePath = path.join(staticDir, file);
|
|
3709
|
+
try {
|
|
3710
|
+
const stats = fs.statSync(filePath);
|
|
3711
|
+
const etag = generateETag(stats);
|
|
3712
|
+
if (!_assetCache.has(etag)) {
|
|
3713
|
+
const raw = fs.readFileSync(filePath);
|
|
3714
|
+
_assetCache.set(etag, raw.length < 860 ? { raw, gz: null } : { raw, gz: zlib.gzipSync(raw, { level: 6 }) });
|
|
3715
|
+
count++;
|
|
3716
|
+
}
|
|
3717
|
+
} catch (_) {}
|
|
3718
|
+
}
|
|
3719
|
+
if (count > 0) console.log(`[CACHE] Pre-warmed ${count} static assets`);
|
|
3720
|
+
}
|
|
3721
|
+
|
|
3687
3722
|
function serveFile(filePath, res, req) {
|
|
3688
3723
|
const ext = path.extname(filePath).toLowerCase();
|
|
3689
3724
|
const contentType = MIME_TYPES[ext] || 'application/octet-stream';
|
|
@@ -5109,6 +5144,7 @@ function onServerReady() {
|
|
|
5109
5144
|
}
|
|
5110
5145
|
|
|
5111
5146
|
recoverStaleSessions();
|
|
5147
|
+
warmAssetCache();
|
|
5112
5148
|
|
|
5113
5149
|
try {
|
|
5114
5150
|
jsonlWatcher = new JsonlWatcher({ broadcastSync, queries, ownedSessionIds });
|
|
@@ -5120,6 +5156,20 @@ function onServerReady() {
|
|
|
5120
5156
|
|
|
5121
5157
|
resumeInterruptedStreams().catch(err => console.error('[RESUME] Startup error:', err.message));
|
|
5122
5158
|
|
|
5159
|
+
setInterval(() => {
|
|
5160
|
+
try {
|
|
5161
|
+
const streaming = queries.getStreamingConversations();
|
|
5162
|
+
let cleared = 0;
|
|
5163
|
+
for (const c of streaming) {
|
|
5164
|
+
if (!activeExecutions.has(c.id)) {
|
|
5165
|
+
queries.setIsStreaming(c.id, false);
|
|
5166
|
+
cleared++;
|
|
5167
|
+
}
|
|
5168
|
+
}
|
|
5169
|
+
if (cleared > 0) debugLog(`[HEALTH] Cleared ${cleared} stale streaming flag(s)`);
|
|
5170
|
+
} catch (e) { debugLog(`[HEALTH] Error: ${e.message}`); }
|
|
5171
|
+
}, 5 * 60 * 1000);
|
|
5172
|
+
|
|
5123
5173
|
installGMAgentConfigs().catch(err => console.error('[GM-CONFIG] Startup error:', err.message));
|
|
5124
5174
|
|
|
5125
5175
|
startACPTools().then(() => {
|
package/static/js/client.js
CHANGED
|
@@ -591,6 +591,27 @@ class AgentGUIClient {
|
|
|
591
591
|
});
|
|
592
592
|
}
|
|
593
593
|
|
|
594
|
+
document.addEventListener('keydown', (e) => {
|
|
595
|
+
if (e.key === 'n' && (e.ctrlKey || e.metaKey) && !e.shiftKey) {
|
|
596
|
+
e.preventDefault();
|
|
597
|
+
const newBtn = document.querySelector('[data-new-conversation], #newConversationBtn, .new-conversation-btn');
|
|
598
|
+
if (newBtn) newBtn.click();
|
|
599
|
+
}
|
|
600
|
+
if (e.key === 'b' && (e.ctrlKey || e.metaKey) && !e.shiftKey) {
|
|
601
|
+
e.preventDefault();
|
|
602
|
+
const toggleBtn = document.querySelector('[data-sidebar-toggle]');
|
|
603
|
+
if (toggleBtn) toggleBtn.click();
|
|
604
|
+
}
|
|
605
|
+
if (e.key === 'Escape') {
|
|
606
|
+
const activeEl = document.activeElement;
|
|
607
|
+
if (activeEl && activeEl.tagName === 'TEXTAREA') { activeEl.blur(); return; }
|
|
608
|
+
if (this.state.isStreaming) {
|
|
609
|
+
const cancelBtn = document.querySelector('#cancelBtn, [data-cancel-btn]');
|
|
610
|
+
if (cancelBtn && cancelBtn.offsetParent !== null) cancelBtn.click();
|
|
611
|
+
}
|
|
612
|
+
}
|
|
613
|
+
});
|
|
614
|
+
|
|
594
615
|
// Setup theme toggle
|
|
595
616
|
const themeToggle = document.querySelector('[data-theme-toggle]');
|
|
596
617
|
if (themeToggle) {
|
|
@@ -917,6 +938,9 @@ class AgentGUIClient {
|
|
|
917
938
|
}
|
|
918
939
|
|
|
919
940
|
handleStreamingProgress(data) {
|
|
941
|
+
try { return this._handleStreamingProgressInner(data); } catch (e) { console.error('[render-error] streaming progress:', e); }
|
|
942
|
+
}
|
|
943
|
+
_handleStreamingProgressInner(data) {
|
|
920
944
|
if (!data.block || !data.sessionId) return;
|
|
921
945
|
|
|
922
946
|
// Deduplicate by seq number to guarantee exactly-once rendering
|
|
@@ -2186,6 +2210,9 @@ class AgentGUIClient {
|
|
|
2186
2210
|
}
|
|
2187
2211
|
|
|
2188
2212
|
renderChunk(chunk) {
|
|
2213
|
+
try { return this._renderChunkInner(chunk); } catch (e) { console.error('[render-error] chunk:', e); }
|
|
2214
|
+
}
|
|
2215
|
+
_renderChunkInner(chunk) {
|
|
2189
2216
|
if (!chunk || !chunk.block) return;
|
|
2190
2217
|
const seq = chunk.sequence;
|
|
2191
2218
|
if (seq !== undefined) {
|
|
@@ -781,6 +781,7 @@ class StreamingRenderer {
|
|
|
781
781
|
const details = document.createElement('details');
|
|
782
782
|
details.className = 'block-tool-use folded-tool';
|
|
783
783
|
if (block.id) details.dataset.toolUseId = block.id;
|
|
784
|
+
details.dataset.startedAt = Date.now();
|
|
784
785
|
details.classList.add(this._getBlockTypeClass('tool_use'));
|
|
785
786
|
details.classList.add(this._getToolColorClass(toolName));
|
|
786
787
|
const normalizedForOpen = toolName.replace(/^mcp__.*?__/, '');
|
|
@@ -2228,6 +2229,16 @@ class StreamingRenderer {
|
|
|
2228
2229
|
statusSpan.className = 'folded-tool-status';
|
|
2229
2230
|
statusSpan.innerHTML = statusSvg;
|
|
2230
2231
|
summary.appendChild(statusSpan);
|
|
2232
|
+
const startedAt = parseInt(toolUseEl.dataset.startedAt);
|
|
2233
|
+
if (startedAt > 0) {
|
|
2234
|
+
const ms = Date.now() - startedAt;
|
|
2235
|
+
const label = ms < 1000 ? ms + 'ms' : (ms / 1000).toFixed(1) + 's';
|
|
2236
|
+
const dur = document.createElement('span');
|
|
2237
|
+
dur.className = 'folded-tool-duration';
|
|
2238
|
+
dur.style.cssText = 'margin-left:0.375rem;font-size:0.6rem;opacity:0.45;font-weight:400';
|
|
2239
|
+
dur.textContent = label;
|
|
2240
|
+
summary.appendChild(dur);
|
|
2241
|
+
}
|
|
2231
2242
|
}
|
|
2232
2243
|
|
|
2233
2244
|
const renderedContent = StreamingRenderer.renderSmartContentHTML(contentStr, this.escapeHtml.bind(this), true);
|