- Activity bar moved to bottom of message list (before messagesEnd) - Smart scroll: auto-scroll only during streaming or when near bottom - Scrolling up pauses auto-scroll until next send - Removed duplicate $effect block - Plan: tool timeline in sidebar (plans/2026-07-14-tool-timeline-sidebar.md)
4.4 KiB
2026-07-14 — Tool timeline in sidebar + session analysis
Status: Planned
Session analysis: 1d614a1f (2026-07-14T09:21)
"Audit all homelab hosts and active LXCs for pending apt updates"
What went well
list_lxcs(state="active")worked — only returned live LXCs (our Phase 5 fix)- Agent discovered that
apt-get updateon host targets gets classified asconfig_mutation(modifies apt cache) — all 9 needed approval, user bulk-approved - 162 tool calls across 4 turns: 30 + 78 + 0 (auto-resume fail) + 54 = legitimate fleet audit
- Our Phase 1 fix worked: 3 auto-resume failures persisted "task is paused" notes instead of auto-failing
What failed
- Agent never called
complete_task. Session status stuck at"planning"despite:set_goalcalled in first turn- 162 tool calls executed
- Final message has full audit text
- But
propose_plannever cleared the gate — the agent entered planning and never left
- The 3 auto-resume failures filled the transcript with
[System: auto-resume failed…]noise
Root cause: apt-get update triggers config_mutation classification
- The command classifier correctly treats
apt-get updateas state-changing (it writes to the apt cache) - But the agent just wanted to READ package lists. The audit batch of 9
apt-get updatecalls all needed approval - Lesson:
apt-get updateshould be in a separate audit/update pair where the audit phase uses a read-only inspection command (e.g.apt list --upgradabledoesn't mutate cache)
Tool usage pattern
- 30 tool calls in turn 1 (set_goal, propose_plan, list_lxcs, run × 9 for apt update on hosts, update_plan_step × 8, ask_operator)
- 78 tool calls in turn 3 (run × 25+ for apt audits, get_execution_status × 10+, update_plan_step × 10)
- 54 tool calls in turn 7 (final result synthesis)
These 162 tool calls are ALL rendered inline in chat today. The operator sees a massive wall of collapsed ToolCallGroup entries.
Plan
1. Scroll fixes (DONE above)
- Activity bar moved to bottom of messages (before messagesEnd)
- Smart scroll: auto-scroll only during streaming OR when user is near bottom
- Scrolling up pauses auto-scroll until user sends a new message
2. Tool timeline in sidebar
Goal: Decouple tool execution noise from conversation. Chat shows agent's thinking; sidebar shows what it's doing.
2.1 — Chat: compact tool indicator
Replace the full ToolCallGroup in chat with a single compact line:
[N tools used — view in Activity]
- Clicking it opens/highlights the sidebar timeline
- Pending approvals still show inline in chat (InlineApproval stays)
- Inline tool renderers (entity cards, health summary, etc.) stay — they're informational, not noise
2.2 — Sidebar: live tool timeline
The "This session" section (SessionDigest) becomes a live tool timeline:
- Each agent turn gets a timestamp header
- Within each turn: tool calls shown as a compact list with status icons (running spinner / done check / failed X)
- Tool names are the same compact format from ToolCallGroup
- Results stay collapsible (click to expand)
- Auto-scrolls to latest, but doesn't force-follow if user is reading history
- UPDATEs live (no reload needed) — same polling mechanism as SessionDigest
2.3 — Sidebar: merge plan steps
PlanProgress and SessionDigest merge into one "Activity" panel:
- Top: plan steps with progress bar (from PlanProgress)
- Middle: live tool timeline (from SessionDigest)
- Bottom: knowledge created this session (from SessionDigest)
3. complete_task enforcement (session finding)
The agent called set_goal and then the task entered planning status.
But the flow is:
set_goal→ status changes toplanningpropose_plan→ status changes toexecuting
The agent called set_goal but never propose_plan that clears planning.
Looking at the code: setGoal in store.go sets status = 'planning', and
proposePlan sets status = 'executing'. So the agent must have called
set_goal but the subsequent propose_plan failed or the agent skipped it.
Fix: In setGoal, if the agent has enough information to propose a plan,
auto-transition to executing when the first tool call is made (not when
set_goal is called — that's too early). The status planning should only
stick if the agent explicitly calls ask_operator for more info. Otherwise,
status planning is indistinguishable from active — it just means the
agent never formalized the transition.