agentgui 1.0.267 → 1.0.268

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.
@@ -736,6 +736,196 @@ registry.register({
736
736
  }
737
737
  });
738
738
 
739
+ /**
740
+ * Common ACP protocol handler for all ACP agents
741
+ */
742
+ function createACPProtocolHandler() {
743
+ return function(message, context) {
744
+ if (!message || typeof message !== 'object') return null;
745
+
746
+ // Handle ACP session/update notifications
747
+ if (message.method === 'session/update') {
748
+ const params = message.params || {};
749
+ const update = params.update || {};
750
+
751
+ // Agent message chunk (text response)
752
+ if (update.sessionUpdate === 'agent_message_chunk' && update.content) {
753
+ let contentBlock;
754
+
755
+ // Handle different content formats
756
+ if (typeof update.content === 'string') {
757
+ contentBlock = { type: 'text', text: update.content };
758
+ } else if (update.content.type === 'text' && update.content.text) {
759
+ contentBlock = update.content;
760
+ } else if (update.content.text) {
761
+ contentBlock = { type: 'text', text: update.content.text };
762
+ } else if (update.content.content) {
763
+ const inner = update.content.content;
764
+ if (typeof inner === 'string') {
765
+ contentBlock = { type: 'text', text: inner };
766
+ } else if (inner.type === 'text' && inner.text) {
767
+ contentBlock = inner;
768
+ } else {
769
+ contentBlock = { type: 'text', text: JSON.stringify(inner) };
770
+ }
771
+ } else {
772
+ contentBlock = { type: 'text', text: JSON.stringify(update.content) };
773
+ }
774
+
775
+ return {
776
+ type: 'assistant',
777
+ message: {
778
+ role: 'assistant',
779
+ content: [contentBlock]
780
+ },
781
+ session_id: params.sessionId
782
+ };
783
+ }
784
+
785
+ // Tool call
786
+ if (update.sessionUpdate === 'tool_call') {
787
+ return {
788
+ type: 'assistant',
789
+ message: {
790
+ role: 'assistant',
791
+ content: [{
792
+ type: 'tool_use',
793
+ id: update.toolCallId,
794
+ name: update.title || update.kind || 'tool',
795
+ kind: update.kind || 'other',
796
+ input: update.rawInput || update.input || {}
797
+ }]
798
+ },
799
+ session_id: params.sessionId
800
+ };
801
+ }
802
+
803
+ // Tool call update (result) - handle all statuses
804
+ if (update.sessionUpdate === 'tool_call_update') {
805
+ const status = update.status;
806
+ const isError = status === 'failed';
807
+ const isCompleted = status === 'completed';
808
+
809
+ if (!isCompleted && !isError) {
810
+ return {
811
+ type: 'tool_status',
812
+ tool_use_id: update.toolCallId,
813
+ status: status,
814
+ kind: update.kind || 'other',
815
+ locations: update.locations || [],
816
+ session_id: params.sessionId
817
+ };
818
+ }
819
+
820
+ const contentParts = [];
821
+ if (update.content && Array.isArray(update.content)) {
822
+ for (const item of update.content) {
823
+ if (item.type === 'content' && item.content) {
824
+ const innerContent = item.content;
825
+ if (innerContent.type === 'text' && innerContent.text) {
826
+ contentParts.push(innerContent.text);
827
+ } else if (innerContent.type === 'resource' && innerContent.resource) {
828
+ contentParts.push(innerContent.resource.text || JSON.stringify(innerContent.resource));
829
+ } else {
830
+ contentParts.push(JSON.stringify(innerContent));
831
+ }
832
+ } else if (item.type === 'diff') {
833
+ const diffText = item.oldText
834
+ ? `--- ${item.path}\n+++ ${item.path}\n${item.oldText}\n---\n${item.newText}`
835
+ : `+++ ${item.path}\n${item.newText}`;
836
+ contentParts.push(diffText);
837
+ } else if (item.type === 'terminal') {
838
+ contentParts.push(`[Terminal: ${item.terminalId}]`);
839
+ }
840
+ }
841
+ }
842
+
843
+ const combinedContent = contentParts.join('\n') || (update.rawOutput ? JSON.stringify(update.rawOutput) : '');
844
+
845
+ return {
846
+ type: 'user',
847
+ message: {
848
+ role: 'user',
849
+ content: [{
850
+ type: 'tool_result',
851
+ tool_use_id: update.toolCallId,
852
+ content: combinedContent,
853
+ is_error: isError
854
+ }]
855
+ },
856
+ session_id: params.sessionId
857
+ };
858
+ }
859
+
860
+ // Usage update
861
+ if (update.sessionUpdate === 'usage_update') {
862
+ return {
863
+ type: 'usage',
864
+ usage: {
865
+ used: update.used,
866
+ size: update.size,
867
+ cost: update.cost
868
+ },
869
+ session_id: params.sessionId
870
+ };
871
+ }
872
+
873
+ // Plan update
874
+ if (update.sessionUpdate === 'plan') {
875
+ return {
876
+ type: 'plan',
877
+ entries: update.entries || [],
878
+ session_id: params.sessionId
879
+ };
880
+ }
881
+
882
+ return null;
883
+ }
884
+
885
+ // Handle prompt response (end of turn)
886
+ if (message.id && message.result && message.result.stopReason) {
887
+ return {
888
+ type: 'result',
889
+ result: '',
890
+ stopReason: message.result.stopReason,
891
+ usage: message.result.usage,
892
+ session_id: context.sessionId
893
+ };
894
+ }
895
+
896
+ if (message.method === 'error' || message.error) {
897
+ return {
898
+ type: 'error',
899
+ error: message.error || message.params || { message: 'Unknown error' }
900
+ };
901
+ }
902
+
903
+ return null;
904
+ };
905
+ }
906
+
907
+ // Handle prompt response (end of turn)
908
+ if (message.id && message.result && message.result.stopReason) {
909
+ return {
910
+ type: 'result',
911
+ result: '',
912
+ stopReason: message.result.stopReason,
913
+ usage: message.result.usage,
914
+ session_id: context.sessionId
915
+ };
916
+ }
917
+
918
+ if (message.method === 'error' || message.error) {
919
+ return {
920
+ type: 'error',
921
+ error: message.error || message.params || { message: 'Unknown error' }
922
+ };
923
+ }
924
+
925
+ return null;
926
+ }
927
+ });
928
+
739
929
  /**
740
930
  * Common ACP protocol handler for all ACP agents
741
931
  */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agentgui",
