docs(28-03): complete Hermes runtime dashboard plan — stateJson merge, HermesRuntimeCard

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Nexus Dev 2026-04-02 17:05:32 +00:00
parent a9783f00b0
commit 93b9fa2dbd
4 changed files with 139 additions and 18 deletions

View file

@ -7,8 +7,8 @@
- [x] **HERM-03** — Heartbeat execution spawns `hermes chat -q`, processes task, returns result - [x] **HERM-03** — Heartbeat execution spawns `hermes chat -q`, processes task, returns result
- [x] **HERM-04** — Session persistence works across heartbeats via `--resume` flag - [x] **HERM-04** — Session persistence works across heartbeats via `--resume` flag
- [ ] **HERM-05** — Nexus-managed skills are visible alongside Hermes native skills in agent config - [ ] **HERM-05** — Nexus-managed skills are visible alongside Hermes native skills in agent config
- [ ] **HERM-06** — Cost tracking captures token usage and model costs for Hermes agents - [x] **HERM-06** — Cost tracking captures token usage and model costs for Hermes agents
- [ ] **HERM-07** — Dashboard shows Hermes-specific info (model name, memory usage, native skill count) - [x] **HERM-07** — Dashboard shows Hermes-specific info (model name, memory usage, native skill count)
## Ollama Integration (5) ## Ollama Integration (5)
@ -46,8 +46,8 @@ None deferred — all PRD items included in this milestone.
| HERM-03 | Phase 27 | Complete | | HERM-03 | Phase 27 | Complete |
| HERM-04 | Phase 27 | Complete | | HERM-04 | Phase 27 | Complete |
| HERM-05 | Phase 28 | Pending | | HERM-05 | Phase 28 | Pending |
| HERM-06 | Phase 28 | Pending | | HERM-06 | Phase 28 | Complete |
| HERM-07 | Phase 28 | Pending | | HERM-07 | Phase 28 | Complete |
| OLLA-01 | Phase 28 | Complete | | OLLA-01 | Phase 28 | Complete |
| OLLA-02 | Phase 28 | Complete | | OLLA-02 | Phase 28 | Complete |
| OLLA-03 | Phase 28 | Pending | | OLLA-03 | Phase 28 | Pending |

View file

@ -11,7 +11,7 @@
## Phases ## Phases
- [x] **Phase 27: Hermes Adapter** — Install and enable the Hermes adapter, expose it in the Add Agent dialog, and deliver working heartbeat execution with session persistence (completed 2026-04-02) - [x] **Phase 27: Hermes Adapter** — Install and enable the Hermes adapter, expose it in the Add Agent dialog, and deliver working heartbeat execution with session persistence (completed 2026-04-02)
- [ ] **Phase 28: Ollama Integration & Agent Surface** — Detect Ollama, list and recommend models, expose skill/cost/dashboard data for Hermes agents - [x] **Phase 28: Ollama Integration & Agent Surface** — Detect Ollama, list and recommend models, expose skill/cost/dashboard data for Hermes agents (completed 2026-04-02)
- [ ] **Phase 29: Default Provider & End-to-End** — Onboarding fallback to Hermes, agent template compatibility, GSD workflow validation, full end-to-end smoke test - [ ] **Phase 29: Default Provider & End-to-End** — Onboarding fallback to Hermes, agent template compatibility, GSD workflow validation, full end-to-end smoke test
--- ---
@ -43,11 +43,11 @@ Plans:
5. The agent config page shows Nexus-managed skills alongside Hermes native skills in a single unified list 5. The agent config page shows Nexus-managed skills alongside Hermes native skills in a single unified list
6. The dashboard agent card for a Hermes agent shows model name, memory usage, and native skill count 6. The dashboard agent card for a Hermes agent shows model name, memory usage, and native skill count
7. Token usage and estimated model cost are recorded per heartbeat and surfaced in the cost tracking view 7. Token usage and estimated model cost are recorded per heartbeat and surfaced in the cost tracking view
**Plans:** 1/3 plans executed **Plans:** 3/3 plans complete
Plans: Plans:
- [x] 28-01-PLAN.md — Ollama service, routes, model catalog, and unit tests - [x] 28-01-PLAN.md — Ollama service, routes, model catalog, and unit tests
- [ ] 28-02-PLAN.md — UI model selector dropdown, install callout, Hermes skill badge - [x] 28-02-PLAN.md — UI model selector dropdown, install callout, Hermes skill badge
- [ ] 28-03-PLAN.md — Hermes stateJson runtime data and dashboard HermesRuntimeCard - [x] 28-03-PLAN.md — Hermes stateJson runtime data and dashboard HermesRuntimeCard
**UI hint**: yes **UI hint**: yes
### Phase 29: Default Provider & End-to-End ### Phase 29: Default Provider & End-to-End
@ -93,5 +93,5 @@ All 16 v1 requirements are mapped to exactly one phase. No orphans.
| Phase | Milestone | Plans Complete | Status | Completed | | Phase | Milestone | Plans Complete | Status | Completed |
|-------|-----------|----------------|--------|-----------| |-------|-----------|----------------|--------|-----------|
| 27. Hermes Adapter | v1.4 | 1/1 | Complete | 2026-04-02 | | 27. Hermes Adapter | v1.4 | 1/1 | Complete | 2026-04-02 |
| 28. Ollama Integration & Agent Surface | v1.4 | 1/3 | In Progress| | | 28. Ollama Integration & Agent Surface | v1.4 | 3/3 | Complete | 2026-04-02 |
| 29. Default Provider & End-to-End | v1.4 | 0/? | Not started | - | | 29. Default Provider & End-to-End | v1.4 | 0/? | Not started | - |

