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
+14
-10
@@ -165,7 +165,7 @@ gott/
|
||||
│ ├── KreaService.cs # Krea Image Generation API
|
||||
│ ├── ImageService.cs # Bild-Resize + WebP-Konvertierung (512px + 256px Thumbnail)
|
||||
│ ├── StreamingTracker.cs # Tracking aktiver Streams + CancellationTokenSource für Stop
|
||||
│ ├── WorkflowProgressTracker.cs # In-Memory-Telemetrie laufender dynamischer Workflows (Ultracode) pro Session
|
||||
│ ├── WorkflowProgressTracker.cs # Registry der dynamischen Workflows (Ultracode) pro Session + WorkflowTelemetryReader
|
||||
│ ├── WorkspaceHelper.cs # Workspace-Pfad-Auflösung (relativ → absolut)
|
||||
│ ├── AppPaths.cs # Zentrale Pfade (WwwRoot) – Dev vs. Publish-Modus
|
||||
│ ├── KeyScannerService.cs # API-Key-Scanner (GitHub Code Search, GitHub Issues/PRs, HuggingFace Hub, Google/Serper)
|
||||
@@ -382,7 +382,7 @@ TelegramBot (1) ─── (*) Session (via telegram_bot_id)
|
||||
- `ToolNames` – Komma-separierte Liste der verwendeten Tools
|
||||
- `ArchiveId` – Zugehöriges Archiv (wenn archiviert)
|
||||
- `StreamErrorInfo` – Fehlerdetails bei Stream-Abbrüchen
|
||||
- `WorkflowInfo` – JSON-Telemetrie eines dynamischen Workflows (Ultracode): Subagent-Anzahl, Tool-Aufrufe (+ Aufschlüsselung), aggregierte Subagent-Tokens. `null` wenn die Chain keinen Workflow gespawnt hat (siehe [Workflow-Status](#workflow-status))
|
||||
- `WorkflowInfo` – JSON-Telemetrie eines dynamischen Workflows (Ultracode): die reiche, beim Lauf-Ende aus der `<runId>.json` gelesene Telemetrie – Phasen + Per-Agent-Details (Label/Phase/State/Tokens/Tool-Calls/Dauer) + Summen. `null` wenn die Chain keinen Workflow gespawnt hat (siehe [Workflow-Status](#workflow-status))
|
||||
- `PlanMode` – Ob die Chain im Plan-Modus erstellt wurde (informativ)
|
||||
- `DurationMs` – Gesamtdauer der Chain
|
||||
|
||||
@@ -462,7 +462,7 @@ RemoteConnectionManager → AuthService → DatabaseService → EnsureDefaultUse
|
||||
| `KreaService` | Singleton | Krea API für Bildgenerierung, Job-Polling, Image-Download |
|
||||
| `ImageService` | Singleton | Bild-Resize + WebP-Konvertierung (512px + 256px Thumbnail) für Avatare und Session-Bilder |
|
||||
| `StreamingTracker` | Singleton | Trackt aktive Streams + CancellationTokenSource pro Session für Stop-Button |
|
||||
| `WorkflowProgressTracker` | Singleton | In-Memory-Telemetrie laufender dynamischer Workflows (Ultracode) pro Session: Subagent-Anzahl, Tool-Aufrufe (+ Aufschlüsselung), aggregierte Subagent-Tokens. Wird beim Chain-Finalize in `chains.workflow_info` persistiert (analog `StreamingTracker`) |
|
||||
| `WorkflowProgressTracker` | Singleton | **Registry** laufender dynamischer Workflows (Ultracode) pro Session: merkt sich pro Session die aus dem Workflow-`tool_result` abgegriffenen Transcript-Pfade (`journal.jsonl` / `<runId>.json`). Die Telemetrie wird **on demand** via `WorkflowTelemetryReader` aus diesen Dateien gelesen (live aus `journal.jsonl`, abgeschlossen aus `<runId>.json`) und beim Chain-Finalize in `chains.workflow_info` persistiert (analog `StreamingTracker`) |
|
||||
| `KeyScannerService` | Singleton | API-Key-Scanner (GitHub Code Search, GitHub Issues/PRs, HuggingFace Hub, Google/Serper), täglicher Scan |
|
||||
| `GitHubEventScannerService` | Background | Echtzeit-Scanner: GitHub Public Events API (PushEvent-Diffs nach geleakten Keys), 30s Poll-Intervall |
|
||||
| `KeyPatterns` | Static | Zentrale Key-Pattern-Definitionen für alle Scanner (Regex, Typ-Erkennung, Display-Names) |
|
||||
@@ -1520,20 +1520,24 @@ Wenn im Plan-Modus eine `ask_user_choice`-Rückfrage kommt und der User antworte
|
||||
|
||||
### Workflow-Status
|
||||
|
||||
Da die CLI Subagent-Events (Top-Level-Messages mit gesetztem `parent_tool_use_id`) bewusst aus dem sichtbaren Stream/History ausblendet (zentraler `parent_tool_use_id`-Guard in `ClaudeCliService.RunCliStreamingAsync`), gäbe es ohne separates Tracking keine Sichtbarkeit auf den Workflow-Fortschritt. Der **Workflow-Status** macht diese Subagent-Telemetrie live und nachträglich abrufbar.
|
||||
Dynamische Ultracode-Workflows laufen als **detachter Hintergrund-Task mit eigenem Transcript-Verzeichnis** – ihre Subagent-Events fließen **nicht** durch den Haupt-(`-p`)-Stream (anders als klassische Task-Subagenten, die mit gesetztem `parent_tool_use_id` inline im Parent-Stream ankommen). Die echte Telemetrie liegt daher in zwei Dateien des CLI-Transcripts und wird **on demand** gelesen, nicht aus dem Stream akkumuliert. Der **Workflow-Status** macht diese Telemetrie live und nachträglich abrufbar.
|
||||
|
||||
**Backend:**
|
||||
- **`WorkflowProgressTracker`** (Singleton, registriert in `Program.cs`) – In-Memory-Tracking pro Session: Subagent-Anzahl (dedupliziert per `parent_tool_use_id`), Tool-Aufrufe + Aufschlüsselung (`ToolBreakdown`), aggregierte Subagent-Input-/Output-Tokens, `RecentTools` (letzte 12). Lock-leicht, defensiv – Telemetrie darf den Stream nie blockieren.
|
||||
- **Tap statt Filter:** Der `parent_tool_use_id`-Guard blendet Subagent-Events weiterhin aus dem Stream aus (`continue`), greift aber für `type == "assistant"`-Turns via `TapSubagentEvent()` die Tool-Namen + Token ab und meldet sie an `RecordSubagent()`. Der existierende Guard bleibt unverändert.
|
||||
- **Persistierung:** Neue Spalte `chains.workflow_info` (TEXT, via `TryAddColumn` in `DatabaseService.cs`; geschrieben in `DatabaseService.Sessions.cs` `UpdateChain`), Feld `ChainRecord.WorkflowInfo`. Beim `FinalizeChain` (`AnthropicService.cs`) wird die Telemetrie als JSON in die Chain serialisiert und der Live-Tracker freigegeben – mit **ChainId-Abgleich** (`ChainIdFor`), damit ein anderer Finalize-Pfad derselben Session (z.B. ein Panel-Member) den Workflow-Eintrag nicht fälschlich abräumt.
|
||||
- **Endpoint:** `GET /api/sessions/{id}/workflow-status` (`SessionEndpoint.HandleWorkflowStatus`) liefert in dieser Reihenfolge: (1) `status: "running"` + Live-Snapshot, solange der Tracker einen Eintrag hat; (2) `status: "completed"` + persistiertes `workflow_info` der jüngsten aktiven Chain; (3) `status: "none"` mit `workflow: null`. Top-Level-Felder: `sessionId`, `streaming`, `status`, `chainId` (entfällt bei `status: "none"`) und das Telemetrie-Objekt `workflow` (`null` bei `none`).
|
||||
- **Capture (`ClaudeCliService.TapWorkflowLaunch`)** – Im `tool_result`-Pfad: Enthält der Inhalt `"Workflow launched"`, parst die Methode die Zeile `Transcript dir: <absoluter Pfad>` und ruft `_workflowTracker.Register(...)`. Rein additiv, wirft nie. Der `parent_tool_use_id`-Guard in `ClaudeCliService.RunCliStreamingAsync` blendet klassische Subagent-Events weiterhin per `continue` aus dem sichtbaren Stream aus – **tappt sie aber nicht mehr** ab.
|
||||
- **Registry (`WorkflowProgressTracker`)** (Singleton, registriert in `Program.cs`) – ist **nur eine Registry**, kein Stream-Akkumulator. Speichert pro Session einen `WorkflowRunRef` (`ChainId`, `RunId`, `JournalPath`, `RichJsonPath`, `StartedAt`), abgeleitet aus der Transcript-Dir. Methoden: `Register` / `Get` / `ChainIdFor` / `End`. In-Memory pro Session, defensiv.
|
||||
- **Reader (`WorkflowTelemetryReader`, statisch)** – liest die Telemetrie aus zwei Dateien:
|
||||
- **Live:** `<transcriptDir>/journal.jsonl` (append-only; pro Subagent `started`/`result`-Zeilen). `ReadLive` ⇒ `status: "running"`, `subagentCount` (= Anzahl `started`), `completedCount` (= Anzahl `result`), `durationMs`. **Keine** Tokens/Tool-Calls live verfügbar.
|
||||
- **Abgeschlossen:** `<proj>/workflows/<runId>.json` (wird **erst am Lauf-Ende** geschrieben). `ReadCompleted` ⇒ `status: "completed"`, `workflowName`, `summary`, `durationMs`, `phases[]` (index/title/detail), `agents[]` (index/label/phaseTitle/state/model/tokens/toolCalls/durationMs/resultPreview), `totalTokens`, `totalToolCalls`, `subagentCount`, `completedCount`.
|
||||
- `Read()` bevorzugt die reiche, fertige `<runId>.json` und fällt sonst auf das Live-`journal.jsonl` zurück.
|
||||
- **Persistierung:** Spalte `chains.workflow_info` (TEXT, via `TryAddColumn` in `DatabaseService.cs`; geschrieben in `DatabaseService.Sessions.cs` `UpdateChain`), Feld `ChainRecord.WorkflowInfo`. Beim `FinalizeChain` (`AnthropicService.cs`) wird die reiche `WorkflowProgress`-JSON in die Chain serialisiert und der Tracker-Eintrag freigegeben – mit **ChainId-Abgleich** (`ChainIdFor`), damit ein anderer Finalize-Pfad derselben Session (z.B. ein Panel-Member) den Workflow-Eintrag nicht fälschlich abräumt.
|
||||
- **Endpoint:** `GET /api/sessions/{id}/workflow-status` (`SessionEndpoint.HandleWorkflowStatus`) liefert in dieser Reihenfolge: (1) ist für die Session ein Lauf registriert → Telemetrie aus den Transcript-Dateien lesen (`running` aus `journal.jsonl` / `completed` aus `<runId>.json`); (2) sonst die jüngste aktive Chain mit nicht-leerem `workflow_info` → `status: "completed"`; (3) sonst `status: "none"`, `workflow: null`. JSON-Shape: `running`/`completed` → `{ sessionId, streaming, status, chainId, workflow }`; `none` → `{ sessionId, streaming, status: "none", workflow: null }` (**kein `chainId` im `none`-Zweig**).
|
||||
|
||||
**Felder im `workflow`-Objekt** (camelCase): `subagentCount`, `subagentToolCalls`, `subagentInputTokens`, `subagentOutputTokens`, `toolBreakdown`, `recentTools`, `startedAt`, `lastActivityAt`.
|
||||
**Felder im `workflow`-Objekt** (camelCase, via `WriteAsJsonAsync`): `status`, `chainId`, `workflowName`, `summary`, `startedAt`, `subagentCount`, `completedCount`, `totalTokens`, `totalToolCalls`, `durationMs`, `phases[]` (`index`, `title`, `detail`), `agents[]` (`index`, `label`, `phaseTitle`, `state`, `model`, `tokens`, `toolCalls`, `durationMs`, `resultPreview`).
|
||||
|
||||
**Frontend:**
|
||||
- Header-Menü-Eintrag **"Workflow-Status"** (Button `#workflowStatusBtn` in `index.html`, direkt nach "Payload anzeigen")
|
||||
- API-Wrapper `apiGetWorkflowStatus(sessionId)` in `js/api.js`
|
||||
- `showWorkflowStatusModal(sessionId)` in `js/modals.js` – rendert Status-Head, Statistik-Grid (Subagenten/Tool-Aufrufe/Input-/Output-Tokens), Tool-Aufschlüsselung und Zeitstempel. **Pollt alle 2 s**, solange `status === "running"`, und stoppt automatisch, sobald das Modal geschlossen wird (Overlay nicht mehr im DOM) oder der Workflow endet.
|
||||
- `showWorkflowStatusModal(sessionId)` in `js/modals.js` – rendert Status-Head, Summen-Kacheln (Subagenten/Fertig/Tokens/Tool-Aufrufe), die **Phasen** und eine **Per-Agent-Liste** (Label, Phase, State, Tokens, Tool-Calls, Dauer). **Pollt alle 2 s**, solange `status === "running"`, und stoppt automatisch, sobald das Modal geschlossen wird (Overlay nicht mehr im DOM) oder der Workflow endet.
|
||||
- Click-Handler in `js/pages/chat-page.js`, Styles `.workflow-status-modal` (+ `.wf-*`) in `css/chat.css`
|
||||
|
||||
---
|
||||
|
||||
Reference in new issue
Block a user