clementine-agent 1.18.85 → 1.18.86
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/dist/cli/dashboard.js +159 -3
- package/package.json +1 -1
package/dist/cli/dashboard.js
CHANGED
|
@@ -23700,7 +23700,7 @@ function renderRecentHistoryList(runs) {
|
|
|
23700
23700
|
+ '</div>'
|
|
23701
23701
|
+ '<div style="font-size:12px;color:var(--text-secondary);line-height:18px">' + esc(startedLabel) + '</div>'
|
|
23702
23702
|
+ '<div style="font-size:12px;color:var(--text-muted);line-height:18px">' + esc(durationLabel) + '</div>'
|
|
23703
|
-
+ '<div style="display:flex;gap:6px;align-items:center"><button class="btn-sm" onclick="event.stopPropagation();
|
|
23703
|
+
+ '<div style="display:flex;gap:6px;align-items:center"><button class="btn-sm" onclick="event.stopPropagation();openRunOrTrace(\\x27' + safeName + '\\x27,' + (entry.id ? '\\x27' + jsStr(entry.id) + '\\x27' : 'null') + ')" style="font-size:11px;padding:3px 8px">' + (entry.id ? 'Open run' : 'Trace') + '</button></div>'
|
|
23704
23704
|
+ '</div>';
|
|
23705
23705
|
}
|
|
23706
23706
|
return '<div class="history-list" style="background:var(--bg-secondary);border:1px solid var(--border);border-radius:var(--radius)">'
|
|
@@ -23906,7 +23906,7 @@ function renderRunListBody(allRuns) {
|
|
|
23906
23906
|
+ '<div style="font-size:11px;color:' + triggerColor + ';line-height:18px">' + esc(triggerLabel) + '</div>'
|
|
23907
23907
|
+ '<div style="font-size:12px;color:var(--text-secondary);line-height:18px">' + esc(startedLabel) + '</div>'
|
|
23908
23908
|
+ '<div style="font-size:12px;color:var(--text-muted);line-height:18px">' + esc(durationLabel) + '</div>'
|
|
23909
|
-
+ '<div style="display:flex;gap:6px;align-items:center"><button class="btn-sm" onclick="event.stopPropagation();
|
|
23909
|
+
+ '<div style="display:flex;gap:6px;align-items:center"><button class="btn-sm" onclick="event.stopPropagation();openRunOrTrace(\\x27' + safeName + '\\x27,' + (entry.id ? '\\x27' + jsStr(entry.id) + '\\x27' : 'null') + ')" style="font-size:11px;padding:3px 8px">' + (entry.id ? 'Open run' : 'Trace') + '</button></div>'
|
|
23910
23910
|
+ '</div>';
|
|
23911
23911
|
}
|
|
23912
23912
|
html += '</div>';
|
|
@@ -24384,6 +24384,162 @@ async function refreshCron() {
|
|
|
24384
24384
|
|
|
24385
24385
|
var traceData = [];
|
|
24386
24386
|
|
|
24387
|
+
// PRD Phase 4b / 1.18.86: smart router. If the run entry has a stable
|
|
24388
|
+
// runId (1.18.85+ runs), open the new Run detail viewer reading from the
|
|
24389
|
+
// Event store; otherwise fall back to the legacy trace viewer (which now
|
|
24390
|
+
// just renders the friendly empty state explaining where to find the
|
|
24391
|
+
// real error). Both viewers share the same modal shell.
|
|
24392
|
+
function openRunOrTrace(jobName, runId) {
|
|
24393
|
+
if (runId && typeof runId === 'string') {
|
|
24394
|
+
return openRunDetail(runId, jobName);
|
|
24395
|
+
}
|
|
24396
|
+
return openTraceViewer(jobName);
|
|
24397
|
+
}
|
|
24398
|
+
|
|
24399
|
+
// PRD Phase 4b / 1.18.86: Run detail viewer. Renders a waterfall of
|
|
24400
|
+
// RunEvent rows from /api/runs/:runId/events. Color-coded by kind, paired
|
|
24401
|
+
// tool_call→tool_result by toolUseId, with expandable per-span content.
|
|
24402
|
+
async function openRunDetail(runId, jobName) {
|
|
24403
|
+
document.getElementById('trace-modal-title').textContent = 'Run detail · ' + (jobName || runId);
|
|
24404
|
+
document.getElementById('trace-run-selector').innerHTML = '';
|
|
24405
|
+
document.getElementById('trace-content').innerHTML = '<div style="padding:20px;color:var(--text-muted)">Loading run events…</div>';
|
|
24406
|
+
document.getElementById('trace-modal').classList.add('show');
|
|
24407
|
+
try {
|
|
24408
|
+
var r = await apiFetch('/api/runs/' + encodeURIComponent(runId) + '/events');
|
|
24409
|
+
var d = await r.json();
|
|
24410
|
+
if (!r.ok || d.ok === false) {
|
|
24411
|
+
document.getElementById('trace-content').innerHTML = '<div style="padding:20px;color:var(--red)">Failed to load run: ' + esc(d.error || 'unknown') + '</div>';
|
|
24412
|
+
return;
|
|
24413
|
+
}
|
|
24414
|
+
var events = (d && d.events) || [];
|
|
24415
|
+
if (events.length === 0) {
|
|
24416
|
+
document.getElementById('trace-content').innerHTML = '<div style="padding:24px;color:var(--text-muted);line-height:1.6"><div style="font-weight:500;color:var(--text-secondary);margin-bottom:8px">No events captured for this run</div><div style="font-size:12px">Either the run pre-dates 1.18.85 (when the Event store was added) or the SDK errored before any message landed.<br/>The Recent history row carries the high-level status, error message, and goal verdict.</div></div>';
|
|
24417
|
+
return;
|
|
24418
|
+
}
|
|
24419
|
+
document.getElementById('trace-content').innerHTML = renderRunDetailWaterfall(events, runId, jobName);
|
|
24420
|
+
} catch (e) {
|
|
24421
|
+
document.getElementById('trace-content').innerHTML = '<div style="padding:20px;color:var(--red)">Failed to load run: ' + esc(String(e)) + '</div>';
|
|
24422
|
+
}
|
|
24423
|
+
}
|
|
24424
|
+
|
|
24425
|
+
// Renders the waterfall. Each event becomes a row with:
|
|
24426
|
+
// color border (by kind) · kind badge · time offset · brief preview · expand link
|
|
24427
|
+
// tool_call rows pair with their tool_result by toolUseId so the duration
|
|
24428
|
+
// is computed and shown alongside the call.
|
|
24429
|
+
function renderRunDetailWaterfall(events, runId, jobName) {
|
|
24430
|
+
if (!events.length) return '';
|
|
24431
|
+
var firstTs = events[0].ts ? new Date(events[0].ts).getTime() : Date.now();
|
|
24432
|
+
var lastTs = events[events.length - 1].ts ? new Date(events[events.length - 1].ts).getTime() : firstTs;
|
|
24433
|
+
var totalMs = Math.max(1, lastTs - firstTs);
|
|
24434
|
+
|
|
24435
|
+
// Pair tool_call with its tool_result for duration.
|
|
24436
|
+
var resultByToolUseId = {};
|
|
24437
|
+
for (var i = 0; i < events.length; i++) {
|
|
24438
|
+
var e = events[i];
|
|
24439
|
+
if (e.kind === 'tool_result' && e.toolUseId) {
|
|
24440
|
+
resultByToolUseId[e.toolUseId] = e;
|
|
24441
|
+
}
|
|
24442
|
+
}
|
|
24443
|
+
|
|
24444
|
+
// Per-event color + label
|
|
24445
|
+
function kindColor(k) {
|
|
24446
|
+
if (k === 'session_start' || k === 'session_end') return 'var(--text-muted)';
|
|
24447
|
+
if (k === 'llm_text') return 'var(--accent)';
|
|
24448
|
+
if (k === 'thinking') return 'var(--purple)';
|
|
24449
|
+
if (k === 'tool_call') return '#22c55e';
|
|
24450
|
+
if (k === 'tool_result') return '#22c55e';
|
|
24451
|
+
if (k === 'subagent_start' || k === 'subagent_stop') return '#a855f7';
|
|
24452
|
+
if (k === 'rate_limit') return 'var(--yellow)';
|
|
24453
|
+
if (k === 'hook') return 'var(--blue)';
|
|
24454
|
+
if (k === 'error') return 'var(--red)';
|
|
24455
|
+
return 'var(--text-muted)';
|
|
24456
|
+
}
|
|
24457
|
+
function kindLabel(k) {
|
|
24458
|
+
return (k || 'event').toUpperCase().replace(/_/g, ' ');
|
|
24459
|
+
}
|
|
24460
|
+
|
|
24461
|
+
// Header strip with summary
|
|
24462
|
+
var startLabel = events[0].ts ? new Date(events[0].ts).toLocaleString() : '—';
|
|
24463
|
+
var endEvent = events.find(function(e) { return e.kind === 'session_end'; });
|
|
24464
|
+
var costStr = endEvent && endEvent.costUsd != null ? '$' + endEvent.costUsd.toFixed(4) : '—';
|
|
24465
|
+
var stopReason = endEvent && endEvent.stopReason ? endEvent.stopReason : '—';
|
|
24466
|
+
var html = '<div style="padding:16px 20px;border-bottom:1px solid var(--border);background:var(--bg-secondary);position:sticky;top:0;z-index:1">'
|
|
24467
|
+
+ '<div style="display:flex;align-items:center;gap:14px;font-size:11px;color:var(--text-muted);flex-wrap:wrap">'
|
|
24468
|
+
+ '<span><strong style="color:var(--text-primary)">' + esc(events.length) + '</strong> events</span>'
|
|
24469
|
+
+ '<span>·</span><span>started ' + esc(startLabel) + '</span>'
|
|
24470
|
+
+ '<span>·</span><span>duration <strong style="color:var(--text-primary)">' + esc(formatDurationMs(totalMs)) + '</strong></span>'
|
|
24471
|
+
+ '<span>·</span><span>cost <strong style="color:var(--text-primary)">' + esc(costStr) + '</strong></span>'
|
|
24472
|
+
+ '<span>·</span><span>stop reason <strong style="color:var(--text-primary)">' + esc(stopReason) + '</strong></span>'
|
|
24473
|
+
+ '<span style="flex:1"></span>'
|
|
24474
|
+
+ '<code style="font-size:10px;color:var(--text-muted)">runId ' + esc(String(runId).slice(0, 12)) + '…</code>'
|
|
24475
|
+
+ '</div>'
|
|
24476
|
+
+ '</div>';
|
|
24477
|
+
|
|
24478
|
+
// Waterfall rows
|
|
24479
|
+
html += '<div style="padding:0">';
|
|
24480
|
+
for (var j = 0; j < events.length; j++) {
|
|
24481
|
+
var ev = events[j];
|
|
24482
|
+
var color = kindColor(ev.kind);
|
|
24483
|
+
var label = kindLabel(ev.kind);
|
|
24484
|
+
var tsMs = ev.ts ? new Date(ev.ts).getTime() : firstTs;
|
|
24485
|
+
var offsetMs = tsMs - firstTs;
|
|
24486
|
+
var offsetLabel = offsetMs === 0 ? '+0ms' : '+' + formatDurationMs(offsetMs);
|
|
24487
|
+
var widthPct = Math.max(2, Math.min(100, (offsetMs / totalMs) * 100));
|
|
24488
|
+
// For tool_call, compute duration to its paired tool_result.
|
|
24489
|
+
var pairedDuration = '';
|
|
24490
|
+
if (ev.kind === 'tool_call' && ev.toolUseId && resultByToolUseId[ev.toolUseId]) {
|
|
24491
|
+
var resultTs = new Date(resultByToolUseId[ev.toolUseId].ts).getTime();
|
|
24492
|
+
pairedDuration = ' · ran ' + formatDurationMs(Math.max(0, resultTs - tsMs));
|
|
24493
|
+
}
|
|
24494
|
+
|
|
24495
|
+
// Brief preview: text for llm_text, thinking for thinking, tool name + first arg for tool_call/result, error for error.
|
|
24496
|
+
var preview = '';
|
|
24497
|
+
var fullContent = '';
|
|
24498
|
+
if (ev.kind === 'llm_text' && ev.text) {
|
|
24499
|
+
preview = String(ev.text).slice(0, 160).replace(/\\s+/g, ' ');
|
|
24500
|
+
fullContent = String(ev.text);
|
|
24501
|
+
} else if (ev.kind === 'thinking' && ev.thinking) {
|
|
24502
|
+
preview = String(ev.thinking).slice(0, 160).replace(/\\s+/g, ' ');
|
|
24503
|
+
fullContent = String(ev.thinking);
|
|
24504
|
+
} else if (ev.kind === 'tool_call') {
|
|
24505
|
+
preview = (ev.toolName || 'tool') + (ev.toolInput ? ' · ' + JSON.stringify(ev.toolInput).slice(0, 120) : '');
|
|
24506
|
+
fullContent = ev.toolInput ? JSON.stringify(ev.toolInput, null, 2) : '';
|
|
24507
|
+
} else if (ev.kind === 'tool_result') {
|
|
24508
|
+
preview = ev.toolError ? '✗ ' + ev.toolError : (typeof ev.toolResult === 'string' ? ev.toolResult.slice(0, 160) : (ev.toolResult ? JSON.stringify(ev.toolResult).slice(0, 160) : ''));
|
|
24509
|
+
fullContent = typeof ev.toolResult === 'string' ? ev.toolResult : JSON.stringify(ev.toolResult, null, 2);
|
|
24510
|
+
} else if (ev.kind === 'error') {
|
|
24511
|
+
preview = ev.toolError || '';
|
|
24512
|
+
fullContent = ev.toolError || '';
|
|
24513
|
+
} else if (ev.kind === 'session_start') {
|
|
24514
|
+
preview = ev.sessionId ? 'session ' + String(ev.sessionId).slice(0, 8) + '…' : '';
|
|
24515
|
+
} else if (ev.kind === 'session_end') {
|
|
24516
|
+
preview = '$' + (ev.costUsd != null ? ev.costUsd.toFixed(4) : '?') + ' · ' + (ev.stopReason || '?');
|
|
24517
|
+
}
|
|
24518
|
+
|
|
24519
|
+
var rowId = 'run-evt-' + j;
|
|
24520
|
+
var canExpand = !!fullContent && fullContent.length > preview.length;
|
|
24521
|
+
html += '<div style="display:grid;grid-template-columns:90px 110px 1fr;gap:14px;padding:10px 20px;border-bottom:1px solid var(--border);align-items:start">';
|
|
24522
|
+
html += '<div style="font-size:10px;color:var(--text-muted);font-family:\\x27JetBrains Mono\\x27,monospace;line-height:18px">' + esc(offsetLabel) + '</div>';
|
|
24523
|
+
html += '<div><span style="display:inline-block;background:' + color + '20;color:' + color + ';padding:2px 8px;border-radius:4px;font-size:10px;font-weight:600;letter-spacing:0.04em">' + esc(label) + '</span></div>';
|
|
24524
|
+
html += '<div style="min-width:0">';
|
|
24525
|
+
html += '<div style="font-size:12px;color:var(--text-primary);line-height:1.45;word-break:break-word">'
|
|
24526
|
+
+ esc(preview)
|
|
24527
|
+
+ (pairedDuration ? '<span style="color:var(--text-muted);font-size:11px"> ' + esc(pairedDuration) + '</span>' : '')
|
|
24528
|
+
+ '</div>';
|
|
24529
|
+
if (canExpand) {
|
|
24530
|
+
html += '<button class="btn-sm" onclick="document.getElementById(\\x27' + rowId + '\\x27).style.display=document.getElementById(\\x27' + rowId + '\\x27).style.display===\\x27none\\x27?\\x27block\\x27:\\x27none\\x27" style="margin-top:6px;font-size:10px;padding:2px 8px">Expand</button>';
|
|
24531
|
+
html += '<pre id="' + rowId + '" style="display:none;margin-top:8px;font-size:11px;font-family:\\x27JetBrains Mono\\x27,monospace;background:var(--bg-secondary);border:1px solid var(--border);padding:10px;border-radius:6px;white-space:pre-wrap;word-break:break-word;max-height:400px;overflow-y:auto">' + esc(fullContent) + '</pre>';
|
|
24532
|
+
}
|
|
24533
|
+
// Show toolUseId hint when present so user can correlate with logs.
|
|
24534
|
+
if (ev.toolUseId) {
|
|
24535
|
+
html += '<div style="font-size:10px;color:var(--text-muted);font-family:\\x27JetBrains Mono\\x27,monospace;margin-top:4px">use_id ' + esc(String(ev.toolUseId).slice(0, 12)) + '…</div>';
|
|
24536
|
+
}
|
|
24537
|
+
html += '</div></div>';
|
|
24538
|
+
}
|
|
24539
|
+
html += '</div>';
|
|
24540
|
+
return html;
|
|
24541
|
+
}
|
|
24542
|
+
|
|
24387
24543
|
async function openTraceViewer(jobName) {
|
|
24388
24544
|
document.getElementById('trace-modal-title').textContent = 'Trace: ' + jobName;
|
|
24389
24545
|
document.getElementById('trace-content').innerHTML = '<div style="padding:20px;color:var(--text-muted)">Loading...</div>';
|
|
@@ -25708,7 +25864,7 @@ function renderCronRunDetails(lr) {
|
|
|
25708
25864
|
if (Array.isArray(lr.mcpServersApplied) && lr.mcpServersApplied.length) {
|
|
25709
25865
|
html += '<div style="font-size:11px;color:var(--text-muted);margin-bottom:6px">MCP servers: ' + esc(lr.mcpServersApplied.join(', ')) + '</div>';
|
|
25710
25866
|
}
|
|
25711
|
-
html += '<div style="margin-top:14px;display:flex;gap:8px"><button class="btn-sm" onclick="
|
|
25867
|
+
html += '<div style="margin-top:14px;display:flex;gap:8px"><button class="btn-sm" onclick="openRunOrTrace(\\x27' + jsStr(lr.jobName || editingCronJob || '') + '\\x27,' + (lr.id ? '\\x27' + jsStr(lr.id) + '\\x27' : 'null') + ')" style="font-size:11px">' + (lr.id ? 'Open run' : 'Open trace') + '</button></div>';
|
|
25712
25868
|
html += '</div>';
|
|
25713
25869
|
return html;
|
|
25714
25870
|
}
|