Skip to content

feat(chat): open background tasks as live tabs in the conversation view (#63) - #64

Merged
vlinx-io merged 4 commits into
vlinx-io:devfrom
mguttmann:feat/conversation-task-view
Sep 20, 2026
Merged

vlinx-io merged 4 commits into
vlinx-io:devfrom
mguttmann:feat/conversation-task-view

Conversation

@mguttmann

@mguttmann mguttmann commented Sep 10, 2026 •

Copy link
Copy Markdown

Closes #63

What

In the experimental conversation view, a running background task (a Claude Code workflow with many agents, a subagent, a backgrounded shell command) was one truncated line in the Tasks chip with a Stop button. Nothing could be opened. This PR makes each task openable as a tab of its own and renders the state that Claude Code already streams.

  • Tasks chip: each row shows the live description (Phase: agent for workflows), the task type, and its status once finished. The badge counts running tasks only. Finished tasks stay listed with their final state until the next turn starts, so their result can still be opened.
  • Task tab: clicking a row opens a tab in the TabBar (parallel to document and browser tabs, client-local, not persisted, not mirrored). Several tasks open as several tabs, each closable on its own; tabs stay mounted when another tab is in front. The tab shows type, status, elapsed time (ticking while running), total tokens, tool uses, the current agent, start and end time, the final summary and output file, and for workflows the phase list with every agent: label, model, state, attempt, duration, tokens, prompt preview and result preview.
  • Lifecycle and mirror mode: when the Claude process exits or a new one takes the session over, an open task tab shows the task as ended instead of a running clock and a Stop that could only fail. This holds for as long as the tab receives the session's chat events: on the Tauri transport always; on the WebSocket transport until a reconnect happens while no conversation pane for that session is mounted any more (the tab does not re-register the session's events itself yet, see the open points). A task tab that outlives its conversation pane keeps getting the task's final status, summary and output file: the process is no longer released on the inventory drop itself but only after the task's task_notification, bounded by a short grace (TASK_SETTLE_GRACE) so a task whose frames never come cannot keep the process alive. In mirror mode a client that sits in a task tab publishes a session tab in its place (with that tab's session and first pane), so the peer never sees an empty stage; and the client itself stays in its task tab whatever an incoming frame names as active, on both sides (publisher receiving the reconcile echo or an ordinary peer push, follower receiving the publisher's pushes) until the user leaves the task tab.
  • Rust engine: ClaudeExtras.background_tasks becomes a typed, merged view per task_id over background_tasks_changed, task_started, task_progress, task_updated and task_notification (live description, last_tool_name, usage, workflow_progress, started_at, ended_at, status, summary, output_file). A task the inventory drops before its terminal frame arrives (the real wire order, see below) is marked ended instead of vanishing. Finished tasks are pruned when the next user turn starts, capped at 20. No new command, no transport change: the existing extras snapshot and event carry it on Tauri IPC and WebSocket alike.

Why the data is there

Verified on Claude Code 2.1.267 with a three-agent probe workflow in --output-format stream-json: every task_progress frame of a local_workflow task carries the live description, last_tool_name, usage and, about every second frame, workflow_progress, the same phase and agent tree the TUI renders under /workflows. The Rust tests replay those frames in wire order (inventory empties before task_updated and task_notification arrive).

Scope and non-goals

  • No version bump, no changelog entry, no change to protocol.rs, command_core.rs, commands.rs or web/dispatch.rs.
  • Task tabs are not persisted across restart (the process behind them is gone by then) and are not published to mirror peers.
  • Workflow log() narrator lines are not part of the protocol and are not shown.
  • Foreground subagents that never appear in the background inventory are held but not listed, as before.
  • The Workflow card in the timeline does not open the tab yet.
  • 23 new i18n keys (chat.tasks.*) in all 11 locales; German translated, the others carry the repo's // TODO translate marker.

Known minor points left open

  • "Finished" is defined twice (Rust allow-list, TS deny-list); an unknown future terminal status would read as finished in the UI and as running in Rust.
  • The cap of 20 finished tasks also counts unlisted foreground subagents.
  • A task marked ended because the inventory dropped it is not revived if the inventory lists it again without a status.
  • Elapsed time while running mixes the backend's started_at with the client clock; on a remote browser with clock skew the figure can be off until the task ends.
  • The phase and agent lists in the task tab use the wire's index as React key; the TS types declare it required, the Rust side forwards workflow_progress unvalidated, so a frame without it would warn in dev.
  • debug.ts tab invariants do not know task tabs (pre-existing class: they do not know browser tabs either).
  • When a publisher opens a task tab, the peer is moved to the derived anchor's first pane (and, if the publisher came from a document or browser tab, to the last session tab), not to the pane it mirrored before.
  • Browser/Electron transport: a task tab whose conversation pane is gone does not re-register the session's chat events after a WebSocket reconnect; until the pane is opened again it stops receiving updates.
  • A task_started or a task_updated back to running for an already finished task id keeps ended_at, summary and output file from the previous life of that id (frozen elapsed time next to a Running badge); task ids are random per process, so this is not reachable in practice.

