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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agentgui",
3
- "version": "1.0.227",
3
+ "version": "1.0.229",
4
4
  "description": "Multi-agent ACP client with real-time communication",
5
5
  "type": "module",
6
6
  "main": "server.js",
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
- sendJSON(req, res, 200, { conversations: queries.getConversationsList() });
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
- broadcastSync({
2536
- type: 'streaming_error',
2537
- sessionId: entry.sessionId,
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;
@@ -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; }