Skip to content

feat(shell): improve background job control - #3673

Merged
ErikBjare merged 3 commits into
gptme:masterfrom
Ricky-7-Yan:fix/background-shell-jobs
Aug 29, 2026
Merged

ErikBjare merged 3 commits into
gptme:masterfrom
Ricky-7-Yan:fix/background-shell-jobs

Conversation

@Ricky-7-Yan

@Ricky-7-Yan Ricky-7-Yan commented Aug 28, 2026 •

Copy link
Copy Markdown
Contributor

Closes #3667.

Summary

  • place the shell's /dev/null redirect before parsed background & operators so background processes cannot consume later commands sent to the persistent shell
  • redirect a trailing foreground command in a compound asynchronous list as well, so commands such as sleep 1 & cat cannot retain the persistent shell's stdin
  • add wait <job-id> [timeout] with seconds, minutes, and hours suffix support
  • add output <job-id> --new for incremental polling without discarding accumulated output
  • keep finished jobs available until their output reader has drained inherited pipes, and report when output is still draining
  • document the new background-job controls in the shell tool prompt

Testing

  • ruff format and ruff check on the four changed files
  • focused pytest cases covering ampersand parsing, wait dispatch/timeouts, incremental output, and output-reader draining
  • 70 background-job tests passed on Windows; 2 platform-specific cases were deselected (getpgid is unavailable and the local WSL bash bridge does not preserve the loop variable)

mypy reports 12 existing Windows-only errors for POSIX os.getpgid, os.killpg, and signal.SIGKILL references; the changed code introduces no additional mypy errors.

@greptile-apps

greptile-apps Bot commented Aug 28, 2026 •

Copy link
Copy Markdown
Contributor

Greptile Summary

The PR improves persistent-shell stdin isolation and expands background-job management with waiting, incremental output, and reader-drain tracking.

  • Rewrites asynchronous shell lists so commands cannot consume later persistent-shell input.
  • Adds timeout-aware wait and incremental output --new controls.
  • Retains completed jobs while inherited output pipes are still draining.

Confidence Score: 4/5

The PR is not yet safe to merge because a normal jobs or subsequent bg operation can delete a completed job before its newly drained output is collected.

The drain fix preserves jobs only while the reader thread is alive; once it terminates, cleanup invoked by listing or starting jobs removes the buffered output even if no caller has read it.

Files Needing Attention: gptme/tools/shell_background.py

Important Files Changed

Filename Overview
gptme/tools/shell.py Adds parsed stdin redirection for asynchronous lists, wait dispatch, and concise actionable job-control guidance; the previously reported foreground and prompt issues appear fixed.
gptme/tools/shell_background.py Adds wait, incremental reads, and drain-aware retention, but cleanup can discard newly drained output before callers collect it.
tests/test_tools_shell.py Adds focused coverage for ampersand rewriting, trailing foreground redirection, and wait-command dispatch.
tests/test_tools_shell_background.py Covers incremental reads and active draining, but not the transition from drained output to cleanup before collection.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
  A[Background parent exits] --> B{Reader still draining?}
  B -- Yes --> C[Keep job tracked]
  C --> D[Descendant closes pipe]
  D --> E[Reader terminates]
  E --> F[Caller runs jobs or starts another job]
  F --> G[Cleanup deletes finished job]
  G --> H[Trailing buffered output becomes unavailable]
  B -- No --> F
Loading

Reviews (2): Last reviewed commit: "fix(shell): keep tool description under ..." | Re-trigger Greptile

Comment thread gptme/tools/shell.py Resolved
Comment thread gptme/tools/shell_background.py Resolved
Comment thread gptme/tools/shell.py Resolved
@codecov

codecov Bot commented Aug 28, 2026 •

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 66.03774% with 72 lines in your changes missing coverage. Please review.
✅ All tests successful. No failed tests found.

Files with missing lines Patch % Lines
tests/test_tools_shell_background.py 22.22% 49 Missing ⚠️
tests/test_tools_shell.py 25.00% 15 Missing ⚠️
gptme/tools/shell.py 89.74% 1 Missing and 3 partials ⚠️
gptme/tools/shell_background.py 95.55% 3 Missing and 1 partial ⚠️

📢 Thoughts on this report? Let us know!

CI failed test_tool_descriptions_within_openai_limit at 1064 chars
after documenting wait/output --new. Compress the tool-format
instructions so the new primitives still fit, including the
wait-vs-poll guidance.
@TimeToBuildBob

Copy link
Copy Markdown
Member

Pushed a maintainer commit onto this branch: 8f77fce (fix(shell): keep tool description under OpenAI 1024-char limit).

CI was red on the only real leftover: test_tool_descriptions_within_openai_limit — shell instructions were 1064 chars vs OpenAI's 1024-char function-description cap (tests.test_tools.py:637). That's the same failure as run 33198345325. The new wait / output --new docs were the overage, not the implementation.

I compressed the tool-format instructions (not the examples) so the new primitives still fit, including when to poll vs wait. Locally the description is 840 chars with this machine's program list; worst-case all 12 candidate programs still leaves ~150 chars of headroom. Focused tests: 75 passed (test_tool_descriptions_within_openai_limit, ampersand redirect cases, wait dispatch, background-job tests).

Rest of the PR looks like the right slice for #3667:

  • Ampersand rewrite tests cover the actual bug (sleep 1 & cat) and the easy false positives (2>&1, &>, quoted &, heredoc).
  • wait <id> [timeout] + output --new are the missing poll primitives. Timeout-leaves-it-running is the correct contract.
  • Greptile P1s on stdin isolation and drain reporting look addressed in 5d40c4c.

Not merging from here — waiting on CI for 8f77fce. No competing PR.

@TimeToBuildBob

Copy link
Copy Markdown
Member

@greptileai review

Comment thread gptme/tools/shell_background.py Resolved
@TimeToBuildBob

Copy link
Copy Markdown
Member

Approved the fork-PR GitHub Actions runs on 8f77fce. After the maintainer push they were sitting in action_required (only Greptile had actually executed). Test / Lint / Build / Docs / Benchmark / Security Advisories are in progress now.

Greptile's new P1 (cleanup discarding drained output) is the existing finished-job cleanup contract, not a drain regression. jobs/bg have always dropped completed jobs once the reader exits; this PR keeps them while draining. Collect-before-cleanup would be a separate design change and would break test_excludes_finished_jobs. Not changing that here.

Still not merging until CI on 8f77fce is actually green.

@TimeToBuildBob

Copy link
Copy Markdown
Member

CI on 8f77fce is green. Self-merge not eligible here (external author, core shell files, Greptile summary still 4/5 after the leftover P1 was withdrawn as the existing finished-job cleanup contract). Waiting for human review or merge.

@ErikBjare
ErikBjare merged commit 01b6ad5 into gptme:master Aug 29, 2026
17 checks passed
@TimeToBuildBob

Copy link
Copy Markdown
Member

Erik merged this (01b6ad50a). Re-ran the #3667 symptoms on master; all three pass. Closed #3667.

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.

shell tool: background command handling is fragile (ampersand mangled; no wait/poll primitive)

3 participants