Skip to content

feat(background): run long-running shell commands without blocking the conversation - #39978

Closed
openchat-ai wants to merge 2 commits into
anomalyco:devfrom
openchat-ai:background-shell-pr
Closed

feat(background): run long-running shell commands without blocking the conversation#39978
openchat-ai wants to merge 2 commits into
anomalyco:devfrom
openchat-ai:background-shell-pr

Conversation

@openchat-ai

Copy link
Copy Markdown

-- Closes #39769

Summary

Long-running shell commands (e.g. builds, tests, daemons) currently block the entire conversation until they finish. This PR lets commands run in the background:

  • Add HTTP API to list running jobs and cancel them
  • Show a badge in the TUI when background jobs are active, with a dialog to cancel
  • Notify on background job failure
  • Set the command to the first line and take the rest as args

Changes

  • \packages/opencode/src/effect/runtime-flags.ts: \OPENCODE_EXPERIMENTAL_BACKGROUND_SHELL\ flag
  • \packages/opencode/src/tool/shell.ts: background execution, flattened shell closures
  • \packages/opencode/src/server/routes/instance/httpapi/: \jobs\ + \jobCancel\ endpoints + schemas
  • \packages/tui/src/feature-plugins/system/background-jobs.tsx: TUI badge + cancel dialog
  • \packages/sdk/js/src/v2/gen/: regenerated \Background\ client + types
  • \packages/opencode/openapi.json: regenerated
  • \packages/opencode/test/tool/shell.test.ts: tests for background job execution and failure notification

Testing

  • \�un typecheck\ in \packages/opencode, \packages/tui\ and SDK all pass
  • All shell tests pass (4 background + 1 failure notification)

Admin added 2 commits July 31, 2026 16:42
…ions

- Add GET /experimental/jobs and POST /experimental/jobs/:jobID endpoints with BackgroundJobInfo schema and regenerated SDK client methods

- Add TUI background-jobs plugin: running-job badge in app_bottom slot and a cancel dialog (background.jobs palette command)

- Render failed background commands as error state carrying the exit code; surface errors via notify

- Gate background shell behind the experimentalBackgroundShell runtime flag
- Drop the unrequested SummaryMode (head/error/tail) feature and its summary_mode parameter

- Collapse runShell/runTask/notify into inline run() calls; keep only renderBackgroundOutput and injectBackgroundResult helpers

- Inject the background result once via onPromote instead of a duplicate wait
Copilot AI review requested due to automatic review settings August 1, 2026 00:08
@github-actions github-actions Bot added the needs:compliance This means the issue will auto-close after 2 hours. label Aug 1, 2026
@github-actions

github-actions Bot commented Aug 1, 2026

Copy link
Copy Markdown
Contributor

This PR doesn't fully meet our contributing guidelines and PR template.

What needs to be fixed:

  • PR description is missing required template sections. Please use the PR template.

Please edit this PR description to address the above within 2 hours, or it will be automatically closed.

If you believe this was flagged incorrectly, please let a maintainer know.

@github-actions

github-actions Bot commented Aug 1, 2026

Copy link
Copy Markdown
Contributor

The following comment was made by an LLM, it may be inaccurate:

Potential Duplicates Found:

  1. PR feat(opencode): run bash commands in background #33310 - feat(opencode): run bash commands in background

  2. PR fix(tui): keep background shell status inline #36897 - fix(tui): keep background shell status inline

  3. PR fix(core): resolve spawn completion on exit, not only close (Windows detached-child hang) #29831 - fix(core): resolve spawn completion on exit, not only close (Windows detached-child hang)

