Read workflow-status telemetry from CLI transcript files
Dynamic ultracode workflows run as a detached background task with their own transcript dir, so their subagent events never flow through the main -p stream. The previous parent_tool_use_id tap therefore captured nothing for real workflows and the status stayed "none" the whole run. Replace the tap with a file-based reader: - TapWorkflowLaunch parses the "Transcript dir:" line from the Workflow tool_result and registers the run. - WorkflowProgressTracker becomes a per-session registry of transcript paths (no longer a stream accumulator). - WorkflowTelemetryReader reads journal.jsonl for live counts (running) and <runId>.json for rich per-agent telemetry (completed); FinalizeChain persists the rich JSON into chains.workflow_info. - Modal renders phases + a per-agent list (label/phase/state/tokens/ tool-calls/duration) instead of the obsolete tool breakdown. Sync ARCHITECTURE.md, ARCHITECTURE_QUICK.md and README.md to the new mechanism. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
1 parent
b6e546f618
commit
2b95161596
9 files changed
+453
-175
No files matched your search
+68
-20
@@ -100,6 +100,14 @@ async function showPayloadPreviewModal(sessionId) {
|
||||
|
||||
function _wfNum(n) { return Number(n || 0).toLocaleString('de-DE'); }
|
||||
function _wfTime(t) { try { return new Date(t).toLocaleTimeString('de-DE'); } catch (e) { return ''; } }
|
||||
function _wfDur(ms) {
|
||||
ms = Number(ms || 0);
|
||||
if (ms <= 0) return '–';
|
||||
var s = Math.round(ms / 1000);
|
||||
if (s < 60) return s + 's';
|
||||
var m = Math.floor(s / 60);
|
||||
return m + 'm ' + (s % 60) + 's';
|
||||
}
|
||||
|
||||
function _renderWorkflowStatus(body, data) {
|
||||
var status = (data && data.status) || 'none';
|
||||
@@ -127,11 +135,22 @@ function _renderWorkflowStatus(body, data) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Name / Zusammenfassung des Workflows (nur nach Abschluss vorhanden).
|
||||
if (wf.workflowName || wf.summary) {
|
||||
var title = document.createElement('div');
|
||||
title.className = 'wf-title';
|
||||
title.innerHTML = (wf.workflowName ? '<span class="wf-name">' + esc(wf.workflowName) + '</span>' : '') +
|
||||
(wf.summary ? '<span class="wf-summary">' + esc(wf.summary) + '</span>' : '');
|
||||
body.appendChild(title);
|
||||
}
|
||||
|
||||
// Kennzahlen.
|
||||
var stats = [
|
||||
{ label: 'Subagenten', value: _wfNum(wf.subagentCount) },
|
||||
{ label: 'Tool-Aufrufe', value: _wfNum(wf.subagentToolCalls) },
|
||||
{ label: 'Input-Tokens', value: _wfNum(wf.subagentInputTokens) },
|
||||
{ label: 'Output-Tokens', value: _wfNum(wf.subagentOutputTokens) }
|
||||
{ label: 'Fertig', value: _wfNum(wf.completedCount) + ' / ' + _wfNum(wf.subagentCount) },
|
||||
{ label: 'Tokens', value: _wfNum(wf.totalTokens) },
|
||||
{ label: 'Tool-Aufrufe', value: _wfNum(wf.totalToolCalls) },
|
||||
{ label: status === 'running' ? 'Läuft seit' : 'Dauer', value: _wfDur(wf.durationMs) }
|
||||
];
|
||||
var grid = document.createElement('div');
|
||||
grid.className = 'wf-stat-grid';
|
||||
@@ -144,30 +163,59 @@ function _renderWorkflowStatus(body, data) {
|
||||
});
|
||||
body.appendChild(grid);
|
||||
|
||||
var tb = wf.toolBreakdown || {};
|
||||
var keys = Object.keys(tb).sort(function (a, b) { return tb[b] - tb[a]; });
|
||||
if (keys.length) {
|
||||
var sec = document.createElement('div');
|
||||
sec.className = 'wf-section';
|
||||
sec.innerHTML = '<div class="wf-section-lbl">Tool-Aufschlüsselung</div>';
|
||||
var list = document.createElement('div');
|
||||
list.className = 'wf-tool-list';
|
||||
keys.forEach(function (k) {
|
||||
// Phasen (nur nach Abschluss).
|
||||
var phases = wf.phases || [];
|
||||
if (phases.length) {
|
||||
var psec = document.createElement('div');
|
||||
psec.className = 'wf-section';
|
||||
psec.innerHTML = '<div class="wf-section-lbl">Phasen</div>';
|
||||
var plist = document.createElement('div');
|
||||
plist.className = 'wf-phase-list';
|
||||
phases.forEach(function (p) {
|
||||
var row = document.createElement('div');
|
||||
row.className = 'wf-tool-row';
|
||||
row.innerHTML = '<span class="wf-tool-name">' + esc(k) +
|
||||
'</span><span class="wf-tool-count">' + esc(String(tb[k])) + '</span>';
|
||||
list.appendChild(row);
|
||||
row.className = 'wf-phase-row';
|
||||
row.innerHTML = '<span class="wf-phase-title">' + esc(p.title || '') + '</span>' +
|
||||
(p.detail ? '<span class="wf-phase-detail">' + esc(p.detail) + '</span>' : '');
|
||||
plist.appendChild(row);
|
||||
});
|
||||
sec.appendChild(list);
|
||||
body.appendChild(sec);
|
||||
psec.appendChild(plist);
|
||||
body.appendChild(psec);
|
||||
}
|
||||
|
||||
// Subagenten-Details (nur nach Abschluss; live liegen noch keine Labels vor).
|
||||
var agents = wf.agents || [];
|
||||
if (agents.length) {
|
||||
var asec = document.createElement('div');
|
||||
asec.className = 'wf-section';
|
||||
asec.innerHTML = '<div class="wf-section-lbl">Subagenten</div>';
|
||||
var alist = document.createElement('div');
|
||||
alist.className = 'wf-agent-list';
|
||||
agents.forEach(function (a) {
|
||||
var st = a.state || '';
|
||||
var stCls = st === 'done' ? 'wf-ag-done' : (st === 'running' ? 'wf-ag-running' : 'wf-ag-other');
|
||||
var row = document.createElement('div');
|
||||
row.className = 'wf-agent-row';
|
||||
row.innerHTML =
|
||||
'<div class="wf-agent-main">' +
|
||||
'<span class="wf-agent-state ' + stCls + '"></span>' +
|
||||
'<span class="wf-agent-label">' + esc(a.label || ('Agent ' + (a.index || ''))) + '</span>' +
|
||||
(a.phaseTitle ? '<span class="wf-agent-phase">' + esc(a.phaseTitle) + '</span>' : '') +
|
||||
'</div>' +
|
||||
'<div class="wf-agent-metrics">' +
|
||||
'<span title="Tokens"><i class="fa fa-database"></i> ' + _wfNum(a.tokens) + '</span>' +
|
||||
'<span title="Tool-Aufrufe"><i class="fa fa-wrench"></i> ' + _wfNum(a.toolCalls) + '</span>' +
|
||||
'<span title="Dauer"><i class="fa fa-clock-o"></i> ' + _wfDur(a.durationMs) + '</span>' +
|
||||
'</div>';
|
||||
alist.appendChild(row);
|
||||
});
|
||||
asec.appendChild(alist);
|
||||
body.appendChild(asec);
|
||||
}
|
||||
|
||||
if (wf.startedAt) {
|
||||
var ts = document.createElement('div');
|
||||
ts.className = 'wf-meta';
|
||||
ts.textContent = 'Start: ' + _wfTime(wf.startedAt) +
|
||||
(wf.lastActivityAt ? ' · letzte Aktivität: ' + _wfTime(wf.lastActivityAt) : '');
|
||||
ts.textContent = 'Start: ' + _wfTime(wf.startedAt);
|
||||
body.appendChild(ts);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user