Skip to content
This repository was archived by the owner on May 15, 2026. It is now read-only.

Latest commit

 

History

History
327 lines (233 loc) · 12.9 KB

File metadata and controls

327 lines (233 loc) · 12.9 KB

Phase 6: Background Task Visibility and Interaction

Architectural design document for Issue #12330
Phase 6 of "Support parallel execution of specialized agents and improve context handoff between modes"

1. Context

Phase 5 (Background Tasks Panel UI) is complete. This document proposes the scope, priority, and architecture for Phase 6, which focuses on enabling better visibility and interaction with background tasks.

2. Current Architecture

Task Lifecycle

ClineProvider maintains a clineStack: Task[] (LIFO). Only the top-of-stack task is "current" -- all state updates, webview messages, and user interactions route through getCurrentTask().

ClineProvider
├── clineStack: Task[]          # LIFO stack, sequential execution
├── taskHistoryStore             # Per-task file persistence
├── getCurrentTask()             # Returns top of stack
├── addClineToStack(task)        # Push new task
└── removeClineFromStack()       # Pop completed task

Task Persistence

Layer File Purpose
Messages taskMessages.ts Save/load ClineMessage[] per task
API History apiMessages.ts Save/load API conversation history
History Items TaskHistoryStore.ts Per-task metadata files with in-memory cache
Metadata taskMetadata.ts Task metadata helpers

Webview Communication

The extension sends typed ExtensionMessage objects to the webview. Key message types:

  • state -- Full state snapshot (includes clineMessages, currentTaskId)
  • taskHistoryUpdated -- Full history list refresh
  • taskHistoryItemUpdated -- Single history item update

Currently, postStateToWebviewWithoutTaskHistory() sends state for only the current task. There is no mechanism to send updates for background tasks.

Subtask Support

Parent-child relationships exist via parentTaskId and childIds on HistoryItem. The new_task tool creates subtasks that push onto the stack. When a subtask completes, it pops and returns control to the parent.

3. Agreed Scope for Phase 6

In scope (Items 1-3):

  1. Full conversation replay for completed background tasks
  2. Tab switching / multi-task view
  3. Real-time progress streaming for active background tasks

Deferred to Phase 7 (Items 4-5): 4. Write-capable background tasks + basic file locking -- see phase-7-controlled-write-parallelism.md (Phases 7a, 7b) 5. Persistent background task history across sessions -- see phase-7-controlled-write-parallelism.md (Phase 7c)

4. Feasibility Analysis

Item 1: Full Conversation Replay

Complexity: Medium | Risk: Low

readTaskMessages(taskId, globalStoragePath) already loads the full ClineMessage[] array from disk for any task. The existing ChatView component renders these messages. The main work is creating a read-only wrapper that:

  • Accepts a taskId prop instead of reading from global state
  • Loads messages on mount via a new webview message
  • Hides input controls (chat box, approval buttons)
  • Renders tool calls, outputs, and assistant responses in the same format

Why it's low risk: No changes to task execution, persistence, or the foreground task flow. Purely additive UI + a new message handler.

Item 2: Tab Switching / Multi-task View

Complexity: Medium-High | Risk: Medium

The webview already has a tab system in App.tsx (tab === "history", tab === "settings", tab === "chat"). Adding a background tasks view requires:

  • A new tab or panel within the chat view
  • A list of active/completed background tasks with status indicators
  • Navigation to open a task's replay view or live view
  • State management to track which background task is currently being viewed

Key challenge: The webview currently receives state for only one task. Viewing a background task must not disrupt the foreground task's state. This requires either:

  • (a) A separate message channel for background task data, or
  • (b) A secondary state context in the webview that can hold background task data alongside the primary task state

Option (a) is cleaner and avoids polluting the existing state management.

Item 3: Real-time Progress Streaming

Complexity: High | Risk: Medium-High