Tests

  • Rust: 7 tests in engine.rs replaying the probe frames (merge, inventory drop before terminal frame, detached release waiting for the terminal frames of a dropped task, release after the grace when no frames come, pruning and cap, unlisted foreground subagents, lenient frames). cargo test: 837 passed, 0 failed.
  • Frontend: new taskTabs.test.ts, TaskView.test.tsx, TabBar.taskTab.test.tsx; extended extras.test.tsx, mirrorLayout.test.ts, mirrorSync.test.ts (reconcile round trip through the real store; a client in a task tab stays there as publisher and as follower). pnpm test: 651 passed. pnpm build and pnpm lint green.
  • Not verified end to end: the browser/Electron transport with a live WebSocket, a manual run with a real multi-agent workflow in the desktop app, the look of the chip row after the split into two sibling buttons, and the real width of the flush window between Claude's inventory drop and its terminal frames (the grace length is a setting, not a measurement).

🤖 Generated with Claude Code

@mguttmann

Copy link
Copy Markdown
Author

Rebased onto v0.2.0 (af9e7de), no functional change. Conflict resolution: v0.2.0 brought its own tasks chip into ChatPane.tsx, so the branch now only wires the onOpen handler into that chip instead of adding a second one; the task view CSS is appended after the v0.2.0 block, and the TabBar test mock follows the dateLocale to getLocale rename. On the rebased branch: cargo test 1038 passed, pnpm test 116 files / 1039 tests passed.

@mguttmann
mguttmann force-pushed the feat/conversation-task-view branch 3 times, most recently from 19e1ed3 to 87f41ce Compare September 13, 2026 16:28
Claude Agent and others added 4 commits September 15, 2026 10:42
…ew (vlinx-io#63)

The Tasks chip of the conversation view listed a running background task as
one truncated line with a Stop button. Claude Code's task protocol delivers
far more: a live "phase: agent" description, usage, and for workflows the
complete phase and agent tree that the TUI shows under /workflows.

The Rust engine now merges every task_* frame and the background_tasks_changed
inventory per task_id into the extras snapshot and keeps finished tasks with
their final state until the next turn starts. Each chip row shows the live
description and opens a client-local task tab (parallel to document and
browser tabs) that renders elapsed time, tokens, tool uses, the current agent
and, for workflows, every phase with its agents: model, state, duration,
tokens, prompt and result preview. Several tasks open as several tabs.

Refs vlinx-io#63

Co-Authored-By: Claude Fable 5.1 <[email protected]>
…irror echo

- TaskView treats `exited` and `reset` as the end of its task: status Ended,
  clock stopped, no Stop button, instead of showing a running task until the
  next process happens to list it.
- In mirror mode a client whose active tab is a task tab publishes the session
  tab the peer would land on anyway, with its session and pane, so the peer
  never shows an empty stage and its reconcile does not push a repair back.
- The Tasks chip row is a container with two sibling buttons (open, stop):
  no nested interactive content, both reachable by keyboard.

Refs vlinx-io#63

Co-Authored-By: Claude Fable 5.1 <[email protected]>
… mode

- Release-when-idle now waits for the task_notification of tasks the
  inventory just dropped, bounded by TASK_SETTLE_GRACE, so a task tab that
  outlives its conversation pane still receives status, summary and output
  file instead of ending on a killed process
- applyMirrorLayout keeps the locally active task tab across the reconcile
  echo and ordinary peer pushes, for publisher and follower alike
- TaskView requests a paged chat_snapshot instead of the whole transcript,
  since it only reads the flattened extras

Follow-up to vlinx-io#63 from a fresh-eyes review of the branch.

Co-Authored-By: Claude Fable 5.1 <[email protected]>
@mguttmann
mguttmann force-pushed the feat/conversation-task-view branch from 87f41ce to 460d315 Compare September 15, 2026 14:43
@mguttmann

Copy link
Copy Markdown
Author

Rebased onto v0.2.2 (7874081). One conflict, the chat import line in termStore.ts (v0.2.2 added chatSend and chatStart for auto-continue, this branch adds the ChatBackgroundTask type); both kept. pnpm test 120 files / 1070 tests, build and lint green on the rebased branch.

@mguttmann
mguttmann changed the base branch from main to dev September 17, 2026 15:14
@vlinx-io
vlinx-io merged commit 1b1abb8 into vlinx-io:dev Sep 20, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Conversation view: background tasks (workflows, subagents) cannot be opened or inspected although the protocol delivers the full progress tree

2 participants