- 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.
7.9 KiB
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_createaction torequest_executionhandler. - 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_executionwithpct_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
WrenchIconpulsing ORCheckIconORXIcon— 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-uiCollapsible+ CSS transition). - On load from history (not streaming), always starts collapsed.
Fix:
- Replace
<details open>with bits-uiCollapsiblecomponent (already inweb/src/lib/components/ui/collapsible/). - Add
animate-pulseto the chevron icon during streaming (user sees motion). - Add a
statusTextderived that shows the in-progress tool name + count. - CSS animation:
Collapsible.ContentsupportsforceMountwith 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_getMCP tool that returns sanitized body text (strip scripts, truncate to 8KB). Rate-limited per-turn. - Or: add
web_fetchas 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_entitywas called 20 times (one per entity found bylist_entities).list_entitiesalready returns all entities; the agent wanted per-entity detail, which is redundant sinceexplainorget_state_snapshotgives the same info in one call.
Fix (already planned in 2026-07-09-chat-sessions-improvements.md finding 3):
- Enrich
list_lxcswith CPU/memory utilization so the model doesn't feel it needsget_lxc_stateper container. - Add
get_tools_summaryto 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
- Add
pct_createtorequest_execution(internal/mcp/server.go) — hard blocker, needed for the session's objective. - ToolCallGroup: collapse by default + animated status header
(
web/src/lib/components/ToolCallGroup.svelte) — immediate UX win, the user explicitly asked for this. - 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. session-reviewlocal skill — lives in.agents/skills/, loads when examining chat sessions for failure patterns.http_getMCP 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-reviewskill and ask it to analyze the TypeType failure session; confirm it identifies the missing action as the root cause.