Currently, Task.ts calls provider.postStateToWebviewWithoutTaskHistory() to update the UI. This method sends the full state for the current task only. For background tasks to stream progress:

  1. Task.ts must emit incremental updates even when it is not the "current" task
  2. A new message type (backgroundTaskProgress) must carry task-scoped updates
  3. The webview must handle concurrent update streams without degrading performance
  4. Throttling/batching is needed to prevent excessive re-renders

Why it's harder: Requires changes to the core task execution loop (Task.ts), not just additive UI. The task currently assumes it IS the visible task when posting updates.

5. Recommended Priority Order

Phase 6a: Conversation Replay         (Foundation -- standalone value)
    │
    ▼
Phase 6b: Tab/Panel Switching          (Navigation framework, depends on 6a)
    │
    ▼
Phase 6c: Real-time Progress Streaming (Highest complexity, builds on 6b)

Each sub-phase is independently shippable and testable.

6. Detailed Design

6a. Conversation Replay

New Message Types

// Webview → Extension
interface RequestBackgroundTaskMessages {
  type: "requestBackgroundTaskMessages"
  taskId: string
}

// Extension → Webview
interface BackgroundTaskMessages {
  type: "backgroundTaskMessages"
  taskId: string
  messages: ClineMessage[]
}

Extension Handler (webviewMessageHandler.ts)

case "requestBackgroundTaskMessages": {
  const taskId = message.taskId
  const globalStoragePath = provider.contextProxy.globalStorageUri.fsPath
  const messages = await readTaskMessages(taskId, globalStoragePath)
  provider.postMessageToWebview({
    type: "backgroundTaskMessages",
    taskId,
    messages: messages ?? [],
  })
  break
}

Webview Component

BackgroundTaskReplayView
├── Props: { taskId: string, onClose: () => void }
├── State: messages (ClineMessage[]), loading (boolean)
├── On mount: sends requestBackgroundTaskMessages
├── On message: receives backgroundTaskMessages, filters by taskId
├── Renders: read-only message list (reuses ChatRow components)
└── No input controls, no approval buttons

6b. Tab/Panel Switching

UI Structure

A new icon is added to the existing tab bar (alongside chat, history, settings) as the entry point. The background task view occupies the full tab area.

App.tsx
├── tab === "chat"     → ChatView (foreground task)
├── tab === "history"  → HistoryView
├── tab === "settings" → SettingsView
└── tab === "bgTask"   → BackgroundTaskView
    ├── BackgroundTasksList (task list with status badges + error badge on tab icon)
    │   ├── Active tasks
    │   └── Completed tasks
    └── BackgroundTaskReplayView (from 6a) OR BackgroundTaskLiveView (from 6c)

State Management

// New webview state (in App.tsx or dedicated context)
interface BackgroundTaskViewState {
  selectedTaskId: string | null
  viewMode: "replay" | "live"
}

Navigation Flow

  1. User clicks background tasks icon in the tab bar
  2. App switches to tab === "bgTask"
  3. BackgroundTasksList shows available tasks
  4. User clicks a task → sets selectedTaskId
  5. If task is completed → opens BackgroundTaskReplayView
  6. If task is active → opens BackgroundTaskLiveView (Phase 6c)

6c. Real-time Progress Streaming (Minimal Viable Version)

Design principle: Keep Phase 6c tightly scoped to avoid expanding the phase. Ship the simplest useful version first; richer detail can be added incrementally in later phases.

MVP Scope

The minimal viable version streams only:

  • Tool name + status (started / completed / errored) -- not full parameters or output
  • Last N updates (rolling window of ~20 items) -- older entries are discarded client-side
  • Status changes (running, paused, completed, errored)

What is explicitly out of scope for the MVP:

  • Full tool call parameters or output payloads
  • Assistant text streaming
  • Persistent storage of streamed updates (replay from disk covers completed tasks)

New Message Types

