agentgui 1.0.242 → 1.0.244
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 +102 -45
- package/static/index.html +4 -0
- package/static/js/client.js +14 -0
- package/static/js/streaming-renderer.js +38 -1
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,9 @@ 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,
|
|
3034
|
+
stderrText: error.stderrText,
|
|
2978
3035
|
recoverable: elapsed < 60000,
|
|
2979
3036
|
timestamp: Date.now()
|
|
2980
3037
|
});
|
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 {
|
package/static/js/client.js
CHANGED
|
@@ -793,6 +793,20 @@ class AgentGUIClient {
|
|
|
793
793
|
// Stop polling for chunks
|
|
794
794
|
this.stopChunkPolling();
|
|
795
795
|
|
|
796
|
+
// If this is a premature ACP end, render distinct warning block
|
|
797
|
+
if (data.isPrematureEnd) {
|
|
798
|
+
this.renderer.queueEvent({
|
|
799
|
+
type: 'streaming_error',
|
|
800
|
+
isPrematureEnd: true,
|
|
801
|
+
exitCode: data.exitCode,
|
|
802
|
+
error: data.error,
|
|
803
|
+
stderrText: data.stderrText,
|
|
804
|
+
sessionId: data.sessionId,
|
|
805
|
+
conversationId: data.conversationId,
|
|
806
|
+
timestamp: data.timestamp || Date.now()
|
|
807
|
+
});
|
|
808
|
+
}
|
|
809
|
+
|
|
796
810
|
const sessionId = data.sessionId || this.state.currentSession?.id;
|
|
797
811
|
const streamingEl = document.getElementById(`streaming-${sessionId}`);
|
|
798
812
|
if (streamingEl) {
|
|
@@ -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
|
}
|
|
@@ -415,7 +421,10 @@ class StreamingRenderer {
|
|
|
415
421
|
'system': 6,
|
|
416
422
|
'result': 7,
|
|
417
423
|
'error': 8,
|
|
418
|
-
'image': 9
|
|
424
|
+
'image': 9,
|
|
425
|
+
'plan': 10,
|
|
426
|
+
'usage': 11,
|
|
427
|
+
'premature': 8
|
|
419
428
|
};
|
|
420
429
|
return typeColors[blockType] !== undefined ? typeColors[blockType] : 0;
|
|
421
430
|
}
|
|
@@ -465,6 +474,7 @@ class StreamingRenderer {
|
|
|
465
474
|
renderBlockCode(block, context) {
|
|
466
475
|
const div = document.createElement('div');
|
|
467
476
|
div.className = 'block-code';
|
|
477
|
+
div.style.borderLeft = `3px solid var(--block-color-${this._getBlockColorIndex('code')})`;
|
|
468
478
|
|
|
469
479
|
const code = block.code || '';
|
|
470
480
|
const language = (block.language || 'plaintext').toLowerCase();
|
|
@@ -510,6 +520,8 @@ class StreamingRenderer {
|
|
|
510
520
|
renderBlockThinking(block, context) {
|
|
511
521
|
const div = document.createElement('div');
|
|
512
522
|
div.className = 'block-thinking';
|
|
523
|
+
const colorIndex = this._getBlockColorIndex('thinking');
|
|
524
|
+
div.style.borderLeft = `3px solid var(--block-color-${colorIndex})`;
|
|
513
525
|
|
|
514
526
|
const thinking = block.thinking || '';
|
|
515
527
|
div.innerHTML = `
|
|
@@ -1240,6 +1252,7 @@ class StreamingRenderer {
|
|
|
1240
1252
|
renderBlockImage(block, context) {
|
|
1241
1253
|
const div = document.createElement('div');
|
|
1242
1254
|
div.className = 'block-image';
|
|
1255
|
+
div.style.borderLeft = `3px solid var(--block-color-${this._getBlockColorIndex('image')})`;
|
|
1243
1256
|
|
|
1244
1257
|
let src = block.image || block.src || '';
|
|
1245
1258
|
const alt = block.alt || 'Image';
|
|
@@ -1263,6 +1276,8 @@ class StreamingRenderer {
|
|
|
1263
1276
|
renderBlockBash(block, context) {
|
|
1264
1277
|
const div = document.createElement('div');
|
|
1265
1278
|
div.className = 'block-bash';
|
|
1279
|
+
const colorIndex = this._getBlockColorIndex('bash');
|
|
1280
|
+
div.style.borderLeft = `3px solid var(--block-color-${colorIndex})`;
|
|
1266
1281
|
|
|
1267
1282
|
const command = block.command || block.code || '';
|
|
1268
1283
|
const output = block.output || '';
|
|
@@ -1290,6 +1305,7 @@ class StreamingRenderer {
|
|
|
1290
1305
|
const details = document.createElement('details');
|
|
1291
1306
|
details.className = 'folded-tool folded-tool-info';
|
|
1292
1307
|
details.dataset.eventType = 'system';
|
|
1308
|
+
details.style.borderLeft = `3px solid var(--block-color-${this._getBlockColorIndex('system')})`;
|
|
1293
1309
|
const desc = block.model ? this.escapeHtml(block.model) : 'Session';
|
|
1294
1310
|
const summary = document.createElement('summary');
|
|
1295
1311
|
summary.className = 'folded-tool-bar';
|
|
@@ -1326,6 +1342,9 @@ class StreamingRenderer {
|
|
|
1326
1342
|
const details = document.createElement('details');
|
|
1327
1343
|
details.className = isError ? 'folded-tool folded-tool-error' : 'folded-tool';
|
|
1328
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})`;
|
|
1329
1348
|
|
|
1330
1349
|
const iconSvg = isError
|
|
1331
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>'
|
|
@@ -1380,6 +1399,7 @@ class StreamingRenderer {
|
|
|
1380
1399
|
const div = document.createElement('div');
|
|
1381
1400
|
div.className = 'block-tool-status';
|
|
1382
1401
|
div.dataset.toolUseId = block.tool_use_id || '';
|
|
1402
|
+
div.style.borderLeft = `3px solid var(--block-color-${this._getBlockColorIndex('tool_use')})`;
|
|
1383
1403
|
div.innerHTML = `
|
|
1384
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)">
|
|
1385
1405
|
${statusIcons[status] || statusIcons.pending}
|
|
@@ -1400,6 +1420,7 @@ class StreamingRenderer {
|
|
|
1400
1420
|
|
|
1401
1421
|
const div = document.createElement('div');
|
|
1402
1422
|
div.className = 'block-usage';
|
|
1423
|
+
div.style.borderLeft = `3px solid var(--block-color-${this._getBlockColorIndex('usage')})`;
|
|
1403
1424
|
div.innerHTML = `
|
|
1404
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">
|
|
1405
1426
|
${used ? `<span><strong>Used:</strong> ${used.toLocaleString()}</span>` : ''}
|
|
@@ -1430,6 +1451,7 @@ class StreamingRenderer {
|
|
|
1430
1451
|
|
|
1431
1452
|
const div = document.createElement('div');
|
|
1432
1453
|
div.className = 'block-plan';
|
|
1454
|
+
div.style.borderLeft = `3px solid var(--block-color-${this._getBlockColorIndex('plan')})`;
|
|
1433
1455
|
div.innerHTML = `
|
|
1434
1456
|
<details class="folded-tool folded-tool-info">
|
|
1435
1457
|
<summary class="folded-tool-bar">
|
|
@@ -1452,6 +1474,21 @@ class StreamingRenderer {
|
|
|
1452
1474
|
return div;
|
|
1453
1475
|
}
|
|
1454
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
|
+
|
|
1455
1492
|
/**
|
|
1456
1493
|
* Render generic block with formatted key-value pairs
|
|
1457
1494
|
*/
|