Files
oikos/plans/2026-07-09-session-execution-and-ux-fixes.md
dtoro ea62d744ed
Some checks failed
ci / build-test (push) Has been cancelled
ci / docker-build (push) Has been cancelled
feat: pct_create action, ToolCallGroup collapse+animation, inline chat approval
- 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.
2026-07-09 11:15:28 +02:00

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'trequest_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.