- Add pct_create to request_execution (MCP) and executeApprovedAction (httpapi) Parses JSON config: vmid, hostname, cores, memory, disk_gb, ip, gw, storage, template, privileged, nesting, mounts, nameserver, searchdomain. Creates entity (state=provisioning), hosts relationship, entity_status on success. Fixes action string parsing to use Index instead of SplitN (colons in JSON). - Rewrite ToolCallGroup.svelte: bits-ui Collapsible replaces native <details>. Collapsed by default. Animated header shows live tool count + running tool name while streaming. Auto-expands during streaming, auto-collapses on done. - Add InlineApproval component: parses 'execution UUID queued' from agent response, renders Approve/Deny buttons inline in chat, calls decideApproval. - Document pct_create in nomos/SOUL.md with params, risk class, and approval flow. - Add session-review skill at .agents/skills/session-review/SKILL.md. - Add plan: 2026-07-09-session-execution-and-ux-fixes.md.
157 lines
7.9 KiB
Markdown
157 lines
7.9 KiB
Markdown
# 2026-07-09 — Session execution, UX, and learning improvements
|
|
|
|
**Status:** Planned
|
|
|
|
## Goal
|
|
|
|
Fix the hard blocker and UX issues found in the latest production Nomos session
|
|
(`b9c5de7c-e0b5-424c-9149-9fd45b2e7011` — "deploy TypeType as an LXC on strong").
|
|
The user asked Nomos to deploy an LXC; Nomos gathered data, proposed a plan, but
|
|
at the final step ("run all yourself") said it *couldn't* — `request_execution`
|
|
has no `pct_create` action. The user's objective was not achieved.
|
|
|
|
## Session analysis
|
|
|
|
10 messages (5 user / 5 assistant), 150 tool calls, zero LXC created.
|
|
|
|
| Msg | Role | Tool calls | Top tools | Summary |
|
|
|-----|------|-----------|-----------|---------|
|
|
| 0 | user | 0 | — | "deploy TypeType on strong as LXC" |
|
|
| 1 | assistant | 40 | get_entity(20), search_knowledge(12) | Fleet scan, entity detail per host/LXC |
|
|
| 2 | user | 0 | — | "it's github.com/Priveetee/TypeType, use tube.hubris.network" |
|
|
| 3 | assistant | 2 | search_knowledge(2) | MCP has no web fetch tool → couldn't read GitHub |
|
|
| 4 | user | 0 | — | "I think you can figure out those" |
|
|
| 5 | assistant | 60 | search_knowledge(32), request_execution(14) | Built the plan, tried provisioning, failed silently |
|
|
| 6 | user | 0 | — | "connect to strong media disk, no youtube login, proceed" |
|
|
| 7 | assistant | 44 | search_knowledge(10), get_entity(10), request_execution(8) | Full deployment plan laid out |
|
|
| 8 | user | 0 | — | "run all yourself" |
|
|
| 9 | assistant | 4 | request_execution(4) | **"I can't run pct create — only pct_exec, systemctl, restart, apt_upgrade"** — failure |
|
|
|
|
## Findings
|
|
|
|
### 1. HARD BLOCKER: `pct_create` missing from `request_execution`
|
|
|
|
`request_execution` (`internal/mcp/server.go:264-376`) supports `restart`,
|
|
`systemctl`, `pct_exec`, `apt_upgrade`. The actuator has `ProvisionLXC()`
|
|
(`internal/actuator/actuator.go:209`) that calls `pct create` via SSH, but it
|
|
is only wired into the auto-act signal pipeline — Nomos has no way to invoke
|
|
it through MCP.
|
|
|
|
Nomos's final response laid out the exact `pct create` command the operator
|
|
needs to run manually on the Proxmox host. That's a dead end for the user, who
|
|
expected the agent to execute from chat.
|
|
|
|
**Fix:**
|
|
- Add `pct_create` action to `request_execution` handler.
|
|
- Wire it to the existing `ProvisionLXC()` function.
|
|
- Classification: `config_mutation` — requires operator approval. Once approved
|
|
(from the Ops page or Matrix), the actuator picks it up and provisions the
|
|
LXC with the step callback reporting progress.
|
|
- Alternatively (for the "run from chat" expectation): add a chat-level approval
|
|
flow — when Nomos proposes `request_execution` with `pct_create`, the frontend
|
|
renders an inline "Approve" button in the chat bubble. Operator clicks → it runs.
|
|
This is the UX the user described: "allow the agent to run things directly from
|
|
the chat I'm in."
|
|
|
|
### 2. UI: ToolCallGroup expanded by default during streaming — no status animation
|
|
|
|
`ToolCallGroup.svelte` uses `<details open>` when `active=true`. During a
|
|
streaming turn with 40+ tool calls, the collapsed group fills the viewport
|
|
with raw JSON. The summary header shows only a static icon and "N tools" text.
|
|
|
|
**What happens now:**
|
|
- Streaming starts → group opens and stays open → all tool results visible as raw JSON.
|
|
- When streaming ends → auto-collapses. No animation.
|
|
- Header shows `WrenchIcon` pulsing OR `CheckIcon` OR `XIcon` — but no live
|
|
running count, no per-tool status in the collapsed summary bar.
|
|
|
|
**What should happen:**
|
|
- Group starts **collapsed** by default. The summary header shows a live animated
|
|
status: "⠋ Running get_lxc_state (caddy)… [2/40 done]" with the active tool
|
|
name + a progress fraction, updating in real time.
|
|
- When a tool completes, the header briefly reflects it ("✓ get_lxc_state (caddy)")
|
|
before moving to the next.
|
|
- Clicking the summary expands the group with a smooth animated open/close
|
|
(replacing native `<details>` with bits-ui `Collapsible` + CSS transition).
|
|
- On load from history (not streaming), always starts collapsed.
|
|
|
|
**Fix:**
|
|
- Replace `<details open>` with bits-ui `Collapsible` component (already in
|
|
`web/src/lib/components/ui/collapsible/`).
|
|
- Add `animate-pulse` to the chevron icon during streaming (user sees motion).
|
|
- Add a `statusText` derived that shows the in-progress tool name + count.
|
|
- CSS animation: `Collapsible.Content` supports `forceMount` with transitions.
|
|
|
|
### 3. No web-fetch tool → Nomos can't read GitHub READMEs
|
|
|
|
Msg 3: Nomos needed to inspect `github.com/Priveetee/TypeType` to understand the
|
|
stack. It used `search_knowledge` (DB FTS), which returned nothing because the
|
|
repo isn't in the DB. The agent had no way to fetch external URLs.
|
|
|
|
The MCP has 27 tools (21 listed in AGENTS.md + 6 more added since), but none
|
|
for HTTP/web fetching. Nomos can only query the DB or execute SSH commands on
|
|
existing hosts.
|
|
|
|
This forced the human to provide context that should have been machine-read.
|
|
|
|
**Fix options (non-blocking):**
|
|
- Add an `http_get` MCP tool that returns sanitized body text (strip scripts,
|
|
truncate to 8KB). Rate-limited per-turn.
|
|
- Or: add `web_fetch` as a first-class action in the MCP gateway itself, since
|
|
the gateway container already makes outbound HTTP calls to OpenRouter.
|
|
|
|
### 4. Task: Bulk-tool awareness already in SOUL.md but not enough
|
|
|
|
The SOUL.md already says "prefer `list_lxcs` over `get_lxc_state` for fleet-wide"
|
|
(line 27-28). But msg 1 still made 40 calls. The issue:
|
|
|
|
- `get_entity` was called 20 times (one per entity found by `list_entities`).
|
|
`list_entities` already returns all entities; the agent wanted per-entity
|
|
detail, which is redundant since `explain` or `get_state_snapshot` gives the
|
|
same info in one call.
|
|
|
|
**Fix (already planned in `2026-07-09-chat-sessions-improvements.md` finding 3):**
|
|
- Enrich `list_lxcs` with CPU/memory utilization so the model doesn't feel it
|
|
needs `get_lxc_state` per container.
|
|
- Add `get_tools_summary` to SOUL.md preamble that lists each tool's intended
|
|
use and warns about N+1 call patterns.
|
|
|
|
### 5. Skill gaps
|
|
|
|
| Missing | Why | Where to add |
|
|
|---------|-----|-------------|
|
|
| `http_get` / `web_fetch` | Agent can't read external URLs | MCP tool in `internal/mcp/server.go` |
|
|
| `session-review` skill | No way to learn from failed sessions | `.agents/skills/session-review/SKILL.md` |
|
|
| `pct_create` action | Can't provision new LXCs from chat | `internal/mcp/server.go` + `internal/actuator/` |
|
|
| `request_execution` approval from chat | Operator must switch to Ops page | Inline chat approval component |
|
|
|
|
## Implementation order
|
|
|
|
1. **Add `pct_create` to `request_execution`** (`internal/mcp/server.go`) —
|
|
hard blocker, needed for the session's objective.
|
|
2. **ToolCallGroup: collapse by default + animated status header**
|
|
(`web/src/lib/components/ToolCallGroup.svelte`) — immediate UX win, the user
|
|
explicitly asked for this.
|
|
3. **Inline chat approval for `request_execution`** — renders an "Approve"/"Deny"
|
|
button inside the chat when an execution is queued for approval. Lets the
|
|
operator approve from the same chat.
|
|
4. **`session-review` local skill** — lives in `.agents/skills/`, loads when
|
|
examining chat sessions for failure patterns.
|
|
5. **`http_get` MCP tool** — non-blocking but addresses a real gap seen in this
|
|
session.
|
|
|
|
## Verification
|
|
|
|
- Re-run the TypeType deploy prompt against the patched agent. Confirm:
|
|
- Agent proposes plan as before (keep plan-proposal behavior).
|
|
- When user says "run all", agent calls `request_execution(target=lxc:typetype,
|
|
action=pct_create, params=<json>)`.
|
|
- Approval fires → operator sees inline approval in chat → approves → LXC created.
|
|
- ToolCallGroup: start a session that triggers 5+ tool calls. Confirm:
|
|
- Group starts collapsed.
|
|
- Summary header shows animated status: tool name + count updating in real time.
|
|
- Clicking expands smoothly.
|
|
- On session reload (history), stays collapsed.
|
|
- Load `session-review` skill and ask it to analyze the TypeType failure session;
|
|
confirm it identifies the missing action as the root cause.
|