Background Processes
shell runs a command and waits for it, which is right for a build or a test
run and wrong for a dev server, a file watcher, or anything else that is meant
to keep going. For those the model has three tools:
| Tool | Level | Does |
|---|---|---|
background | 2 | Starts a command in the worktree and returns a job number, at once |
job_status | 0 | The job's state and the last lines of its output; with no number, a list |
job_kill | 2 | Stops the job and everything it started |
background asks as shell does: it runs its command through a shell, so its
patterns are exact commands with no wildcard.
background(npm run dev) remembers exactly that dev-server command. job_kill
asks too, since stopping a process the model started is still stopping a
process. job_status only reads.
Execution resources
A locked background call waits before spawning and before reporting a running job. The job's supervisor retains its resources after the launch call returns, until termination and cleanup. A long-lived job can block a later command in the same ref; there is no reentrant exemption.
job_status and job_kill do not reacquire a target's resources implicitly. If
their own configured rules overlap the target's held set, they fail instead of
waiting on themselves. /job-kill ID requests user-authorized cancellation
independently of those rules, even while publication waits on the job.
What a job is
A job runs in the ref's worktree with the same environment a shell command
gets. Locked jobs are children of a dedicated supervisor, in their own process
group; the job registry owns their control channel. background waits a moment
before answering so a command that dies at once (command not found, a bad
flag) is reported as dead with its output, not as started. Its answer is the
same report job_status gives:
job 0 running 0:02 npm run dev
> vite v5.0.0 dev server running at:
> http://localhost:5173/
job_status {"id": 0} reads more; job_kill {"id": 0} stops it.
Output is kept in memory, the last 2000 lines of each job, with stderr lines
marked ! . The model reads a tail (50 lines unless it asks for more), as you
would with tail. Nothing is written to disk and nothing streams into the
conversation; a job's line in the conversation is the background call that
started it, and its output appears when the model asks for it.
Ending
Every job ends with the session. Leaving heph requests process cleanup. For
locked jobs, a surviving Linux supervisor also handles heph crashes by stopping
and reaping descendants before releasing resources. Killing that supervisor
itself or deliberately escaping supervision is outside this guarantee. Within
the session, job_kill stops one, and its output stays readable afterwards. At
most eight jobs run at once; the ninth is refused until one is stopped.