agentgui 1.0.713 → 1.0.715
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/lib/jsonl-watcher.js +61 -35
- package/package.json +1 -1
- package/static/js/client.js +34 -35
- package/static/js/streaming-renderer.js +30 -49
package/lib/jsonl-watcher.js
CHANGED
|
@@ -4,7 +4,6 @@ import os from 'os';
|
|
|
4
4
|
|
|
5
5
|
const PROJECTS_DIR = path.join(os.homedir(), '.claude', 'projects');
|
|
6
6
|
const DEBOUNCE_MS = 16;
|
|
7
|
-
const genId = (p) => `${p}-${Date.now()}-${Math.random().toString(36).slice(2, 10)}`;
|
|
8
7
|
|
|
9
8
|
export class JsonlWatcher {
|
|
10
9
|
constructor({ broadcastSync, queries, ownedSessionIds }) {
|
|
@@ -16,12 +15,13 @@ export class JsonlWatcher {
|
|
|
16
15
|
this._frags = new Map();
|
|
17
16
|
this._timers = new Map();
|
|
18
17
|
this._seqs = new Map();
|
|
18
|
+
this._streaming = new Set();
|
|
19
19
|
this._watcher = null;
|
|
20
20
|
}
|
|
21
21
|
|
|
22
22
|
start() {
|
|
23
23
|
if (!fs.existsSync(PROJECTS_DIR)) return;
|
|
24
|
-
this.
|
|
24
|
+
this._scanDir(PROJECTS_DIR, 0);
|
|
25
25
|
try {
|
|
26
26
|
this._watcher = fs.watch(PROJECTS_DIR, { recursive: true }, (_, f) => {
|
|
27
27
|
if (f && f.endsWith('.jsonl')) this._debounce(path.join(PROJECTS_DIR, f));
|
|
@@ -41,34 +41,32 @@ export class JsonlWatcher {
|
|
|
41
41
|
for (const sid of sids) {
|
|
42
42
|
this._convMap.delete(sid);
|
|
43
43
|
this._seqs.delete(sid);
|
|
44
|
+
this._streaming.delete(sid);
|
|
44
45
|
for (const key of [...this._frags.keys()]) if (key.startsWith(`${sid}:`)) this._frags.delete(key);
|
|
45
|
-
const fp
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
46
|
+
for (const [fp, s] of this._tails.entries()) {
|
|
47
|
+
if (!fp.includes(sid)) continue;
|
|
48
|
+
if (s.fd !== null) try { fs.closeSync(s.fd); } catch (_) {}
|
|
49
|
+
this._tails.delete(fp);
|
|
50
|
+
const t = this._timers.get(fp);
|
|
51
|
+
if (t) { clearTimeout(t); this._timers.delete(fp); }
|
|
52
|
+
}
|
|
51
53
|
}
|
|
52
54
|
}
|
|
53
55
|
|
|
54
56
|
removeAllConversations() {
|
|
55
57
|
for (const s of this._tails.values()) if (s.fd !== null) try { fs.closeSync(s.fd); } catch (_) {}
|
|
56
58
|
for (const t of this._timers.values()) clearTimeout(t);
|
|
57
|
-
this._tails.clear();
|
|
58
|
-
this.
|
|
59
|
-
this._frags.clear();
|
|
60
|
-
this._timers.clear();
|
|
61
|
-
this._seqs.clear();
|
|
59
|
+
this._tails.clear(); this._convMap.clear(); this._frags.clear();
|
|
60
|
+
this._timers.clear(); this._seqs.clear(); this._streaming.clear();
|
|
62
61
|
}
|
|
63
62
|
|
|
64
|
-
|
|
63
|
+
_scanDir(dir, depth) {
|
|
64
|
+
if (depth > 4) return;
|
|
65
65
|
try {
|
|
66
|
-
for (const d of fs.readdirSync(
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
if (f.endsWith('.jsonl')) this._debounce(path.join(PROJECTS_DIR, d.name, f));
|
|
71
|
-
} catch (_) {}
|
|
66
|
+
for (const d of fs.readdirSync(dir, { withFileTypes: true })) {
|
|
67
|
+
const fp = path.join(dir, d.name);
|
|
68
|
+
if (d.isFile() && d.name.endsWith('.jsonl')) this._debounce(fp);
|
|
69
|
+
else if (d.isDirectory()) this._scanDir(fp, depth + 1);
|
|
72
70
|
}
|
|
73
71
|
} catch (_) {}
|
|
74
72
|
}
|
|
@@ -112,7 +110,7 @@ export class JsonlWatcher {
|
|
|
112
110
|
if (cid) this._route(cid, e.sessionId, e);
|
|
113
111
|
}
|
|
114
112
|
|
|
115
|
-
_conv(sid, e
|
|
113
|
+
_conv(sid, e) {
|
|
116
114
|
if (this._convMap.has(sid)) return this._convMap.get(sid);
|
|
117
115
|
const found = this._q.getConversations().find(c => c.claudeSessionId === sid);
|
|
118
116
|
if (found) { this._convMap.set(sid, found.id); return found.id; }
|
|
@@ -130,39 +128,67 @@ export class JsonlWatcher {
|
|
|
130
128
|
|
|
131
129
|
_seq(sid) { const n = (this._seqs.get(sid) || 0) + 1; this._seqs.set(sid, n); return n; }
|
|
132
130
|
|
|
131
|
+
_emit(cid, sid, block, role, extra) {
|
|
132
|
+
this._bc({ type: 'streaming_progress', sessionId: sid, conversationId: cid, block, blockRole: role, seq: this._seq(sid), timestamp: Date.now(), ...extra });
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
_startStreaming(cid, sid) {
|
|
136
|
+
if (this._streaming.has(sid)) return;
|
|
137
|
+
this._streaming.add(sid);
|
|
138
|
+
this._bc({ type: 'streaming_start', sessionId: sid, conversationId: cid, agentId: 'cli-claude', timestamp: Date.now() });
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
_endStreaming(cid, sid) {
|
|
142
|
+
if (!this._streaming.has(sid)) return;
|
|
143
|
+
this._streaming.delete(sid);
|
|
144
|
+
this._bc({ type: 'streaming_complete', sessionId: sid, conversationId: cid, agentId: 'cli-claude', eventCount: 0, seq: this._seq(sid), timestamp: Date.now() });
|
|
145
|
+
}
|
|
146
|
+
|
|
133
147
|
_route(cid, sid, e) {
|
|
134
148
|
if (e.type === 'queue-operation' || (e.type === 'user' && e.isMeta)) return;
|
|
135
149
|
|
|
136
150
|
if (e.type === 'system') {
|
|
137
|
-
if (e.subtype === 'init') { this.
|
|
138
|
-
if (e.subtype === 'turn_duration'
|
|
139
|
-
|
|
140
|
-
this._bc({ type: 'streaming_progress', sessionId: sid, conversationId: cid, block: b, blockRole: 'system', seq: this._seq(sid), timestamp: Date.now() });
|
|
151
|
+
if (e.subtype === 'init') { this._startStreaming(cid, sid); return; }
|
|
152
|
+
if (e.subtype === 'turn_duration' || e.subtype === 'stop_hook_summary') { this._endStreaming(cid, sid); return; }
|
|
153
|
+
this._emit(cid, sid, { type: 'system', subtype: e.subtype, model: e.model, cwd: e.cwd, tools: e.tools, preTokens: e.compactMetadata?.preTokens }, 'system');
|
|
141
154
|
return;
|
|
142
155
|
}
|
|
143
156
|
|
|
144
157
|
if (e.type === 'assistant' && e.message?.content) {
|
|
158
|
+
this._startStreaming(cid, sid);
|
|
145
159
|
const key = `${sid}:${e.message.id}`;
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
160
|
+
const prevCount = this._frags.get(key)?.message?.content?.length || 0;
|
|
161
|
+
const newBlocks = e.message.content.slice(prevCount);
|
|
162
|
+
if (e.message.stop_reason === null || e.message.stop_reason === undefined) this._frags.set(key, e);
|
|
163
|
+
else this._frags.delete(key);
|
|
164
|
+
for (const b of newBlocks) this._emit(cid, sid, b, 'assistant');
|
|
149
165
|
return;
|
|
150
166
|
}
|
|
151
167
|
|
|
152
168
|
if (e.type === 'user' && e.message?.content) {
|
|
153
|
-
if (e.isCompactSummary) { this.
|
|
154
|
-
|
|
169
|
+
if (e.isCompactSummary) { this._emit(cid, sid, { type: 'compact_summary', content: e.message.content }, 'system'); return; }
|
|
170
|
+
this._startStreaming(cid, sid);
|
|
171
|
+
const blocks = Array.isArray(e.message.content) ? e.message.content : [];
|
|
172
|
+
for (const b of blocks) if (b.type === 'tool_result') this._emit(cid, sid, b, 'tool_result');
|
|
155
173
|
return;
|
|
156
174
|
}
|
|
157
175
|
|
|
158
|
-
if (e.type === 'progress') {
|
|
176
|
+
if (e.type === 'progress') {
|
|
177
|
+
this._startStreaming(cid, sid);
|
|
178
|
+
const dataType = e.data?.type || e.subtype || 'progress';
|
|
179
|
+
const content = dataType === 'agent_progress' ? e.data?.message : (e.data || e.content);
|
|
180
|
+
this._emit(cid, sid, { type: dataType, agentId: e.agentId, content }, 'progress');
|
|
181
|
+
return;
|
|
182
|
+
}
|
|
159
183
|
|
|
160
|
-
if (e.isApiErrorMessage && e.error === 'rate_limit') {
|
|
184
|
+
if (e.isApiErrorMessage && e.error === 'rate_limit') {
|
|
185
|
+
this._bc({ type: 'streaming_error', sessionId: sid, conversationId: cid, error: 'Rate limit hit', recoverable: true, timestamp: Date.now() });
|
|
186
|
+
return;
|
|
187
|
+
}
|
|
161
188
|
|
|
162
189
|
if (e.type === 'result') {
|
|
163
|
-
|
|
164
|
-
this.
|
|
165
|
-
this._bc({ type: 'streaming_complete', sessionId: sid, conversationId: cid, agentId: 'cli-claude', eventCount: 0, seq: this._seq(sid), timestamp: Date.now() });
|
|
190
|
+
this._emit(cid, sid, { type: 'result', result: e.result, subtype: e.subtype, duration_ms: e.duration_ms, total_cost_usd: e.total_cost_usd, is_error: e.is_error || false }, 'result', { isResult: true });
|
|
191
|
+
this._endStreaming(cid, sid);
|
|
166
192
|
}
|
|
167
193
|
}
|
|
168
194
|
}
|
package/package.json
CHANGED
package/static/js/client.js
CHANGED
|
@@ -910,7 +910,12 @@ class AgentGUIClient {
|
|
|
910
910
|
if (chunk.block.type === 'tool_result') {
|
|
911
911
|
const lastInFrag = bFrag.lastElementChild;
|
|
912
912
|
if (lastInFrag?.classList?.contains('block-tool-use')) {
|
|
913
|
-
|
|
913
|
+
lastInFrag.classList.remove('has-success', 'has-error');
|
|
914
|
+
lastInFrag.classList.add(chunk.block.is_error ? 'has-error' : 'has-success');
|
|
915
|
+
const parentIsOpen = lastInFrag.hasAttribute('open');
|
|
916
|
+
const contextWithParent = { ...chunk, parentIsOpen };
|
|
917
|
+
const el = this.renderer.renderBlock(chunk.block, contextWithParent, bFrag);
|
|
918
|
+
if (el) { lastInFrag.appendChild(el); }
|
|
914
919
|
return;
|
|
915
920
|
}
|
|
916
921
|
}
|
|
@@ -1582,13 +1587,10 @@ class AgentGUIClient {
|
|
|
1582
1587
|
let html = '<div class="message-blocks">';
|
|
1583
1588
|
if (content.blocks && Array.isArray(content.blocks)) {
|
|
1584
1589
|
let pendingToolUseClose = false;
|
|
1585
|
-
let pendingHasInput = false;
|
|
1586
1590
|
content.blocks.forEach((block, blockIdx, blocks) => {
|
|
1587
1591
|
if (block.type !== 'tool_result' && pendingToolUseClose) {
|
|
1588
|
-
if (pendingHasInput) html += '</div>';
|
|
1589
1592
|
html += '</details>';
|
|
1590
1593
|
pendingToolUseClose = false;
|
|
1591
|
-
pendingHasInput = false;
|
|
1592
1594
|
}
|
|
1593
1595
|
if (block.type === 'text') {
|
|
1594
1596
|
const parts = this.parseMarkdownCodeBlocks(block.text);
|
|
@@ -1619,11 +1621,10 @@ class AgentGUIClient {
|
|
|
1619
1621
|
html += `<div class="message-code"><details class="collapsible-code"><summary class="collapsible-code-summary">${this.escapeHtml(block.language || 'code')} - ${blkLineCount} line${blkLineCount !== 1 ? 's' : ''}</summary><pre style="margin:0;border-radius:0 0 0.375rem 0.375rem">${this.escapeHtml(block.code)}</pre></details></div>`;
|
|
1620
1622
|
}
|
|
1621
1623
|
} else if (block.type === 'tool_use') {
|
|
1622
|
-
let
|
|
1623
|
-
|
|
1624
|
-
if (hasInput) {
|
|
1624
|
+
let inputHtml = '';
|
|
1625
|
+
if (block.input && Object.keys(block.input).length > 0) {
|
|
1625
1626
|
const inputStr = JSON.stringify(block.input, null, 2);
|
|
1626
|
-
|
|
1627
|
+
inputHtml = `<div class="folded-tool-body"><pre class="tool-input-pre">${this.escapeHtml(inputStr)}</pre></div>`;
|
|
1627
1628
|
}
|
|
1628
1629
|
const tn = block.name || 'unknown';
|
|
1629
1630
|
const hasRenderer = typeof StreamingRenderer !== 'undefined';
|
|
@@ -1633,40 +1634,27 @@ class AgentGUIClient {
|
|
|
1633
1634
|
const typeClass = hasRenderer && this.renderer ? this.renderer._getBlockTypeClass('tool_use') : 'block-type-tool_use';
|
|
1634
1635
|
const toolColorClass = hasRenderer && this.renderer ? this.renderer._getToolColorClass(tn) : 'tool-color-default';
|
|
1635
1636
|
const nextBlock = blocks[blockIdx + 1];
|
|
1636
|
-
const resultClass = nextBlock?.type === 'tool_result' ? (nextBlock.is_error ? 'has-error
|
|
1637
|
-
|
|
1638
|
-
? `<span class="folded-tool-status">${nextBlock.is_error
|
|
1639
|
-
? '<svg viewBox="0 0 20 20" fill="currentColor"><path 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"/></svg>'
|
|
1640
|
-
: '<svg viewBox="0 0 20 20" fill="currentColor"><path 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"/></svg>'
|
|
1641
|
-
}</span>` : '';
|
|
1642
|
-
if (hasInput) {
|
|
1643
|
-
html += `<details class="block-tool-use folded-tool ${typeClass} ${toolColorClass} ${resultClass}"><summary class="folded-tool-bar">${iconHtml}<span class="folded-tool-name">${this.escapeHtml(dName)}</span>${tTitle ? `<span class="folded-tool-desc">${this.escapeHtml(tTitle)}</span>` : ''}${resultStatusIcon}</summary><div class="folded-tool-body">${inputContentHtml}`;
|
|
1644
|
-
pendingHasInput = true;
|
|
1645
|
-
} else {
|
|
1646
|
-
html += `<details class="block-tool-use folded-tool ${typeClass} ${toolColorClass} ${resultClass}"><summary class="folded-tool-bar">${iconHtml}<span class="folded-tool-name">${this.escapeHtml(dName)}</span>${tTitle ? `<span class="folded-tool-desc">${this.escapeHtml(tTitle)}</span>` : ''}${resultStatusIcon}</summary>`;
|
|
1647
|
-
pendingHasInput = false;
|
|
1648
|
-
}
|
|
1637
|
+
const resultClass = nextBlock?.type === 'tool_result' ? (nextBlock.is_error ? 'has-error' : 'has-success') : '';
|
|
1638
|
+
html += `<details class="block-tool-use folded-tool ${typeClass} ${toolColorClass} ${resultClass}"><summary class="folded-tool-bar">${iconHtml}<span class="folded-tool-name">${this.escapeHtml(dName)}</span>${tTitle ? `<span class="folded-tool-desc">${this.escapeHtml(tTitle)}</span>` : ''}</summary>${inputHtml}`;
|
|
1649
1639
|
pendingToolUseClose = true;
|
|
1650
1640
|
} else if (block.type === 'tool_result') {
|
|
1651
1641
|
const content = typeof block.content === 'string' ? block.content : JSON.stringify(block.content);
|
|
1652
1642
|
const smartHtml = typeof StreamingRenderer !== 'undefined' ? StreamingRenderer.renderSmartContentHTML(content, this.escapeHtml.bind(this), true) : `<pre class="tool-result-pre">${this.escapeHtml(content.length > 2000 ? content.substring(0, 2000) + '\n... (truncated)' : content)}</pre>`;
|
|
1653
|
-
const
|
|
1643
|
+
const resultPreview = content.length > 80 ? content.substring(0, 77).replace(/\n/g, ' ') + '...' : content.replace(/\n/g, ' ');
|
|
1644
|
+
const resultIcon = block.is_error
|
|
1645
|
+
? '<svg viewBox="0 0 20 20" fill="currentColor"><path 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"/></svg>'
|
|
1646
|
+
: '<svg viewBox="0 0 20 20" fill="currentColor"><path 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"/></svg>';
|
|
1647
|
+
const resultTypeClass = hasRenderer && this.renderer ? this.renderer._getBlockTypeClass('tool_result') : 'block-type-tool_result';
|
|
1648
|
+
const resultHtml = `<div class="tool-result-inline${block.is_error ? ' tool-result-error' : ' tool-result-success'} ${resultTypeClass}"><div class="tool-result-status"><span class="folded-tool-icon">${resultIcon}</span><span class="folded-tool-name">${block.is_error ? 'Error' : 'Success'}</span></div><div class="folded-tool-body">${smartHtml}</div></div>`;
|
|
1654
1649
|
if (pendingToolUseClose) {
|
|
1655
|
-
|
|
1656
|
-
html += resultContentHtml + '</div></details>';
|
|
1657
|
-
} else {
|
|
1658
|
-
html += `<div class="folded-tool-body">${resultContentHtml}</div></details>`;
|
|
1659
|
-
}
|
|
1650
|
+
html += resultHtml + '</details>';
|
|
1660
1651
|
pendingToolUseClose = false;
|
|
1661
1652
|
} else {
|
|
1662
|
-
html +=
|
|
1653
|
+
html += resultHtml;
|
|
1663
1654
|
}
|
|
1664
1655
|
}
|
|
1665
1656
|
});
|
|
1666
|
-
if (pendingToolUseClose)
|
|
1667
|
-
if (pendingHasInput) html += '</div>';
|
|
1668
|
-
html += '</details>';
|
|
1669
|
-
}
|
|
1657
|
+
if (pendingToolUseClose) html += '</details>';
|
|
1670
1658
|
}
|
|
1671
1659
|
html += '</div>';
|
|
1672
1660
|
return html;
|
|
@@ -2151,8 +2139,12 @@ class AgentGUIClient {
|
|
|
2151
2139
|
const lastEl = blocksEl.lastElementChild;
|
|
2152
2140
|
const toolUseEl = matchById || (lastEl?.classList?.contains('block-tool-use') ? lastEl : null);
|
|
2153
2141
|
if (toolUseEl) {
|
|
2154
|
-
|
|
2155
|
-
|
|
2142
|
+
toolUseEl.classList.remove('has-success', 'has-error');
|
|
2143
|
+
toolUseEl.classList.add(chunk.block.is_error ? 'has-error' : 'has-success');
|
|
2144
|
+
const parentIsOpen = toolUseEl.hasAttribute('open');
|
|
2145
|
+
const contextWithParent = { ...chunk, parentIsOpen };
|
|
2146
|
+
const element = this.renderer.renderBlock(chunk.block, contextWithParent, blocksEl);
|
|
2147
|
+
if (element) { toolUseEl.appendChild(element); this.scrollToBottom(); }
|
|
2156
2148
|
return;
|
|
2157
2149
|
}
|
|
2158
2150
|
}
|
|
@@ -2893,7 +2885,14 @@ class AgentGUIClient {
|
|
|
2893
2885
|
? blocksEl.querySelector(`.block-tool-use[data-tool-use-id="${toolUseId}"]`)
|
|
2894
2886
|
: blocksEl.lastElementChild?.classList?.contains('block-type-tool_use') ? blocksEl.lastElementChild : null;
|
|
2895
2887
|
if (!toolUseEl) return;
|
|
2896
|
-
|
|
2888
|
+
toolUseEl.classList.remove('has-success', 'has-error');
|
|
2889
|
+
toolUseEl.classList.add(chunk.block.is_error ? 'has-error' : 'has-success');
|
|
2890
|
+
const contextWithParent = { ...chunk, parentIsOpen: toolUseEl.hasAttribute('open') };
|
|
2891
|
+
const element = this.renderer.renderBlock(chunk.block, contextWithParent, toolUseEl);
|
|
2892
|
+
if (element) {
|
|
2893
|
+
element.classList.add('block-loaded');
|
|
2894
|
+
toolUseEl.appendChild(element);
|
|
2895
|
+
}
|
|
2897
2896
|
});
|
|
2898
2897
|
|
|
2899
2898
|
if (isCurrentActiveSession) {
|
|
@@ -1261,17 +1261,33 @@ class StreamingRenderer {
|
|
|
1261
1261
|
const contentStr = typeof content === 'string' ? content : JSON.stringify(content, null, 2);
|
|
1262
1262
|
const isError = (block.is_error || false) && !contentStr.trimStart().startsWith('exec ran successfully.');
|
|
1263
1263
|
|
|
1264
|
-
const
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1264
|
+
const details = document.createElement('details');
|
|
1265
|
+
details.className = 'folded-tool' + (isError ? ' folded-tool-error' : ' folded-tool-success');
|
|
1266
|
+
details.dataset.eventType = 'tool_result';
|
|
1267
|
+
// Only open by default if the content is an image and it's not an error
|
|
1268
|
+
const isImageContent = contentStr.includes('data:image/') || (block.content && block.content.type === 'base64');
|
|
1269
|
+
if (!isError && isImageContent) details.open = true;
|
|
1270
|
+
if (block.tool_use_id) details.dataset.toolUseId = block.tool_use_id;
|
|
1271
|
+
details.classList.add(this._getBlockTypeClass('tool_result'));
|
|
1272
|
+
|
|
1273
|
+
const summary = document.createElement('summary');
|
|
1274
|
+
summary.className = 'folded-tool-bar';
|
|
1275
|
+
const iconSvg = isError
|
|
1276
|
+
? '<svg viewBox="0 0 20 20" fill="currentColor"><path 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"/></svg>'
|
|
1277
|
+
: '<svg viewBox="0 0 20 20" fill="currentColor"><path 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"/></svg>';
|
|
1278
|
+
summary.innerHTML = `
|
|
1279
|
+
<span class="folded-tool-icon">${iconSvg}</span>
|
|
1280
|
+
<span class="folded-tool-name">${isError ? 'Error' : 'Success'}</span>
|
|
1281
|
+
`;
|
|
1282
|
+
details.appendChild(summary);
|
|
1270
1283
|
|
|
1271
1284
|
const renderedContent = StreamingRenderer.renderSmartContentHTML(contentStr, this.escapeHtml.bind(this), true);
|
|
1272
|
-
|
|
1285
|
+
const body = document.createElement('div');
|
|
1286
|
+
body.className = 'folded-tool-body';
|
|
1287
|
+
body.innerHTML = renderedContent;
|
|
1288
|
+
details.appendChild(body);
|
|
1273
1289
|
|
|
1274
|
-
return
|
|
1290
|
+
return details;
|
|
1275
1291
|
}
|
|
1276
1292
|
|
|
1277
1293
|
/**
|
|
@@ -2143,14 +2159,14 @@ class StreamingRenderer {
|
|
|
2143
2159
|
if (!this.outputContainer) return;
|
|
2144
2160
|
|
|
2145
2161
|
const toolUseBlocks = this.outputContainer.querySelectorAll('details.block-tool-use[data-tool-use-id]');
|
|
2146
|
-
const toolResultBlocks = this.outputContainer.querySelectorAll('
|
|
2162
|
+
const toolResultBlocks = this.outputContainer.querySelectorAll('details[data-event-type="tool_result"][data-tool-use-id]');
|
|
2147
2163
|
|
|
2148
2164
|
toolResultBlocks.forEach(resultBlock => {
|
|
2149
2165
|
const toolUseId = resultBlock.dataset.toolUseId;
|
|
2150
|
-
const isError = resultBlock.dataset.isError === '1';
|
|
2151
2166
|
const toolUseBlock = Array.from(toolUseBlocks).find(b => b.dataset.toolUseId === toolUseId);
|
|
2152
2167
|
|
|
2153
|
-
if (toolUseBlock) {
|
|
2168
|
+
if (toolUseBlock && toolUseBlock.parentElement === resultBlock.parentElement) {
|
|
2169
|
+
const isError = resultBlock.classList.contains('folded-tool-error');
|
|
2154
2170
|
const toolUseSummary = toolUseBlock.querySelector(':scope > summary');
|
|
2155
2171
|
|
|
2156
2172
|
if (toolUseSummary && !toolUseSummary.querySelector('.folded-tool-status')) {
|
|
@@ -2164,7 +2180,8 @@ class StreamingRenderer {
|
|
|
2164
2180
|
toolUseBlock.classList.add(isError ? 'tool-result-error' : 'tool-result-success');
|
|
2165
2181
|
}
|
|
2166
2182
|
|
|
2167
|
-
|
|
2183
|
+
const resultBody = resultBlock.querySelector('.folded-tool-body');
|
|
2184
|
+
if (resultBody && resultBody.innerHTML.trim()) {
|
|
2168
2185
|
let toolUseBody = toolUseBlock.querySelector(':scope > .folded-tool-body');
|
|
2169
2186
|
if (!toolUseBody) {
|
|
2170
2187
|
toolUseBody = document.createElement('div');
|
|
@@ -2173,12 +2190,10 @@ class StreamingRenderer {
|
|
|
2173
2190
|
}
|
|
2174
2191
|
const resultContent = document.createElement('div');
|
|
2175
2192
|
resultContent.className = 'folded-tool-result-content';
|
|
2176
|
-
resultContent.innerHTML =
|
|
2193
|
+
resultContent.innerHTML = resultBody.innerHTML;
|
|
2177
2194
|
toolUseBody.appendChild(resultContent);
|
|
2178
2195
|
}
|
|
2179
2196
|
|
|
2180
|
-
resultBlock.remove();
|
|
2181
|
-
} else {
|
|
2182
2197
|
resultBlock.remove();
|
|
2183
2198
|
}
|
|
2184
2199
|
});
|
|
@@ -2187,40 +2202,6 @@ class StreamingRenderer {
|
|
|
2187
2202
|
/**
|
|
2188
2203
|
* Auto-scroll to bottom of container
|
|
2189
2204
|
*/
|
|
2190
|
-
mergeResultIntoToolUse(toolUseEl, block) {
|
|
2191
|
-
const content = block.content || '';
|
|
2192
|
-
const contentStr = typeof content === 'string' ? content : JSON.stringify(content, null, 2);
|
|
2193
|
-
const isError = (block.is_error || false) && !contentStr.trimStart().startsWith('exec ran successfully.');
|
|
2194
|
-
|
|
2195
|
-
toolUseEl.classList.remove('has-success', 'has-error');
|
|
2196
|
-
toolUseEl.classList.add(isError ? 'has-error tool-result-error' : 'has-success tool-result-success');
|
|
2197
|
-
|
|
2198
|
-
const summary = toolUseEl.querySelector(':scope > summary');
|
|
2199
|
-
if (summary && !summary.querySelector('.folded-tool-status')) {
|
|
2200
|
-
const statusSvg = isError
|
|
2201
|
-
? '<svg viewBox="0 0 20 20" fill="currentColor"><path 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"/></svg>'
|
|
2202
|
-
: '<svg viewBox="0 0 20 20" fill="currentColor"><path 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"/></svg>';
|
|
2203
|
-
const statusSpan = document.createElement('span');
|
|
2204
|
-
statusSpan.className = 'folded-tool-status';
|
|
2205
|
-
statusSpan.innerHTML = statusSvg;
|
|
2206
|
-
summary.appendChild(statusSpan);
|
|
2207
|
-
}
|
|
2208
|
-
|
|
2209
|
-
const renderedContent = StreamingRenderer.renderSmartContentHTML(contentStr, this.escapeHtml.bind(this), true);
|
|
2210
|
-
if (renderedContent && renderedContent.trim()) {
|
|
2211
|
-
let toolUseBody = toolUseEl.querySelector(':scope > .folded-tool-body');
|
|
2212
|
-
if (!toolUseBody) {
|
|
2213
|
-
toolUseBody = document.createElement('div');
|
|
2214
|
-
toolUseBody.className = 'folded-tool-body';
|
|
2215
|
-
toolUseEl.appendChild(toolUseBody);
|
|
2216
|
-
}
|
|
2217
|
-
const resultContent = document.createElement('div');
|
|
2218
|
-
resultContent.className = 'folded-tool-result-content';
|
|
2219
|
-
resultContent.innerHTML = renderedContent;
|
|
2220
|
-
toolUseBody.appendChild(resultContent);
|
|
2221
|
-
}
|
|
2222
|
-
}
|
|
2223
|
-
|
|
2224
2205
|
autoScroll() {
|
|
2225
2206
|
if (this._scrollRafPending || this._userScrolledUp) return;
|
|
2226
2207
|
this._scrollRafPending = true;
|