Skip to content

Conversation

@ThomasK33
Copy link
Member

Summary

  • Adds a View output action for running background bash processes (from both the Background Processes banner and backgrounded bash tool calls).
  • Opens a small log viewer that polls incrementally and keeps output bounded (reuses the existing ~1MB truncation behavior).

Implementation notes

  • New ORPC endpoint workspace.backgroundBashes.getOutput lets the UI peek background output.log without consuming the agent cursor (bash_output / task_await).
  • Backend supports an initial tail read (default ~64KB) and subsequent reads by byte offset.

Testing

  • make static-check

📋 Implementation Plan

Plan: Show buffered output for backgrounded bash tasks

Goal

When a bash tool call is running in the background (run_in_background: true or foreground→background), let the user inspect the accumulated stdout/stderr so far in the UI (similar to the foreground BashToolCall output view).

UX target:

  • From the BackgroundProcessesBanner row and from the BashToolCall card, the user can click a background process to open a small log viewer (popover or modal).
  • While open, the log viewer can incrementally update (poll) until the process exits.
  • Output display is bounded (e.g. keep last ~1MB like liveBashOutputBuffer).

Non-goals (initial cut):

  • Persistence across app restart.
  • Separate stdout vs stderr streams (background output is already merged into output.log).

Implementation (chosen): On-demand popover log viewer + new ORPC endpoint (net +250–450 LoC)

Backend: expose “peek” output without consuming the tool cursor

Why: BackgroundProcessManager.getOutput() mutates proc.outputBytesRead (used by bash_output / task_await). The UI must not advance that cursor or the agent will miss output.

Implement a separate read path that:

  • reads output.log at a caller-provided byte offset
  • returns { chunk, nextOffset, status }
  • does not mutate proc.outputBytesRead or proc.incompleteLineBuffer

Concrete steps:

  1. ORPC schema

    • Add workspace.backgroundBashes.getOutput:
      • input: { workspaceId, processId, fromOffset?: number, tailBytes?: number }
        • if fromOffset is omitted, treat as “initial open” and return a tail chunk (default tailBytes = 64_000).
      • output: { success, data?: { status, output, nextOffset, truncatedStart }, error? }
        • truncatedStart: boolean indicates we started from a non-zero offset.
  2. router + workspaceService

    • Add router handler for backgroundBashes.getOutput.
    • Add WorkspaceService.getBackgroundProcessOutput(...) that:
      • verifies process exists and belongs to workspaceId (same pattern as terminateBackgroundProcess).
      • delegates to the manager.
  3. backgroundProcessManager

    • Add readOutput(processId, { fromOffset?: number; tailBytes?: number }).
    • Implementation notes:
      • use getProcess(processId) to refresh status/exitCode.
      • if fromOffset is provided: proc.handle.readOutput(fromOffset).
      • else: compute a start offset for “tail” and read from there.
      • Important: do not touch proc.outputBytesRead.
  4. BackgroundHandle support for tail
    BackgroundHandle.readOutput(offset) reads “offset → EOF”; for initial open we want “EOF - N → EOF”.

    Preferred implementation:

    • Extend BackgroundHandle with getOutputFileSize(): Promise<number>.
    • Implement cross-platform size lookup in handles:
      • try stat -c%s, fallback to stat -f%z, fallback to wc -c.
    • In manager: startOffset = Math.max(0, size - tailBytes).
  5. Defensive guardrails

    • Assert non-negative offsets.
    • Clamp tailBytes to a sane max (e.g. 1MB).
    • If the log file doesn’t exist yet: return empty output.

Renderer: reusable BackgroundBashOutputViewer (popover or modal)

  1. Add a component that:

    • takes { workspaceId, processId, displayName? }
    • calls api.workspace.backgroundBashes.getOutput()
    • maintains local state:
      • offset cursor (starts undefined to trigger the initial tail fetch)
      • buffer (append chunks; keep last ~1MB)
      • truncatedStart + bufferTruncated
      • status (running|exited|killed|failed)
  2. Polling (only while viewer is open)

    • While open and running, poll ~every 500ms:
      • getOutput({ fromOffset: offset })
      • append + update offset
    • Stop polling when closed or when status != running.
  3. UI integration

    • BackgroundProcessesBanner (src/browser/components/BackgroundProcessesBanner.tsx)
      • Add a “View output” icon button per row (next to terminate).
      • Clicking opens a Popover (or Dialog) that renders the viewer.
    • BashToolCall (src/browser/components/tools/BashToolCall.tsx)
      • When "backgroundProcessId" in result, add an inline “View output” action.
  4. Display details

    • Render output using the same monospace primitives as tool calls (DetailContent / OutputSection).
    • Show small notes when:
      • truncatedStart (e.g., “Showing last 64KB”).
      • buffer truncates (“Showing last ~1MB”).
    • Optional: “Copy output” button.

Validation

  • Unit test pure logic:
    • tail start offset calculation + buffer truncation.
  • Optional lightweight integration test if it’s easy to hook up:
    • spawn a background bash that prints lines with delays and verify the ORPC call returns incremental output.

Generated with mux • Model: openai:gpt-5.2 • Thinking: xhigh • Cost: $9.12

Change-Id: I87fc81de659d490d4f60f5445c145920ac890cf9
Signed-off-by: Thomas Kosiewski <tk@coder.com>
@github-actions github-actions bot added the enhancement New feature or functionality label Jan 18, 2026
Change-Id: I47d7e428078196b9ceda42b246831664fe5600e9
Signed-off-by: Thomas Kosiewski <tk@coder.com>
@ThomasK33 ThomasK33 force-pushed the background-bash-preview branch from 9b172af to 03ecc18 Compare January 18, 2026 12:51
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or functionality

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant