agentgui 1.0.241 → 1.0.243
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/claude-runner.js +13 -2
- package/package.json +1 -1
- package/server.js +101 -45
- package/static/index.html +174 -19
- package/static/js/client.js +47 -12
- package/static/js/streaming-renderer.js +68 -25
package/lib/claude-runner.js
CHANGED
|
@@ -197,7 +197,7 @@ class AgentRunner {
|
|
|
197
197
|
try {
|
|
198
198
|
return await this._runACPOnce(prompt, cwd, config);
|
|
199
199
|
} catch (err) {
|
|
200
|
-
const isEmptyExit = err.message && err.message.includes('ACP exited with code');
|
|
200
|
+
const isEmptyExit = err.isPrematureEnd || (err.message && err.message.includes('ACP exited with code'));
|
|
201
201
|
const isBinaryError = err.code === 'ENOENT' || (err.message && err.message.includes('ENOENT'));
|
|
202
202
|
if ((isEmptyExit || isBinaryError) && _retryCount < maxRetries) {
|
|
203
203
|
const delay = Math.min(1000 * Math.pow(2, _retryCount), 5000);
|
|
@@ -205,6 +205,13 @@ class AgentRunner {
|
|
|
205
205
|
await new Promise(r => setTimeout(r, delay));
|
|
206
206
|
return this.runACP(prompt, cwd, config, _retryCount + 1);
|
|
207
207
|
}
|
|
208
|
+
if (err.isPrematureEnd) {
|
|
209
|
+
const premErr = new Error(err.message);
|
|
210
|
+
premErr.isPrematureEnd = true;
|
|
211
|
+
premErr.exitCode = err.exitCode;
|
|
212
|
+
premErr.stderrText = err.stderrText;
|
|
213
|
+
throw premErr;
|
|
214
|
+
}
|
|
208
215
|
throw err;
|
|
209
216
|
}
|
|
210
217
|
}
|
|
@@ -437,7 +444,11 @@ class AgentRunner {
|
|
|
437
444
|
resolve({ outputs, sessionId });
|
|
438
445
|
} else {
|
|
439
446
|
const detail = stderrText ? `: ${stderrText.substring(0, 200)}` : '';
|
|
440
|
-
|
|
447
|
+
const err = new Error(`${this.name} ACP exited with code ${code}${detail}`);
|
|
448
|
+
err.isPrematureEnd = true;
|
|
449
|
+
err.exitCode = code;
|
|
450
|
+
err.stderrText = stderrText;
|
|
451
|
+
reject(err);
|
|
441
452
|
}
|
|
442
453
|
});
|
|
443
454
|
|
package/package.json
CHANGED
package/server.js
CHANGED
|
@@ -35,62 +35,71 @@ const modelDownloadState = {
|
|
|
35
35
|
complete: false
|
|
36
36
|
};
|
|
37
37
|
|
|
38
|
+
function broadcastModelProgress(progress) {
|
|
39
|
+
modelDownloadState.progress = progress;
|
|
40
|
+
broadcastSync({ type: 'model_download_progress', progress });
|
|
41
|
+
}
|
|
42
|
+
|
|
38
43
|
async function ensureModelsDownloaded() {
|
|
39
|
-
const { createRequire: cr } = await import('module');
|
|
40
|
-
const r = cr(import.meta.url);
|
|
41
|
-
const models = r('sttttsmodels');
|
|
42
|
-
const checkAllFilesExist = models.checkAllFilesExist;
|
|
43
|
-
const downloadModels = models.downloadModels;
|
|
44
|
-
|
|
45
|
-
if (checkAllFilesExist && checkAllFilesExist()) {
|
|
46
|
-
modelDownloadState.complete = true;
|
|
47
|
-
return true;
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
if (!downloadModels) {
|
|
51
|
-
console.log('[MODELS] Download function not available, skipping');
|
|
52
|
-
modelDownloadState.complete = true;
|
|
53
|
-
return true;
|
|
54
|
-
}
|
|
55
|
-
|
|
56
44
|
if (modelDownloadState.downloading) {
|
|
57
|
-
// Wait for current download
|
|
58
45
|
while (modelDownloadState.downloading) {
|
|
59
46
|
await new Promise(r => setTimeout(r, 100));
|
|
60
47
|
}
|
|
61
48
|
return modelDownloadState.complete;
|
|
62
49
|
}
|
|
63
|
-
|
|
64
|
-
modelDownloadState.downloading = true;
|
|
65
|
-
modelDownloadState.error = null;
|
|
66
|
-
|
|
50
|
+
|
|
67
51
|
try {
|
|
68
|
-
await
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
52
|
+
const { createRequire: cr } = await import('module');
|
|
53
|
+
const r = cr(import.meta.url);
|
|
54
|
+
const sttttsmodels = r('sttttsmodels');
|
|
55
|
+
const { sttDir, ttsDir } = sttttsmodels;
|
|
56
|
+
|
|
57
|
+
const sttOk = fs.existsSync(sttDir) && fs.readdirSync(sttDir).length > 0;
|
|
58
|
+
const ttsOk = fs.existsSync(ttsDir) && fs.readdirSync(ttsDir).length > 0;
|
|
59
|
+
|
|
60
|
+
if (sttOk && ttsOk) {
|
|
61
|
+
console.log('[MODELS] All model files present');
|
|
62
|
+
modelDownloadState.complete = true;
|
|
63
|
+
return true;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
modelDownloadState.downloading = true;
|
|
67
|
+
modelDownloadState.error = null;
|
|
68
|
+
|
|
69
|
+
const webtalkWhisper = r('webtalk/whisper-models');
|
|
70
|
+
const webtalkTTS = r('webtalk/tts-models');
|
|
71
|
+
const { createConfig } = r('webtalk/config');
|
|
72
|
+
const config = createConfig({ sdkDir: path.dirname(fileURLToPath(import.meta.url)) });
|
|
73
|
+
config.modelsDir = path.dirname(sttDir);
|
|
74
|
+
config.ttsModelsDir = ttsDir;
|
|
75
|
+
config.sttModelsDir = sttDir;
|
|
76
|
+
config.whisperBaseUrl = 'https://huggingface.co/onnx-community/whisper-base/resolve/main/';
|
|
77
|
+
config.ttsBaseUrl = 'https://huggingface.co/datasets/AnEntrypoint/sttttsmodels/resolve/main/tts/';
|
|
78
|
+
|
|
79
|
+
const totalFiles = 16;
|
|
80
|
+
let completedFiles = 0;
|
|
81
|
+
|
|
82
|
+
if (!sttOk) {
|
|
83
|
+
console.log('[MODELS] Downloading STT model...');
|
|
84
|
+
broadcastModelProgress({ started: true, done: false, downloading: true, type: 'stt', completedFiles, totalFiles });
|
|
85
|
+
await webtalkWhisper.ensureModel('whisper-base', config);
|
|
86
|
+
completedFiles += 10;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
if (!ttsOk) {
|
|
90
|
+
console.log('[MODELS] Downloading TTS models...');
|
|
91
|
+
broadcastModelProgress({ started: true, done: false, downloading: true, type: 'tts', completedFiles, totalFiles });
|
|
92
|
+
await webtalkTTS.ensureTTSModels(config);
|
|
93
|
+
completedFiles += 6;
|
|
94
|
+
}
|
|
95
|
+
|
|
86
96
|
modelDownloadState.complete = true;
|
|
97
|
+
broadcastModelProgress({ started: true, done: true, downloading: false, completedFiles: totalFiles, totalFiles });
|
|
87
98
|
return true;
|
|
88
99
|
} catch (err) {
|
|
100
|
+
console.error('[MODELS] Download error:', err.message);
|
|
89
101
|
modelDownloadState.error = err.message;
|
|
90
|
-
|
|
91
|
-
type: 'model_download_progress',
|
|
92
|
-
progress: { error: err.message, done: true }
|
|
93
|
-
});
|
|
102
|
+
broadcastModelProgress({ done: true, error: err.message });
|
|
94
103
|
return false;
|
|
95
104
|
} finally {
|
|
96
105
|
modelDownloadState.downloading = false;
|
|
@@ -327,12 +336,57 @@ const AGENT_DEFAULT_MODELS = {
|
|
|
327
336
|
]
|
|
328
337
|
};
|
|
329
338
|
|
|
339
|
+
async function fetchClaudeModelsFromAPI() {
|
|
340
|
+
const apiKey = process.env.ANTHROPIC_API_KEY;
|
|
341
|
+
if (!apiKey) return null;
|
|
342
|
+
try {
|
|
343
|
+
const https = await import('https');
|
|
344
|
+
return new Promise((resolve) => {
|
|
345
|
+
const req = https.default.request({
|
|
346
|
+
hostname: 'api.anthropic.com', path: '/v1/models', method: 'GET',
|
|
347
|
+
headers: { 'x-api-key': apiKey, 'anthropic-version': '2023-06-01' },
|
|
348
|
+
timeout: 8000
|
|
349
|
+
}, (res) => {
|
|
350
|
+
let body = '';
|
|
351
|
+
res.on('data', d => body += d);
|
|
352
|
+
res.on('end', () => {
|
|
353
|
+
try {
|
|
354
|
+
const data = JSON.parse(body);
|
|
355
|
+
const items = (data.data || []).filter(m => m.id && m.id.startsWith('claude-'));
|
|
356
|
+
if (items.length === 0) return resolve(null);
|
|
357
|
+
const models = [{ id: '', label: 'Default' }];
|
|
358
|
+
for (const m of items) {
|
|
359
|
+
const label = m.display_name || m.id.replace(/^claude-/, '').replace(/-/g, ' ').replace(/\b\w/g, c => c.toUpperCase());
|
|
360
|
+
models.push({ id: m.id, label });
|
|
361
|
+
}
|
|
362
|
+
resolve(models);
|
|
363
|
+
} catch { resolve(null); }
|
|
364
|
+
});
|
|
365
|
+
});
|
|
366
|
+
req.on('error', () => resolve(null));
|
|
367
|
+
req.on('timeout', () => { req.destroy(); resolve(null); });
|
|
368
|
+
req.end();
|
|
369
|
+
});
|
|
370
|
+
} catch { return null; }
|
|
371
|
+
}
|
|
372
|
+
|
|
330
373
|
async function getModelsForAgent(agentId) {
|
|
331
374
|
const cached = modelCache.get(agentId);
|
|
332
|
-
if (cached && Date.now() - cached.timestamp <
|
|
375
|
+
if (cached && Date.now() - cached.timestamp < 3600000) {
|
|
333
376
|
return cached.models;
|
|
334
377
|
}
|
|
335
378
|
|
|
379
|
+
if (agentId === 'claude-code') {
|
|
380
|
+
const apiModels = await fetchClaudeModelsFromAPI();
|
|
381
|
+
if (apiModels) {
|
|
382
|
+
modelCache.set(agentId, { models: apiModels, timestamp: Date.now() });
|
|
383
|
+
return apiModels;
|
|
384
|
+
}
|
|
385
|
+
const models = AGENT_DEFAULT_MODELS[agentId];
|
|
386
|
+
modelCache.set(agentId, { models, timestamp: Date.now() });
|
|
387
|
+
return models;
|
|
388
|
+
}
|
|
389
|
+
|
|
336
390
|
if (AGENT_DEFAULT_MODELS[agentId]) {
|
|
337
391
|
const models = AGENT_DEFAULT_MODELS[agentId];
|
|
338
392
|
modelCache.set(agentId, { models, timestamp: Date.now() });
|
|
@@ -2975,6 +3029,8 @@ async function processMessageWithStreaming(conversationId, messageId, sessionId,
|
|
|
2975
3029
|
sessionId,
|
|
2976
3030
|
conversationId,
|
|
2977
3031
|
error: error.message,
|
|
3032
|
+
isPrematureEnd: error.isPrematureEnd || false,
|
|
3033
|
+
exitCode: error.exitCode,
|
|
2978
3034
|
recoverable: elapsed < 60000,
|
|
2979
3035
|
timestamp: Date.now()
|
|
2980
3036
|
});
|
package/static/index.html
CHANGED
|
@@ -48,6 +48,8 @@
|
|
|
48
48
|
--block-color-7: #f43f5e;
|
|
49
49
|
--block-color-8: #22c55e;
|
|
50
50
|
--block-color-9: #eab308;
|
|
51
|
+
--block-color-10: #0ea5e9;
|
|
52
|
+
--block-color-11: #d946ef;
|
|
51
53
|
}
|
|
52
54
|
|
|
53
55
|
html.dark {
|
|
@@ -66,6 +68,8 @@
|
|
|
66
68
|
--block-color-7: #fb7185;
|
|
67
69
|
--block-color-8: #4ade80;
|
|
68
70
|
--block-color-9: #facc15;
|
|
71
|
+
--block-color-10: #38bdf8;
|
|
72
|
+
--block-color-11: #e879f9;
|
|
69
73
|
}
|
|
70
74
|
|
|
71
75
|
html, body {
|
|
@@ -1883,33 +1887,15 @@
|
|
|
1883
1887
|
padding: 0.3rem 0.625rem;
|
|
1884
1888
|
font-size: 0.75rem;
|
|
1885
1889
|
line-height: 1.3;
|
|
1886
|
-
cursor:
|
|
1890
|
+
cursor: default;
|
|
1887
1891
|
user-select: none;
|
|
1888
|
-
list-style: none;
|
|
1889
1892
|
}
|
|
1890
1893
|
.tool-result-status::-webkit-details-marker { display: none; }
|
|
1891
1894
|
.tool-result-status::marker { display: none; content: ''; }
|
|
1892
|
-
.tool-result-status::before {
|
|
1893
|
-
content: '\25b6';
|
|
1894
|
-
font-size: 0.5rem;
|
|
1895
|
-
margin-right: 0.125rem;
|
|
1896
|
-
display: inline-block;
|
|
1897
|
-
transition: transform 0.15s;
|
|
1898
|
-
color: #16a34a;
|
|
1899
|
-
flex-shrink: 0;
|
|
1900
|
-
}
|
|
1901
|
-
html.dark .tool-result-status::before { color: #4ade80; }
|
|
1902
|
-
.tool-result-inline[open] > .tool-result-status::before { transform: rotate(90deg); }
|
|
1903
|
-
.tool-result-status:hover { background: #bbf7d0; }
|
|
1904
|
-
html.dark .tool-result-status:hover { background: #14532d; }
|
|
1905
1895
|
.tool-result-inline > .folded-tool-body { border-top: 1px solid #bbf7d0; }
|
|
1906
1896
|
html.dark .tool-result-inline > .folded-tool-body { border-top-color: #166534; }
|
|
1907
1897
|
.tool-result-error { background: #fef2f2; border-top-color: #fecaca; }
|
|
1908
1898
|
html.dark .tool-result-error { background: #1f0a0a; border-top-color: #991b1b; }
|
|
1909
|
-
.tool-result-error > .tool-result-status:hover { background: #fecaca; }
|
|
1910
|
-
html.dark .tool-result-error > .tool-result-status:hover { background: #2d0f0f; }
|
|
1911
|
-
.tool-result-error .tool-result-status::before { color: #dc2626; }
|
|
1912
|
-
html.dark .tool-result-error .tool-result-status::before { color: #f87171; }
|
|
1913
1899
|
.tool-result-error .folded-tool-icon { color: #dc2626; }
|
|
1914
1900
|
html.dark .tool-result-error .folded-tool-icon { color: #f87171; }
|
|
1915
1901
|
.tool-result-error .folded-tool-name { color: #991b1b; }
|
|
@@ -2390,6 +2376,174 @@
|
|
|
2390
2376
|
animation: pulse 1s ease-in-out infinite;
|
|
2391
2377
|
background-color: var(--color-warning) !important;
|
|
2392
2378
|
}
|
|
2379
|
+
|
|
2380
|
+
/* ===== IN-UI DIALOGS ===== */
|
|
2381
|
+
.dialog-overlay {
|
|
2382
|
+
position: fixed;
|
|
2383
|
+
top: 0;
|
|
2384
|
+
left: 0;
|
|
2385
|
+
right: 0;
|
|
2386
|
+
bottom: 0;
|
|
2387
|
+
z-index: 10000;
|
|
2388
|
+
display: flex;
|
|
2389
|
+
align-items: center;
|
|
2390
|
+
justify-content: center;
|
|
2391
|
+
opacity: 0;
|
|
2392
|
+
transition: opacity 0.2s ease;
|
|
2393
|
+
}
|
|
2394
|
+
.dialog-overlay.visible { opacity: 1; }
|
|
2395
|
+
.dialog-backdrop {
|
|
2396
|
+
position: absolute;
|
|
2397
|
+
top: 0;
|
|
2398
|
+
left: 0;
|
|
2399
|
+
right: 0;
|
|
2400
|
+
bottom: 0;
|
|
2401
|
+
background: rgba(0, 0, 0, 0.5);
|
|
2402
|
+
backdrop-filter: blur(2px);
|
|
2403
|
+
}
|
|
2404
|
+
.dialog-container {
|
|
2405
|
+
position: fixed;
|
|
2406
|
+
top: 0;
|
|
2407
|
+
left: 0;
|
|
2408
|
+
right: 0;
|
|
2409
|
+
bottom: 0;
|
|
2410
|
+
display: flex;
|
|
2411
|
+
align-items: center;
|
|
2412
|
+
justify-content: center;
|
|
2413
|
+
z-index: 10001;
|
|
2414
|
+
opacity: 0;
|
|
2415
|
+
transition: opacity 0.2s ease;
|
|
2416
|
+
}
|
|
2417
|
+
.dialog-container.visible { opacity: 1; }
|
|
2418
|
+
.dialog-box {
|
|
2419
|
+
background: var(--color-bg-primary);
|
|
2420
|
+
border-radius: 0.75rem;
|
|
2421
|
+
box-shadow: 0 20px 60px rgba(0, 0, 0, 0.3);
|
|
2422
|
+
max-width: 90vw;
|
|
2423
|
+
width: 400px;
|
|
2424
|
+
overflow: hidden;
|
|
2425
|
+
transform: scale(0.95) translateY(-10px);
|
|
2426
|
+
transition: transform 0.2s ease;
|
|
2427
|
+
}
|
|
2428
|
+
.dialog-container.visible .dialog-box {
|
|
2429
|
+
transform: scale(1) translateY(0);
|
|
2430
|
+
}
|
|
2431
|
+
.dialog-box-progress { width: 450px; }
|
|
2432
|
+
.dialog-header {
|
|
2433
|
+
padding: 1rem 1.25rem;
|
|
2434
|
+
border-bottom: 1px solid var(--color-border);
|
|
2435
|
+
}
|
|
2436
|
+
.dialog-title {
|
|
2437
|
+
margin: 0;
|
|
2438
|
+
font-size: 1.1rem;
|
|
2439
|
+
font-weight: 600;
|
|
2440
|
+
}
|
|
2441
|
+
.dialog-body {
|
|
2442
|
+
padding: 1.25rem;
|
|
2443
|
+
}
|
|
2444
|
+
.dialog-message {
|
|
2445
|
+
margin: 0;
|
|
2446
|
+
font-size: 0.9rem;
|
|
2447
|
+
line-height: 1.5;
|
|
2448
|
+
color: var(--color-text-primary);
|
|
2449
|
+
}
|
|
2450
|
+
.dialog-label {
|
|
2451
|
+
display: block;
|
|
2452
|
+
margin-bottom: 0.5rem;
|
|
2453
|
+
font-size: 0.9rem;
|
|
2454
|
+
color: var(--color-text-primary);
|
|
2455
|
+
}
|
|
2456
|
+
.dialog-input {
|
|
2457
|
+
width: 100%;
|
|
2458
|
+
padding: 0.625rem 0.875rem;
|
|
2459
|
+
border: 1px solid var(--color-border);
|
|
2460
|
+
border-radius: 0.5rem;
|
|
2461
|
+
background: var(--color-bg-secondary);
|
|
2462
|
+
color: var(--color-text-primary);
|
|
2463
|
+
font-size: 0.9rem;
|
|
2464
|
+
outline: none;
|
|
2465
|
+
transition: border-color 0.15s, box-shadow 0.15s;
|
|
2466
|
+
}
|
|
2467
|
+
.dialog-input:focus {
|
|
2468
|
+
border-color: var(--color-primary);
|
|
2469
|
+
box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.15);
|
|
2470
|
+
}
|
|
2471
|
+
.dialog-footer {
|
|
2472
|
+
padding: 0.75rem 1.25rem;
|
|
2473
|
+
display: flex;
|
|
2474
|
+
justify-content: flex-end;
|
|
2475
|
+
gap: 0.5rem;
|
|
2476
|
+
border-top: 1px solid var(--color-border);
|
|
2477
|
+
background: var(--color-bg-secondary);
|
|
2478
|
+
}
|
|
2479
|
+
.dialog-btn {
|
|
2480
|
+
padding: 0.5rem 1.25rem;
|
|
2481
|
+
border: none;
|
|
2482
|
+
border-radius: 0.5rem;
|
|
2483
|
+
font-size: 0.875rem;
|
|
2484
|
+
font-weight: 500;
|
|
2485
|
+
cursor: pointer;
|
|
2486
|
+
transition: all 0.15s;
|
|
2487
|
+
}
|
|
2488
|
+
.dialog-btn-primary {
|
|
2489
|
+
background: var(--color-primary);
|
|
2490
|
+
color: white;
|
|
2491
|
+
}
|
|
2492
|
+
.dialog-btn-primary:hover { background: var(--color-primary-dark); }
|
|
2493
|
+
.dialog-btn-secondary {
|
|
2494
|
+
background: var(--color-bg-primary);
|
|
2495
|
+
color: var(--color-text-primary);
|
|
2496
|
+
border: 1px solid var(--color-border);
|
|
2497
|
+
}
|
|
2498
|
+
.dialog-btn-secondary:hover { background: var(--color-bg-secondary); }
|
|
2499
|
+
.dialog-btn-danger { background: var(--color-error); }
|
|
2500
|
+
.dialog-btn-danger:hover { background: #dc2626; }
|
|
2501
|
+
.dialog-progress-bar {
|
|
2502
|
+
height: 8px;
|
|
2503
|
+
background: var(--color-bg-secondary);
|
|
2504
|
+
border-radius: 4px;
|
|
2505
|
+
overflow: hidden;
|
|
2506
|
+
margin: 1rem 0 0.5rem;
|
|
2507
|
+
}
|
|
2508
|
+
.dialog-progress-fill {
|
|
2509
|
+
height: 100%;
|
|
2510
|
+
background: var(--color-primary);
|
|
2511
|
+
border-radius: 4px;
|
|
2512
|
+
transition: width 0.3s ease;
|
|
2513
|
+
}
|
|
2514
|
+
.dialog-progress-percent {
|
|
2515
|
+
margin: 0;
|
|
2516
|
+
text-align: center;
|
|
2517
|
+
font-size: 0.8rem;
|
|
2518
|
+
color: var(--color-text-secondary);
|
|
2519
|
+
}
|
|
2520
|
+
|
|
2521
|
+
/* ===== TOAST NOTIFICATIONS ===== */
|
|
2522
|
+
.toast-notification {
|
|
2523
|
+
position: fixed;
|
|
2524
|
+
bottom: 100px;
|
|
2525
|
+
left: 50%;
|
|
2526
|
+
transform: translateX(-50%) translateY(20px);
|
|
2527
|
+
padding: 0.75rem 1.5rem;
|
|
2528
|
+
border-radius: 0.5rem;
|
|
2529
|
+
font-size: 0.875rem;
|
|
2530
|
+
font-weight: 500;
|
|
2531
|
+
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
|
|
2532
|
+
z-index: 20000;
|
|
2533
|
+
opacity: 0;
|
|
2534
|
+
transition: all 0.3s ease;
|
|
2535
|
+
display: flex;
|
|
2536
|
+
align-items: center;
|
|
2537
|
+
gap: 0.5rem;
|
|
2538
|
+
}
|
|
2539
|
+
.toast-notification.visible {
|
|
2540
|
+
opacity: 1;
|
|
2541
|
+
transform: translateX(-50%) translateY(0);
|
|
2542
|
+
}
|
|
2543
|
+
.toast-info { background: var(--color-primary); color: white; }
|
|
2544
|
+
.toast-success { background: var(--color-success); color: white; }
|
|
2545
|
+
.toast-error { background: var(--color-error); color: white; }
|
|
2546
|
+
.toast-warning { background: var(--color-warning); color: white; }
|
|
2393
2547
|
</style>
|
|
2394
2548
|
</head>
|
|
2395
2549
|
<body>
|
|
@@ -2597,6 +2751,7 @@
|
|
|
2597
2751
|
<script defer src="/gm/js/websocket-manager.js"></script>
|
|
2598
2752
|
<script defer src="/gm/js/event-filter.js"></script>
|
|
2599
2753
|
<script defer src="/gm/js/syntax-highlighter.js"></script>
|
|
2754
|
+
<script defer src="/gm/js/dialogs.js"></script>
|
|
2600
2755
|
<script defer src="/gm/js/ui-components.js"></script>
|
|
2601
2756
|
<script defer src="/gm/js/conversations.js"></script>
|
|
2602
2757
|
<script defer src="/gm/js/client.js"></script>
|
package/static/js/client.js
CHANGED
|
@@ -282,10 +282,12 @@ class AgentGUIClient {
|
|
|
282
282
|
|
|
283
283
|
try {
|
|
284
284
|
const position = localStorage.getItem(`scroll_${conversationId}`);
|
|
285
|
+
const scrollContainer = document.getElementById(this.config.scrollContainerId);
|
|
286
|
+
if (!scrollContainer) return;
|
|
287
|
+
|
|
285
288
|
if (position !== null) {
|
|
286
289
|
const scrollTop = parseInt(position, 10);
|
|
287
|
-
|
|
288
|
-
if (scrollContainer && !isNaN(scrollTop)) {
|
|
290
|
+
if (!isNaN(scrollTop)) {
|
|
289
291
|
requestAnimationFrame(() => {
|
|
290
292
|
requestAnimationFrame(() => {
|
|
291
293
|
const maxScroll = scrollContainer.scrollHeight - scrollContainer.clientHeight;
|
|
@@ -293,6 +295,12 @@ class AgentGUIClient {
|
|
|
293
295
|
});
|
|
294
296
|
});
|
|
295
297
|
}
|
|
298
|
+
} else {
|
|
299
|
+
requestAnimationFrame(() => {
|
|
300
|
+
requestAnimationFrame(() => {
|
|
301
|
+
scrollContainer.scrollTop = 0;
|
|
302
|
+
});
|
|
303
|
+
});
|
|
296
304
|
}
|
|
297
305
|
} catch (e) {
|
|
298
306
|
console.warn('Failed to restore scroll position:', e);
|
|
@@ -368,7 +376,7 @@ class AgentGUIClient {
|
|
|
368
376
|
if (this.ui.injectButton) {
|
|
369
377
|
this.ui.injectButton.addEventListener('click', async () => {
|
|
370
378
|
if (!this.state.currentConversation) return;
|
|
371
|
-
const instructions = prompt('Enter instructions to inject into the running agent:');
|
|
379
|
+
const instructions = await window.UIDialog.prompt('Enter instructions to inject into the running agent:', '', 'Inject Instructions');
|
|
372
380
|
if (!instructions) return;
|
|
373
381
|
try {
|
|
374
382
|
const resp = await fetch(`${window.__BASE_URL}/api/conversations/${this.state.currentConversation.id}/inject`, {
|
|
@@ -981,17 +989,17 @@ class AgentGUIClient {
|
|
|
981
989
|
btn.addEventListener('click', async (e) => {
|
|
982
990
|
const index = parseInt(e.target.dataset.index);
|
|
983
991
|
const msgId = queue[index].messageId;
|
|
984
|
-
if (confirm('Delete this queued message?')) {
|
|
992
|
+
if (await window.UIDialog.confirm('Delete this queued message?', 'Delete Message')) {
|
|
985
993
|
await fetch(window.__BASE_URL + `/api/conversations/${conversationId}/queue/${msgId}`, { method: 'DELETE' });
|
|
986
994
|
}
|
|
987
995
|
});
|
|
988
996
|
});
|
|
989
997
|
|
|
990
998
|
queueEl.querySelectorAll('.queue-edit-btn').forEach(btn => {
|
|
991
|
-
btn.addEventListener('click', (e) => {
|
|
999
|
+
btn.addEventListener('click', async (e) => {
|
|
992
1000
|
const index = parseInt(e.target.dataset.index);
|
|
993
1001
|
const q = queue[index];
|
|
994
|
-
const newContent = prompt('Edit message:', q.content);
|
|
1002
|
+
const newContent = await window.UIDialog.prompt('Edit message:', q.content, 'Edit Queued Message');
|
|
995
1003
|
if (newContent !== null && newContent !== q.content) {
|
|
996
1004
|
fetch(window.__BASE_URL + `/api/conversations/${conversationId}/queue/${q.messageId}`, {
|
|
997
1005
|
method: 'PATCH',
|
|
@@ -1176,7 +1184,8 @@ class AgentGUIClient {
|
|
|
1176
1184
|
const dName = hasRenderer ? StreamingRenderer.getToolDisplayName(tn) : tn;
|
|
1177
1185
|
const tTitle = hasRenderer && block.input ? StreamingRenderer.getToolTitle(tn, block.input) : '';
|
|
1178
1186
|
const iconHtml = hasRenderer && this.renderer ? `<span class="folded-tool-icon">${this.renderer.getToolIcon(tn)}</span>` : '';
|
|
1179
|
-
|
|
1187
|
+
const colorIdx = hasRenderer && this.renderer ? this.renderer._getBlockColorIndex('tool_use') : 1;
|
|
1188
|
+
html += `<details class="block-tool-use folded-tool" open style="border-left:3px solid var(--block-color-${colorIdx})"><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}`;
|
|
1180
1189
|
pendingToolUseClose = true;
|
|
1181
1190
|
} else if (block.type === 'tool_result') {
|
|
1182
1191
|
const content = typeof block.content === 'string' ? block.content : JSON.stringify(block.content);
|
|
@@ -1185,7 +1194,8 @@ class AgentGUIClient {
|
|
|
1185
1194
|
const resultIcon = block.is_error
|
|
1186
1195
|
? '<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>'
|
|
1187
1196
|
: '<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>';
|
|
1188
|
-
const
|
|
1197
|
+
const colorIdx = hasRenderer && this.renderer ? this.renderer._getBlockColorIndex('tool_result') : 2;
|
|
1198
|
+
const resultHtml = `<div class="tool-result-inline${block.is_error ? ' tool-result-error' : ''}" style="border-left:3px solid var(--block-color-${colorIdx})"><div class="tool-result-status"><span class="folded-tool-icon">${resultIcon}</span><span class="folded-tool-name">${block.is_error ? 'Error' : 'Success'}</span><span class="folded-tool-desc">${this.escapeHtml(resultPreview)}</span></div><div class="folded-tool-body">${smartHtml}</div></div>`;
|
|
1189
1199
|
if (pendingToolUseClose) {
|
|
1190
1200
|
html += resultHtml + '</details>';
|
|
1191
1201
|
pendingToolUseClose = false;
|
|
@@ -1996,6 +2006,13 @@ class AgentGUIClient {
|
|
|
1996
2006
|
this._modelDownloadInProgress = false;
|
|
1997
2007
|
console.error('[Models] Download error:', progress.error);
|
|
1998
2008
|
this._updateConnectionIndicator(this.wsManager?.latency?.quality || 'unknown');
|
|
2009
|
+
if (window._voiceProgressDialog) {
|
|
2010
|
+
window._voiceProgressDialog.close();
|
|
2011
|
+
window._voiceProgressDialog = null;
|
|
2012
|
+
}
|
|
2013
|
+
if (window.UIDialog) {
|
|
2014
|
+
window.UIDialog.alert('Failed to download voice models: ' + progress.error, 'Download Error');
|
|
2015
|
+
}
|
|
1999
2016
|
return;
|
|
2000
2017
|
}
|
|
2001
2018
|
|
|
@@ -2004,12 +2021,30 @@ class AgentGUIClient {
|
|
|
2004
2021
|
console.log('[Models] Download complete');
|
|
2005
2022
|
this._updateConnectionIndicator(this.wsManager?.latency?.quality || 'unknown');
|
|
2006
2023
|
this._updateVoiceTabState();
|
|
2024
|
+
if (window._voiceProgressDialog) {
|
|
2025
|
+
window._voiceProgressDialog.update(100, 'Voice models ready!');
|
|
2026
|
+
setTimeout(function() {
|
|
2027
|
+
if (window._voiceProgressDialog) {
|
|
2028
|
+
window._voiceProgressDialog.close();
|
|
2029
|
+
window._voiceProgressDialog = null;
|
|
2030
|
+
}
|
|
2031
|
+
}, 500);
|
|
2032
|
+
}
|
|
2007
2033
|
return;
|
|
2008
2034
|
}
|
|
2009
2035
|
|
|
2010
2036
|
if (progress.started || progress.downloading) {
|
|
2011
2037
|
this._modelDownloadInProgress = true;
|
|
2012
2038
|
this._updateConnectionIndicator(this.wsManager?.latency?.quality || 'unknown');
|
|
2039
|
+
|
|
2040
|
+
if (window._voiceProgressDialog && progress.totalBytes > 0) {
|
|
2041
|
+
var pct = Math.round((progress.totalDownloaded / progress.totalBytes) * 100);
|
|
2042
|
+
var mb = Math.round(progress.totalBytes / 1024 / 1024);
|
|
2043
|
+
var downloaded = Math.round((progress.totalDownloaded || 0) / 1024 / 1024);
|
|
2044
|
+
window._voiceProgressDialog.update(pct, 'Downloading ' + downloaded + 'MB / ' + mb + 'MB');
|
|
2045
|
+
} else if (window._voiceProgressDialog && progress.file) {
|
|
2046
|
+
window._voiceProgressDialog.update(0, 'Loading ' + progress.file + '...');
|
|
2047
|
+
}
|
|
2013
2048
|
}
|
|
2014
2049
|
}
|
|
2015
2050
|
|
|
@@ -2018,8 +2053,7 @@ class AgentGUIClient {
|
|
|
2018
2053
|
if (voiceBtn) {
|
|
2019
2054
|
var isReady = this._modelDownloadProgress?.done === true ||
|
|
2020
2055
|
this._modelDownloadProgress?.complete === true;
|
|
2021
|
-
voiceBtn.
|
|
2022
|
-
voiceBtn.title = isReady ? 'Voice' : 'Downloading voice models...';
|
|
2056
|
+
voiceBtn.title = isReady ? 'Voice' : 'Voice (click to download models)';
|
|
2023
2057
|
}
|
|
2024
2058
|
}
|
|
2025
2059
|
|
|
@@ -2438,8 +2472,9 @@ class AgentGUIClient {
|
|
|
2438
2472
|
*/
|
|
2439
2473
|
showError(message) {
|
|
2440
2474
|
console.error(message);
|
|
2441
|
-
|
|
2442
|
-
|
|
2475
|
+
if (window.UIDialog) {
|
|
2476
|
+
window.UIDialog.alert(message, 'Error');
|
|
2477
|
+
}
|
|
2443
2478
|
}
|
|
2444
2479
|
|
|
2445
2480
|
/**
|
|
@@ -300,6 +300,10 @@ class StreamingRenderer {
|
|
|
300
300
|
return this.renderBlock(event.block, event);
|
|
301
301
|
}
|
|
302
302
|
|
|
303
|
+
if (event.type === 'streaming_error' && event.isPrematureEnd) {
|
|
304
|
+
return this.renderBlockPremature({ type: 'premature', error: event.error, exitCode: event.exitCode });
|
|
305
|
+
}
|
|
306
|
+
|
|
303
307
|
switch (event.type) {
|
|
304
308
|
case 'streaming_start':
|
|
305
309
|
return this.renderStreamingStart(event);
|
|
@@ -366,6 +370,8 @@ class StreamingRenderer {
|
|
|
366
370
|
return this.renderBlockUsage(block, context);
|
|
367
371
|
case 'plan':
|
|
368
372
|
return this.renderBlockPlan(block, context);
|
|
373
|
+
case 'premature':
|
|
374
|
+
return this.renderBlockPremature(block, context);
|
|
369
375
|
default:
|
|
370
376
|
return this.renderBlockGeneric(block, context);
|
|
371
377
|
}
|
|
@@ -399,23 +405,28 @@ class StreamingRenderer {
|
|
|
399
405
|
div.className = 'block-text';
|
|
400
406
|
if (isHtml) div.classList.add('html-content');
|
|
401
407
|
div.innerHTML = html;
|
|
402
|
-
const colorIndex = this.
|
|
408
|
+
const colorIndex = this._getBlockColorIndex('text');
|
|
403
409
|
div.style.borderLeft = `3px solid var(--block-color-${colorIndex})`;
|
|
404
410
|
return div;
|
|
405
411
|
}
|
|
406
412
|
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
413
|
+
_getBlockColorIndex(blockType) {
|
|
414
|
+
const typeColors = {
|
|
415
|
+
'text': 0,
|
|
416
|
+
'tool_use': 1,
|
|
417
|
+
'tool_result': 2,
|
|
418
|
+
'code': 3,
|
|
419
|
+
'thinking': 4,
|
|
420
|
+
'bash': 5,
|
|
421
|
+
'system': 6,
|
|
422
|
+
'result': 7,
|
|
423
|
+
'error': 8,
|
|
424
|
+
'image': 9,
|
|
425
|
+
'plan': 10,
|
|
426
|
+
'usage': 11,
|
|
427
|
+
'premature': 8
|
|
428
|
+
};
|
|
429
|
+
return typeColors[blockType] !== undefined ? typeColors[blockType] : 0;
|
|
419
430
|
}
|
|
420
431
|
|
|
421
432
|
containsHtmlTags(text) {
|
|
@@ -463,6 +474,7 @@ class StreamingRenderer {
|
|
|
463
474
|
renderBlockCode(block, context) {
|
|
464
475
|
const div = document.createElement('div');
|
|
465
476
|
div.className = 'block-code';
|
|
477
|
+
div.style.borderLeft = `3px solid var(--block-color-${this._getBlockColorIndex('code')})`;
|
|
466
478
|
|
|
467
479
|
const code = block.code || '';
|
|
468
480
|
const language = (block.language || 'plaintext').toLowerCase();
|
|
@@ -508,6 +520,8 @@ class StreamingRenderer {
|
|
|
508
520
|
renderBlockThinking(block, context) {
|
|
509
521
|
const div = document.createElement('div');
|
|
510
522
|
div.className = 'block-thinking';
|
|
523
|
+
const colorIndex = this._getBlockColorIndex('thinking');
|
|
524
|
+
div.style.borderLeft = `3px solid var(--block-color-${colorIndex})`;
|
|
511
525
|
|
|
512
526
|
const thinking = block.thinking || '';
|
|
513
527
|
div.innerHTML = `
|
|
@@ -733,7 +747,10 @@ class StreamingRenderer {
|
|
|
733
747
|
|
|
734
748
|
const details = document.createElement('details');
|
|
735
749
|
details.className = 'block-tool-use folded-tool';
|
|
750
|
+
details.setAttribute('open', '');
|
|
736
751
|
if (block.id) details.dataset.toolUseId = block.id;
|
|
752
|
+
const colorIndex = this._getBlockColorIndex('tool_use');
|
|
753
|
+
details.style.borderLeft = `3px solid var(--block-color-${colorIndex})`;
|
|
737
754
|
const summary = document.createElement('summary');
|
|
738
755
|
summary.className = 'folded-tool-bar';
|
|
739
756
|
const displayName = this.getToolUseDisplayName(toolName);
|
|
@@ -1199,33 +1216,34 @@ class StreamingRenderer {
|
|
|
1199
1216
|
const isError = block.is_error || false;
|
|
1200
1217
|
const content = block.content || '';
|
|
1201
1218
|
const contentStr = typeof content === 'string' ? content : JSON.stringify(content, null, 2);
|
|
1202
|
-
const preview = contentStr.length > 80 ? contentStr.substring(0, 77).replace(/\n/g, ' ') + '...' : contentStr.replace(/\n/g, ' ');
|
|
1203
1219
|
|
|
1204
|
-
const
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
if (block.tool_use_id)
|
|
1220
|
+
const wrapper = document.createElement('div');
|
|
1221
|
+
wrapper.className = 'tool-result-inline' + (isError ? ' tool-result-error' : '');
|
|
1222
|
+
wrapper.dataset.eventType = 'tool_result';
|
|
1223
|
+
if (block.tool_use_id) wrapper.dataset.toolUseId = block.tool_use_id;
|
|
1224
|
+
const colorIndex = this._getBlockColorIndex('tool_result');
|
|
1225
|
+
wrapper.style.borderLeft = `3px solid var(--block-color-${colorIndex})`;
|
|
1208
1226
|
|
|
1227
|
+
const header = document.createElement('div');
|
|
1228
|
+
header.className = 'tool-result-status';
|
|
1209
1229
|
const iconSvg = isError
|
|
1210
1230
|
? '<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>'
|
|
1211
1231
|
: '<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>';
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
summary.className = 'tool-result-status';
|
|
1215
|
-
summary.innerHTML = `
|
|
1232
|
+
const preview = contentStr.length > 80 ? contentStr.substring(0, 77).replace(/\n/g, ' ') + '...' : contentStr.replace(/\n/g, ' ');
|
|
1233
|
+
header.innerHTML = `
|
|
1216
1234
|
<span class="folded-tool-icon">${iconSvg}</span>
|
|
1217
1235
|
<span class="folded-tool-name">${isError ? 'Error' : 'Success'}</span>
|
|
1218
1236
|
<span class="folded-tool-desc">${this.escapeHtml(preview)}</span>
|
|
1219
1237
|
`;
|
|
1220
|
-
|
|
1238
|
+
wrapper.appendChild(header);
|
|
1221
1239
|
|
|
1222
1240
|
const renderedContent = StreamingRenderer.renderSmartContentHTML(contentStr, this.escapeHtml.bind(this));
|
|
1223
1241
|
const body = document.createElement('div');
|
|
1224
1242
|
body.className = 'folded-tool-body';
|
|
1225
1243
|
body.innerHTML = renderedContent;
|
|
1226
|
-
|
|
1244
|
+
wrapper.appendChild(body);
|
|
1227
1245
|
|
|
1228
|
-
return
|
|
1246
|
+
return wrapper;
|
|
1229
1247
|
}
|
|
1230
1248
|
|
|
1231
1249
|
/**
|
|
@@ -1234,6 +1252,7 @@ class StreamingRenderer {
|
|
|
1234
1252
|
renderBlockImage(block, context) {
|
|
1235
1253
|
const div = document.createElement('div');
|
|
1236
1254
|
div.className = 'block-image';
|
|
1255
|
+
div.style.borderLeft = `3px solid var(--block-color-${this._getBlockColorIndex('image')})`;
|
|
1237
1256
|
|
|
1238
1257
|
let src = block.image || block.src || '';
|
|
1239
1258
|
const alt = block.alt || 'Image';
|
|
@@ -1257,6 +1276,8 @@ class StreamingRenderer {
|
|
|
1257
1276
|
renderBlockBash(block, context) {
|
|
1258
1277
|
const div = document.createElement('div');
|
|
1259
1278
|
div.className = 'block-bash';
|
|
1279
|
+
const colorIndex = this._getBlockColorIndex('bash');
|
|
1280
|
+
div.style.borderLeft = `3px solid var(--block-color-${colorIndex})`;
|
|
1260
1281
|
|
|
1261
1282
|
const command = block.command || block.code || '';
|
|
1262
1283
|
const output = block.output || '';
|
|
@@ -1284,6 +1305,7 @@ class StreamingRenderer {
|
|
|
1284
1305
|
const details = document.createElement('details');
|
|
1285
1306
|
details.className = 'folded-tool folded-tool-info';
|
|
1286
1307
|
details.dataset.eventType = 'system';
|
|
1308
|
+
details.style.borderLeft = `3px solid var(--block-color-${this._getBlockColorIndex('system')})`;
|
|
1287
1309
|
const desc = block.model ? this.escapeHtml(block.model) : 'Session';
|
|
1288
1310
|
const summary = document.createElement('summary');
|
|
1289
1311
|
summary.className = 'folded-tool-bar';
|
|
@@ -1320,6 +1342,9 @@ class StreamingRenderer {
|
|
|
1320
1342
|
const details = document.createElement('details');
|
|
1321
1343
|
details.className = isError ? 'folded-tool folded-tool-error' : 'folded-tool';
|
|
1322
1344
|
details.dataset.eventType = 'result';
|
|
1345
|
+
if (!isError) details.setAttribute('open', '');
|
|
1346
|
+
const colorIndex = this._getBlockColorIndex(isError ? 'error' : 'result');
|
|
1347
|
+
details.style.borderLeft = `3px solid var(--block-color-${colorIndex})`;
|
|
1323
1348
|
|
|
1324
1349
|
const iconSvg = isError
|
|
1325
1350
|
? '<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>'
|
|
@@ -1374,6 +1399,7 @@ class StreamingRenderer {
|
|
|
1374
1399
|
const div = document.createElement('div');
|
|
1375
1400
|
div.className = 'block-tool-status';
|
|
1376
1401
|
div.dataset.toolUseId = block.tool_use_id || '';
|
|
1402
|
+
div.style.borderLeft = `3px solid var(--block-color-${this._getBlockColorIndex('tool_use')})`;
|
|
1377
1403
|
div.innerHTML = `
|
|
1378
1404
|
<div style="display:flex;align-items:center;gap:0.5rem;padding:0.25rem 0.5rem;font-size:0.75rem;color:var(--color-text-secondary)">
|
|
1379
1405
|
${statusIcons[status] || statusIcons.pending}
|
|
@@ -1394,6 +1420,7 @@ class StreamingRenderer {
|
|
|
1394
1420
|
|
|
1395
1421
|
const div = document.createElement('div');
|
|
1396
1422
|
div.className = 'block-usage';
|
|
1423
|
+
div.style.borderLeft = `3px solid var(--block-color-${this._getBlockColorIndex('usage')})`;
|
|
1397
1424
|
div.innerHTML = `
|
|
1398
1425
|
<div style="display:flex;gap:1rem;padding:0.25rem 0.5rem;font-size:0.7rem;color:var(--color-text-secondary);background:var(--color-bg-secondary);border-radius:0.25rem">
|
|
1399
1426
|
${used ? `<span><strong>Used:</strong> ${used.toLocaleString()}</span>` : ''}
|
|
@@ -1424,6 +1451,7 @@ class StreamingRenderer {
|
|
|
1424
1451
|
|
|
1425
1452
|
const div = document.createElement('div');
|
|
1426
1453
|
div.className = 'block-plan';
|
|
1454
|
+
div.style.borderLeft = `3px solid var(--block-color-${this._getBlockColorIndex('plan')})`;
|
|
1427
1455
|
div.innerHTML = `
|
|
1428
1456
|
<details class="folded-tool folded-tool-info">
|
|
1429
1457
|
<summary class="folded-tool-bar">
|
|
@@ -1446,6 +1474,21 @@ class StreamingRenderer {
|
|
|
1446
1474
|
return div;
|
|
1447
1475
|
}
|
|
1448
1476
|
|
|
1477
|
+
renderBlockPremature(block, context) {
|
|
1478
|
+
const div = document.createElement('div');
|
|
1479
|
+
div.className = 'folded-tool folded-tool-error block-premature';
|
|
1480
|
+
div.style.borderLeft = `3px solid var(--block-color-${this._getBlockColorIndex('premature')})`;
|
|
1481
|
+
const code = block.exitCode != null ? ` (exit ${block.exitCode})` : '';
|
|
1482
|
+
div.innerHTML = `
|
|
1483
|
+
<div class="folded-tool-bar" style="background:rgba(245,158,11,0.1)">
|
|
1484
|
+
<span class="folded-tool-icon" style="color:#f59e0b"><svg viewBox="0 0 20 20" fill="currentColor"><path fill-rule="evenodd" d="M8.257 3.099c.765-1.36 2.722-1.36 3.486 0l5.58 9.92c.75 1.334-.213 2.98-1.742 2.98H4.42c-1.53 0-2.493-1.646-1.743-2.98l5.58-9.92zM11 13a1 1 0 11-2 0 1 1 0 012 0zm-1-8a1 1 0 00-1 1v3a1 1 0 002 0V6a1 1 0 00-1-1z" clip-rule="evenodd"/></svg></span>
|
|
1485
|
+
<span class="folded-tool-name" style="color:#f59e0b">ACP Ended Prematurely${this.escapeHtml(code)}</span>
|
|
1486
|
+
<span class="folded-tool-desc">${this.escapeHtml(block.error || 'Process exited without output')}</span>
|
|
1487
|
+
</div>
|
|
1488
|
+
`;
|
|
1489
|
+
return div;
|
|
1490
|
+
}
|
|
1491
|
+
|
|
1449
1492
|
/**
|
|
1450
1493
|
* Render generic block with formatted key-value pairs
|
|
1451
1494
|
*/
|