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 +4 -4
- package/package.json +1 -1
- package/response-formatter.js +285 -0
- package/server.js +22 -1
- package/static/app.js +136 -1
- package/static/styles.css +160 -0
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
|
-
},
|
|
71
|
-
const result = await this.sendRequest('session/new', { cwd, mcpServers: [] },
|
|
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' },
|
|
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 (
|
|
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
|
@@ -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, '&')
|
|
225
|
+
.replace(/</g, '<')
|
|
226
|
+
.replace(/>/g, '>')
|
|
227
|
+
.replace(/"/g, '"')
|
|
228
|
+
.replace(/'/g, ''');
|
|
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
|
-
|
|
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
|
-
|
|
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, '&')
|
|
680
|
+
.replace(/</g, '<')
|
|
681
|
+
.replace(/>/g, '>')
|
|
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
|
+
}
|