Skip to content

feat(core): auto-background slow tools after 10s through Tool.execute - #60495

Closed
kitlangton wants to merge 1 commit into
devfrom
t902-tool-seam
Closed

kitlangton wants to merge 1 commit into
devfrom
t902-tool-seam

Conversation

@kitlangton

@kitlangton kitlangton commented Oct 30, 2026 •

Copy link
Copy Markdown
Contributor

Slow shell commands, Code Mode scripts, MCP calls, and custom plugin tools now move to the background automatically after 10 seconds instead of blocking the turn or getting killed at 2 minutes.
Fast calls (< 10 seconds) and instant local mutators or prompts (edit, write, patch, question, todowrite, read, grep, glob) behave identically to before. Targets dev directly (#60491 is merged).

🔍 Origin: reported in #50278, #54221, #49216, and #56729

💡 Why: Previously only shell, subagent, and experimental_image_generate hand-rolled background jobs — each forking its own plugin-scoped notifyWhenDone watcher that dropped completions on plugin reload (#56729) — while execute (Code Mode), MCP tools, and custom plugins blocked the turn with no way to background them (#54221, #49216).

🆕 Now you can: Run long builds, servers, Code Mode scripts, or slow MCP/plugin calls and have them automatically yield status: "running" with a jobID and live output/logs after 10 seconds (yield_after overrides per call; 0 or background: true backgrounds immediately), or move any active tool to the background with Ctrl+B.

flowchart LR
  Call["Top-level tool call<br/>(shell · execute · MCP · plugins)"] --> Seam["Tool.executeTopLevel + Job.block (10s)"]
  Seam -->|"finishes < 10s"| Inline["Return inline output"]
  Seam -->|"10s yield_after / Ctrl+B"| Bg["Return jobID + output so far"]
  Bg --> Settle["Global Job.settle -> Session.synthetic wake"]
  style Seam fill:#dcfce7,stroke:#16a34a,color:#14532d
  style Settle fill:#dcfce7,stroke:#16a34a,color:#14532d
Loading
- ShellTool / ImageGenerateTool: bespoke plugin-scoped notifyWhenDone + duplicate restart formatters
- execute (Code Mode) / MCP / plugin tools: block until completion; Ctrl+B is a no-op
+ Tool.executeTopLevel + Job.block({ yield_after: 10_000 }): wraps execute, MCP, web, and plugin tools automatically
+ Global Job.settle + Job.formatNotice: single completion & restart delivery path (survives Location reload)
+ Tool.Options.background = false: instant mutators & prompts (edit, write, patch, question) stay synchronous

📊 Proof

Case Before After
shell or execute (Code Mode) runs past 10 s (#50278, #54221) ❌ Blocks turn (or shell SIGTERMs at 2 m) ✅ Yields jobID + live output/logs so far; explicit timeout preserved
Custom @opencode/plugin or MCP tool runs long / user presses Ctrl+B (#49216) ❌ Cannot move to background ✅ Yields jobID + latest progress title, wakes session on finish
Location or plugin reloads while a background job runs (#56729) ❌ Plugin-scoped watcher interrupted; notice lost ✅ Global Job scope delivers notice once
Permission dialog open longer than yield_after — ✅ deferYield pauses yield timer until user replies

New integration tests in packages/core/test/tool-auto-background.test.ts fail without the change; 136 tests across 6 codemode and core suites pass.

👀 Read: packages/core/src/tool.ts in executeTopLevel and packages/core/src/job.ts in block — executeTopLevel wraps only top-level Snapshot.execute calls so nested tools.* calls inside Code Mode keep their declared JS return types, and block resolves foreground vs background ownership under the state.jobs lock so boundary completions never double-deliver. The rest is routine.

Checks run
  • bun test test/codemode.test.ts (packages/codemode): 54 pass, 0 fail
  • bun run script/test.ts test/tool-auto-background.test.ts test/job.test.ts test/tool-image-generate.test.ts test/tool-execute.test.ts test/session-execution.test.ts (packages/core): 82 pass, 0 fail
  • bun run typecheck && bun run typecheck:tsc (packages/schema, packages/codemode, packages/plugin, packages/core): 0 errors

@kitlangton

Copy link
Copy Markdown
Contributor Author

Closing in favor of the split stack (in merge order), keeping the t902-tool-seam branch in place until #60497 retargets:

  1. refactor(core): share job notice formatting and shell output snapshots #60704 (Job.formatNotice and Shell.snapshot)
  2. feat(core): deliver background job notices from the job service #60705 (Job owns background notice delivery)
  3. feat(core): yield slow top-level tools to the background after 10s #60706 (Tool.execute 10s yield wrapper)
  4. feat(core): auto-background slow shell commands via the job service #60707 (ShellTool auto-yield via Job)
  5. refactor(core): deliver image generation notices via the job service #60708 (ImageGenerateTool notice delivery via Job)
  6. feat(core): auto-background slow Code Mode executions #60709 (Code Mode execute auto-yield and "tool" idle background kind)

@kitlangton kitlangton closed this Oct 30, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant