Commit graph

6 commits

Author SHA1 Message Date
Bryan Helmkamp
9b9ebdf50b
feat(web): migrate to generated API client
Expand the OpenAPI contract for frontend auth and workflow routes, regenerate the TypeScript Axios client, and route web API calls through generated client classes while preserving SSE and install exceptions.
2026-05-08 07:44:33 -07:00
Bryan Helmkamp
befb2e00ec
feat(runs): merge command output streams
Route command stderr into stdout at execution time and expose a single output log across events, projections, API clients, and the web UI. Keep replay compatibility for older command.completed events that still contain split stdout/stderr fields.
2026-05-07 22:07:13 -07:00
fabro-sh-0530[bot]
786a6f67e1
Read billing and stages from RunProjection with live runtimes (#213)
### Summary
Billing and stage lists now use the event-sourced `RunProjection` as
their source of truth, so running and retrying stages appear immediately
and runtimes keep advancing in the UI. This removes the checkpoint
completed-node bypass that hid in-flight work and froze totals until the
next server response.

### Plan Summary
- Store stage `started_at`, terminal `duration_ms`, server-internal
`usage`, and lifecycle `state` on `StageProjection`.
- Populate those fields from stage lifecycle events, including retry
transitions and per-attempt reset on new starts.
- Render `/runs/{id}/stages` and `/runs/{id}/billing` from
`RunProjection.iter_stages()`.
- Expose the new API/client fields and tick in-flight billing runtimes
on the web UI.

```mermaid
flowchart TB
  Events["Stage lifecycle events"] --> Projection["RunProjection StageProjection"]
  Projection --> StagesAPI["GET /runs/{id}/stages"]
  Projection --> BillingAPI["GET /runs/{id}/billing"]
  StagesAPI --> StageUI["Stage sidebar/stages view"]
  BillingAPI --> BillingUI["Billing tab live totals"]
```

### Key decisions
Retry and revisit handling stays one row per node id: latest visit data
wins, while first-seen event sequence keeps ordering stable with
finalize output. `state` is stored rather than derived so `Retrying` is
representable, and old serialized projections still work through the
`effective_state()` fallback. Billing `usage` remains server-internal
and is skipped on the wire; public schemas only expose the fields needed
by `/stages`, `/billing`, and the frontend live timer.

Added focused reducer, server retry/revisit, API round-trip, billing UI,
and event invalidation coverage.

⚒️ Generated with [Fabro](https://fabro.sh)

---------

Co-authored-by: Fabro <noreply@fabro.sh>
Co-authored-by: Bryan Helmkamp <bryan@brynary.com>
2026-05-05 09:32:33 -04:00
fabro-sh-0530[bot]
6e36d8350e
Render stage activity from scoped events endpoint (#212)
## Summary
Stage detail now loads activity from a canonical stage-scoped events
endpoint instead of falling back to the first 1000 run-wide events. This
fixes empty panes for late stages in long runs and removes the
presentation-shaped `StageTurn` API from the wire.

### Plan Summary
- Add `GET /runs/{id}/stages/{stageId}/events` with cursor pagination
and server-side `node_id` filtering.
- Replace frontend stage-turn/fallback loading with paginated
stage-events loading and local event-to-activity projection.
- Broaden SSE/SWR invalidation so every activity event consumed by the
reducer refreshes the per-stage cache.
- Remove `StageTurn` schemas/client models and update demo fixtures plus
pagination/handler/reducer tests.

## What changed and why
The store now scans the run event prefix and filters by `node_id` before
applying the `limit + 1` cutoff. That preserves sparse late-stage
matches that would otherwise be dropped if we reused the run-wide
limited scan and filtered afterward. The real-mode handler returns an
empty page for an unknown stage id in an existing run, while preserving
404 for missing runs.

On the frontend, `run-stages` fetches all pages for the selected stage
and feeds them through `eventsToActivity`, keeping `TurnType` as a local
presentation model. Invalidation now targets `runs.stageEvents(runId,
stageId)` for lifecycle and reducer-consumed activity events
(`stage.prompt`, agent messages/tools, and command events), so active
panes refresh from the existing run event subscription.

The OpenAPI document and generated TS client now expose
`listStageEvents` and drop stale `StageTurn` models. Demo mode serves a
`detect-drift` stage-events fixture using the same cursor semantics as
the real endpoint.

## API notes
`/runs/{id}/stages/{stageId}/turns` is removed; clients should use
`/runs/{id}/stages/{stageId}/events?since_seq=&limit=` and project
events locally. The `stageId` path segment for this endpoint is the
workflow node id, not the visit-qualified `node_id@visit` form used by
command logs/artifacts.

⚒️ Generated with [Fabro](https://fabro.sh)

---------

Co-authored-by: Fabro <noreply@fabro.sh>
Co-authored-by: Bryan Helmkamp <bryan@brynary.com>
2026-05-05 09:16:52 -04:00
Bryan Helmkamp
8ac400df1c
feat(command): stream command logs from CAS-backed storage
Persist command stdout/stderr through scratch logs and finalized CAS refs, expose byte-offset tailing through the API, and render separate streaming panels in the web run view.

Resolve command output blob refs for execution-time consumers such as edge routing and retros, and make Docker streaming timeout/cancel drain output before returning.
2026-04-30 22:45:37 -04:00
Bryan Helmkamp
a1f032e166
refactor(web): move server state to SWR
Replace React Router loader/action state paths with SWR query and mutation hooks.

Add targeted run and board EventSource managers that invalidate SWR keys, and refresh embedded SPA assets.
2026-04-25 07:16:41 -04:00