From b36bb88caed6e415994477916451a1a3d3cafd63 Mon Sep 17 00:00:00 2001 From: Nexus Dev Date: Thu, 2 Apr 2026 17:05:32 +0000 Subject: [PATCH] =?UTF-8?q?docs(28-03):=20complete=20Hermes=20runtime=20da?= =?UTF-8?q?shboard=20plan=20=E2=80=94=20stateJson=20merge,=20HermesRuntime?= =?UTF-8?q?Card?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Sonnet 4.6 --- .planning/REQUIREMENTS.md | 8 +- .planning/ROADMAP.md | 10 +- .planning/STATE.md | 21 ++-- .../28-ollama-integration/28-03-SUMMARY.md | 118 ++++++++++++++++++ 4 files changed, 139 insertions(+), 18 deletions(-) create mode 100644 .planning/phases/28-ollama-integration/28-03-SUMMARY.md diff --git a/.planning/REQUIREMENTS.md b/.planning/REQUIREMENTS.md index 25d22338..70cac088 100644 --- a/.planning/REQUIREMENTS.md +++ b/.planning/REQUIREMENTS.md @@ -7,8 +7,8 @@ - [x] **HERM-03** — Heartbeat execution spawns `hermes chat -q`, processes task, returns result - [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-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-06** — Cost tracking captures token usage and model costs for Hermes agents +- [x] **HERM-07** — Dashboard shows Hermes-specific info (model name, memory usage, native skill count) ## Ollama Integration (5) @@ -46,8 +46,8 @@ None deferred — all PRD items included in this milestone. | HERM-03 | Phase 27 | Complete | | HERM-04 | Phase 27 | Complete | | HERM-05 | Phase 28 | Pending | -| HERM-06 | Phase 28 | Pending | -| HERM-07 | Phase 28 | Pending | +| HERM-06 | Phase 28 | Complete | +| HERM-07 | Phase 28 | Complete | | OLLA-01 | Phase 28 | Complete | | OLLA-02 | Phase 28 | Complete | | OLLA-03 | Phase 28 | Pending | diff --git a/.planning/ROADMAP.md b/.planning/ROADMAP.md index 11194121..e7a081da 100644 --- a/.planning/ROADMAP.md +++ b/.planning/ROADMAP.md @@ -11,7 +11,7 @@ ## 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) -- [ ] **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 --- @@ -43,11 +43,11 @@ Plans: 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 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: - [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 -- [ ] 28-03-PLAN.md — Hermes stateJson runtime data and dashboard HermesRuntimeCard +- [x] 28-02-PLAN.md — UI model selector dropdown, install callout, Hermes skill badge +- [x] 28-03-PLAN.md — Hermes stateJson runtime data and dashboard HermesRuntimeCard **UI hint**: yes ### 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 | |-------|-----------|----------------|--------|-----------| | 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 | - | diff --git a/.planning/STATE.md b/.planning/STATE.md index f12f4baa..7633835c 100644 --- a/.planning/STATE.md +++ b/.planning/STATE.md @@ -2,15 +2,15 @@ gsd_state_version: 1.0 milestone: v1.4 milestone_name: milestone -status: executing -stopped_at: Completed 28-ollama-integration-28-01-PLAN.md -last_updated: "2026-04-02T16:56:46.973Z" +status: verifying +stopped_at: Completed 28-ollama-integration-28-03-PLAN.md +last_updated: "2026-04-02T17:05:32.272Z" last_activity: 2026-04-02 progress: total_phases: 3 - completed_phases: 1 + completed_phases: 2 total_plans: 4 - completed_plans: 2 + completed_plans: 4 percent: 0 --- @@ -26,8 +26,8 @@ See: .planning/PROJECT.md (updated 2026-04-02) ## Current Position Phase: 28 (ollama-integration) — EXECUTING -Plan: 2 of 3 -Status: Ready to execute +Plan: 3 of 3 +Status: Phase complete — ready for verification Last activity: 2026-04-02 Progress: [__________] 0% @@ -92,6 +92,7 @@ Progress: [__________] 0% | Phase 26-pwa-performance P04 | 15 | 2 tasks | 10 files | | Phase 27-hermes-adapter P01 | 2 | 3 tasks | 3 files | | Phase 28-ollama-integration P01 | 3 | 2 tasks | 6 files | +| Phase 28 P03 | 12 | 2 tasks | 3 files | ## 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 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]: 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 @@ -194,6 +197,6 @@ None identified yet. ## Session Continuity -Last session: 2026-04-02T16:56:46.970Z -Stopped at: Completed 28-ollama-integration-28-01-PLAN.md +Last session: 2026-04-02T17:05:12.136Z +Stopped at: Completed 28-ollama-integration-28-03-PLAN.md Resume file: None diff --git a/.planning/phases/28-ollama-integration/28-03-SUMMARY.md b/.planning/phases/28-ollama-integration/28-03-SUMMARY.md new file mode 100644 index 00000000..af8b97a2 --- /dev/null +++ b/.planning/phases/28-ollama-integration/28-03-SUMMARY.md @@ -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