agentgui 1.0.13 → 1.0.15

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/acp-launcher.js CHANGED
@@ -67,13 +67,13 @@ export default class ACPConnection {
67
67
  await this.sendRequest('initialize', {
68
68
  protocolVersion: 1,
69
69
  clientCapabilities: { fs: { readTextFile: true, writeTextFile: true } },
70
- }, 4000);
71
- const result = await this.sendRequest('session/new', { cwd, mcpServers: [] }, 4000);
70
+ }, 10000);
71
+ const result = await this.sendRequest('session/new', { cwd, mcpServers: [] }, 30000);
72
72
  this.sessionId = result.sessionId;
73
- await this.sendRequest('session/set_mode', { sessionId: this.sessionId, modeId: 'bypassPermissions' }, 2000);
73
+ await this.sendRequest('session/set_mode', { sessionId: this.sessionId, modeId: 'bypassPermissions' }, 10000);
74
74
  };
75
75
 
76
- const deadline = new Promise((_, reject) => setTimeout(() => reject(new Error('ACP handshake timeout (5s)')), 5000));
76
+ const deadline = new Promise((_, reject) => setTimeout(() => reject(new Error('ACP handshake timeout (60s)')), 60000));
77
77
 
78
78
  try {
79
79
  await Promise.race([acpSetup(), deadline]);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agentgui",
3
- "version": "1.0.13",
3
+ "version": "1.0.15",
4
4
  "description": "Multi-agent ACP client with real-time communication",
5
5
  "type": "module",
6
6
  "main": "server.js",
@@ -0,0 +1,285 @@
1
+ /**
2
+ * Response formatter for Claude Code outputs
3
+ * Handles segmentation of text, code blocks, tool calls, thinking blocks, etc.
4
+ */
5
+
6
+ export class ResponseFormatter {
7
+ /**
8
+ * Parse Claude Code response into structured segments
9
+ */
10
+ static parseResponse(text) {
11
+ if (!text || typeof text !== 'string') return [];
12
+
13
+ const segments = [];
14
+ const lines = text.split('\n');
15
+ let current = null;
16
+ let codeBlockLang = null;
17
+ let inCodeBlock = false;
18
+
19
+ for (let i = 0; i < lines.length; i++) {
20
+ const line = lines[i];
21
+
22
+ // Check for code block markers
23
+ const codeBlockMatch = line.match(/^```(\w+)?$/);
24
+ if (codeBlockMatch) {
25
+ if (!inCodeBlock) {
26
+ // Starting a code block
27
+ if (current && current.type === 'text' && current.content.trim()) {
28
+ segments.push(current);
29
+ current = null;
30
+ }
31
+ inCodeBlock = true;
32
+ codeBlockLang = codeBlockMatch[1] || 'text';
33
+ current = { type: 'code', language: codeBlockLang, content: '' };
34
+ } else {
35
+ // Ending a code block
36
+ if (current) {
37
+ segments.push(current);
38
+ current = null;
39
+ }
40
+ inCodeBlock = false;
41
+ codeBlockLang = null;
42
+ }
43
+ continue;
44
+ }
45
+
46
+ if (inCodeBlock) {
47
+ current.content += (current.content ? '\n' : '') + line;
48
+ } else {
49
+ // Check for markdown formatting
50
+ if (line.match(/^#+\s/)) {
51
+ // Heading
52
+ if (current && current.type === 'text' && current.content.trim()) {
53
+ segments.push(current);
54
+ }
55
+ segments.push({ type: 'heading', level: line.match(/^#+/)[0].length, content: line.replace(/^#+\s/, '') });
56
+ current = null;
57
+ } else if (line.match(/^>\s/) || line.match(/^-\s/) || line.match(/^\d+\.\s/)) {
58
+ // Quote, bullet, or numbered list
59
+ if (current && current.type === 'text') {
60
+ segments.push(current);
61
+ }
62
+ if (line.match(/^>\s/)) {
63
+ segments.push({ type: 'blockquote', content: line.replace(/^>\s/, '') });
64
+ } else {
65
+ segments.push({ type: 'list_item', content: line.replace(/^[-\d+.]\s+/, '') });
66
+ }
67
+ current = null;
68
+ } else if (line.trim()) {
69
+ // Regular text
70
+ if (!current || current.type !== 'text') {
71
+ if (current) segments.push(current);
72
+ current = { type: 'text', content: line };
73
+ } else {
74
+ current.content += '\n' + line;
75
+ }
76
+ } else if (current && current.type === 'text' && current.content.trim()) {
77
+ // Empty line - could indicate paragraph break
78
+ current.content += '\n\n';
79
+ }
80
+ }
81
+ }
82
+
83
+ if (current) {
84
+ segments.push(current);
85
+ }
86
+
87
+ return segments;
88
+ }
89
+
90
+ /**
91
+ * Extract tool calls, thinking blocks, and task information
92
+ */
93
+ static extractMetadata(text) {
94
+ if (!text || typeof text !== 'string') return { tools: [], thinking: [], tasks: [] };
95
+
96
+ const metadata = {
97
+ tools: [],
98
+ thinking: [],
99
+ tasks: [],
100
+ subagents: []
101
+ };
102
+
103
+ // Find tool call patterns
104
+ const toolPattern = /(?:^|\n)\s*(?:Using|Calling|Invoking|Running)\s+(?:the\s+)?(\w+(?:\s+\w+)*?)(?:\s+(?:tool|command|function))?\s*(?:with|to)?\s*(.+?)(?:\n|$)/gi;
105
+ let match;
106
+ while ((match = toolPattern.exec(text)) !== null) {
107
+ metadata.tools.push({
108
+ name: match[1].trim(),
109
+ description: match[2]?.trim() || ''
110
+ });
111
+ }
112
+
113
+ // Find thinking/reasoning blocks
114
+ const thinkingPattern = /(?:thinking|reasoning|analyzing|considering)[\s:]+(.+?)(?:\n\n|$)/gi;
115
+ while ((match = thinkingPattern.exec(text)) !== null) {
116
+ metadata.thinking.push(match[1].trim());
117
+ }
118
+
119
+ // Find task references
120
+ const taskPattern = /(?:task|step|doing)[\s:]+(.+?)(?:\n|$)/gi;
121
+ while ((match = taskPattern.exec(text)) !== null) {
122
+ metadata.tasks.push(match[1].trim());
123
+ }
124
+
125
+ // Find subagent references
126
+ const subagentPattern = /(?:using|with|via)\s+(?:the\s+)?(\w+)\s+(?:subagent|agent)/gi;
127
+ while ((match = subagentPattern.exec(text)) !== null) {
128
+ metadata.subagents.push(match[1].trim());
129
+ }
130
+
131
+ return metadata;
132
+ }
133
+
134
+ /**
135
+ * Segment a response into logical parts
136
+ * Splits on natural boundaries like tool calls, thinking, etc.
137
+ */
138
+ static segmentResponse(text) {
139
+ if (!text || typeof text !== 'string') return [];
140
+
141
+ const segments = [];
142
+ const parts = [];
143
+
144
+ // Split by major transitions
145
+ const transitions = [
146
+ { pattern: /I'll\s+use\s+the\s+\w+\s+(?:subagent|tool|command)/i, type: 'tool_transition' },
147
+ { pattern: /Let\s+me\s+(?:use|run|execute|call)\s+/i, type: 'action_start' },
148
+ { pattern: /Here['s]*\s+(?:what|the|a)\s+(?:happened|result|output)/i, type: 'result' },
149
+ { pattern: /^(✓|✅|✗|❌|-|•)\s+/m, type: 'bullet' }
150
+ ];
151
+
152
+ let currentPart = '';
153
+ const lines = text.split('\n');
154
+
155
+ for (const line of lines) {
156
+ let isTransition = false;
157
+
158
+ for (const { pattern, type } of transitions) {
159
+ if (pattern.test(line)) {
160
+ if (currentPart.trim()) {
161
+ parts.push({ text: currentPart.trim(), type: 'text' });
162
+ currentPart = '';
163
+ }
164
+ isTransition = true;
165
+ break;
166
+ }
167
+ }
168
+
169
+ if (isTransition || line.match(/^#+\s/) || line.match(/^```/)) {
170
+ if (currentPart.trim()) {
171
+ parts.push({ text: currentPart.trim(), type: 'text' });
172
+ currentPart = '';
173
+ }
174
+ }
175
+
176
+ currentPart += (currentPart ? '\n' : '') + line;
177
+ }
178
+
179
+ if (currentPart.trim()) {
180
+ parts.push({ text: currentPart.trim(), type: 'text' });
181
+ }
182
+
183
+ // Further parse each part
184
+ for (const part of parts) {
185
+ segments.push({
186
+ ...part,
187
+ parsed: this.parseResponse(part.text),
188
+ metadata: this.extractMetadata(part.text)
189
+ });
190
+ }
191
+
192
+ return segments;
193
+ }
194
+
195
+ /**
196
+ * Format segments for display with proper HTML
197
+ */
198
+ static formatForDisplay(segments) {
199
+ if (!Array.isArray(segments)) return '';
200
+
201
+ const html = [];
202
+
203
+ for (const segment of segments) {
204
+ if (segment.type === 'code') {
205
+ html.push(`<pre class="code-block language-${segment.language}"><code>${this.escapeHtml(segment.content)}</code></pre>`);
206
+ } else if (segment.type === 'heading') {
207
+ const tag = `h${Math.min(segment.level, 6)}`;
208
+ html.push(`<${tag} class="response-heading">${this.escapeHtml(segment.content)}</${tag}>`);
209
+ } else if (segment.type === 'blockquote') {
210
+ html.push(`<blockquote class="response-quote">${this.escapeHtml(segment.content)}</blockquote>`);
211
+ } else if (segment.type === 'list_item') {
212
+ html.push(`<li class="response-list-item">${this.escapeHtml(segment.content)}</li>`);
213
+ } else if (segment.type === 'text') {
214
+ html.push(`<p class="response-text">${this.escapeHtml(segment.content).replace(/\*\*(.*?)\*\*/g, '<strong>$1</strong>').replace(/\*(.*?)\*/g, '<em>$1</em>')}</p>`);
215
+ }
216
+ }
217
+
218
+ return html.join('\n');
219
+ }
220
+
221
+ static escapeHtml(text) {
222
+ if (typeof text !== 'string') return '';
223
+ return text
224
+ .replace(/&/g, '&amp;')
225
+ .replace(/</g, '&lt;')
226
+ .replace(/>/g, '&gt;')
227
+ .replace(/"/g, '&quot;')
228
+ .replace(/'/g, '&#039;');
229
+ }
230
+
231
+ /**
232
+ * Create rich metadata display
233
+ */
234
+ static createMetadataDisplay(metadata) {
235
+ if (!metadata || Object.keys(metadata).every(k => !metadata[k] || metadata[k].length === 0)) {
236
+ return null;
237
+ }
238
+
239
+ const html = ['<div class="response-metadata">'];
240
+
241
+ if (metadata.tools?.length) {
242
+ html.push('<div class="metadata-section tools">');
243
+ html.push('<strong>Tools Used:</strong>');
244
+ html.push('<ul>');
245
+ for (const tool of metadata.tools) {
246
+ html.push(`<li><code>${this.escapeHtml(tool.name)}</code>${tool.description ? ': ' + this.escapeHtml(tool.description) : ''}</li>`);
247
+ }
248
+ html.push('</ul></div>');
249
+ }
250
+
251
+ if (metadata.thinking?.length) {
252
+ html.push('<details class="metadata-section thinking">');
253
+ html.push('<summary>Reasoning</summary>');
254
+ for (const thought of metadata.thinking) {
255
+ html.push(`<p>${this.escapeHtml(thought)}</p>`);
256
+ }
257
+ html.push('</details>');
258
+ }
259
+
260
+ if (metadata.subagents?.length) {
261
+ html.push('<div class="metadata-section subagents">');
262
+ html.push('<strong>Subagents:</strong>');
263
+ html.push('<ul>');
264
+ for (const agent of metadata.subagents) {
265
+ html.push(`<li>${this.escapeHtml(agent)}</li>`);
266
+ }
267
+ html.push('</ul></div>');
268
+ }
269
+
270
+ if (metadata.tasks?.length) {
271
+ html.push('<div class="metadata-section tasks">');
272
+ html.push('<strong>Tasks:</strong>');
273
+ html.push('<ul>');
274
+ for (const task of metadata.tasks) {
275
+ html.push(`<li>${this.escapeHtml(task)}</li>`);
276
+ }
277
+ html.push('</ul></div>');
278
+ }
279
+
280
+ html.push('</div>');
281
+ return html.join('\n');
282
+ }
283
+ }
284
+
285
+ export default ResponseFormatter;
package/server.js CHANGED
@@ -7,6 +7,7 @@ import os from 'os';
7
7
  import { execSync } from 'child_process';
8
8
  import { queries } from './database.js';
9
9
  import ACPConnection from './acp-launcher.js';
10
+ import { ResponseFormatter } from './response-formatter.js';
10
11
 
11
12
  const __dirname = path.dirname(fileURLToPath(import.meta.url));
12
13
  const PORT = process.env.PORT || 3000;
@@ -313,17 +314,21 @@ async function processMessage(conversationId, messageId, sessionId, content, age
313
314
 
314
315
  let fullText = '';
315
316
  const blocks = [];
317
+ const updateChunks = []; // Track all message chunks in order
316
318
  conn.onUpdate = (params) => {
317
319
  const u = params.update;
318
320
  if (!u) return;
319
321
  const kind = u.sessionUpdate;
320
322
  if (kind === 'agent_message_chunk' && u.content?.text) {
321
323
  fullText += u.content.text;
324
+ updateChunks.push({ type: 'text', content: u.content.text, timestamp: Date.now() });
322
325
  } else if (kind === 'html_content' && u.content?.html) {
323
326
  blocks.push({ type: 'html', html: u.content.html, title: u.content.title, id: u.content.id });
327
+ updateChunks.push({ type: 'html', content: u.content.html, title: u.content.title, timestamp: Date.now() });
324
328
  } else if (kind === 'image_content' && u.content?.path) {
325
329
  const imageUrl = BASE_URL + '/api/image/' + encodeURIComponent(u.content.path);
326
330
  blocks.push({ type: 'image', path: u.content.path, url: imageUrl, title: u.content.title, alt: u.content.alt });
331
+ updateChunks.push({ type: 'image', path: u.content.path, url: imageUrl, title: u.content.title, timestamp: Date.now() });
327
332
  }
328
333
  };
329
334
 
@@ -331,7 +336,23 @@ async function processMessage(conversationId, messageId, sessionId, content, age
331
336
  conn.onUpdate = null;
332
337
 
333
338
  const responseText = fullText || result?.result || (result?.stopReason ? `Completed: ${result.stopReason}` : 'No response.');
334
- const messageContent = blocks.length > 0 ? { text: responseText, blocks } : responseText;
339
+
340
+ // Segment and format the response for better display
341
+ const segments = ResponseFormatter.segmentResponse(responseText);
342
+ const metadata = ResponseFormatter.extractMetadata(responseText);
343
+
344
+ const messageContent = blocks.length > 0 ? {
345
+ text: responseText,
346
+ blocks,
347
+ segments,
348
+ metadata,
349
+ updateChunks
350
+ } : {
351
+ text: responseText,
352
+ segments,
353
+ metadata,
354
+ updateChunks
355
+ };
335
356
 
336
357
  const assistantMessage = queries.createMessage(conversationId, 'assistant', messageContent);
337
358
  queries.updateSession(sessionId, { status: 'completed', response: { text: responseText, messageId: assistantMessage.id }, completed_at: Date.now() });
package/static/app.js CHANGED
@@ -599,7 +599,13 @@ class GMGUIApp {
599
599
  el.appendChild(bubble);
600
600
  }
601
601
  } else if (typeof msg.content === 'object' && msg.content !== null) {
602
- if (msg.content.text) {
602
+ // Display segmented content if available
603
+ if (msg.content.segments && Array.isArray(msg.content.segments)) {
604
+ msg.content.segments.forEach(segment => {
605
+ el.appendChild(this.renderSegment(segment));
606
+ });
607
+ } else if (msg.content.text) {
608
+ // Fallback to regular text rendering
603
609
  const parsed = this.parseAndRenderContent(msg.content.text);
604
610
  if (parsed) {
605
611
  parsed.forEach(elem => el.appendChild(elem));
@@ -610,6 +616,8 @@ class GMGUIApp {
610
616
  el.appendChild(bubble);
611
617
  }
612
618
  }
619
+
620
+ // Display blocks if available
613
621
  if (msg.content.blocks && Array.isArray(msg.content.blocks)) {
614
622
  msg.content.blocks.forEach(block => {
615
623
  if (block.type === 'html') {
@@ -621,6 +629,12 @@ class GMGUIApp {
621
629
  }
622
630
  });
623
631
  }
632
+
633
+ // Display metadata if available
634
+ if (msg.content.metadata) {
635
+ const metadataEl = this.renderMetadata(msg.content.metadata);
636
+ if (metadataEl) el.appendChild(metadataEl);
637
+ }
624
638
  } else {
625
639
  const bubble = document.createElement('div');
626
640
  bubble.className = 'message-bubble';
@@ -631,6 +645,127 @@ class GMGUIApp {
631
645
  div.appendChild(el);
632
646
  }
633
647
 
648
+ renderSegment(segment) {
649
+ const el = document.createElement('div');
650
+ el.className = `segment segment-${segment.type}`;
651
+
652
+ if (segment.type === 'code') {
653
+ const pre = document.createElement('pre');
654
+ pre.className = `code-block language-${segment.language || 'text'}`;
655
+ const code = document.createElement('code');
656
+ code.textContent = segment.content;
657
+ pre.appendChild(code);
658
+ el.appendChild(pre);
659
+ } else if (segment.type === 'heading') {
660
+ const tag = `h${Math.min(segment.level, 6)}`;
661
+ const heading = document.createElement(tag);
662
+ heading.className = 'response-heading';
663
+ heading.textContent = segment.content;
664
+ el.appendChild(heading);
665
+ } else if (segment.type === 'blockquote') {
666
+ const quote = document.createElement('blockquote');
667
+ quote.className = 'response-quote';
668
+ quote.textContent = segment.content;
669
+ el.appendChild(quote);
670
+ } else if (segment.type === 'list_item') {
671
+ const li = document.createElement('li');
672
+ li.className = 'response-list-item';
673
+ li.textContent = segment.content;
674
+ el.appendChild(li);
675
+ } else if (segment.type === 'text') {
676
+ const p = document.createElement('p');
677
+ p.className = 'response-text';
678
+ p.innerHTML = segment.content
679
+ .replace(/&/g, '&amp;')
680
+ .replace(/</g, '&lt;')
681
+ .replace(/>/g, '&gt;')
682
+ .replace(/\*\*(.*?)\*\*/g, '<strong>$1</strong>')
683
+ .replace(/\*(.*?)\*/g, '<em>$1</em>')
684
+ .replace(/`([^`]+)`/g, '<code>$1</code>');
685
+ el.appendChild(p);
686
+ }
687
+
688
+ return el;
689
+ }
690
+
691
+ renderMetadata(metadata) {
692
+ if (!metadata || Object.keys(metadata).every(k => !metadata[k] || metadata[k].length === 0)) {
693
+ return null;
694
+ }
695
+
696
+ const container = document.createElement('div');
697
+ container.className = 'response-metadata';
698
+
699
+ if (metadata.tools && metadata.tools.length > 0) {
700
+ const section = document.createElement('div');
701
+ section.className = 'metadata-section tools';
702
+ const title = document.createElement('strong');
703
+ title.textContent = 'Tools Used:';
704
+ section.appendChild(title);
705
+ const ul = document.createElement('ul');
706
+ metadata.tools.forEach(tool => {
707
+ const li = document.createElement('li');
708
+ const code = document.createElement('code');
709
+ code.textContent = tool.name;
710
+ li.appendChild(code);
711
+ if (tool.description) {
712
+ li.appendChild(document.createTextNode(`: ${tool.description}`));
713
+ }
714
+ ul.appendChild(li);
715
+ });
716
+ section.appendChild(ul);
717
+ container.appendChild(section);
718
+ }
719
+
720
+ if (metadata.thinking && metadata.thinking.length > 0) {
721
+ const section = document.createElement('details');
722
+ section.className = 'metadata-section thinking';
723
+ const summary = document.createElement('summary');
724
+ summary.textContent = 'Reasoning';
725
+ section.appendChild(summary);
726
+ metadata.thinking.forEach(thought => {
727
+ const p = document.createElement('p');
728
+ p.textContent = thought;
729
+ section.appendChild(p);
730
+ });
731
+ container.appendChild(section);
732
+ }
733
+
734
+ if (metadata.subagents && metadata.subagents.length > 0) {
735
+ const section = document.createElement('div');
736
+ section.className = 'metadata-section subagents';
737
+ const title = document.createElement('strong');
738
+ title.textContent = 'Subagents:';
739
+ section.appendChild(title);
740
+ const ul = document.createElement('ul');
741
+ metadata.subagents.forEach(agent => {
742
+ const li = document.createElement('li');
743
+ li.textContent = agent;
744
+ ul.appendChild(li);
745
+ });
746
+ section.appendChild(ul);
747
+ container.appendChild(section);
748
+ }
749
+
750
+ if (metadata.tasks && metadata.tasks.length > 0) {
751
+ const section = document.createElement('div');
752
+ section.className = 'metadata-section tasks';
753
+ const title = document.createElement('strong');
754
+ title.textContent = 'Tasks:';
755
+ section.appendChild(title);
756
+ const ul = document.createElement('ul');
757
+ metadata.tasks.forEach(task => {
758
+ const li = document.createElement('li');
759
+ li.textContent = task;
760
+ ul.appendChild(li);
761
+ });
762
+ section.appendChild(ul);
763
+ container.appendChild(section);
764
+ }
765
+
766
+ return container;
767
+ }
768
+
634
769
  async startNewChat(folderPath) {
635
770
  if (!this.selectedAgent) {
636
771
  const firstAgent = Array.from(this.agents.keys())[0];
package/static/styles.css CHANGED
@@ -1418,3 +1418,163 @@ html, body {
1418
1418
  overflow: visible;
1419
1419
  }
1420
1420
  }
1421
+
1422
+ /* Rich Response Rendering */
1423
+
1424
+ /* Code blocks */
1425
+ .segment-code {
1426
+ margin: 1rem 0;
1427
+ }
1428
+
1429
+ .code-block {
1430
+ background: #f5f5f5;
1431
+ border-left: 4px solid #007acc;
1432
+ padding: 1rem;
1433
+ border-radius: 4px;
1434
+ overflow-x: auto;
1435
+ font-family: 'Courier New', monospace;
1436
+ font-size: 0.9rem;
1437
+ line-height: 1.5;
1438
+ color: #333;
1439
+ }
1440
+
1441
+ .code-block code {
1442
+ color: inherit;
1443
+ background: none;
1444
+ padding: 0;
1445
+ }
1446
+
1447
+ /* Inline code */
1448
+ p code {
1449
+ background: #f0f0f0;
1450
+ padding: 0.2em 0.4em;
1451
+ border-radius: 3px;
1452
+ font-family: 'Courier New', monospace;
1453
+ color: #d73a49;
1454
+ font-size: 0.9em;
1455
+ }
1456
+
1457
+ /* Headings */
1458
+ .response-heading {
1459
+ font-weight: 600;
1460
+ margin: 1rem 0 0.5rem 0;
1461
+ line-height: 1.3;
1462
+ }
1463
+
1464
+ .segment-heading {
1465
+ margin-top: 1.5rem;
1466
+ }
1467
+
1468
+ .segment-heading:first-child {
1469
+ margin-top: 0;
1470
+ }
1471
+
1472
+ /* Blockquotes */
1473
+ .response-quote {
1474
+ border-left: 4px solid #ccc;
1475
+ padding-left: 1rem;
1476
+ color: #666;
1477
+ font-style: italic;
1478
+ margin: 0.5rem 0;
1479
+ }
1480
+
1481
+ /* Lists */
1482
+ .response-list-item {
1483
+ margin-left: 2rem;
1484
+ margin-bottom: 0.25rem;
1485
+ }
1486
+
1487
+ /* Text segments */
1488
+ .segment-text {
1489
+ margin: 0.5rem 0;
1490
+ }
1491
+
1492
+ .response-text {
1493
+ margin: 0.5rem 0;
1494
+ line-height: 1.6;
1495
+ color: #333;
1496
+ }
1497
+
1498
+ /* Metadata display */
1499
+ .response-metadata {
1500
+ background: #f9f9f9;
1501
+ border: 1px solid #e0e0e0;
1502
+ border-radius: 8px;
1503
+ padding: 1rem;
1504
+ margin: 1rem 0 0 0;
1505
+ font-size: 0.9rem;
1506
+ }
1507
+
1508
+ .metadata-section {
1509
+ margin-bottom: 1rem;
1510
+ }
1511
+
1512
+ .metadata-section:last-child {
1513
+ margin-bottom: 0;
1514
+ }
1515
+
1516
+ .metadata-section strong {
1517
+ display: block;
1518
+ margin-bottom: 0.5rem;
1519
+ color: #555;
1520
+ font-weight: 600;
1521
+ }
1522
+
1523
+ .metadata-section ul {
1524
+ list-style: none;
1525
+ padding-left: 1rem;
1526
+ margin: 0;
1527
+ }
1528
+
1529
+ .metadata-section li {
1530
+ margin-bottom: 0.25rem;
1531
+ padding-left: 0.5rem;
1532
+ }
1533
+
1534
+ .metadata-section li::before {
1535
+ content: '▸ ';
1536
+ color: #007acc;
1537
+ font-weight: bold;
1538
+ margin-right: 0.5rem;
1539
+ }
1540
+
1541
+ .metadata-section.tools li code {
1542
+ background: #e8f4f8;
1543
+ color: #0071bc;
1544
+ padding: 0.2em 0.4em;
1545
+ border-radius: 3px;
1546
+ }
1547
+
1548
+ .metadata-section.thinking {
1549
+ background: #fff9e6;
1550
+ border: 1px solid #f5e6cc;
1551
+ }
1552
+
1553
+ .metadata-section.thinking summary {
1554
+ cursor: pointer;
1555
+ font-weight: 600;
1556
+ color: #997700;
1557
+ user-select: none;
1558
+ }
1559
+
1560
+ .metadata-section.thinking summary:hover {
1561
+ color: #bb9900;
1562
+ }
1563
+
1564
+ .metadata-section.thinking p {
1565
+ margin: 0.5rem 0;
1566
+ padding-left: 1rem;
1567
+ border-left: 3px solid #f0c674;
1568
+ padding-left: 1rem;
1569
+ color: #666;
1570
+ font-size: 0.9rem;
1571
+ }
1572
+
1573
+ .metadata-section.subagents li::before {
1574
+ content: '🤖 ';
1575
+ }
1576
+
1577
+ .metadata-section.tasks li::before {
1578
+ content: '✓ ';
1579
+ color: #28a745;
1580
+ }