The current PR appears to be a more comprehensive implementation of background shell execution (PR #33310), with additional HTTP API endpoints for job management and TUI improvements. You may want to review the previous attempts to understand any design decisions or implementation challenges.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds experimental support for running long-running shell tool invocations in the background so the conversation stays responsive, plus HTTP API + TUI affordances to observe/cancel jobs and regenerated SDK/OpenAPI artifacts.

Changes:

  • Introduces OPENCODE_EXPERIMENTAL_BACKGROUND_SHELL and implements background execution + follow-up result injection for the shell tool.
  • Adds experimental HTTP endpoints to list jobs and cancel a job, and wires a new TUI plugin that shows an “bg jobs active” badge + cancel dialog.
  • Regenerates SDK/OpenAPI outputs and adds tests covering background shell behavior (including failure signaling).

Reviewed changes

Copilot reviewed 10 out of 11 changed files in this pull request and generated 5 comments.

Show a summary per file
File Description
packages/tui/src/feature-plugins/system/background-jobs.tsx New TUI plugin to poll/list/cancel background jobs and show a running-jobs badge
packages/tui/src/feature-plugins/builtins.ts Registers the new built-in background jobs plugin
packages/sdk/js/src/v2/gen/types.gen.ts Generated types for background jobs and new experimental endpoints
packages/sdk/js/src/v2/gen/sdk.gen.ts Generated client methods for listing/cancelling background jobs
packages/opencode/test/tool/shell.test.ts Adds live tests for background shell execution + error reporting
packages/opencode/src/tool/shell/prompt.ts Documents new background parameter and behavior in shell tool prompt text
packages/opencode/src/tool/shell.ts Implements background execution mode, job tracking, and dynamic default timeouts
packages/opencode/src/server/routes/instance/httpapi/handlers/experimental.ts Implements /experimental/jobs and /experimental/jobs/:jobID handlers
packages/opencode/src/server/routes/instance/httpapi/groups/experimental.ts Defines schemas and endpoints for background job list/cancel
packages/opencode/src/effect/runtime-flags.ts Adds the experimental runtime flag for background shell support
Suppressed comments (3)

packages/opencode/src/server/routes/instance/httpapi/handlers/experimental.ts:184

  • /experimental/jobs is polled in the TUI every 5s, but this handler includes output and error for every job when present. Background shell output can be large (even if truncated), making polling heavier than necessary. Consider omitting output/error from the list endpoint (or gating them behind a query flag) and adding a dedicated endpoint to fetch details for a single job when needed.
        startedAt: job.started_at,
        ...(job.completed_at ? { completedAt: job.completed_at } : {}),
        ...(job.output ? { output: job.output } : {}),
        ...(job.error ? { error: job.error } : {}),

packages/tui/src/feature-plugins/system/background-jobs.tsx:132

  • Same issue as the dialog view: using createResource for an interval side effect means the timer won’t be disposed with the component. Prefer createEffect + onCleanup here as well (importing them from solid-js).
  createResource(async () => {
    await refresh()
    const timer = setInterval(refresh, POLL_MS)
    return () => clearInterval(timer)
  })

packages/opencode/test/tool/shell.test.ts:1273

  • Ping output casing differs across platforms (often ttl= on Unix). Using a case-insensitive matcher avoids a platform-specific failure.
          expect(job.info?.status).toBe("completed")
          expect(job.info?.output).not.toContain("terminated command after exceeding timeout")
          expect(job.info?.output).toMatch(/TTL=/)
        }).pipe(Effect.provide(RuntimeFlags.layer({ experimentalBackgroundShell: true, bashDefaultTimeoutMs: 30_000 }))),

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +102 to +103
startedAt: Schema.Number,
completedAt: Schema.optional(Schema.Number),
id: job.id,
type: job.type,
...(job.title ? { title: job.title } : {}),
status: job.status,
Comment on lines +67 to +70
props.api.ui.toast({
variant: result.data ? "success" : "error",
message: result.data ? `Cancelled background job ${jobLabel(jobs().find((x) => x.id === jobID)!)!}` : "Job already finished",
})
Comment on lines +44 to +48
createResource(async () => {
await refresh()
const timer = setInterval(refresh, POLL_MS)
return () => clearInterval(timer)
})
Comment on lines +1257 to +1258
command: `ping -n 18 127.0.0.1`,
background: true,
@github-actions

github-actions Bot commented Aug 1, 2026

Copy link
Copy Markdown
Contributor

This pull request has been automatically closed because it was not updated to meet our contributing guidelines within the 2-hour window.

Feel free to open a new pull request that follows our guidelines.

@github-actions github-actions Bot removed the needs:compliance This means the issue will auto-close after 2 hours. label Aug 1, 2026
@github-actions github-actions Bot closed this Aug 1, 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.

[FEATURE]: Long-running shell commands block the entire conversation

2 participants