View file

@ -2,15 +2,15 @@
gsd_state_version: 1.0 gsd_state_version: 1.0
milestone: v1.4 milestone: v1.4
milestone_name: milestone milestone_name: milestone
status: executing status: verifying
stopped_at: Completed 28-ollama-integration-28-01-PLAN.md stopped_at: Completed 28-ollama-integration-28-03-PLAN.md
last_updated: "2026-04-02T16:56:46.973Z" last_updated: "2026-04-02T17:05:32.272Z"
last_activity: 2026-04-02 last_activity: 2026-04-02
progress: progress:
total_phases: 3 total_phases: 3
completed_phases: 1 completed_phases: 2
total_plans: 4 total_plans: 4
completed_plans: 2 completed_plans: 4
percent: 0 percent: 0
--- ---
@ -26,8 +26,8 @@ See: .planning/PROJECT.md (updated 2026-04-02)
## Current Position ## Current Position
Phase: 28 (ollama-integration) — EXECUTING Phase: 28 (ollama-integration) — EXECUTING
Plan: 2 of 3 Plan: 3 of 3
Status: Ready to execute Status: Phase complete — ready for verification
Last activity: 2026-04-02 Last activity: 2026-04-02
Progress: [__________] 0% Progress: [__________] 0%
@ -92,6 +92,7 @@ Progress: [__________] 0%
| Phase 26-pwa-performance P04 | 15 | 2 tasks | 10 files | | Phase 26-pwa-performance P04 | 15 | 2 tasks | 10 files |
| Phase 27-hermes-adapter P01 | 2 | 3 tasks | 3 files | | Phase 27-hermes-adapter P01 | 2 | 3 tasks | 3 files |
| Phase 28-ollama-integration P01 | 3 | 2 tasks | 6 files | | Phase 28-ollama-integration P01 | 3 | 2 tasks | 6 files |
| Phase 28 P03 | 12 | 2 tasks | 3 files |
## Accumulated Context ## Accumulated Context
@ -183,6 +184,8 @@ Recent decisions affecting current work:
- [Phase 27-hermes-adapter]: Hermes session codec has no cwd field (unlike claude/codex/cursor/gemini) — only sessionId tracked - [Phase 27-hermes-adapter]: Hermes session codec has no cwd field (unlike claude/codex/cursor/gemini) — only sessionId tracked
- [Phase 28-ollama-integration]: Force-added server/src/data/ with git add -f — source catalog JSON is not generated data despite data/ gitignore pattern - [Phase 28-ollama-integration]: Force-added server/src/data/ with git add -f — source catalog JSON is not generated data despite data/ gitignore pattern
- [Phase 28-ollama-integration]: getRecommendedModel uses QUALITY_RANK map (best>reasoning>balanced>fast) to pick highest quality variant within 75% RAM budget - [Phase 28-ollama-integration]: getRecommendedModel uses QUALITY_RANK map (best>reasoning>balanced>fast) to pick highest quality variant within 75% RAM budget
- [Phase 28]: hermesNativeSkillCount derived from agentsApi.skills in UI (not stateJson) — avoids cross-DB query in heartbeat path
- [Phase 28]: COALESCE jsonb concat pattern used for stateJson merge in hermes_local heartbeat — prevents overwriting existing fields
### Pending Todos ### Pending Todos
@ -194,6 +197,6 @@ None identified yet.
## Session Continuity ## Session Continuity
Last session: 2026-04-02T16:56:46.970Z Last session: 2026-04-02T17:05:12.136Z
Stopped at: Completed 28-ollama-integration-28-01-PLAN.md Stopped at: Completed 28-ollama-integration-28-03-PLAN.md
Resume file: None Resume file: None

View file