3
- "version": "1.0.267",
3
+ "version": "1.0.268",
4
4
  "description": "Multi-agent ACP client with real-time communication",
5
5
  "type": "module",
6
6
  "main": "server.js",
package/server.js CHANGED
@@ -2821,6 +2821,19 @@ async function processMessageWithStreaming(conversationId, messageId, sessionId,
2821
2821
  return;
2822
2822
  }
2823
2823
 
2824
+ if (activeExecutions.has(conversationId)) {
2825
+ debugLog(`[stream] Conversation ${conversationId} already has active execution, aborting duplicate`);
2826
+ return;
2827
+ }
2828
+
2829
+ if (rateLimitState.has(conversationId)) {
2830
+ const rlState = rateLimitState.get(conversationId);
2831
+ if (rlState.retryAt > Date.now()) {
2832
+ debugLog(`[stream] Conversation ${conversationId} is in rate limit cooldown, aborting`);
2833
+ return;
2834
+ }
2835
+ }
2836
+
2824
2837
  activeExecutions.set(conversationId, { pid: null, startTime, sessionId, lastActivity: startTime });
2825
2838
  queries.setIsStreaming(conversationId, true);
2826
2839
  queries.updateSession(sessionId, { status: 'active' });
package/static/index.html CHANGED
@@ -970,6 +970,10 @@
970
970
  details { margin: 0.25rem 0; }
971
971
  summary { cursor: pointer; user-select: none; padding: 0.375rem; border-radius: 0.25rem; transition: background-color 0.2s; }
972
972
  summary:hover { background-color: var(--color-bg-secondary); }
973
+ .permanently-expanded > summary { cursor: default; }
974
+ .permanently-expanded > summary::before { display: none; }
975
+ .permanently-expanded > summary::-webkit-details-marker { display: none; }
976
+ .permanently-expanded > summary::marker { display: none; content: ''; }
973
977
 
974
978
  /* ===== Folder Browser Modal ===== */
975
979
  .folder-modal-overlay { display:none; position:fixed; top:0; left:0; width:100%; height:100%; background:rgba(0,0,0,0.5); z-index:2000; align-items:center; justify-content:center; }
