Files
oikos/plans/2026-07-14-activity-timeline.md
dtoro b414722fc7
Some checks failed
ci / build-test (push) Has been cancelled
ci / docker-build (push) Has been cancelled
Desktop App / Build Linux (amd64) (push) Has been cancelled
Desktop App / Attach to Release (push) Has been cancelled
sidebar activity timeline replaces tool display in chat
- New ActivityTimeline: unified timeline in sidebar showing all agent actions
  (goal, plan steps, tool calls, knowledge, completion) in reverse chron order
- activityLog derived store merges messages + planSteps + currentTask
- AgentIndicator stays in chat (thinking/working indicator), simplified props
- ToolCallGroup removed from chat — tools visible only in sidebar timeline
- SessionDigest replaced by ActivityTimeline
- PlanProgress restored in sidebar (conceptual steps, separate from timeline)
2026-07-14 12:19:26 +02:00

5.4 KiB

2026-07-14 — Unified sidebar activity timeline

Status: Planned

Current state (broken)

The sidebar has three sections that appear/disappear independently:

Section When visible Shows
Plan (top) When $planSteps.length > 0 Plan step names with progress bar
Tool activity (middle) When toolCount > 0 Compact tool list, grouped by turn
This session (bottom) When digest.total_executions > 0 Post-hoc execution count + knowledge

State changes cause sections to pop in/out as the agent moves between planning → executing → done. The "0 tools · 0 running" counter flashes briefly then vanishes. Tool activity appears/disappears between turns.

Target: single unified timeline

One section, always present when a session is loaded. Every agent action appears as an entry in reverse-chronological order (newest at top).

┌─ Activity ───────────────────────── ─┐
│                                       │
│ ✓ Task completed: "Upgraded 4 LXCs"   │  ← newest
│ ◉ Running: apt upgrade on lxc:dns     │
│ ✓ run: apt upgrade on lxc:gitea       │  ← tool completed
│ ✓ Verified gitea: HTTP 200            │
│ ◉ Step 3/5 — Upgrade dns             │  ← plan step running
│ ✓ Step 2/5 — Upgrade gitea           │  ← plan step done
│ ✓ run: apt upgrade on lxc:nfs-export  │
│ ◉ Step 1/5 — Upgrade nfs-export      │
│ ✓ Knowledge recorded                 │
│ 📋 Plan set: 5 steps                 │  ← plan proposed
│ 🎯 Goal: Upgrade 4 low-risk LXCs      │  ← goal set
│                                       │  ← oldest
└───────────────────────────────────────┘

Entry types

Type Icon Example description
goal 🎯 "Audit all LXCs for updates"
plan 📋 "Plan set: 5 steps"
step_start ◉ spinner "Step 2/5 — Upgrade gitea"
step_done "Step 2/5 — Upgrade gitea"
tool_start ◉ spinner "run: Upgrade nfs-export (21 pkgs)"
tool_done "run: 0 upgraded, 0 newly installed"
tool_error "run: SSH handshake failed"
knowledge "Recorded: How to run fleet upgrades"
complete "Task completed: success"
question "Asked: Which host for the LXC?"
error "Auto-resume failed: context deadline exceeded"

Data source

Entries come from all available sources, merged and deduplicated:

  1. toolTimeline store (live tool_use/tool_result pairs)
  2. planSteps store (step status transitions)
  3. Session digest API (knowledge created, final outcome)
  4. currentTask store (goal, status)

Deduplication: when a plan step links to a tool call via execution_id, show them as one entry instead of two (e.g. "Step 3: Upgrade dns ◉ running" includes the tool — don't show a separate "run: apt upgrade" entry).

Behavior

  • Always visible when $currentSession is set
  • Reverse chronological — newest entries at top, scrolls naturally
  • Auto-expands the entry for the currently-running tool/step
  • Collapses completed entries to one line (expandable)
  • Polls every 3s for live updates (same as current startPolling)
  • No flashing — entries only change status in place (tool_start → tool_done), never removed
  • Persists across page navigation (rehydrated from REST on load)
  • Empty state when no session: "Open a session to see agent activity"

What gets removed from chat

  • ToolCallGroup — the compact tool counter. Tools live in the timeline now.
  • AgentIndicator at bottom — partially. Keep it ONLY for the initial "thinking" state (before any tools fire). Once the first tool fires, the timeline is the source of truth and the chat indicator is redundant. Actually: remove it entirely. The timeline IS the indicator.

What stays in chat

  • Agent text responses — the thinking, conclusions, reports
  • InlineApproval cards — approvals need operator action, must be in chat
  • Inline tool renderers — entity cards, health summary, etc. (informational)
  • User messages — obviously

Implementation

1. Data layer: activityLog derived store

Add to chat.ts:

export interface ActivityEntry {
  id: string
  type: 'goal' | 'plan' | 'step_start' | 'step_done' | 'step_failed' |
        'tool_start' | 'tool_done' | 'tool_error' |
        'knowledge' | 'complete' | 'question' | 'error'
  description: string
  detail?: string       // tool result text, step detail, etc.
  timestamp: number      // Date.now() when created
  seq?: number           // plan step seq, for ordering
  toolName?: string      // for tool entries
  status: 'running' | 'done' | 'failed'
  collapsed: boolean     // initial collapsed state (true for completed)
}

Derived reactively from messages, planSteps, currentTask, and session digest data. Uses $derived.by() to recompute when any source changes.

2. New component: ActivityTimeline.svelte

Replaces all three sidebar sections. Renders activityLog entries as a vertical timeline with connecting lines.

3. Remove from chat

  • <ToolCallGroup> rendered in chat
  • <AgentIndicator> at bottom

4. Update TaskContextPanel

Replace PlanProgress + SessionDigest with ActivityTimeline.