agentgui 1.0.126 → 1.0.128

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.126",
3
+ "version": "1.0.128",
4
4
  "description": "Multi-agent ACP client with real-time communication",
5
5
  "type": "module",
6
6
  "main": "server.js",
@@ -581,22 +581,14 @@ class StreamingRenderer {
581
581
  case 'dev__execute':
582
582
  case 'dev_execute':
583
583
  case 'execute': {
584
- // Handle mcp__plugin_gm_dev__execute and similar dev execution tools
585
584
  let html = '<div class="tool-params">';
586
585
 
587
- // Show working directory if present
588
586
  if (input.workingDirectory) {
589
- html += `<div style="margin-bottom:0.5rem;font-size:0.75rem;color:var(--color-text-secondary)">
590
- <span style="opacity:0.7">📁</span> ${this.escapeHtml(input.workingDirectory)}
591
- </div>`;
587
+ html += `<div style="margin-bottom:0.5rem;font-size:0.75rem;color:var(--color-text-secondary)"><span style="opacity:0.7">📁</span> ${this.escapeHtml(input.workingDirectory)}</div>`;
592
588
  }
593
589
 
594
- // Show timeout if present
595
590
  if (input.timeout) {
596
- const seconds = Math.round(input.timeout / 1000);
597
- html += `<div style="margin-bottom:0.5rem;font-size:0.75rem;color:var(--color-text-secondary)">
598
- <span style="opacity:0.7">⏱️</span> Timeout: ${seconds}s
599
- </div>`;
591
+ html += `<div style="margin-bottom:0.5rem;font-size:0.75rem;color:var(--color-text-secondary)"><span style="opacity:0.7">⏱️</span> Timeout: ${Math.round(input.timeout / 1000)}s</div>`;
600
592
  }
601
593
 
602
594
  // Render code with syntax highlighting
@@ -605,16 +597,16 @@ class StreamingRenderer {
605
597
  const lineCount = codeLines.length;
606
598
  const truncated = lineCount > 50;
607
599
  const displayCode = truncated ? codeLines.slice(0, 50).join('\n') : input.code;
608
-
609
600
  const lang = input.runtime || 'javascript';
610
- html += `<div style="margin-top:0.5rem">
611
- <div style="display:flex;justify-content:space-between;align-items:center;margin-bottom:0.25rem">
612
- <span style="font-size:0.7rem;font-weight:600;color:#0891b2;text-transform:uppercase">${this.escapeHtml(lang)}</span>
613
- <span style="font-size:0.7rem;color:var(--color-text-secondary)">${lineCount} lines</span>
614
- </div>
615
- ${StreamingRenderer.renderCodeWithHighlight(displayCode, this.escapeHtml.bind(this))}
616
- ${truncated ? `<div style="font-size:0.7rem;color:var(--color-text-secondary);text-align:center;padding:0.25rem">... ${lineCount - 50} more lines</div>` : ''}
617
- </div>`;
601
+ html += `<div style="margin-top:0.5rem"><div style="display:flex;justify-content:space-between;align-items:center;margin-bottom:0.25rem"><span style="font-size:0.7rem;font-weight:600;color:#0891b2;text-transform:uppercase">${this.escapeHtml(lang)}</span><span style="font-size:0.7rem;color:var(--color-text-secondary)">${lineCount} lines</span></div>${StreamingRenderer.renderCodeWithHighlight(displayCode, this.escapeHtml.bind(this))}${truncated ? `<div style="font-size:0.7rem;color:var(--color-text-secondary);text-align:center;padding:0.25rem">... ${lineCount - 50} more lines</div>` : ''}</div>`;
602
+ }
603
+
604
+ // Render commands (bash commands)
605
+ if (input.commands) {
606
+ const cmds = Array.isArray(input.commands) ? input.commands : [input.commands];
607
+ cmds.forEach(cmd => {
608
+ html += `<div style="margin-top:0.375rem"><div class="tool-param-command"><span class="prompt-char">$</span><span class="command-text">${this.escapeHtml(typeof cmd === 'string' ? cmd : JSON.stringify(cmd))}</span></div></div>`;
609
+ });
618
610
  }
619
611
 
620
612
  html += '</div>';
@@ -756,14 +748,23 @@ class StreamingRenderer {
756
748
  return `<div style="padding:0.5rem"><img src="${esc(trimmed)}" style="max-width:100%;max-height:24rem;border-radius:0.375rem" loading="lazy"></div>`;
757
749
  }
758
750
 
759
- // Instead of rendering JSON as parameters, check if it looks like code output
751
+ // Parse JSON and render as structured content
760
752
  if ((trimmed.startsWith('{') && trimmed.endsWith('}')) || (trimmed.startsWith('[') && trimmed.endsWith(']'))) {
761
753
  try {
762
- // Validate it's JSON, then render as highlighted code
763
- JSON.parse(trimmed);
764
- // Format JSON with proper indentation
765
- const formatted = JSON.stringify(JSON.parse(trimmed), null, 2);
766
- return StreamingRenderer.renderCodeWithHighlight(formatted, esc);
754
+ const parsed = JSON.parse(trimmed);
755
+
756
+ // Handle Claude content block arrays: [{type:"text", text:"..."}]
757
+ if (Array.isArray(parsed) && parsed.length > 0 && parsed[0] && parsed[0].type === 'text') {
758
+ const textParts = parsed.filter(b => b.type === 'text' && b.text);
759
+ if (textParts.length > 0) {
760
+ const combined = textParts.map(b => b.text).join('\n');
761
+ // Re-enter renderSmartContentHTML with the extracted text
762
+ return StreamingRenderer.renderSmartContentHTML(combined, esc);
763
+ }
764
+ }
765
+
766
+ // For other JSON, render as itemized key-value structure
767
+ return `<div style="padding:0.5rem 0.75rem">${StreamingRenderer.renderParamsHTML(parsed, 0, esc)}</div>`;
767
768
  } catch (e) {
768
769
  // Not valid JSON, might be code with braces
769
770
  }
@@ -990,81 +991,64 @@ class StreamingRenderer {
990
991
  * Render code with basic syntax highlighting
991
992
  */
992
993
  static renderCodeWithHighlight(code, esc) {
993
- // Escape HTML first
994
- let highlighted = esc(code);
994
+ // Tokenize-then-replace approach: collect spans as tokens so regexes
995
+ // never see previously-injected HTML, preventing cascading corruption.
996
+ const tokens = [];
997
+ const mkToken = (cls, text) => {
998
+ const id = `\x00T${tokens.length}\x00`;
999
+ tokens.push({ id, cls, text });
1000
+ return id;
1001
+ };
995
1002
 
996
- // Detect if this is JSON and apply JSON-specific highlighting
1003
+ let src = code;
1004
+
1005
+ // Detect JSON
997
1006
  const isJSON = (code.trim().startsWith('{') || code.trim().startsWith('[')) &&
998
1007
  code.includes('"') && (code.includes(':') || code.includes(','));
999
1008
 
1000
1009
  if (isJSON) {
1001
- // JSON-specific highlighting
1002
- const jsonHighlights = [
1003
- // Property names (keys) in quotes
1004
- { pattern: /"([^"]+)"\s*:/g, replacement: '"<span style="color:#3b82f6;font-weight:600">$1</span>":' },
1005
- // String values
1006
- { pattern: /:\s*"([^"]*)"/g, replacement: ': "<span style="color:#10b981">$1</span>"' },
1007
- // Numbers
1008
- { pattern: /:\s*(\d+(?:\.\d+)?(?:[eE][+-]?\d+)?)/g, replacement: ': <span style="color:#f59e0b">$1</span>' },
1009
- // Booleans and null
1010
- { pattern: /:\s*(true|false|null)/g, replacement: ': <span style="color:#ef4444">$1</span>' },
1011
- // Array/object brackets
1012
- { pattern: /([\[\]{}])/g, replacement: '<span style="color:#6b7280;font-weight:600">$1</span>' },
1013
- ];
1014
-
1015
- jsonHighlights.forEach(({ pattern, replacement }) => {
1016
- highlighted = highlighted.replace(pattern, replacement);
1017
- });
1010
+ // JSON keys
1011
+ src = src.replace(/"([^"]+)"\s*:/g, (m, k) => `"${mkToken('jk', k)}":`);
1012
+ // JSON string values
1013
+ src = src.replace(/:\s*"([^"]*)"/g, (m, v) => `: "${mkToken('js', v)}"`);
1014
+ // JSON numbers
1015
+ src = src.replace(/:\s*(\d+(?:\.\d+)?(?:[eE][+-]?\d+)?)/g, (m, n) => `: ${mkToken('jn', n)}`);
1016
+ // JSON booleans/null
1017
+ src = src.replace(/:\s*(true|false|null)/g, (m, b) => `: ${mkToken('jb', b)}`);
1018
1018
  } else {
1019
- // General code syntax highlighting
1020
- const highlights = [
1021
- // Comments (do these first to avoid conflicts)
1022
- { pattern: /(\/\/[^\n]*)/g, replacement: '<span style="color:#6b7280;font-style:italic">$1</span>' },
1023
- { pattern: /(\/\*[\s\S]*?\*\/)/g, replacement: '<span style="color:#6b7280;font-style:italic">$1</span>' },
1024
- { pattern: /(#[^\n]*)/g, replacement: '<span style="color:#6b7280;font-style:italic">$1</span>' },
1025
-
1026
- // Strings (improved to handle escaped quotes)
1027
- { pattern: /(["'])(?:[^\\]|\\.)*?\1/g, replacement: (match) => `<span style="color:#10b981">${match}</span>` },
1028
-
1029
- // Template literals (backticks)
1030
- { pattern: /`([^`]*)`/g, replacement: '<span style="color:#10b981">`$1`</span>' },
1031
-
1032
- // Keywords
1033
- { pattern: /\b(function|const|let|var|class|import|export|async|await|return|if|else|for|while|try|catch|throw|new|typeof|instanceof|this|super|switch|case|default|break|continue|do)\b/g,
1034
- replacement: '<span style="color:#8b5cf6;font-weight:600">$1</span>' },
1035
- { pattern: /\b(def|class|import|from|return|if|elif|else|for|while|try|except|raise|with|as|lambda|pass|break|continue|yield|global|nonlocal)\b/g,
1036
- replacement: '<span style="color:#8b5cf6;font-weight:600">$1</span>' },
1037
- { pattern: /\b(public|private|protected|static|final|abstract|interface|extends|implements|package|void|int|string|boolean|float|double|char)\b/g,
1038
- replacement: '<span style="color:#8b5cf6;font-weight:600">$1</span>' },
1039
-
1040
- // Type annotations
1041
- { pattern: /:\s*([A-Z][a-zA-Z0-9_]*)/g, replacement: ': <span style="color:#0891b2">$1</span>' },
1042
-
1043
- // Numbers
1044
- { pattern: /\b(\d+(?:\.\d+)?(?:[eE][+-]?\d+)?)\b/g, replacement: '<span style="color:#f59e0b">$1</span>' },
1045
-
1046
- // Booleans and null
1047
- { pattern: /\b(true|false|null|undefined|None|True|False|nil)\b/g, replacement: '<span style="color:#ef4444">$1</span>' },
1048
-
1049
- // Function/method names (improved)
1050
- { pattern: /\b([a-zA-Z_][a-zA-Z0-9_]*)(?=\s*\()/g, replacement: '<span style="color:#3b82f6">$1</span>' },
1051
-
1052
- // Operators
1053
- { pattern: /(===|!==|==|!=|<=|>=|&&|\|\||\+=|-=|\*=|\/=|%=|=>|->)/g, replacement: '<span style="color:#a855f7">$1</span>' },
1054
- ];
1055
-
1056
- // Apply highlights
1057
- highlights.forEach(({ pattern, replacement }) => {
1058
- if (typeof replacement === 'function') {
1059
- highlighted = highlighted.replace(pattern, replacement);
1060
- } else {
1061
- highlighted = highlighted.replace(pattern, replacement);
1062
- }
1063
- });
1019
+ // Comments
1020
+ src = src.replace(/(\/\/[^\n]*)/g, (m) => mkToken('cm', m));
1021
+ src = src.replace(/(\/\*[\s\S]*?\*\/)/g, (m) => mkToken('cm', m));
1022
+ src = src.replace(/(#[^\n]*)/g, (m) => mkToken('cm', m));
1023
+ // Strings
1024
+ src = src.replace(/(["'])(?:[^\\]|\\.)*?\1/g, (m) => mkToken('st', m));
1025
+ src = src.replace(/`([^`]*)`/g, (m) => mkToken('st', m));
1026
+ // Keywords
1027
+ src = src.replace(/\b(function|const|let|var|class|import|export|async|await|return|if|else|for|while|try|catch|throw|new|typeof|instanceof|this|super|switch|case|default|break|continue|do|def|from|elif|except|raise|with|as|lambda|pass|yield|global|nonlocal|public|private|protected|static|final|abstract|interface|extends|implements|package|void)\b/g, (m) => mkToken('kw', m));
1028
+ // Booleans/null
1029
+ src = src.replace(/\b(true|false|null|undefined|None|True|False|nil)\b/g, (m) => mkToken('bl', m));
1030
+ // Numbers
1031
+ src = src.replace(/\b(\d+(?:\.\d+)?(?:[eE][+-]?\d+)?)\b/g, (m) => mkToken('nu', m));
1032
+ // Functions
1033
+ src = src.replace(/\b([a-zA-Z_][a-zA-Z0-9_]*)(?=\s*\()/g, (m) => mkToken('fn', m));
1034
+ // Operators
1035
+ src = src.replace(/(===|!==|==|!=|<=|>=|&&|\|\||\+=|-=|\*=|\/=|%=|=>|->)/g, (m) => mkToken('op', m));
1064
1036
  }
1065
1037
 
1066
- // Use a dark theme that works well for code
1067
- return `<pre style="background:#1e293b;padding:1rem;border-radius:0.375rem;overflow-x:auto;font-family:'Monaco','Menlo','Ubuntu Mono',monospace;font-size:0.875rem;line-height:1.6;color:#e2e8f0;border:1px solid #334155;box-shadow:0 2px 4px rgba(0,0,0,0.1)">${highlighted}</pre>`;
1038
+ // Now escape HTML on the tokenized source (tokens are safe null-byte markers)
1039
+ let highlighted = esc(src);
1040
+
1041
+ // Replace tokens with actual styled spans
1042
+ const styles = {
1043
+ jk: 'color:#3b82f6;font-weight:600', js: 'color:#10b981', jn: 'color:#f59e0b', jb: 'color:#ef4444',
1044
+ cm: 'color:#6b7280;font-style:italic', st: 'color:#10b981', kw: 'color:#8b5cf6;font-weight:600',
1045
+ bl: 'color:#ef4444', nu: 'color:#f59e0b', fn: 'color:#3b82f6', op: 'color:#a855f7'
1046
+ };
1047
+ for (const { id, cls, text } of tokens) {
1048
+ highlighted = highlighted.replace(id, `<span style="${styles[cls]}">${esc(text)}</span>`);
1049
+ }
1050
+
1051
+ return `<pre style="background:#1e293b;padding:1rem;border-radius:0.375rem;overflow-x:auto;font-family:'Monaco','Menlo','Ubuntu Mono',monospace;font-size:0.875rem;line-height:1.6;color:#e2e8f0;border:1px solid #334155">${highlighted}</pre>`;
1068
1052
  }
1069
1053
 
1070
1054
  /**
@@ -1076,6 +1060,11 @@ class StreamingRenderer {
1076
1060
  if (typeof data === 'number') return `<span style="color:#7c3aed;font-weight:600">${data}</span>`;
1077
1061
 
1078
1062
  if (typeof data === 'string') {
1063
+ if (data.length > 200 && StreamingRenderer.detectCodeContent(data)) {
1064
+ const displayData = data.length > 1000 ? data.substring(0, 1000) : data;
1065
+ const suffix = data.length > 1000 ? `<div style="font-size:0.7rem;color:var(--color-text-secondary);text-align:center;padding:0.25rem">... ${data.length - 1000} more characters</div>` : '';
1066
+ return `<div style="max-height:200px;overflow-y:auto">${StreamingRenderer.renderCodeWithHighlight(displayData, esc)}${suffix}</div>`;
1067
+ }
1079
1068
  if (data.length > 500) {
1080
1069
  return `<div style="font-family:'Monaco','Menlo','Ubuntu Mono',monospace;font-size:0.75rem;white-space:pre-wrap;word-break:break-all;max-height:200px;overflow-y:auto;background:var(--color-bg-code);color:#d1d5db;padding:0.5rem;border-radius:0.375rem;line-height:1.5">${esc(data.substring(0, 1000))}${data.length > 1000 ? '\n... (' + (data.length - 1000) + ' more chars)' : ''}</div>`;
1081
1070
  }