From 9d9cbb63c4130040cbb88120e57a7a1af7f63a62 Mon Sep 17 00:00:00 2001 From: dtoro Date: Wed, 8 Jul 2026 13:47:24 +0200 Subject: [PATCH] plans: rename resident agent Hermes -> Nomos, add implementable rename phase N0 Nomos (from oikonomos, steward of the oikos) avoids the name collision with Nous Research's Hermes Agent. N0 enumerates the full rename scope: cmd/, hermes/ dir, env vars, config fields, compose service, Caddy vhost, identity-preserving DB slug migration + seed update, persona docs. History, the Matrix bot user, and legacy bin/hermes stay untouched. Co-Authored-By: Claude Fable 5 --- plans/2026-07-08-control-room-webui.md | 12 +- plans/2026-07-08-hermes-resident-agent.md | 196 ---------------- plans/2026-07-08-nomos-resident-agent.md | 264 ++++++++++++++++++++++ plans/index.md | 2 +- 4 files changed, 271 insertions(+), 203 deletions(-) delete mode 100644 plans/2026-07-08-hermes-resident-agent.md create mode 100644 plans/2026-07-08-nomos-resident-agent.md diff --git a/plans/2026-07-08-control-room-webui.md b/plans/2026-07-08-control-room-webui.md index 384c546..b0fe630 100644 --- a/plans/2026-07-08-control-room-webui.md +++ b/plans/2026-07-08-control-room-webui.md @@ -116,12 +116,12 @@ Persistent nav + live top status strip (health dots, open-signal badge, pending-approval badge): 0. **Agent chat (home view)** — *amendment 2026-07-08, see the - [Hermes resident agent plan](2026-07-08-hermes-resident-agent.md)*: the - default view at `/ui/#/` is a conversation with the resident Hermes agent - (streamed via `/agent/chat`), with a live right rail showing pending - approvals (decidable in place), recent events, and health. A persistent - chat drawer is reachable from every other page. Lands with resident-agent - milestone H3; until then, Overview is the home view. + [Nomos resident agent plan](2026-07-08-nomos-resident-agent.md)*: the + default view at `/ui/#/` is a conversation with the resident Nomos agent + (formerly Hermes; streamed via `/agent/chat`), with a live right rail + showing pending approvals (decidable in place), recent events, and health. + A persistent chat drawer is reachable from every other page. Lands with + resident-agent milestone N3; until then, Overview is the home view. 1. **Overview** — summary cards from `/dashboard/summary`, event-rate sparkline, live event ticker, top degraded entities. 2. **Graph explorer** — d3-force over `/graph`; node color = health; filters diff --git a/plans/2026-07-08-hermes-resident-agent.md b/plans/2026-07-08-hermes-resident-agent.md deleted file mode 100644 index b868c4d..0000000 --- a/plans/2026-07-08-hermes-resident-agent.md +++ /dev/null @@ -1,196 +0,0 @@ -# 2026-07-08 — Hermes resident agent - -**Status:** Planned - -## Goal - -Turn Hermes from a keyword router into a resident LLM-backed agent: a -long-running service you converse with, which reasons over the full 28-tool -MCP surface, holds session context, and is surfaced as the **main entry point -of the control-room web UI** ([companion plan](2026-07-08-control-room-webui.md)). -Oikos already has the agentic substrate — policy classes, approval gating, -`agent_activity`, an OODA loop; this gives it the conversational front half. - -## What exists vs what's added - -Today `cmd/hermes/main.go` contains no LLM anywhere: `routeQuery` is -`strings.Contains` over ~6 phrases, `extractEntity` knows 5 hardcoded -services, and everything else silently falls back to `get_health_summary`. -But the hard plumbing already exists and is reused as-is: - -- `mcpClient` (main.go:190) — working Streamable-HTTP MCP client - (initialize → session → `tools/call` with SSE frame parsing). -- `listTools()` (main.go:318) — currently dead code; extended (below) it - becomes the bridge that feeds MCP tool schemas to Claude. -- `.agents/HERMES.md` / `hermes/SOUL.md` — the persona, becomes the system - prompt. -- `agent_activity` hypertable + `correlation_id` conventions — where tool - calls get logged so the control room can show the agent working. - -## Architecture decisions - -### Hand-rolled tool loop, not a hosted connector or off-the-shelf agent - -Three ways to get an LLM driving the tools; decision is (3): - -1. **Hosted MCP connector** (e.g. Anthropic's `mcp_servers` param): zero - loop code, but the provider's servers must reach the MCP endpoint over - the public internet. `mcp.hubris.network` is currently exposed *without - auth* — a hole the [gaps plan](2026-07-08-oikos-gaps-and-improvements.md) - says to close, not to build on. Keeping the MCP surface mesh-private is - the right posture for a homelab control plane. -2. **Off-the-shelf agent framework** (evaluated: Nous Research's - [Hermes Agent](https://hermes-agent.nousresearch.com/) — MIT, self-hostable, - multi-channel personal assistant with memory/subagents/browsing/code-exec). - Rejected for this role: it is a desktop/personal-assistant framework with - chat-platform frontends, not an embeddable service — there is no clean - `/chat` API for the control-room UI to stream from; the oikos-native - integration (sessions in Postgres, `agent_activity` + correlation-id joins, - approval rail) would not exist; and its browsing/code-exec surface is far - larger than a loop confined to the 28 MCP tools. Note it (or any MCP - client) remains usable as an *external* agent against the MCP endpoint — - that works today and is orthogonal to the resident agent. -3. **Hand-rolled agent loop** (~200 lines): Hermes calls the LLM API with - tool definitions translated from `tools/list`, executes returned tool - calls through its *existing* `mcpClient` inside the mesh, feeds results - back, repeats until the model answers in text. Only conversation text - leaves the mesh; the tool transport stays private; the agent is confined - to the MCP surface by construction. - -### LLM provider: OpenRouter, default model DeepSeek V4 Flash - -- **OpenRouter** (OpenAI-compatible Chat Completions API) rather than a - single-vendor SDK: use the official `openai-go` client with - `base_url=https://openrouter.ai/api/v1` and `OPENROUTER_API_KEY`. Model - choice becomes one env var; any OpenRouter model can be trialed. -- **Default model: `deepseek/deepseek-v4-flash`** — supports tool calling, - 1M context, $0.09/M input / $0.18/M output. Use OpenRouter's **Exacto** - routing (highest measured tool-calling accuracy) since tool calls are this - agent's entire job; low temperature; strict system prompt. -- MCP tool `inputSchema` is already JSON Schema — exactly what - `tools[].function.parameters` expects — so translation is a field rename. -- **Privacy:** conversation text and tool results (hostnames, log excerpts) - transit OpenRouter and the upstream provider. Pin OpenRouter provider - preferences to ZDR / no-training providers in the request payload. -- A flash-class MoE will be weaker than frontier models on long multi-step - chains; mitigations are the iteration cap, Exacto routing, and bumping - `HERMES_MODEL` per-deployment when a task warrants it. - -## Design - -### Agent loop (`cmd/hermes/agent.go`, new) - -``` -system prompt = SOUL.md content (mounted; already provisioned by tools/setup-hermes-soul.sh) -tools = listToolsFull() // extend listTools() to return name, description, inputSchema -loop (max 15 iterations): - resp = chat.completions (OpenRouter, model, system, history, tools) - if resp has tool_calls: - for each: result = mcpClient.callTool(name, args) // existing code path - log to agent_activity (correlation_id = session turn id) - append assistant msg + tool-role result msgs to history - else: final text → stream to caller, persist turn -``` - -- Model: `deepseek/deepseek-v4-flash` default, `HERMES_MODEL` override. - `max_tokens` and iteration cap configurable; hard per-turn budget so a - pathological loop can't burn the API bill. -- `OPENROUTER_API_KEY` from env / Infisical (same secret path as other creds). -- Mutations need no new guardrails: the agent's only write path is - `request_execution`, which flows through the existing risk-class / - approval machinery. The agent's actor identity is `agent:hermes`. - **Depends on gaps-plan bug A1** (approvals FK) — without that fix the - agent's config mutations dead-end silently, which is worse when a - conversational agent confidently reports "queued for approval". - -### HTTP surface (`cmd/hermes/main.go`) - -- `POST /chat` `{session_id?, message}` → **SSE stream** of typed events: - `text` (deltas), `tool_use` (name + args), `tool_result` (truncated), - `done` (session_id, usage). The web UI renders tool calls as inline chips - as they happen. -- `GET /sessions`, `GET /sessions/{id}` — history for the UI. -- `/query` kept for scripts/structured callers. The toy NLU is **removed** - (per gaps plan §C): a `query` with no `tool` returns "natural language - belongs to /chat" plus the tool list from the now-live `listTools()`. -- `/healthz` unchanged. - -### Sessions (Postgres, new migration) - -`agent_sessions (id, title, actor, created_at, last_active_at)` and -`agent_messages (id, session_id, role, content JSONB, created_at)` in the -shared oikos DB — Hermes gains a `DATABASE_URL` (it's in the same compose -stack). DB-backed rather than in-memory so conversations survive restarts -and the control room can list/replay them. Tool invocations additionally go -to `agent_activity` with the session's correlation_id, so the existing -agent-activity page and the ops ledger join up with zero new query paths. - -### Connectivity & auth - -- Compose: hermes already runs under the `full` profile; add - `OPENROUTER_API_KEY`, `HERMES_MODEL`, `DATABASE_URL` env. -- Caddy: route `oikos.hubris.network/agent/*` → `hermes:8092` **behind the - same Authentik forward_auth** as the rest of the vhost, stripping the - `/agent` prefix. The web UI then calls same-origin `/agent/chat` — no - CORS, and EventSource/fetch-streaming work unmodified. -- Hermes enforces trusted-proxy headers (`X-Authentik-Username`) when - `HERMES_TRUSTED_PROXY=true`, and finally implements the `mesh_only: true` - check that `hermes/config.yaml:9` promises but `main.go` never enforces - (gaps plan B3). Direct :8092 access stays mesh-only for agents/scripts. - -### Web UI entry point (amends the control-room plan) - -The agent chat becomes the **home view** of the control room (`/ui/#/`): - -- Center: conversation pane (streamed text, expandable tool-call chips - showing args/results, correlation-id links into the ops ledger). -- Right rail: live context — pending approvals with approve/deny buttons, - recent events, health strip. When the agent's `request_execution` needs - approval, the approval card appears in the rail *mid-conversation* (via - the `approval.created` SSE event) and can be decided without leaving chat. - That's the whole product in one screen: ask → watch it act → approve → - watch it complete. -- A persistent chat drawer is available from every other page. -- Session list in the nav; sessions resumable. - -### Later (explicitly out of scope for v1) - -- Matrix bridge: Hermes as a Matrix bot in the operator room, reusing the - notifier's homeserver credentials — same `/chat` loop, different frontend. -- Proactive mode: agent opens a session itself when a signal fires - (escalation-with-context instead of a bare alert). - -## Milestones - -- **H1 — loop:** `agent.go` + openai-go (OpenRouter base URL); `listToolsFull()`; `/chat` - (non-streaming JSON first); remove toy NLU, fix `/query` fallback + help. - Verify by curl: multi-tool question ("what's degraded and what depends on - it?") produces chained `get_health_summary` → `get_blast_radius` calls. -- **H2 — state + streaming:** sessions migration, DB persistence, SSE - streaming on `/chat`, `agent_activity` logging, budgets. -- **H3 — UI entry point:** home chat view + context rail + drawer in the - control-room SPA (needs control-room M1; rail approvals need M2's - `approval.created` event and gaps-plan A1). -- **H4 (optional):** Matrix bridge, proactive sessions. - -## Files - -``` -cmd/hermes/agent.go # Claude loop (new) -cmd/hermes/main.go # /chat, /sessions, NLU removal, mesh/proxy auth -cmd/hermes/store.go # session persistence (new) -migrations/0xx_agent_sessions.up.sql -hermes/config.yaml # model, budgets; drop query_routing block -compose/ (env), Caddyfile.oikos (/agent route) -web/src/pages/Chat.svelte + lib/stores/chat.ts # control-room home view -go.mod # openai-go (OpenRouter-compatible client) -``` - -## Verification - -- H1: `curl -N /agent/chat` with a question requiring 2+ tools; confirm the - tool chain in the response and rows in `agent_activity`. -- H2: restart hermes mid-session, resume by session_id, history intact. -- H3: from the UI home, ask Hermes to restart a low-risk service; watch the - tool chips stream, the execution appear in the rail, and (for a gated - action) the approval card arrive and be decidable in place. diff --git a/plans/2026-07-08-nomos-resident-agent.md b/plans/2026-07-08-nomos-resident-agent.md new file mode 100644 index 0000000..080c938 --- /dev/null +++ b/plans/2026-07-08-nomos-resident-agent.md @@ -0,0 +1,264 @@ +# 2026-07-08 — Nomos resident agent (renames Hermes) + +**Status:** Planned + +## Goal + +Rename the gateway formerly known as **Hermes** to **Nomos** (from +*oikonomos*, the steward of the oikos — and avoiding confusion with Nous +Research's unrelated "Hermes Agent" product), and turn it from a keyword +router into a resident LLM-backed agent: a long-running service you converse +with, which reasons over the full 28-tool MCP surface, holds session context, +and is surfaced as the **main entry point of the control-room web UI** +([companion plan](2026-07-08-control-room-webui.md)). Oikos already has the +agentic substrate — policy classes, approval gating, `agent_activity`, an +OODA loop; this gives it the conversational front half. + +## What exists vs what's added + +Today `cmd/hermes/main.go` contains no LLM anywhere: `routeQuery` is +`strings.Contains` over ~6 phrases, `extractEntity` knows 5 hardcoded +services, and everything else silently falls back to `get_health_summary`. +But the hard plumbing already exists and is reused as-is: + +- `mcpClient` (main.go:190) — working Streamable-HTTP MCP client + (initialize → session → `tools/call` with SSE frame parsing). +- `listTools()` (main.go:318) — currently dead code; extended (below) it + becomes the bridge that feeds MCP tool schemas to the LLM. +- `.agents/HERMES.md` / `hermes/SOUL.md` — the persona, becomes the system + prompt (renamed in N0). +- `agent_activity` hypertable + `correlation_id` conventions — where tool + calls get logged so the control room can show the agent working. + +## Milestone N0 — the rename (Hermes → Nomos) + +Do this first, as its own commit, before any agent code lands. Scope is the +**live service identity**; history (`archive/`, `plans/done/`, past +investigations) is never rewritten. + +**Code & build** +- `cmd/hermes/` → `cmd/nomos/` (binary `nomos`; keep `serve` subcommand). +- Env vars in main.go: `HERMES_MCP_URL`, `HERMES_LISTEN`, + `HERMES_AGENT_SLUG` → `NOMOS_*`. Default slug `agent:nomos`; + `clientInfo.name` → `nomos`. +- `internal/config/config.go`: `HermesAgentSlug` field + + `OIKOS_HERMES_AGENT_SLUG` → `NomosAgentSlug` / `OIKOS_NOMOS_AGENT_SLUG`; + `hermesAgentID` in `internal/httpapi/server.go:140-149` renamed to match. +- `hermes/` directory → `nomos/` (`SOUL.md`, `config.yaml`, + `skills/homelab-ops/`); `tools/setup-hermes-soul.sh` → + `tools/setup-nomos-soul.sh` with paths updated. + +**Deploy** +- `compose/hermes/Dockerfile` → `compose/nomos/Dockerfile`; docker-compose + service `hermes:` → `nomos:` (docker-compose.yml:111, env at :63 and :121). +- Caddy: `hermes.hubris.network` vhost → `nomos.hubris.network` + (compose/caddy/Caddyfile.oikos:26); keep the old hostname as a `redir` + block for one transition window; add the DNS record. (These live in the + external caddy-conf repo / DNS — operator step.) + +**Data (identity-preserving — do NOT create a new entity)** +- Live DB: `UPDATE entities SET slug='agent:nomos', + attributes = jsonb_set(attributes,'{name}','"nomos"') WHERE + slug='agent:hermes';` — relationships/audit reference the UUID, so history + survives the slug change. Ship as a migration so every environment gets it. +- `seeds/inventory.yaml:328` (`agent:hermes` entity) and `:541` (owns + relationship) → `agent:nomos`. Seed upserts by slug, so the migration must + run before seeding or the seed would mint a duplicate entity. + +**Docs & persona** +- `.agents/HERMES.md` → `.agents/NOMOS.md`; update every referencing doc + (`AGENTS.md`, `CLIENTS.md`, `README.md`, `CONTRIBUTING.md`, + `.agents/operations/hermes-agent.md` → `nomos-agent.md`, + `.agents/operations/commands.md`, skills docs). +- ADR-0012 (`docs/adr/0012-hermes-oikos-interactions.md`): append a + "renamed to Nomos" note; don't rewrite the ADR. + +**Explicitly out of scope** +- Matrix user `@hermes:hubris.network` (docker-compose.yml:103) — that's the + notifier's homeserver account; renaming it is an operational Matrix task, + optional and later. +- Legacy `bin/hermes` wrapper and `hermesd` on LXC 129 (referenced in + `.sops.yaml:89,110`) — separate legacy systems, untouched. Note: that + `.sops.yaml` entry shows an **OpenRouter API key path already exists** in + the secrets tree — reuse it for N1. +- Historical knowledge/investigation pages and executed plans. + +**Verify N0:** `go build ./...`; `docker compose --profile full config` +resolves; `curl nomos.hubris.network/healthz` (and the old vhost redirects); +`SELECT slug FROM entities WHERE slug LIKE 'agent:%'` shows `agent:nomos` +with its original UUID; `grep -ri hermes` returns only history/ADR/legacy +hits. + +## Architecture decisions + +### Hand-rolled tool loop, not a hosted connector or off-the-shelf agent + +Three ways to get an LLM driving the tools; decision is (3): + +1. **Hosted MCP connector** (e.g. Anthropic's `mcp_servers` param): zero + loop code, but the provider's servers must reach the MCP endpoint over + the public internet. `mcp.hubris.network` is currently exposed *without + auth* — a hole the [gaps plan](2026-07-08-oikos-gaps-and-improvements.md) + says to close, not to build on. Keeping the MCP surface mesh-private is + the right posture for a homelab control plane. +2. **Off-the-shelf agent framework** (evaluated: Nous Research's + [Hermes Agent](https://hermes-agent.nousresearch.com/) — MIT, self-hostable, + multi-channel personal assistant with memory/subagents/browsing/code-exec; + also the naming-collision motivation for N0). Rejected for this role: it + is a desktop/personal-assistant framework with chat-platform frontends, + not an embeddable service — there is no clean `/chat` API for the + control-room UI to stream from; the oikos-native integration (sessions in + Postgres, `agent_activity` + correlation-id joins, approval rail) would + not exist; and its browsing/code-exec surface is far larger than a loop + confined to the 28 MCP tools. It (or any MCP client) remains usable as an + *external* agent against the MCP endpoint — orthogonal to the resident. +3. **Hand-rolled agent loop** (~200 lines): Nomos calls the LLM API with + tool definitions translated from `tools/list`, executes returned tool + calls through its *existing* `mcpClient` inside the mesh, feeds results + back, repeats until the model answers in text. Only conversation text + leaves the mesh; the tool transport stays private; the agent is confined + to the MCP surface by construction. + +### LLM provider: OpenRouter, default model DeepSeek V4 Flash + +- **OpenRouter** (OpenAI-compatible Chat Completions API) rather than a + single-vendor SDK: use the official `openai-go` client with + `base_url=https://openrouter.ai/api/v1` and `OPENROUTER_API_KEY` (secret + path already exists in `.sops.yaml`). Model choice becomes one env var. +- **Default model: `deepseek/deepseek-v4-flash`** — supports tool calling, + 1M context, $0.09/M input / $0.18/M output. Use OpenRouter's **Exacto** + routing (highest measured tool-calling accuracy) since tool calls are this + agent's entire job; low temperature; strict system prompt. +- MCP tool `inputSchema` is already JSON Schema — exactly what + `tools[].function.parameters` expects — so translation is a field rename. +- **Privacy:** conversation text and tool results (hostnames, log excerpts) + transit OpenRouter and the upstream provider. Pin OpenRouter provider + preferences to ZDR / no-training providers in the request payload. +- A flash-class MoE will be weaker than frontier models on long multi-step + chains; mitigations are the iteration cap, Exacto routing, and bumping + `NOMOS_MODEL` per-deployment when a task warrants it. + +## Design + +### Agent loop (`cmd/nomos/agent.go`, new) + +``` +system prompt = SOUL.md content (mounted; provisioned by tools/setup-nomos-soul.sh) +tools = listToolsFull() // extend listTools() to return name, description, inputSchema +loop (max 15 iterations): + resp = chat.completions (OpenRouter, model, system, history, tools) + if resp has tool_calls: + for each: result = mcpClient.callTool(name, args) // existing code path + log to agent_activity (correlation_id = session turn id) + append assistant msg + tool-role result msgs to history + else: final text → stream to caller, persist turn +``` + +- Model: `deepseek/deepseek-v4-flash` default, `NOMOS_MODEL` override. + `max_tokens` and iteration cap configurable; hard per-turn budget so a + pathological loop can't burn the API bill. +- `OPENROUTER_API_KEY` from env / Infisical / SOPS (existing path). +- Mutations need no new guardrails: the agent's only write path is + `request_execution`, which flows through the existing risk-class / + approval machinery. The agent's actor identity is `agent:nomos`. + **Depends on gaps-plan bug A1** (approvals FK) — without that fix the + agent's config mutations dead-end silently, which is worse when a + conversational agent confidently reports "queued for approval". + +### HTTP surface (`cmd/nomos/main.go`) + +- `POST /chat` `{session_id?, message}` → **SSE stream** of typed events: + `text` (deltas), `tool_use` (name + args), `tool_result` (truncated), + `done` (session_id, usage). The web UI renders tool calls as inline chips + as they happen. +- `GET /sessions`, `GET /sessions/{id}` — history for the UI. +- `/query` kept for scripts/structured callers. The toy NLU is **removed** + (per gaps plan §C): a `query` with no `tool` returns "natural language + belongs to /chat" plus the tool list from the now-live `listTools()`. +- `/healthz` unchanged. + +### Sessions (Postgres, new migration) + +`agent_sessions (id, title, actor, created_at, last_active_at)` and +`agent_messages (id, session_id, role, content JSONB, created_at)` in the +shared oikos DB — Nomos gains a `DATABASE_URL` (it's in the same compose +stack). DB-backed rather than in-memory so conversations survive restarts +and the control room can list/replay them. Tool invocations additionally go +to `agent_activity` with the session's correlation_id, so the existing +agent-activity page and the ops ledger join up with zero new query paths. + +### Connectivity & auth + +- Compose: the `nomos` service (renamed in N0, `full` profile) gains + `OPENROUTER_API_KEY`, `NOMOS_MODEL`, `DATABASE_URL` env. +- Caddy: route `oikos.hubris.network/agent/*` → `nomos:8092` **behind the + same Authentik forward_auth** as the rest of the vhost, stripping the + `/agent` prefix. The web UI then calls same-origin `/agent/chat` — no + CORS, and EventSource/fetch-streaming work unmodified. +- Nomos enforces trusted-proxy headers (`X-Authentik-Username`) when + `NOMOS_TRUSTED_PROXY=true`, and finally implements the `mesh_only: true` + check that the config promises but `main.go` never enforces (gaps plan + B3). Direct :8092 access stays mesh-only for agents/scripts. + +### Web UI entry point (amends the control-room plan) + +The agent chat becomes the **home view** of the control room (`/ui/#/`): + +- Center: conversation pane (streamed text, expandable tool-call chips + showing args/results, correlation-id links into the ops ledger). +- Right rail: live context — pending approvals with approve/deny buttons, + recent events, health strip. When the agent's `request_execution` needs + approval, the approval card appears in the rail *mid-conversation* (via + the `approval.created` SSE event) and can be decided without leaving chat. + That's the whole product in one screen: ask → watch it act → approve → + watch it complete. +- A persistent chat drawer is available from every other page. +- Session list in the nav; sessions resumable. + +### Later (explicitly out of scope for v1) + +- Matrix bridge: Nomos as a Matrix bot in the operator room, reusing the + notifier's homeserver credentials — same `/chat` loop, different frontend. +- Proactive mode: agent opens a session itself when a signal fires + (escalation-with-context instead of a bare alert). + +## Milestones + +- **N0 — rename:** see above; standalone commit, deployable on its own. +- **N1 — loop:** `agent.go` + openai-go (OpenRouter base URL); + `listToolsFull()`; `/chat` (non-streaming JSON first); remove toy NLU, fix + `/query` fallback + help. Verify by curl: multi-tool question ("what's + degraded and what depends on it?") produces chained + `get_health_summary` → `get_blast_radius` calls. +- **N2 — state + streaming:** sessions migration, DB persistence, SSE + streaming on `/chat`, `agent_activity` logging, budgets. +- **N3 — UI entry point:** home chat view + context rail + drawer in the + control-room SPA (needs control-room M1; rail approvals need M2's + `approval.created` event and gaps-plan A1). +- **N4 (optional):** Matrix bridge, proactive sessions. + +## Files + +``` +cmd/nomos/{main.go, agent.go, store.go} # renamed + loop + sessions +internal/config/config.go, internal/httpapi/server.go # slug config rename +nomos/{SOUL.md, config.yaml, skills/} # renamed dir; drop query_routing +tools/setup-nomos-soul.sh +migrations/0xx_rename_agent_hermes_to_nomos.up.sql +migrations/0xx_agent_sessions.up.sql +seeds/inventory.yaml +compose/nomos/Dockerfile, docker-compose.yml, Caddyfile.oikos (/agent route, vhost) +.agents/NOMOS.md + referencing docs; docs/adr/0012 note +web/src/pages/Chat.svelte + lib/stores/chat.ts # control-room home view +go.mod # openai-go +``` + +## Verification + +- N0: see milestone N0 verify list. +- N1: `curl -N /agent/chat` with a question requiring 2+ tools; confirm the + tool chain in the response and rows in `agent_activity`. +- N2: restart nomos mid-session, resume by session_id, history intact. +- N3: from the UI home, ask Nomos to restart a low-risk service; watch the + tool chips stream, the execution appear in the rail, and (for a gated + action) the approval card arrive and be decidable in place. diff --git a/plans/index.md b/plans/index.md index 3e1ffbf..f06dff4 100644 --- a/plans/index.md +++ b/plans/index.md @@ -12,7 +12,7 @@ went sideways, open an investigation. | 2026-07-08 | [Plan vs implementation cross-reference](2026-07-08-plan-implementation-audit.md) | Planned | | 2026-07-08 | [Oikos gaps, broken things, and improvements](2026-07-08-oikos-gaps-and-improvements.md) | Planned | | 2026-07-08 | [Control room web UI](2026-07-08-control-room-webui.md) | Planned | -| 2026-07-08 | [Hermes resident agent](2026-07-08-hermes-resident-agent.md) | Planned | +| 2026-07-08 | [Nomos resident agent (renames Hermes)](2026-07-08-nomos-resident-agent.md) | Planned | ## Done