nexus/.planning/phases/28-ollama-integration/28-03-SUMMARY.md
Nexus Dev 93b9fa2dbd docs(28-03): complete Hermes runtime dashboard plan — stateJson merge, HermesRuntimeCard
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-02 17:05:32 +00:00

5.2 KiB

phase plan subsystem tags requires provides affects tech-stack key-files key-decisions patterns-established requirements-completed duration completed
28-ollama-integration 03 dashboard
hermes
ollama
stateJson
jsonb
heartbeat
dashboard
phase provides
28-01 ollama.ts service with detectOllama, listOllamaModels, getRecommendedModel
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
28-02
29-default-provider
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)
created modified
server/src/services/ollama.ts
server/src/services/heartbeat.ts
ui/src/pages/AgentDetail.tsx
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
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
HERM-06
HERM-07
12min 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:

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