feat(ultracode): master-detail tab for Workflow/ultracode run visualization

Opt-in (showUltracodeAgents, default OFF) panel that visualizes ultracode /
Workflow-tool runs like Claude Code's "working agents" TUI: LEFT = runs + phases
(selectable tasks), RIGHT = each run's agents with model, live state, tokens
burned, and tool calls.

Standalone — ZERO edits to subagent-watcher.ts. A new workflow-run-watcher.ts
singleton globs the run-state tree (~/.claude/projects/*/*/workflows/wf_*.json,
disjoint from the transcript tree), strips the heavy script/scriptPath/result/logs
fields (174KB -> ~25KB/run), and emits workflow:run_* SSE events. The LEFT list
ships lightweight summaries (getLightState replay + SSE); the RIGHT pane fetches
the full run (with agents[]) via GET /api/workflows/:runId on selection.

Backend: workflow-run-watcher.ts, types/workflow-run.ts, config/workflow-config.ts,
3 SSE events, getLightState workflowRuns replay, GET /api/workflows[/:runId],
showUltracodeAgents schema key + boot-gate (default OFF) + live toggleService.
Frontend: ultracode-panel.js (debounced master-detail render, run/phase select),
header launcher (btn-ultracode-agents--hidden marker -> mobile-guard-exempt),
App Settings toggle (SYNCED, deliberately not in displayKeys).

Agent states on disk are start|progress|done (start=queued; done has
durationMs/resultPreview). Tests: workflow-run-watcher (9), workflow-routes (3).
Verified: tsc/lint/prettier/frontend-syntax/public-assets/mobile-header-guard
clean; full test:ci green (2986 passed); live server + Playwright e2e against 25
real runs (28-agent grid, phase filter, OFF hides launcher).

Design: docs/ultracode-agent-viz-plan.md (rev. 3).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Codeman maintainer
2026-06-15 08:40:05 +02:00
parent f6a30d7335
commit c15c19fab7
17 changed files with 1800 additions and 1 deletions
+25
View File
@@ -26,6 +26,7 @@ import {
} from '../schemas.js';
import { subagentWatcher } from '../../subagent-watcher.js';
import { imageWatcher } from '../../image-watcher.js';
import { workflowRunWatcher } from '../../workflow-run-watcher.js';
import { applyStatusLineConfig } from '../../hooks-config.js';
import { getLifecycleLog } from '../../session-lifecycle-log.js';
import {
@@ -540,6 +541,9 @@ export function registerSystemRoutes(
// Handle subagent tracking toggle dynamically
toggleService((settings.subagentTrackingEnabled as boolean) ?? true, subagentWatcher, 'Subagent watcher');
// Handle ultracode/workflow run watcher toggle dynamically (default OFF)
toggleService((settings.showUltracodeAgents as boolean) ?? false, workflowRunWatcher, 'Workflow run watcher');
// Handle image watcher toggle dynamically
toggleService((settings.imageWatcherEnabled as boolean) ?? false, imageWatcher, 'Image watcher', () => {
// Re-watch all active sessions that have image watcher enabled
@@ -691,6 +695,27 @@ export function registerSystemRoutes(
}
});
// ========== Workflow Run Monitoring (ultracode) ==========
// LEFT-pane list: lightweight run summaries (no agents[]).
app.get('/api/workflows', async (req) => {
const { minutes } = req.query as { minutes?: string };
const runs = minutes
? workflowRunWatcher.getRecentRunSummaries(parseInt(minutes, 10))
: workflowRunWatcher.getAllRunSummaries();
return { success: true, data: runs };
});
// RIGHT-pane detail: full run incl. agents[] (tokens/toolCalls/state per agent).
app.get('/api/workflows/:runId', async (req) => {
const { runId } = req.params as { runId: string };
const run = workflowRunWatcher.getRun(runId);
if (!run) {
return createErrorResponse(ApiErrorCode.NOT_FOUND, `Workflow run ${runId} not found`);
}
return { success: true, data: run };
});
// ========== Subagent Monitoring ==========
app.get('/api/subagents', async (req) => {