Summary
A long-running tools.exec_command invoked inside functions.exec can yield with its own session still running. If the wrapper only emits result.output, the nested session_id is discarded. Waiting on the outer functions.exec cell can then report completion even though the child shell process is still mutating the filesystem.
Observed impact
This happened during a large git switch. The outer cell completed with no output while the Git process continued in the background holding index.lock. A later status check raced the checkout. When the orphaned Git process was stopped, HEAD and the index were unchanged, but the working tree had been only partially transitioned: thousands of tracked deletions/modifications plus hundreds of untracked files. Recovery was possible only because HEAD/index remained intact and the filesystem state was separately preserved.
Reproduction shape
- Call
functions.exec.
- Inside it, await
tools.exec_command for a command that runs beyond the nested yield window.
- Emit only
result.output, omitting result.session_id.
- The outer call yields a cell ID; wait on that cell.
- Observe the outer cell complete while the nested child process remains alive.
Expected safety behavior
One of the following should hold:
functions.exec automatically retains and surfaces nested exec session IDs;
- the outer cell remains alive until every awaited nested exec finishes;
- nested long-running exec calls are rejected unless their session handle is explicitly consumed; or
- child processes are terminated when the outer isolate completes.
For mutating commands, an outer completed result must not coexist with an unreported live child process. A regression test using a slow filesystem mutation would be valuable.
Summary
A long-running
tools.exec_commandinvoked insidefunctions.execcan yield with its own session still running. If the wrapper only emitsresult.output, the nestedsession_idis discarded. Waiting on the outerfunctions.execcell can then report completion even though the child shell process is still mutating the filesystem.Observed impact
This happened during a large
git switch. The outer cell completed with no output while the Git process continued in the background holdingindex.lock. A later status check raced the checkout. When the orphaned Git process was stopped, HEAD and the index were unchanged, but the working tree had been only partially transitioned: thousands of tracked deletions/modifications plus hundreds of untracked files. Recovery was possible only because HEAD/index remained intact and the filesystem state was separately preserved.Reproduction shape
functions.exec.tools.exec_commandfor a command that runs beyond the nested yield window.result.output, omittingresult.session_id.Expected safety behavior
One of the following should hold:
functions.execautomatically retains and surfaces nested exec session IDs;For mutating commands, an outer completed result must not coexist with an unreported live child process. A regression test using a slow filesystem mutation would be valuable.