agentgui 1.0.419 → 1.0.421

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.421",
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,26 @@ 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 displayAgents = agents;
1837
+
1838
+ if (this.ui.cliSelector) {
1839
+ if (displayAgents.length > 0) {
1840
+ this.ui.cliSelector.innerHTML = displayAgents
1841
+ .map(a => `<option value="${a.id}">${a.name}</option>`)
1830
1842
  .join('');
1831
- this.ui.agentSelector.style.display = 'inline-block';
1843
+ this.ui.cliSelector.style.display = 'inline-block';
1832
1844
  } else {
1833
- this.ui.agentSelector.innerHTML = '';
1834
- this.ui.agentSelector.style.display = 'none';
1845
+ this.ui.cliSelector.innerHTML = '';
1846
+ this.ui.cliSelector.style.display = 'none';
1835
1847
  }
1836
1848
  }
1837
1849
 
1838
1850
  window.dispatchEvent(new CustomEvent('agents-loaded', { detail: { agents } }));
1839
- if (agents.length > 0 && !this._agentLocked) {
1840
- this.loadModelsForAgent(agents[0].id);
1851
+
1852
+ if (displayAgents.length > 0 && !this._agentLocked) {
1853
+ const firstId = displayAgents[0].id;
1854
+ await this.loadSubAgentsForCli(firstId);
1855
+ this.loadModelsForAgent(this.getEffectiveAgentId());
1841
1856
  }
1842
1857
  return agents;
1843
1858
  } catch (error) {
@@ -1847,6 +1862,24 @@ class AgentGUIClient {
1847
1862
  });
1848
1863
  }
1849
1864
 
1865
+ async loadSubAgentsForCli(cliAgentId) {
1866
+ if (this.ui.agentSelector) {
1867
+ this.ui.agentSelector.innerHTML = '';
1868
+ this.ui.agentSelector.style.display = 'none';
1869
+ }
1870
+ try {
1871
+ const { subAgents } = await window.wsClient.rpc('agent.subagents', { id: cliAgentId });
1872
+ if (subAgents && subAgents.length > 0 && this.ui.agentSelector) {
1873
+ this.ui.agentSelector.innerHTML = subAgents
1874
+ .map(a => `<option value="${a.id}">${a.name}</option>`)
1875
+ .join('');
1876
+ this.ui.agentSelector.style.display = 'inline-block';
1877
+ }
1878
+ } catch (_) {
1879
+ // No sub-agents available for this CLI tool — keep hidden
1880
+ }
1881
+ }
1882
+
1850
1883
  async checkSpeechStatus() {
1851
1884
  try {
1852
1885
  const status = await window.wsClient.rpc('speech.status');
@@ -1899,8 +1932,10 @@ class AgentGUIClient {
1899
1932
 
1900
1933
  lockAgentAndModel(agentId, model) {
1901
1934
  this._agentLocked = true;
1935
+ if (this.ui.cliSelector) {
1936
+ this.ui.cliSelector.disabled = true;
1937
+ }
1902
1938
  if (this.ui.agentSelector) {
1903
- this.ui.agentSelector.value = agentId;
1904
1939
  this.ui.agentSelector.disabled = true;
1905
1940
  }
1906
1941
 
@@ -1914,9 +1949,11 @@ class AgentGUIClient {
1914
1949
 
1915
1950
  unlockAgentAndModel() {
1916
1951
  this._agentLocked = false;
1952
+ if (this.ui.cliSelector) {
1953
+ this.ui.cliSelector.disabled = false;
1954
+ }
1917
1955
  if (this.ui.agentSelector) {
1918
1956
  this.ui.agentSelector.disabled = false;
1919
- this.ui.agentSelector.style.display = 'inline-block';
1920
1957
  }
1921
1958
  if (this.ui.modelSelector) {
1922
1959
  this.ui.modelSelector.disabled = false;
@@ -1932,12 +1969,11 @@ class AgentGUIClient {
1932
1969
  const model = conversation.model || null;
1933
1970
 
1934
1971
  if (hasActivity) {
1972
+ this._setCliSelectorToAgent(agentId);
1935
1973
  this.lockAgentAndModel(agentId, model);
1936
1974
  } else {
1937
1975
  this.unlockAgentAndModel();
1938
- if (this.ui.agentSelector) {
1939
- this.ui.agentSelector.value = agentId;
1940
- }
1976
+ this._setCliSelectorToAgent(agentId);
1941
1977
 
1942
1978
  this.loadModelsForAgent(agentId).then(() => {
1943
1979
  if (model && this.ui.modelSelector) {
@@ -1947,6 +1983,15 @@ class AgentGUIClient {
1947
1983
  }
1948
1984
  }
1949
1985
 
1986
+ _setCliSelectorToAgent(agentId) {
1987
+ if (this.ui.cliSelector) {
1988
+ this.ui.cliSelector.value = agentId;
1989
+ if (!this.ui.cliSelector.value) {
1990
+ this.ui.cliSelector.selectedIndex = 0;
1991
+ }
1992
+ }
1993
+ }
1994
+
1950
1995
  /**
1951
1996
  * Load conversations
1952
1997
  */
@@ -2510,14 +2555,15 @@ class AgentGUIClient {
2510
2555
  /**
2511
2556
  * Get current selected agent
2512
2557
  */
2513
- getCurrentAgent() {
2514
- if (this.ui.agentSelector?.value) {
2515
- return this.ui.agentSelector.value;
2516
- }
2517
- if (this.ui.agentSelector?.value) {
2558
+ getEffectiveAgentId() {
2559
+ if (this.ui.agentSelector?.value && this.ui.agentSelector.style.display !== 'none') {
2518
2560
  return this.ui.agentSelector.value;
2519
2561
  }
2520
- return 'claude-code';
2562
+ return this.ui.cliSelector?.value || 'claude-code';
2563
+ }
2564
+
2565
+ getCurrentAgent() {
2566
+ return this.getEffectiveAgentId();
2521
2567
  }
2522
2568
 
2523
2569
  /**