@@ -2196,6 +2200,35 @@
2196
2200
  .folded-tool.has-error > .folded-tool-body { border-top-color: #fecaca; }
2197
2201
  html.dark .folded-tool.has-error > .folded-tool-body { border-top-color: #7f1d1d; }
2198
2202
 
2203
+ /* --- Success/Error status icons in header --- */
2204
+ .folded-tool.has-success > .folded-tool-bar .folded-tool-icon::after,
2205
+ .folded-tool.has-error > .folded-tool-bar .folded-tool-icon::after {
2206
+ content: '';
2207
+ display: inline-block;
2208
+ width: 1rem;
2209
+ height: 1rem;
2210
+ margin-left: 0.375rem;
2211
+ background-size: contain;
2212
+ background-repeat: no-repeat;
2213
+ flex-shrink: 0;
2214
+ }
2215
+ .folded-tool.has-success > .folded-tool-bar .folded-tool-icon::after {
2216
+ background-image: url("data:image/svg+xml,%3Csvg viewBox='0 0 20 20' fill='%2316a34a'%3E%3Cpath fill-rule='evenodd' d='M10 18a8 8 0 100-16 8 8 0 000 16zm3.707-9.293a1 1 0 00-1.414-1.414L9 10.586 7.707 9.293a1 1 0 00-1.414 1.414l2 2a1 1 0 001.414 0l4-4z' clip-rule='evenodd'/%3E%3C/svg%3E");
2217
+ }
2218
+ html.dark .folded-tool.has-success > .folded-tool-bar .folded-tool-icon::after {
2219
+ background-image: url("data:image/svg+xml,%3Csvg viewBox='0 0 20 20' fill='%234ade80'%3E%3Cpath fill-rule='evenodd' d='M10 18a8 8 0 100-16 8 8 0 000 16zm3.707-9.293a1 1 0 00-1.414-1.414L9 10.586 7.707 9.293a1 1 0 00-1.414 1.414l2 2a1 1 0 001.414 0l4-4z' clip-rule='evenodd'/%3E%3C/svg%3E");
2220
+ }
2221
+ .folded-tool.has-error > .folded-tool-bar .folded-tool-icon::after {
2222
+ background-image: url("data:image/svg+xml,%3Csvg viewBox='0 0 20 20' fill='%23ef4444'%3E%3Cpath fill-rule='evenodd' d='M10 18a8 8 0 100-16 8 8 0 000 16zM8.707 7.293a1 1 0 00-1.414 1.414L8.586 10l-1.293 1.293a1 1 0 101.414 1.414L10 11.414l1.293 1.293a1 1 0 001.414-1.414L11.414 10l1.293-1.293a1 1 0 00-1.414-1.414L10 8.586 8.707 7.293z' clip-rule='evenodd'/%3E%3C/svg%3E");
2223
+ }
2224
+ html.dark .folded-tool.has-error > .folded-tool-bar .folded-tool-icon::after {
2225
+ background-image: url("data:image/svg+xml,%3Csvg viewBox='0 0 20 20' fill='%23f87171'%3E%3Cpath fill-rule='evenodd' d='M10 18a8 8 0 100-16 8 8 0 000 16zM8.707 7.293a1 1 0 00-1.414 1.414L8.586 10l-1.293 1.293a1 1 0 101.414 1.414L10 11.414l1.293 1.293a1 1 0 001.414-1.414L11.414 10l1.293-1.293a1 1 0 00-1.414-1.414L10 8.586 8.707 7.293z' clip-rule='evenodd'/%3E%3C/svg%3E");
2226
+ }
2227
+ .folded-tool-icon {
2228
+ display: inline-flex;
2229
+ align-items: center;
2230
+ }
2231
+
2199
2232
  /* --- Consecutive block joining --- */
2200
2233
  .folded-tool + .folded-tool,
2201
2234
  .block-tool-use + .block-tool-use {
@@ -2305,7 +2305,13 @@ class AgentGUIClient {
2305
2305
  } else {
2306
2306
  this.unlockAgentAndModel();
2307
2307
  if (this.ui.agentSelector && cached.conversation.agentType) this.ui.agentSelector.value = cached.conversation.agentType;
2308
- if (cached.conversation.agentType) this.loadModelsForAgent(cached.conversation.agentType);
2308
+ if (cached.conversation.agentType) {
2309
+ this.loadModelsForAgent(cached.conversation.agentType).then(() => {
2310
+ if (cached.conversation.model && this.ui.modelSelector) {
2311
+ this.ui.modelSelector.value = cached.conversation.model;
2312
+ }
2313
+ });
2314
+ }
2309
2315
  }
2310
2316
  this.conversationCache.delete(conversationId);
2311
2317
  this.restoreScrollPosition(conversationId);
@@ -2340,7 +2346,13 @@ class AgentGUIClient {
2340
2346
  } else {
2341
2347
  this.unlockAgentAndModel();
2342
2348
  if (this.ui.agentSelector && conversation.agentType) this.ui.agentSelector.value = conversation.agentType;
2343
- if (conversation.agentType) this.loadModelsForAgent(conversation.agentType);
2349
+ if (conversation.agentType) {
2350
+ this.loadModelsForAgent(conversation.agentType).then(() => {
2351
+ if (conversation.model && this.ui.modelSelector) {
2352
+ this.ui.modelSelector.value = conversation.model;
2353
+ }
2354
+ });
2355
+ }
2344
2356
  }
2345
2357
 
2346
2358
  const chunks = (rawChunks || []).map(chunk => ({
@@ -519,7 +519,7 @@ class StreamingRenderer {
519
519
 
520
520
  const thinking = block.thinking || '';
521
521
  div.innerHTML = `
522
- <details>
522
+ <details open>
523
523
  <summary>
524
524
  <svg viewBox="0 0 20 20" fill="currentColor"><path fill-rule="evenodd" d="M7.293 14.707a1 1 0 010-1.414L10.586 10 7.293 6.707a1 1 0 011.414-1.414l4 4a1 1 0 010 1.414l-4 4a1 1 0 01-1.414 0z" clip-rule="evenodd"/></svg>
525
525
  <span>Thinking Process</span>
@@ -740,7 +740,8 @@ class StreamingRenderer {
740
740
  const input = block.input || {};
741
741
 
742
742
  const details = document.createElement('details');
743
- details.className = 'block-tool-use folded-tool';
743
+ details.className = 'block-tool-use folded-tool permanently-expanded';
744
+ details.setAttribute('open', '');
744
745
  if (block.id) details.dataset.toolUseId = block.id;
745
746
  details.classList.add(this._getBlockTypeClass('tool_use'));
746
747
  details.classList.add(this._getToolColorClass(toolName));
@@ -1296,7 +1297,8 @@ class StreamingRenderer {
1296
1297
  */
1297
1298
  renderBlockSystem(block, context) {
1298
1299
  const details = document.createElement('details');
1299
- details.className = 'folded-tool folded-tool-info';
1300
+ details.className = 'folded-tool folded-tool-info permanently-expanded';
1301
+ details.setAttribute('open', '');
1300
1302
  details.dataset.eventType = 'system';
1301
1303
  details.classList.add(this._getBlockTypeClass('system'));
1302
1304
  const desc = block.model ? this.escapeHtml(block.model) : 'Session';
@@ -1333,7 +1335,8 @@ class StreamingRenderer {
1333
1335
  const statsDesc = [duration, cost, turns ? turns + ' turns' : ''].filter(Boolean).join(' / ');
1334
1336
 
1335
1337
  const details = document.createElement('details');
1336
- details.className = isError ? 'folded-tool folded-tool-error' : 'folded-tool';
1338
+ details.className = isError ? 'folded-tool folded-tool-error permanently-expanded' : 'folded-tool permanently-expanded';
1339
+ details.setAttribute('open', '');
1337
1340
  details.dataset.eventType = 'result';
1338
1341
  details.classList.add(this._getBlockTypeClass(isError ? 'error' : 'result'));
1339
1342
 
@@ -1766,7 +1769,8 @@ class StreamingRenderer {
1766
1769
  const msgPreview = message.length > 80 ? message.substring(0, 77) + '...' : message;
1767
1770
 
1768
1771
  const details = document.createElement('details');
1769
- details.className = 'folded-tool folded-tool-error';
1772
+ details.className = 'folded-tool folded-tool-error permanently-expanded';
1773
+ details.setAttribute('open', '');
1770
1774
  details.dataset.eventId = event.id || '';
1771
1775
  details.dataset.eventType = 'error';
1772
1776
  const summary = document.createElement('summary');