// Extension → Webview (incremental updates)
interface BackgroundTaskProgress {
  type: "backgroundTaskProgress"
  taskId: string
  update: BackgroundTaskUpdate
}

interface BackgroundTaskUpdate {
  kind: "tool_call" | "tool_result" | "status_change" | "error"
  timestamp: number
  toolName?: string       // e.g. "read_file", "execute_command"
  status?: string         // e.g. "started", "completed", "errored"
  errorMessage?: string   // Only for kind === "error"
}

Note: assistant_text is excluded from the MVP. The update interface uses typed optional fields instead of data: any to keep the contract narrow and safe.

Task.ts Changes

Add a method that emits progress regardless of whether the task is "current":

// In Task.ts
private emitBackgroundProgress(update: BackgroundTaskUpdate) {
  const provider = this.providerRef.deref()
  if (!provider) return
  
  // Only emit background updates when this task is NOT the current task
  if (provider.getCurrentTask()?.taskId === this.taskId) return
  
  provider.postMessageToWebview({
    type: "backgroundTaskProgress",
    taskId: this.taskId,
    update,
  })
}

The hook points in Task.ts should be minimal -- emit at tool call start and tool call end only. Avoid adding hooks inside the LLM streaming loop for the MVP.

Throttling Strategy

  • Batch updates in 500ms windows (conservative default; can be tuned down later)
  • Cap at 5 updates per batch per task
  • Drop older updates if buffer exceeds threshold (keep last N = 20)
  • Priority ordering: status_change > error > tool_result > tool_call

Webview: BackgroundTaskLiveView

BackgroundTaskLiveView
├── Props: { taskId: string }
├── State: updates (BackgroundTaskUpdate[], capped at last 20), status
├── Subscribes to backgroundTaskProgress messages filtered by taskId
├── Renders: compact list of recent tool calls with status icons
├── Auto-scrolls to latest update
└── Shows task status badge (running, paused, completed, errored)

The live view intentionally shows a compact summary, not a full chat transcript. Users who want full detail can wait for the task to complete and use the replay view (6a).

Confirmed: Streaming is scoped to the currently selected background task only. The extension should not emit backgroundTaskProgress messages for tasks the user is not viewing. This keeps message traffic low and the implementation simple.

7. Testing Strategy

Area Test Type Key Scenarios
Message handler Unit (vitest) Request/response for task messages, missing task, corrupt data
BackgroundTaskReplayView Component (vitest + RTL) Loading state, message rendering, empty state
Tab switching Component (vitest + RTL) Tab navigation, state preservation, back to foreground
Progress streaming Unit (vitest) Throttling, batching, concurrent tasks
Integration E2E (if feasible) Full flow: start bg task → view progress → replay after completion

8. Confirmed Decisions

The following decisions were confirmed during design review and should guide implementation.

UI Layout

  1. Background task view layout: Full tab (tab === "bgTask")

    Start with a full tab for simplicity in Phase 6. A sidebar/hybrid mode may be considered later based on user feedback.

  2. Entry point placement: New tab bar icon

    Add a new icon in the existing tab bar (alongside chat, history, settings). This is the most discoverable location without cluttering the chat view.

  3. Replay view implementation: Thin wrapper around ChatRow components

    Create a dedicated BackgroundTaskReplayView that wraps ChatRow components directly rather than reusing the full ChatView. This avoids inheriting input controls, scroll management, and approval button logic that don't apply to read-only replay.

Progress Streaming (6c)

  1. Streaming granularity: Minimal level

    Stream tool name + status only (started/completed/errored). This provides enough signal to know what the background task is doing without performance risk. Truncated arguments (medium level) can be added in a follow-up if users need more context.

  2. Streaming scope: Currently selected task only

    Only stream updates for the background task the user is currently viewing. This avoids unnecessary message traffic and keeps the implementation simple.

  3. Error surfacing: Badge on the background tasks tab icon

    Display a badge on the tab icon when a background task encounters an error. Toast notifications can be added later if users miss errors.