feat(dashboard): version /api/state and expose leadSessionId for external consumers - #33
feat(dashboard): version /api/state and expose leadSessionId for external consumers#33Agony1023 wants to merge 4 commits into
Conversation
Adds a bare-integer version field to the /api/state payload and gives buildState() a real return type (EnsembleDashboardState) instead of an inline anonymous shape. Bump ENSEMBLE_STATE_VERSION on any breaking change; additive fields don't require a bump. No changes to teammate-lifecycle logic. Motivated by an external consumer (an OpenCode sidebar TUI plugin) that polls this endpoint and needs exact-match version detection to degrade gracefully instead of crashing on shape drift.
Additive field -- team already carries lead_session_id in the DB schema (NOT NULL since migration 1), buildState() just never selected or exposed it. Lets an external consumer scope teams to "created by this specific session" rather than only "this project directory" -- the current sidebar TUI plugin's project-only scoping still mixes multiple sessions' teams together within one project. No ENSEMBLE_STATE_VERSION bump -- purely additive per the version field's own documented policy (field removed/renamed/retyped needs a bump, new field doesn't).
…api/state Depends on the last_nudged_at column added by the watchdog/nudge-staleness fix (separate PR) — this branch's tests will fail standalone until that migration is present. Confirmed: only failure mode is 'no such column: last_nudged_at' (11 fail/10 errors, all traceable to the missing column, nothing else broken). Merge order: land the watchdog PR first, or rebase this on top of it.
|
Pushed an additional commit ( |
… (backend half) Based on this PR's branch -- needs no additional dependency beyond what's already here. session.status already has a "retry" case in handleSessionStatusEvent (fires a toast) -- the payload was thrown away immediately after. This persists it instead: - Migration 10: four additive, nullable columns on team_member (retry_until, retry_attempt, retry_provider, retry_message). No CHECK constraint change, no new status/execution_status literal. - handleSessionStatusEvent gains an optional retryPayload param; the existing retry branch persists the four columns in addition to the existing toast, not instead of it. status/execution_status untouched. - No hardcoded rate-limit/throttle language: retry_message prefers action.message, falls back to the generic status.message. - retry_until is a TTL; "currently retrying" is a derived, read-time boolean, never a stored enum -- no clear-write anywhere. - team-status.ts's status line gets an additive annotation for a retrying member, same pattern as the existing nudged-timestamp one. status.next resolved as an absolute epoch-ms timestamp (verified against the opencode server bundle's session.retry.scheduled handler and the built-in TUI's own countdown math). Non-goals: no watchdog/stall-detection changes -- checkStalled()/ ProgressTracker.isTimeStalled() never see this signal, by design. 727/727 tests, typecheck/build clean. Companion change surfacing this via /api/state and the dashboard UI is a separate commit against hueyexe#33.
… + UI Depends on the retry_until/retry_attempt/retry_provider/retry_message columns added by the companion backend PR (against hueyexe#31) -- this branch's tests will fail standalone until that migration is present. Confirmed: the only failure mode is the retry_* columns not existing, nothing else broken. - dashboard.ts: additive isRetrying (derived, read-time TTL boolean -- never a stored enum), retryUntil, retryAttempt, retryProvider, retryMessage on the member row in /api/state. No version bump needed (additive-only, same reasoning as the earlier lastNudgedAt field). - dashboard-js-render.ts: additive "retrying" chip on agent cards and the member drawer, generic label + attempt number, only the actual provider message text if present -- no hardcoded rate-limit/throttle wording anywhere in this file either. Merge order: land the backend PR first, or rebase this on top of it.
|
Pushed another additional commit extending this PR further — exposes the provider-retry signal (added in the backend PR against #31: |
…32) (#35) ## Summary This branch is a full review-and-integration pass over **every currently-open PR** (#29, #31, #33, #34) and **every currently-open issue** (#26, #32). Each contribution was reviewed against a real running OpenCode server, reworked where needed, extended where the review surfaced gaps, and landed as a coherent set of commits. Merging this supersedes all four open PRs and closes both open issues. 36 files changed: +2111 / −104 across src and test, no external dependencies added. --- ### Reviewed from open PRs #### PR #31 — Teammate lifecycle robustness (`4feb15d`, `ab4d3bc`) Six fixes found by live stress-testing of the lifecycle, plus a follow-up hardening commit on top of the review: - `team_message` can now re-engage a teammate stuck in `completed`, closing the re-engagement gap - Stall detection covers a member's **first action**: spawn records its busy-start baseline directly (there is no `ready→busy` status-event for a fresh spawn), so a member stalled before its first step-finish is visible to `checkStalled()` - Orphaned-team reconciliation after crashes, including a deadlock the naive fix introduced - `team_merge` no longer reports false success when the merge silently fails - `team_broadcast` now wires chatty-detection like other send paths - Watchdog nudge display-staleness signal + logging parity (`last_nudged_at` schema, nudges visible in `/api/state`) #### PR #33 — Versioned `/api/state` for external consumers (`e8be872`, `4bab276`, `d4fc294`, `95714e8`) Landed the typed/versioned state contract, then extended it additively during review: - `buildState()` returns an exported, documented type; response carries a `version` field so external consumers can detect payload drift instead of crashing on it - Exposes `leadSessionId` (already persisted in the DB, never selected) so consumers can scope teams to the session that created them - Additive `lastNudgedAt` and provider-retry signals exposed in both the API payload and dashboard UI #### PR #34 — Task status + visualization fixes (`276d2bc`, `f0bc580`) - Task status is now reliably reflected across the whole teammate lifecycle: new `src/tasks.ts` centralizes claim/release/reassign, so tasks return to the board when a member stalls, errors, or is shut down instead of staying pinned to a dead assignee - Freshly-spawned teammates no longer stick at `[starting]` with a frozen timestamp — busy transitions bump execution status and time even when the member was inserted already-busy #### PR #29 — Nullish agent normalization ✅ verified The core fix (`agent: null | undefined` → `"build"` instead of hitting the NOT NULL constraint) was already merged via #30. This branch retains and extends its regression coverage in `test/tools/team-spawn.test.ts`; nothing further required. --- ### Closes open issues #### Issue #26 — Update agent parameters in-place (`009e868`) New `src/member-model.ts`: `team_message` accepts `{ model: "provider/model", to: "<member>" }` and hot-swaps that teammate's model without respawning or re-assigning tasks. Includes permission-safe plumbing through `tool.execute.before`, DB persistence of the override, and tests. Closes #26 #### Issue #32 — Dashboard shows 08:00:00 for historical timestamps (`f0bc580`) OpenCode v1 exposes message time as an object (`{ created: number }`) rather than a bare number; the activity view parsed the object as a number, fell back to epoch-0 rendering, and produced identical `08:00:00` stamps for historical entries. Timestamp extraction now handles both shapes. Fixes #32 --- ### Also in this branch - **Notify-the-lead primitive** (`621387f`): new `src/notify.ts` gives any client a direct channel to wake and message the lead; `team_spawn`'s failure rollback path now uses it, so lead notification survives cases where the old in-band message path could not fire - **Provider-retry observability** (`fb5f7d2`): session.status retry signals are persisted (backend half) and surfaced in the dashboard, so provider flapping is distinguishable from genuine stalls - Test-only scoping fix (`ec6a737`) and doc dedup (`89038ea`) left over from the integration work --- ### Testing - `bun run typecheck && bun test && bun run build` — all green - 783 tests / 38 files passing, including new suites for lifecycle recovery, watchdog staleness, task reassignment, model updates, notify paths, and merge-flow accuracy --------- Co-authored-by: Lennox McKenzie <lennox.mckenzie@gmail.com> Co-authored-by: Jared Davies <jared.davies@cesicorp.com>
|
Clean addition Lennox. Shipped in v0.17.0 via #35 with a couple additive fields built on top of yours (lastNudgedAt and provider retry signals). Closing since its released. Thanks for your support on this. |
Summary
Types and versions the
/api/statedashboard endpoint's response shape, and exposes each team'slead_session_id(already stored in the DB, never selected before). Both are additive-only changes motivated by an external consumer — an OpenCode sidebar TUI plugin that polls this endpoint to show live team/agent status — needing a stable contract to detect payload drift instead of crashing or rendering stale data, and a way to scope teams to the session that created them rather than showing every team ever tracked.Changes
buildState()'s return type is now the exported, documentedEnsembleDashboardStateinterface instead of an inline{ projects: unknown[]; teams: unknown[] }shape./api/statenow returns a bare-integerversionfield (ENSEMBLE_STATE_VERSION, currently1). Bump on any breaking change to the payload; additive fields don't require a bump.leadSessionIdalongside the existingleadAgent.Test Plan
bun run typecheck: clean