feat(background): run long-running shell commands without blocking the conversation - #39978
feat(background): run long-running shell commands without blocking the conversation#39978openchat-ai wants to merge 2 commits into
Conversation
…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
|
This PR doesn't fully meet our contributing guidelines and PR template. What needs to be fixed:
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. |
|
The following comment was made by an LLM, it may be inaccurate: Potential Duplicates Found:
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. |
There was a problem hiding this comment.
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_SHELLand implements background execution + follow-up result injection for theshelltool. - 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/jobsis polled in the TUI every 5s, but this handler includesoutputanderrorfor every job when present. Background shell output can be large (even if truncated), making polling heavier than necessary. Consider omittingoutput/errorfrom 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
createResourcefor an interval side effect means the timer won’t be disposed with the component. PrefercreateEffect+onCleanuphere as well (importing them fromsolid-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.
| startedAt: Schema.Number, | ||
| completedAt: Schema.optional(Schema.Number), |
| id: job.id, | ||
| type: job.type, | ||
| ...(job.title ? { title: job.title } : {}), | ||
| status: job.status, |
| 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", | ||
| }) |
| createResource(async () => { | ||
| await refresh() | ||
| const timer = setInterval(refresh, POLL_MS) | ||
| return () => clearInterval(timer) | ||
| }) |
| command: `ping -n 18 127.0.0.1`, | ||
| background: true, |
|
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. |
-- 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:
Changes
Testing