agentgui 1.0.227 → 1.0.229
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/package.json +1 -1
- package/server.js +88 -14
- package/static/index.html +1 -0
- package/static/js/client.js +42 -1
package/package.json
CHANGED
package/server.js
CHANGED
|
@@ -24,6 +24,68 @@ async function ensurePocketTtsSetup(onProgress) {
|
|
|
24
24
|
return serverTTS.ensureInstalled(onProgress);
|
|
25
25
|
}
|
|
26
26
|
|
|
27
|
+
// Model download manager
|
|
28
|
+
const modelDownloadState = {
|
|
29
|
+
downloading: false,
|
|
30
|
+
progress: null,
|
|
31
|
+
error: null,
|
|
32
|
+
complete: false
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
async function ensureModelsDownloaded() {
|
|
36
|
+
const { createRequire: cr } = await import('module');
|
|
37
|
+
const r = cr(import.meta.url);
|
|
38
|
+
const { checkAllFilesExist, downloadModels } = r('sttttsmodels');
|
|
39
|
+
|
|
40
|
+
if (checkAllFilesExist()) {
|
|
41
|
+
modelDownloadState.complete = true;
|
|
42
|
+
return true;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
if (modelDownloadState.downloading) {
|
|
46
|
+
// Wait for current download
|
|
47
|
+
while (modelDownloadState.downloading) {
|
|
48
|
+
await new Promise(r => setTimeout(r, 100));
|
|
49
|
+
}
|
|
50
|
+
return modelDownloadState.complete;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
modelDownloadState.downloading = true;
|
|
54
|
+
modelDownloadState.error = null;
|
|
55
|
+
|
|
56
|
+
try {
|
|
57
|
+
await downloadModels((progress) => {
|
|
58
|
+
modelDownloadState.progress = progress;
|
|
59
|
+
// Broadcast progress to all connected clients
|
|
60
|
+
broadcastSync({
|
|
61
|
+
type: 'model_download_progress',
|
|
62
|
+
progress: {
|
|
63
|
+
started: progress.started,
|
|
64
|
+
done: progress.done,
|
|
65
|
+
error: progress.error,
|
|
66
|
+
downloading: progress.downloading,
|
|
67
|
+
type: progress.type,
|
|
68
|
+
completedFiles: progress.completedFiles,
|
|
69
|
+
totalFiles: progress.totalFiles,
|
|
70
|
+
totalDownloaded: progress.totalDownloaded,
|
|
71
|
+
totalBytes: progress.totalBytes
|
|
72
|
+
}
|
|
73
|
+
});
|
|
74
|
+
});
|
|
75
|
+
modelDownloadState.complete = true;
|
|
76
|
+
return true;
|
|
77
|
+
} catch (err) {
|
|
78
|
+
modelDownloadState.error = err.message;
|
|
79
|
+
broadcastSync({
|
|
80
|
+
type: 'model_download_progress',
|
|
81
|
+
progress: { error: err.message, done: true }
|
|
82
|
+
});
|
|
83
|
+
return false;
|
|
84
|
+
} finally {
|
|
85
|
+
modelDownloadState.downloading = false;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
27
89
|
function eagerTTS(text, conversationId, sessionId) {
|
|
28
90
|
getSpeech().then(speech => {
|
|
29
91
|
const status = speech.getStatus();
|
|
@@ -182,6 +244,7 @@ function discoverAgents() {
|
|
|
182
244
|
{ cmd: 'claude', id: 'claude-code', name: 'Claude Code', icon: 'C' },
|
|
183
245
|
{ cmd: 'opencode', id: 'opencode', name: 'OpenCode', icon: 'O' },
|
|
184
246
|
{ cmd: 'gemini', id: 'gemini', name: 'Gemini CLI', icon: 'G' },
|
|
247
|
+
{ cmd: 'kilo', id: 'kilo', name: 'Kilo Code', icon: 'K' },
|
|
185
248
|
{ cmd: 'goose', id: 'goose', name: 'Goose', icon: 'g' },
|
|
186
249
|
{ cmd: 'openhands', id: 'openhands', name: 'OpenHands', icon: 'H' },
|
|
187
250
|
{ cmd: 'augment', id: 'augment', name: 'Augment Code', icon: 'A' },
|
|
@@ -802,7 +865,14 @@ const server = http.createServer(async (req, res) => {
|
|
|
802
865
|
}
|
|
803
866
|
|
|
804
867
|
if (pathOnly === '/api/conversations' && req.method === 'GET') {
|
|
805
|
-
|
|
868
|
+
const conversations = queries.getConversationsList();
|
|
869
|
+
// Filter out stale streaming state for conversations not in activeExecutions
|
|
870
|
+
for (const conv of conversations) {
|
|
871
|
+
if (conv.isStreaming && !activeExecutions.has(conv.id)) {
|
|
872
|
+
conv.isStreaming = 0;
|
|
873
|
+
}
|
|
874
|
+
}
|
|
875
|
+
sendJSON(req, res, 200, { conversations });
|
|
806
876
|
return;
|
|
807
877
|
}
|
|
808
878
|
|
|
@@ -1601,11 +1671,18 @@ const server = http.createServer(async (req, res) => {
|
|
|
1601
1671
|
pythonDetected: pyInfo.found,
|
|
1602
1672
|
pythonVersion: pyInfo.version || null,
|
|
1603
1673
|
setupMessage: baseStatus.ttsReady ? 'pocket-tts ready' : 'Will setup on first TTS request',
|
|
1674
|
+
modelsDownloading: modelDownloadState.downloading,
|
|
1675
|
+
modelsComplete: modelDownloadState.complete,
|
|
1676
|
+
modelsError: modelDownloadState.error,
|
|
1677
|
+
modelsProgress: modelDownloadState.progress,
|
|
1604
1678
|
});
|
|
1605
1679
|
} catch (err) {
|
|
1606
1680
|
sendJSON(req, res, 200, {
|
|
1607
1681
|
sttReady: false, ttsReady: false, sttLoading: false, ttsLoading: false,
|
|
1608
1682
|
setupMessage: 'Will setup on first TTS request',
|
|
1683
|
+
modelsDownloading: modelDownloadState.downloading,
|
|
1684
|
+
modelsComplete: modelDownloadState.complete,
|
|
1685
|
+
modelsError: modelDownloadState.error,
|
|
1609
1686
|
});
|
|
1610
1687
|
}
|
|
1611
1688
|
return;
|
|
@@ -2532,14 +2609,9 @@ function performAgentHealthCheck() {
|
|
|
2532
2609
|
markAgentDead(conversationId, entry, 'Agent process died unexpectedly');
|
|
2533
2610
|
} else if (now - entry.lastActivity > STUCK_AGENT_THRESHOLD_MS) {
|
|
2534
2611
|
debugLog(`[HEALTH] Agent PID ${entry.pid} for conv ${conversationId} has no activity for ${Math.round((now - entry.lastActivity) / 1000)}s`);
|
|
2535
|
-
|
|
2536
|
-
|
|
2537
|
-
|
|
2538
|
-
conversationId,
|
|
2539
|
-
error: 'Agent may be stuck (no activity for 10 minutes)',
|
|
2540
|
-
recoverable: true,
|
|
2541
|
-
timestamp: now
|
|
2542
|
-
});
|
|
2612
|
+
// Kill stuck agent and clear streaming state
|
|
2613
|
+
try { process.kill(entry.pid, 'SIGTERM'); } catch (e) {}
|
|
2614
|
+
markAgentDead(conversationId, entry, 'Agent was stuck (no activity for 10 minutes)');
|
|
2543
2615
|
}
|
|
2544
2616
|
} else {
|
|
2545
2617
|
if (now - entry.startTime > NO_PID_GRACE_PERIOD_MS) {
|
|
@@ -2555,26 +2627,28 @@ function onServerReady() {
|
|
|
2555
2627
|
console.log(`Agents: ${discoveredAgents.map(a => a.name).join(', ') || 'none'}`);
|
|
2556
2628
|
console.log(`Hot reload: ${watch ? 'on' : 'off'}`);
|
|
2557
2629
|
|
|
2558
|
-
// Clean up empty conversations on startup
|
|
2559
2630
|
const deletedCount = queries.cleanupEmptyConversations();
|
|
2560
2631
|
if (deletedCount > 0) {
|
|
2561
2632
|
console.log(`Cleaned up ${deletedCount} empty conversation(s) on startup`);
|
|
2562
2633
|
}
|
|
2563
2634
|
|
|
2564
|
-
// Recover stale active sessions from previous run
|
|
2565
2635
|
recoverStaleSessions();
|
|
2566
2636
|
|
|
2567
|
-
// Resume interrupted streams after recovery
|
|
2568
2637
|
resumeInterruptedStreams().catch(err => console.error('[RESUME] Startup error:', err.message));
|
|
2569
2638
|
|
|
2639
|
+
ensureModelsDownloaded().then(ok => {
|
|
2640
|
+
if (ok) console.log('[MODELS] Speech models ready');
|
|
2641
|
+
else console.log('[MODELS] Speech model download failed');
|
|
2642
|
+
}).catch(err => {
|
|
2643
|
+
console.error('[MODELS] Download error:', err.message);
|
|
2644
|
+
});
|
|
2645
|
+
|
|
2570
2646
|
getSpeech().then(s => s.preloadTTS()).catch(e => debugLog('[TTS] Preload failed: ' + e.message));
|
|
2571
2647
|
|
|
2572
2648
|
performAutoImport();
|
|
2573
2649
|
|
|
2574
|
-
// Then run it every 30 seconds (constant automatic importing)
|
|
2575
2650
|
setInterval(performAutoImport, 30000);
|
|
2576
2651
|
|
|
2577
|
-
// Agent health check every 30 seconds
|
|
2578
2652
|
setInterval(performAgentHealthCheck, 30000);
|
|
2579
2653
|
|
|
2580
2654
|
}
|
package/static/index.html
CHANGED
|
@@ -2271,6 +2271,7 @@
|
|
|
2271
2271
|
.connection-dot.unknown { background: #6b7280; }
|
|
2272
2272
|
.connection-dot.disconnected { background: #ef4444; animation: pulse 1.5s ease-in-out infinite; }
|
|
2273
2273
|
.connection-dot.reconnecting { background: #f59e0b; animation: pulse 1s ease-in-out infinite; }
|
|
2274
|
+
.connection-dot.downloading { background: #3b82f6; animation: pulse 1s ease-in-out infinite; }
|
|
2274
2275
|
|
|
2275
2276
|
.connection-tooltip {
|
|
2276
2277
|
position: absolute;
|
package/static/js/client.js
CHANGED
|
@@ -432,6 +432,9 @@ class AgentGUIClient {
|
|
|
432
432
|
case 'rate_limit_clear':
|
|
433
433
|
this.handleRateLimitClear(data);
|
|
434
434
|
break;
|
|
435
|
+
case 'model_download_progress':
|
|
436
|
+
this._handleModelDownloadProgress(data.progress);
|
|
437
|
+
break;
|
|
435
438
|
default:
|
|
436
439
|
break;
|
|
437
440
|
}
|
|
@@ -1833,7 +1836,7 @@ class AgentGUIClient {
|
|
|
1833
1836
|
}
|
|
1834
1837
|
|
|
1835
1838
|
_updateConnectionIndicator(quality) {
|
|
1836
|
-
if (this._indicatorDebounce) return;
|
|
1839
|
+
if (this._indicatorDebounce && !this._modelDownloadInProgress) return;
|
|
1837
1840
|
this._indicatorDebounce = true;
|
|
1838
1841
|
setTimeout(() => { this._indicatorDebounce = false; }, 1000);
|
|
1839
1842
|
|
|
@@ -1855,6 +1858,21 @@ class AgentGUIClient {
|
|
|
1855
1858
|
const label = indicator.querySelector('.connection-label');
|
|
1856
1859
|
if (!dot || !label) return;
|
|
1857
1860
|
|
|
1861
|
+
// Check if model download is in progress
|
|
1862
|
+
if (this._modelDownloadInProgress) {
|
|
1863
|
+
dot.className = 'connection-dot downloading';
|
|
1864
|
+
const progress = this._modelDownloadProgress;
|
|
1865
|
+
if (progress && progress.totalBytes > 0) {
|
|
1866
|
+
const pct = Math.round((progress.totalDownloaded / progress.totalBytes) * 100);
|
|
1867
|
+
label.textContent = `Models ${pct}%`;
|
|
1868
|
+
} else if (progress && progress.downloading) {
|
|
1869
|
+
label.textContent = 'Downloading...';
|
|
1870
|
+
} else {
|
|
1871
|
+
label.textContent = 'Loading models...';
|
|
1872
|
+
}
|
|
1873
|
+
return;
|
|
1874
|
+
}
|
|
1875
|
+
|
|
1858
1876
|
dot.className = 'connection-dot';
|
|
1859
1877
|
if (quality === 'disconnected' || quality === 'reconnecting') {
|
|
1860
1878
|
dot.classList.add(quality);
|
|
@@ -1866,6 +1884,29 @@ class AgentGUIClient {
|
|
|
1866
1884
|
}
|
|
1867
1885
|
}
|
|
1868
1886
|
|
|
1887
|
+
_handleModelDownloadProgress(progress) {
|
|
1888
|
+
this._modelDownloadProgress = progress;
|
|
1889
|
+
|
|
1890
|
+
if (progress.error) {
|
|
1891
|
+
this._modelDownloadInProgress = false;
|
|
1892
|
+
console.error('[Models] Download error:', progress.error);
|
|
1893
|
+
this._updateConnectionIndicator(this.wsManager?.latency?.quality || 'unknown');
|
|
1894
|
+
return;
|
|
1895
|
+
}
|
|
1896
|
+
|
|
1897
|
+
if (progress.done) {
|
|
1898
|
+
this._modelDownloadInProgress = false;
|
|
1899
|
+
console.log('[Models] Download complete');
|
|
1900
|
+
this._updateConnectionIndicator(this.wsManager?.latency?.quality || 'unknown');
|
|
1901
|
+
return;
|
|
1902
|
+
}
|
|
1903
|
+
|
|
1904
|
+
if (progress.started || progress.downloading) {
|
|
1905
|
+
this._modelDownloadInProgress = true;
|
|
1906
|
+
this._updateConnectionIndicator(this.wsManager?.latency?.quality || 'unknown');
|
|
1907
|
+
}
|
|
1908
|
+
}
|
|
1909
|
+
|
|
1869
1910
|
_toggleConnectionTooltip() {
|
|
1870
1911
|
let tooltip = document.getElementById('connection-tooltip');
|
|
1871
1912
|
if (tooltip) { tooltip.remove(); return; }
|