agentgui 1.0.688 → 1.0.690
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 +4 -0
- package/lib/ws-handlers-conv.js +10 -2
- package/package.json +1 -1
- package/server.js +47 -1
- package/static/js/streaming-renderer.js +25 -2
package/lib/claude-runner.js
CHANGED
|
@@ -88,6 +88,10 @@ class AgentRunner {
|
|
|
88
88
|
onRateLimit = null
|
|
89
89
|
} = config;
|
|
90
90
|
|
|
91
|
+
if (process.env.DEBUG === '1') {
|
|
92
|
+
const sp = config.systemPrompt;
|
|
93
|
+
console.error(`[prompt-trace] convId=${config.conversationId} promptType=${typeof prompt} promptLen=${String(prompt).length} prompt0=${String(prompt).slice(0, 100)} sysLen=${sp ? String(sp).length : 0}`);
|
|
94
|
+
}
|
|
91
95
|
const args = this.buildArgs(prompt, config);
|
|
92
96
|
const spawnOpts = getSpawnOptions(cwd);
|
|
93
97
|
if (Object.keys(this.spawnEnv).length > 0) {
|
package/lib/ws-handlers-conv.js
CHANGED
|
@@ -7,7 +7,7 @@ function expandTilde(p) { return p && p.startsWith('~') ? path.join(os.homedir()
|
|
|
7
7
|
|
|
8
8
|
export function register(router, deps) {
|
|
9
9
|
const { queries, activeExecutions, messageQueues, rateLimitState,
|
|
10
|
-
broadcastSync, processMessageWithStreaming, cleanupExecution } = deps;
|
|
10
|
+
broadcastSync, processMessageWithStreaming, cleanupExecution, logError = () => {} } = deps;
|
|
11
11
|
|
|
12
12
|
// Per-conversation queue seq counter for event ordering
|
|
13
13
|
const queueSeqByConv = new Map();
|
|
@@ -179,7 +179,7 @@ export function register(router, deps) {
|
|
|
179
179
|
activeExecutions.set(convId, { pid: null, startTime: Date.now(), sessionId: session.id, lastActivity: Date.now() });
|
|
180
180
|
queries.setIsStreaming(convId, true);
|
|
181
181
|
broadcastSync({ type: 'streaming_start', sessionId: session.id, conversationId: convId, messageId: message.id, agentId, timestamp: Date.now() });
|
|
182
|
-
processMessageWithStreaming(convId, message.id, session.id, content, agentId, model, subAgent).catch(
|
|
182
|
+
processMessageWithStreaming(convId, message.id, session.id, content, agentId, model, subAgent).catch(e => logError('startExecution', e, { convId }));
|
|
183
183
|
return session;
|
|
184
184
|
}
|
|
185
185
|
|
|
@@ -199,6 +199,10 @@ export function register(router, deps) {
|
|
|
199
199
|
const subAgent = p.subAgent || conv.subAgent || null;
|
|
200
200
|
const idempotencyKey = p.idempotencyKey || null;
|
|
201
201
|
const rawContent = p.content;
|
|
202
|
+
if (rawContent !== undefined && typeof rawContent !== 'string') {
|
|
203
|
+
console.warn(`[ws-validation] msg.send content is ${typeof rawContent}: ${JSON.stringify(rawContent).slice(0, 200)}`);
|
|
204
|
+
logError('ws-content-type', new TypeError(`non-string content in msg.send`), { method: 'msg.send', type: typeof rawContent });
|
|
205
|
+
}
|
|
202
206
|
p.content = typeof rawContent === 'string' ? rawContent : (rawContent ? JSON.stringify(rawContent) : '');
|
|
203
207
|
const message = queries.createMessage(p.id, 'user', p.content, idempotencyKey);
|
|
204
208
|
queries.createEvent('message.created', { role: 'user', messageId: message.id }, p.id);
|
|
@@ -225,6 +229,10 @@ export function register(router, deps) {
|
|
|
225
229
|
const conv = queries.getConversation(p.id);
|
|
226
230
|
if (!conv) notFound('Conversation not found');
|
|
227
231
|
const rawContent = p.content || p.message;
|
|
232
|
+
if (rawContent !== undefined && typeof rawContent !== 'string') {
|
|
233
|
+
console.warn(`[ws-validation] msg.stream content is ${typeof rawContent}: ${JSON.stringify(rawContent).slice(0, 200)}`);
|
|
234
|
+
logError('ws-content-type', new TypeError(`non-string content in msg.stream`), { method: 'msg.stream', type: typeof rawContent });
|
|
235
|
+
}
|
|
228
236
|
const prompt = typeof rawContent === 'string' ? rawContent : (rawContent ? JSON.stringify(rawContent) : '');
|
|
229
237
|
const agentId = p.agentId || conv.agentType || conv.agentId || 'claude-code';
|
|
230
238
|
const model = p.model || conv.model || null;
|
package/package.json
CHANGED
package/server.js
CHANGED
|
@@ -309,6 +309,14 @@ const debugLog = (msg) => {
|
|
|
309
309
|
console.error(`[${timestamp}] ${msg}`);
|
|
310
310
|
};
|
|
311
311
|
|
|
312
|
+
const _errLogPath = path.join(os.homedir(), 'logs', 'agentgui-errors.log');
|
|
313
|
+
function logError(op, err, ctx = {}) {
|
|
314
|
+
try {
|
|
315
|
+
const line = JSON.stringify({ ts: new Date().toISOString(), op, msg: err?.message, stack: err?.stack, ...ctx }) + '\n';
|
|
316
|
+
fs.appendFile(_errLogPath, line, () => {});
|
|
317
|
+
} catch (_) {}
|
|
318
|
+
}
|
|
319
|
+
|
|
312
320
|
// Atomic cleanup function - ensures consistent state across all data structures
|
|
313
321
|
function cleanupExecution(conversationId, broadcastCompletion = false) {
|
|
314
322
|
debugLog(`[cleanup] Starting cleanup for ${conversationId}`);
|
|
@@ -586,6 +594,7 @@ async function initializeAgentDiscovery() {
|
|
|
586
594
|
console.log('[AGENTS] Total count:', discoveredAgents.length);
|
|
587
595
|
} catch (err) {
|
|
588
596
|
console.error('[AGENTS] Discovery error:', err.message);
|
|
597
|
+
logError('initializeAgentDiscovery', err);
|
|
589
598
|
}
|
|
590
599
|
}
|
|
591
600
|
|
|
@@ -1269,6 +1278,7 @@ const server = http.createServer(async (req, res) => {
|
|
|
1269
1278
|
.catch(err => {
|
|
1270
1279
|
console.error(`[messages] Uncaught error for conv ${conversationId}:`, err.message);
|
|
1271
1280
|
debugLog(`[messages] Uncaught error: ${err.message}`);
|
|
1281
|
+
logError('processMessageWithStreaming', err, { convId: conversationId });
|
|
1272
1282
|
});
|
|
1273
1283
|
return;
|
|
1274
1284
|
}
|
|
@@ -1777,6 +1787,40 @@ const server = http.createServer(async (req, res) => {
|
|
|
1777
1787
|
return;
|
|
1778
1788
|
}
|
|
1779
1789
|
|
|
1790
|
+
if (pathOnly === '/api/health' && req.method === 'GET') {
|
|
1791
|
+
let dbStatus = { ok: true };
|
|
1792
|
+
try { queries._db.prepare('SELECT 1').get(); } catch (e) { dbStatus = { ok: false, error: e.message }; }
|
|
1793
|
+
const queueSizes = {};
|
|
1794
|
+
for (const [k, v] of messageQueues) queueSizes[k] = v.length;
|
|
1795
|
+
sendJSON(req, res, 200, {
|
|
1796
|
+
uptime: process.uptime(),
|
|
1797
|
+
db: dbStatus,
|
|
1798
|
+
activeExecutionCount: activeExecutions.size,
|
|
1799
|
+
queueSizes,
|
|
1800
|
+
wsClientCount: syncClients.size,
|
|
1801
|
+
memory: process.memoryUsage()
|
|
1802
|
+
});
|
|
1803
|
+
return;
|
|
1804
|
+
}
|
|
1805
|
+
|
|
1806
|
+
if (pathOnly === '/api/debug' && req.method === 'GET') {
|
|
1807
|
+
const execSnap = {};
|
|
1808
|
+
for (const [k, v] of activeExecutions) execSnap[k] = { pid: v.pid, startTime: v.startTime, sessionId: v.sessionId, lastActivity: v.lastActivity };
|
|
1809
|
+
const queueSnap = {};
|
|
1810
|
+
for (const [k, v] of messageQueues) queueSnap[k] = { length: v.length, messageIds: v.map(m => m.messageId) };
|
|
1811
|
+
let streamingConvs = [];
|
|
1812
|
+
try { streamingConvs = queries.getConversationsList().filter(c => c.isStreaming).map(c => ({ id: c.id, isStreaming: c.isStreaming })); } catch (_) {}
|
|
1813
|
+
const clientSubs = [];
|
|
1814
|
+
for (const ws of syncClients) clientSubs.push({ clientId: ws.clientId, subs: ws.subscriptions ? [...ws.subscriptions] : [] });
|
|
1815
|
+
let recentErrors = [];
|
|
1816
|
+
try {
|
|
1817
|
+
const raw = fs.readFileSync(_errLogPath, 'utf8');
|
|
1818
|
+
recentErrors = raw.trim().split('\n').slice(-20).map(l => { try { return JSON.parse(l); } catch { return l; } });
|
|
1819
|
+
} catch (_) {}
|
|
1820
|
+
sendJSON(req, res, 200, { activeExecutions: execSnap, messageQueues: queueSnap, streamingConversations: streamingConvs, wsClients: clientSubs, recentErrors });
|
|
1821
|
+
return;
|
|
1822
|
+
}
|
|
1823
|
+
|
|
1780
1824
|
if (pathOnly === '/api/tools' && req.method === 'GET') {
|
|
1781
1825
|
console.log('[TOOLS-API] Handling GET /api/tools');
|
|
1782
1826
|
try {
|
|
@@ -1899,6 +1943,7 @@ const server = http.createServer(async (req, res) => {
|
|
|
1899
1943
|
installCompleted = true;
|
|
1900
1944
|
const error = err?.message || 'Unknown error';
|
|
1901
1945
|
console.error(`[TOOLS-API] Install error for ${toolId}:`, error);
|
|
1946
|
+
logError('toolInstall', err, { toolId });
|
|
1902
1947
|
queries.updateToolStatus(toolId, { status: 'failed', error_message: error });
|
|
1903
1948
|
broadcastSync({ type: 'tool_install_failed', toolId, data: { success: false, error } });
|
|
1904
1949
|
queries.addToolInstallHistory(toolId, 'install', 'failed', error);
|
|
@@ -1958,6 +2003,7 @@ const server = http.createServer(async (req, res) => {
|
|
|
1958
2003
|
updateCompleted = true;
|
|
1959
2004
|
const error = err?.message || 'Unknown error';
|
|
1960
2005
|
console.error(`[TOOLS-API] Update error for ${toolId}:`, error);
|
|
2006
|
+
logError('toolUpdate', err, { toolId });
|
|
1961
2007
|
queries.updateToolStatus(toolId, { status: 'failed', error_message: error });
|
|
1962
2008
|
broadcastSync({ type: 'tool_update_failed', toolId, data: { success: false, error } });
|
|
1963
2009
|
queries.addToolInstallHistory(toolId, 'update', 'failed', error);
|
|
@@ -4095,7 +4141,7 @@ const wsRouter = new WsRouter();
|
|
|
4095
4141
|
|
|
4096
4142
|
registerConvHandlers(wsRouter, {
|
|
4097
4143
|
queries, activeExecutions, messageQueues, rateLimitState,
|
|
4098
|
-
broadcastSync, processMessageWithStreaming, cleanupExecution
|
|
4144
|
+
broadcastSync, processMessageWithStreaming, cleanupExecution, logError
|
|
4099
4145
|
});
|
|
4100
4146
|
|
|
4101
4147
|
console.log('[INIT] About to call registerSessionHandlers, discoveredAgents.length:', discoveredAgents.length);
|
|
@@ -285,6 +285,9 @@ class StreamingRenderer {
|
|
|
285
285
|
if (nodeCount > 0) {
|
|
286
286
|
this.outputContainer.appendChild(fragment);
|
|
287
287
|
this.domNodeCount += nodeCount;
|
|
288
|
+
|
|
289
|
+
// Nest tool result blocks inside their corresponding tool use blocks
|
|
290
|
+
this.nestToolResultsInToolUses();
|
|
288
291
|
}
|
|
289
292
|
|
|
290
293
|
// Auto-scroll to bottom
|
|
@@ -757,7 +760,7 @@ class StreamingRenderer {
|
|
|
757
760
|
const input = block.input || {};
|
|
758
761
|
|
|
759
762
|
const details = document.createElement('details');
|
|
760
|
-
details.className = 'block-tool-use folded-tool
|
|
763
|
+
details.className = 'block-tool-use folded-tool';
|
|
761
764
|
if (block.id) details.dataset.toolUseId = block.id;
|
|
762
765
|
details.classList.add(this._getBlockTypeClass('tool_use'));
|
|
763
766
|
details.classList.add(this._getToolColorClass(toolName));
|
|
@@ -1249,7 +1252,7 @@ class StreamingRenderer {
|
|
|
1249
1252
|
const details = document.createElement('details');
|
|
1250
1253
|
details.className = 'folded-tool' + (isError ? ' folded-tool-error' : ' folded-tool-success');
|
|
1251
1254
|
details.dataset.eventType = 'tool_result';
|
|
1252
|
-
// Only open by default if the content is an image
|
|
1255
|
+
// Only open by default if the content is an image and it's not an error
|
|
1253
1256
|
const isImageContent = contentStr.includes('data:image/') || (block.content && block.content.type === 'base64');
|
|
1254
1257
|
if (!isError && isImageContent) details.open = true;
|
|
1255
1258
|
if (block.tool_use_id) details.dataset.toolUseId = block.tool_use_id;
|
|
@@ -2070,6 +2073,26 @@ class StreamingRenderer {
|
|
|
2070
2073
|
return div;
|
|
2071
2074
|
}
|
|
2072
2075
|
|
|
2076
|
+
/**
|
|
2077
|
+
* Nest tool result blocks inside their corresponding tool use blocks
|
|
2078
|
+
*/
|
|
2079
|
+
nestToolResultsInToolUses() {
|
|
2080
|
+
if (!this.outputContainer) return;
|
|
2081
|
+
|
|
2082
|
+
const toolUseBlocks = this.outputContainer.querySelectorAll('details.block-tool-use[data-tool-use-id]');
|
|
2083
|
+
const toolResultBlocks = this.outputContainer.querySelectorAll('details[data-event-type="tool_result"][data-tool-use-id]');
|
|
2084
|
+
|
|
2085
|
+
toolResultBlocks.forEach(resultBlock => {
|
|
2086
|
+
const toolUseId = resultBlock.dataset.toolUseId;
|
|
2087
|
+
const toolUseBlock = Array.from(toolUseBlocks).find(b => b.dataset.toolUseId === toolUseId);
|
|
2088
|
+
|
|
2089
|
+
if (toolUseBlock && toolUseBlock.parentElement === resultBlock.parentElement) {
|
|
2090
|
+
// Result is a sibling of the tool use block, move it inside
|
|
2091
|
+
toolUseBlock.appendChild(resultBlock);
|
|
2092
|
+
}
|
|
2093
|
+
});
|
|
2094
|
+
}
|
|
2095
|
+
|
|
2073
2096
|
/**
|
|
2074
2097
|
* Auto-scroll to bottom of container
|
|
2075
2098
|
*/
|