agentgui 1.0.419 → 1.0.420

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.
@@ -97,19 +97,42 @@ export function register(router, deps) {
97
97
  return data;
98
98
  });
99
99
 
100
- router.handle('agent.ls', () => {
101
- // Get local agents only (avoid external HTTP calls in WebSocket to prevent recursion)
102
- const localAgents = discoveredAgents.map(agent => ({
103
- id: agent.id,
104
- name: agent.name,
105
- icon: agent.icon,
106
- path: agent.path,
107
- protocol: agent.protocol || 'unknown',
108
- description: agent.description || '',
109
- status: 'available'
110
- }));
111
-
112
- return { agents: localAgents };
100
+ router.handle('agent.ls', () => {
101
+ // Get local agents only (avoid external HTTP calls in WebSocket to prevent recursion)
102
+ const localAgents = discoveredAgents.map(agent => ({
103
+ id: agent.id,
104
+ name: agent.name,
105
+ icon: agent.icon,
106
+ path: agent.path,
107
+ protocol: agent.protocol || 'unknown',
108
+ description: agent.description || '',
109
+ status: 'available'
110
+ }));
111
+
112
+ return { agents: localAgents };
113
+ });
114
+
115
+ router.handle('agent.subagents', async (p) => {
116
+ const agent = discoveredAgents.find(x => x.id === p.id);
117
+ if (!agent) return { subAgents: [] };
118
+ try {
119
+ const port = agent.acpPort || 8080;
120
+ const res = await fetch(`http://localhost:${port}/agents/search`, {
121
+ method: 'POST',
122
+ headers: { 'Content-Type': 'application/json' },
123
+ body: JSON.stringify({}),
124
+ signal: AbortSignal.timeout(3000)
125
+ });
126
+ if (!res.ok) return { subAgents: [] };
127
+ const data = await res.json();
128
+ const subAgents = (data.agents || []).map(a => ({
129
+ id: a.agent_id || a.id,
130
+ name: a.metadata?.ref?.name || a.name || a.agent_id || a.id
131
+ }));
132
+ return { subAgents };
133
+ } catch (_) {
134
+ return { subAgents: [] };
135
+ }
113
136
  });
114
137
 
115
138
  router.handle('agent.get', (p) => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agentgui",
3
- "version": "1.0.419",
3
+ "version": "1.0.420",
4
4
  "description": "Multi-agent ACP client with real-time communication",
5
5
  "type": "module",
6
6
  "main": "server.js",
package/static/index.html CHANGED
@@ -491,10 +491,10 @@
491
491
  color: var(--color-text-secondary); user-select: none;
492
492
  }
493
493
 
494
- .terminal-container {
495
- flex: 1; display: flex; flex-direction: column; overflow: hidden; background: #1e1e1e; min-height: 0;
496
- }
497
- .terminal-output { flex: 1; overflow: hidden; position: relative; min-height: 0; }
494
+ .terminal-container {
495
+ flex: 1; display: flex; flex-direction: column; overflow: hidden; background: #1e1e1e; min-height: 0;
496
+ }
497
+ .terminal-output { flex: 1; overflow: hidden; position: relative; min-height: 0; }
498
498
 
499
499
  /* --- View toggle bar --- */
500
500
  .view-toggle-bar {
@@ -804,29 +804,29 @@
804
804
  align-items: flex-end;
805
805
  }
806
806
 
807
- .agent-selector {
808
- padding: 0.5rem;
809
- border: none;
810
- border-radius: 0.375rem;
811
- background-color: var(--color-bg-secondary);
812
- color: var(--color-text-primary);
813
- font-size: 0.8rem;
814
- cursor: pointer;
815
- flex-shrink: 0;
816
- width: auto;
817
- min-width: 80px;
818
- max-width: 200px;
819
- }
820
-
821
-
822
-
823
-
824
-
825
- .agent-selector:disabled, .model-selector:disabled {
826
- opacity: 0.5;
827
- cursor: not-allowed;
828
- background-color: var(--color-bg-secondary);
829
- }
807
+ .agent-selector {
808
+ padding: 0.5rem;
809
+ border: none;
810
+ border-radius: 0.375rem;
811
+ background-color: var(--color-bg-secondary);
812
+ color: var(--color-text-primary);
813
+ font-size: 0.8rem;
814
+ cursor: pointer;
815
+ flex-shrink: 0;
816
+ width: auto;
817
+ min-width: 80px;
818
+ max-width: 200px;
819
+ }
820
+
821
+
822
+
823
+
824
+
825
+ .agent-selector:disabled, .model-selector:disabled {
826
+ opacity: 0.5;
827
+ cursor: not-allowed;
828
+ background-color: var(--color-bg-secondary);
829
+ }
830
830
 
831
831
  .model-selector {
832
832
  padding: 0.5rem;
@@ -1077,7 +1077,8 @@
1077
1077
  .message { max-width: 95%; }
1078
1078
  .messages-wrapper { padding: 0.375rem 0.5rem; }
1079
1079
  .input-section { padding: 0.5rem; padding-bottom: calc(0.5rem + env(safe-area-inset-bottom)); }
1080
- .agent-selector { display: none; }
1080
+ .cli-selector { display: none; }
1081
+ .sub-agent-selector { display: none; }
1081
1082
  .model-selector { display: none; }
1082
1083
  }
1083
1084
 
@@ -1328,20 +1329,20 @@
1328
1329
  .sidebar-list { contain: strict; content-visibility: auto; }
1329
1330
  .message { contain: layout style; content-visibility: auto; contain-intrinsic-size: auto 80px; }
1330
1331
 
1331
- .voice-block .voice-result-stats {
1332
- font-size: 0.8rem;
1333
- color: var(--color-text-secondary);
1334
- margin-top: 0.5rem;
1335
- padding-top: 0.5rem;
1336
- border-top: 1px solid var(--color-border);
1337
- }
1338
-
1339
- .voice-block-content {
1340
- white-space: pre-wrap;
1341
- display: block;
1342
- }
1343
-
1344
- .voice-reread-btn {
1332
+ .voice-block .voice-result-stats {
1333
+ font-size: 0.8rem;
1334
+ color: var(--color-text-secondary);
1335
+ margin-top: 0.5rem;
1336
+ padding-top: 0.5rem;
1337
+ border-top: 1px solid var(--color-border);
1338
+ }
1339
+
1340
+ .voice-block-content {
1341
+ white-space: pre-wrap;
1342
+ display: block;
1343
+ }
1344
+
1345
+ .voice-reread-btn {
1345
1346
  position: absolute;
1346
1347
  top: 0.5rem;
1347
1348
  right: 0.5rem;
@@ -3171,17 +3172,18 @@
3171
3172
  </div>
3172
3173
 
3173
3174
  <!-- Input area: fixed at bottom -->
3174
- <div class="input-section">
3175
- <div class="input-wrapper">
3176
- <select class="agent-selector" data-agent-selector title="Select agent"></select>
3177
- <select class="model-selector" data-model-selector title="Select model" data-empty="true"></select>
3178
- <textarea
3179
- class="message-textarea"
3180
- data-message-input
3181
- placeholder="Message AgentGUI... (Ctrl+Enter to send)"
3182
- aria-label="Message input"
3183
- rows="1"
3184
- ></textarea>
3175
+ <div class="input-section">
3176
+ <div class="input-wrapper">
3177
+ <select class="agent-selector cli-selector" data-cli-selector title="Select CLI tool"></select>
3178
+ <select class="agent-selector sub-agent-selector" data-agent-selector title="Select sub-agent" style="display:none"></select>
3179
+ <select class="model-selector" data-model-selector title="Select model" data-empty="true"></select>
3180
+ <textarea
3181
+ class="message-textarea"
3182
+ data-message-input
3183
+ placeholder="Message AgentGUI... (Ctrl+Enter to send)"
3184
+ aria-label="Message input"
3185
+ rows="1"
3186
+ ></textarea>
3185
3187
  <button class="inject-btn" id="injectBtn" title="Inject instructions into running agent" aria-label="Inject instructions">
3186
3188
  <svg viewBox="0 0 24 24" fill="currentColor" width="18" height="18">
3187
3189
  <path d="M20 12l-1.41-1.41L13 16.17V4h-2v12.17l-5.58-5.59L4 12l8 8 8-8z"/>
@@ -3226,22 +3228,22 @@
3226
3228
  window._escHtml = function(t) { return t.replace(_escHtmlRe, function(c) { return _escHtmlMap[c]; }); };
3227
3229
  </script>
3228
3230
  <script defer src="/gm/js/event-processor.js"></script>
3229
- <script defer src="/gm/js/streaming-renderer.js"></script>
3230
- <script defer src="/gm/js/kalman-filter.js"></script>
3231
- <script defer src="/gm/js/event-consolidator.js"></script>
3232
- <script defer src="/gm/js/websocket-manager.js"></script>
3233
- <script defer src="/gm/js/ws-client.js"></script>
3234
- <script defer src="/gm/js/event-filter.js"></script>
3235
- <script defer src="/gm/js/syntax-highlighter.js"></script>
3236
- <script defer src="/gm/js/dialogs.js"></script>
3237
- <script defer src="/gm/js/ui-components.js"></script>
3238
- <script defer src="/gm/js/conversations.js"></script>
3239
- <script defer src="/gm/js/terminal.js"></script>
3240
- <script defer src="/gm/js/script-runner.js"></script>
3241
- <script defer src="/gm/js/client.js"></script>
3242
- <script type="module" src="/gm/js/voice.js"></script>
3243
- <script defer src="/gm/js/features.js"></script>
3244
- <script defer src="/gm/js/agent-auth.js"></script>
3231
+ <script defer src="/gm/js/streaming-renderer.js"></script>
3232
+ <script defer src="/gm/js/kalman-filter.js"></script>
3233
+ <script defer src="/gm/js/event-consolidator.js"></script>
3234
+ <script defer src="/gm/js/websocket-manager.js"></script>
3235
+ <script defer src="/gm/js/ws-client.js"></script>
3236
+ <script defer src="/gm/js/event-filter.js"></script>
3237
+ <script defer src="/gm/js/syntax-highlighter.js"></script>
3238
+ <script defer src="/gm/js/dialogs.js"></script>
3239
+ <script defer src="/gm/js/ui-components.js"></script>
3240
+ <script defer src="/gm/js/conversations.js"></script>
3241
+ <script defer src="/gm/js/terminal.js"></script>
3242
+ <script defer src="/gm/js/script-runner.js"></script>
3243
+ <script defer src="/gm/js/client.js"></script>
3244
+ <script type="module" src="/gm/js/voice.js"></script>
3245
+ <script defer src="/gm/js/features.js"></script>
3246
+ <script defer src="/gm/js/agent-auth.js"></script>
3245
3247
 
3246
3248
  <script>
3247
3249
  const savedTheme = localStorage.getItem('theme') || 'light';
@@ -43,6 +43,7 @@ class AgentGUIClient {
43
43
  statusIndicator: null,
44
44
  messageInput: null,
45
45
  sendButton: null,
46
+ cliSelector: null,
46
47
  agentSelector: null,
47
48
  modelSelector: null
48
49
  };
@@ -354,13 +355,23 @@ class AgentGUIClient {
354
355
  this.ui.statusIndicator = document.querySelector('[data-status-indicator]');
355
356
  this.ui.messageInput = document.querySelector('[data-message-input]');
356
357
  this.ui.sendButton = document.querySelector('[data-send-button]');
358
+ this.ui.cliSelector = document.querySelector('[data-cli-selector]');
357
359
  this.ui.agentSelector = document.querySelector('[data-agent-selector]');
358
360
  this.ui.modelSelector = document.querySelector('[data-model-selector]');
359
361
 
362
+ if (this.ui.cliSelector) {
363
+ this.ui.cliSelector.addEventListener('change', () => {
364
+ if (!this._agentLocked) {
365
+ this.loadSubAgentsForCli(this.ui.cliSelector.value);
366
+ this.saveAgentAndModelToConversation();
367
+ }
368
+ });
369
+ }
370
+
360
371
  if (this.ui.agentSelector) {
361
372
  this.ui.agentSelector.addEventListener('change', () => {
362
373
  if (!this._agentLocked) {
363
- this.loadModelsForAgent(this.ui.agentSelector.value);
374
+ this.loadModelsForAgent(this.getEffectiveAgentId());
364
375
  this.saveAgentAndModelToConversation();
365
376
  }
366
377
  });
@@ -1822,22 +1833,27 @@ class AgentGUIClient {
1822
1833
  const { agents } = await window.wsClient.rpc('agent.ls');
1823
1834
  this.state.agents = agents;
1824
1835
 
1825
- // Populate unified agent selector with all agents (both CLI and ACP)
1826
- if (this.ui.agentSelector) {
1827
- if (agents.length > 0) {
1828
- this.ui.agentSelector.innerHTML = agents
1829
- .map(agent => `<option value="${agent.id}">${agent.name}</option>`)
1836
+ const cliAgents = agents.filter(a => a.protocol === 'cli');
1837
+ const displayAgents = cliAgents.length > 0 ? cliAgents : agents;
1838
+
1839
+ if (this.ui.cliSelector) {
1840
+ if (displayAgents.length > 0) {
1841
+ this.ui.cliSelector.innerHTML = displayAgents
1842
+ .map(a => `<option value="${a.id}">${a.name}</option>`)
1830
1843
  .join('');
1831
- this.ui.agentSelector.style.display = 'inline-block';
1844
+ this.ui.cliSelector.style.display = 'inline-block';
1832
1845
  } else {
1833
- this.ui.agentSelector.innerHTML = '';
1834
- this.ui.agentSelector.style.display = 'none';
1846
+ this.ui.cliSelector.innerHTML = '';
1847
+ this.ui.cliSelector.style.display = 'none';
1835
1848
  }
1836
1849
  }
1837
1850
 
1838
1851
  window.dispatchEvent(new CustomEvent('agents-loaded', { detail: { agents } }));
1839
- if (agents.length > 0 && !this._agentLocked) {
1840
- this.loadModelsForAgent(agents[0].id);
1852
+
1853
+ if (displayAgents.length > 0 && !this._agentLocked) {
1854
+ const firstId = displayAgents[0].id;
1855
+ await this.loadSubAgentsForCli(firstId);
1856
+ this.loadModelsForAgent(this.getEffectiveAgentId());
1841
1857
  }
1842
1858
  return agents;
1843
1859
  } catch (error) {
@@ -1847,6 +1863,24 @@ class AgentGUIClient {
1847
1863
  });
1848
1864
  }
1849
1865
 
1866
+ async loadSubAgentsForCli(cliAgentId) {
1867
+ if (this.ui.agentSelector) {
1868
+ this.ui.agentSelector.innerHTML = '';
1869
+ this.ui.agentSelector.style.display = 'none';
1870
+ }
1871
+ try {
1872
+ const { subAgents } = await window.wsClient.rpc('agent.subagents', { id: cliAgentId });
1873
+ if (subAgents && subAgents.length > 0 && this.ui.agentSelector) {
1874
+ this.ui.agentSelector.innerHTML = subAgents
1875
+ .map(a => `<option value="${a.id}">${a.name}</option>`)
1876
+ .join('');
1877
+ this.ui.agentSelector.style.display = 'inline-block';
1878
+ }
1879
+ } catch (_) {
1880
+ // No sub-agents available for this CLI tool — keep hidden
1881
+ }
1882
+ }
1883
+
1850
1884
  async checkSpeechStatus() {
1851
1885
  try {
1852
1886
  const status = await window.wsClient.rpc('speech.status');
@@ -1899,8 +1933,10 @@ class AgentGUIClient {
1899
1933
 
1900
1934
  lockAgentAndModel(agentId, model) {
1901
1935
  this._agentLocked = true;
1936
+ if (this.ui.cliSelector) {
1937
+ this.ui.cliSelector.disabled = true;
1938
+ }
1902
1939
  if (this.ui.agentSelector) {
1903
- this.ui.agentSelector.value = agentId;
1904
1940
  this.ui.agentSelector.disabled = true;
1905
1941
  }
1906
1942
 
@@ -1914,9 +1950,11 @@ class AgentGUIClient {
1914
1950
 
1915
1951
  unlockAgentAndModel() {
1916
1952
  this._agentLocked = false;
1953
+ if (this.ui.cliSelector) {
1954
+ this.ui.cliSelector.disabled = false;
1955
+ }
1917
1956
  if (this.ui.agentSelector) {
1918
1957
  this.ui.agentSelector.disabled = false;
1919
- this.ui.agentSelector.style.display = 'inline-block';
1920
1958
  }
1921
1959
  if (this.ui.modelSelector) {
1922
1960
  this.ui.modelSelector.disabled = false;
@@ -1932,12 +1970,11 @@ class AgentGUIClient {
1932
1970
  const model = conversation.model || null;
1933
1971
 
1934
1972
  if (hasActivity) {
1973
+ this._setCliSelectorToAgent(agentId);
1935
1974
  this.lockAgentAndModel(agentId, model);
1936
1975
  } else {
1937
1976
  this.unlockAgentAndModel();
1938
- if (this.ui.agentSelector) {
1939
- this.ui.agentSelector.value = agentId;
1940
- }
1977
+ this._setCliSelectorToAgent(agentId);
1941
1978
 
1942
1979
  this.loadModelsForAgent(agentId).then(() => {
1943
1980
  if (model && this.ui.modelSelector) {
@@ -1947,6 +1984,15 @@ class AgentGUIClient {
1947
1984
  }
1948
1985
  }
1949
1986
 
1987
+ _setCliSelectorToAgent(agentId) {
1988
+ const allAgents = this.state.agents || [];
1989
+ const agent = allAgents.find(a => a.id === agentId);
1990
+ const cliId = (agent?.protocol === 'cli' || !agent) ? agentId : null;
1991
+ if (cliId && this.ui.cliSelector) {
1992
+ this.ui.cliSelector.value = cliId;
1993
+ }
1994
+ }
1995
+
1950
1996
  /**
1951
1997
  * Load conversations
1952
1998
  */
@@ -2510,14 +2556,15 @@ class AgentGUIClient {
2510
2556
  /**
2511
2557
  * Get current selected agent
2512
2558
  */
2513
- getCurrentAgent() {
2514
- if (this.ui.agentSelector?.value) {
2515
- return this.ui.agentSelector.value;
2516
- }
2517
- if (this.ui.agentSelector?.value) {
2559
+ getEffectiveAgentId() {
2560
+ if (this.ui.agentSelector?.value && this.ui.agentSelector.style.display !== 'none') {
2518
2561
  return this.ui.agentSelector.value;
2519
2562
  }
2520
- return 'claude-code';
2563
+ return this.ui.cliSelector?.value || 'claude-code';
2564
+ }
2565
+
2566
+ getCurrentAgent() {
2567
+ return this.getEffectiveAgentId();
2521
2568
  }
2522
2569
 
2523
2570
  /**