@ -0,0 +1,118 @@
---
phase: 28-ollama-integration
plan: 03
subsystem: dashboard
tags: [hermes, ollama, stateJson, jsonb, heartbeat, dashboard]
# Dependency graph
requires:
- phase: 28-01
provides: ollama.ts service with detectOllama, listOllamaModels, getRecommendedModel
provides:
- getOllamaMemoryUsage() in ollama.ts — queries /api/ps for active model VRAM usage
- Hermes stateJson merge in heartbeat.ts updateRuntimeState — stores hermesModel + hermesMemoryBytes after each run
- HermesRuntimeCard component in AgentDetail.tsx — displays model, native skill count, VRAM in AgentOverview
affects: [28-02, 29-default-provider]
# Tech tracking
tech-stack:
added: []
patterns:
- COALESCE jsonb concat pattern for stateJson merge (no overwrite)
- Best-effort Ollama /api/ps probe (error caught, returns null gracefully)
- Native skill count derived from agentsApi.skills in UI (avoids cross-DB query in heartbeat)
key-files:
created: []
modified:
- server/src/services/ollama.ts
- server/src/services/heartbeat.ts
- ui/src/pages/AgentDetail.tsx
key-decisions:
- "hermesNativeSkillCount derived from agentsApi.skills in HermesRuntimeCard (not stored in stateJson) — avoids cross-DB query in heartbeat path"
- "COALESCE jsonb concat used for stateJson merge — prevents overwriting existing fields from other heartbeat writers"
- "getOllamaMemoryUsage catches all errors and returns null — Ollama absence or model-not-loaded both show Not loaded"
patterns-established:
- "Pattern: Use COALESCE(column, '{}'::jsonb) || patch::jsonb for safe jsonb merge in Drizzle"
- "Pattern: Hermes-specific stateJson written in updateRuntimeState conditional on adapterType === hermes_local"
requirements-completed: [HERM-06, HERM-07]
# Metrics
duration: 12min
completed: 2026-04-01
---
# Phase 28 Plan 03: Hermes Runtime Dashboard Summary
**Hermes heartbeat now persists model name + VRAM via jsonb merge, and AgentOverview renders a HermesRuntimeCard showing model, native skill count, and memory usage.**
## Tasks Completed
| Task | Description | Commit |
|------|-------------|--------|
| 1 | Add getOllamaMemoryUsage + stateJson merge in heartbeat | dbdc62aa |
| 2 | Add HermesRuntimeCard in AgentOverview | 7458753a |
## What Was Built
### Server: getOllamaMemoryUsage (ollama.ts)
New exported function queries Ollama `/api/ps` with a 3-second AbortController timeout. Finds the running model by `name` or `model` field, returns `size_vram` bytes. Returns `null` on any error (graceful degradation per Pitfall 5 from RESEARCH).
### Server: Hermes stateJson Merge (heartbeat.ts)
After the existing `db.update(agentRuntimeState)` cost tracking block in `updateRuntimeState`, a `hermes_local`-gated block merges `hermesModel` and `hermesMemoryBytes` into `stateJson` using Postgres jsonb concat:
```sql
COALESCE(stateJson, '{}'::jsonb) || '{"hermesModel": ..., "hermesMemoryBytes": ...}'::jsonb
```
This never overwrites other fields already stored in stateJson (Pitfall 3 from RESEARCH).
### UI: HermesRuntimeCard (AgentDetail.tsx)
New component defined before `AgentOverview`, rendered inside it gated by `agent.adapterType === "hermes_local" && runtimeState`. Shows:
- **Model**: `stateJson.hermesModel` or "Not set"
- **Native Skills**: count from `agentsApi.skills(agentId).entries` filtered by `originLabel === "Hermes skill"`
- **Memory (VRAM)**: formatted from `stateJson.hermesMemoryBytes` or "Not loaded"
## Deviations from Plan
### Auto-fixed Issues
**1. [Rule 1 - Bug] Plan referenced `adapterEntries` but type has `entries`**
- **Found during:** Task 2
- **Issue:** The plan's action block referenced `skillsData.adapterEntries` but `AgentSkillSnapshot` has `entries: AgentSkillEntry[]`
- **Fix:** Used `skillsData.entries` in the implementation
- **Files modified:** ui/src/pages/AgentDetail.tsx
- **Commit:** 7458753a
**2. [Rule 1 - Bug] Removed unused `createRequire` import from ollama.ts**
- **Found during:** Task 1
- **Issue:** Plan 01 left a `createRequire` import in ollama.ts that was unused
- **Fix:** Removed the unused import when modifying the file
- **Files modified:** server/src/services/ollama.ts
- **Commit:** dbdc62aa
## Known Stubs
None — all data flows are wired (stateJson written by heartbeat, read by HermesRuntimeCard). Model name and memory bytes will be `null`/`undefined` until a Hermes run completes, which is correct behavior (displays "Not set" / "Not loaded").
## HERM-06 Verification (No Code Change)
Cost tracking for Hermes + Ollama runs correctly returns $0.00:
- `result.costUsd` is `undefined` for local Ollama runs
- `normalizeBilledCostCents(undefined, billingType)` returns `0`
- The `if (additionalCostCents > 0 || hasTokenUsage)` guard suppresses cost events when no token data emitted
- This is correct behavior per RESEARCH Pitfall 6
## Self-Check: PASSED
- `/opt/nexus/.claude/worktrees/agent-ad37cce3/server/src/services/ollama.ts` — exists with getOllamaMemoryUsage
- `/opt/nexus/.claude/worktrees/agent-ad37cce3/server/src/services/heartbeat.ts` — exists with hermes_local block
- `/opt/nexus/.claude/worktrees/agent-ad37cce3/ui/src/pages/AgentDetail.tsx` — exists with HermesRuntimeCard
- Commits dbdc62aa, 7458753a — confirmed in git log