| 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 |
|
|
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