Complete Phase 1 of subprocess management in gadget-drone:
- SubProcessService with graceful shutdown (SIGINT→SIGKILL), halt, getLog
- SubprocessTool with create/list/kill/killAll/halt/log commands
- Registered in agent toolbox for build/test/ship/develop modes
- Fixed missing {{process_management_block}} replacement in chat-session.ts
- 34 unit tests across service and tool
- Comprehensive documentation in docs/subprocess.md
145 lines
5.4 KiB
Markdown
145 lines
5.4 KiB
Markdown
# Gadget Drone Sub-Processes
|
|
|
|
The Gadget Code agent manages child processes through gadget-drone's SubProcess system. In the Gadget ecosystem, we refer to a managed child process spawned by gadget-drone as a _SubProcess_.
|
|
|
|
## Phase Status
|
|
|
|
- **Phase 1 (Complete):** SubProcessService + SubprocessTool internal to drone.
|
|
- **Phase 2 (Planned):** Expose SubProcess status and logs to gadget-code:frontend (IDE) via gadget-code:backend.
|
|
|
|
## SubProcessService
|
|
|
|
[Source](../src/services/subprocess.ts)
|
|
|
|
`SubProcessService` is a singleton service that manages child processes on behalf of Gadget Drone. It is started and stopped as part of the standard service lifecycle in `gadget-drone.ts`. On stop, all managed subprocesses are killed (SIGINT with graceful timeout, falling back to SIGKILL) and their log files are removed.
|
|
|
|
### API
|
|
|
|
| Method | Signature | Description |
|
|
|---|---|---|
|
|
| `create` | `(project: IProject, cmd: string, args?: string[]) => Promise<SubProcess>` | Spawns a child process, starts writing stdout/stderr to log files under `<gadgetDir>/subprocess-logs/`. |
|
|
| `ps` | `() => SubProcess[]` | Returns an array of all managed subprocesses. |
|
|
| `summarize` | `(sp: SubProcess) => SubProcessSummary` | Returns a serializable summary (without ChildProcess/WriteStream refs). |
|
|
| `killPid` | `(pid: number) => Promise<boolean>` | Kills a specific subprocess by PID and removes its log files. |
|
|
| `killProject` | `(projectId: GadgetId) => Promise<void>` | Kills all subprocesses for a given project. |
|
|
| `killSubProcess` | `(sp: SubProcess) => Promise<boolean>` | Kills a subprocess and removes its log files. |
|
|
| `killAll` | `() => Promise<boolean>` | Kills all managed subprocesses and removes all log files. |
|
|
| `haltPid` | `(pid: number) => Promise<boolean>` | Stops a subprocess by PID but **retains its log files** for inspection. |
|
|
| `haltSubProcess` | `(sp: SubProcess) => Promise<boolean>` | Stops a subprocess but retains its log files. |
|
|
| `getLog` | `(pid: number, stream: "stdout" \| "stderr") => Promise<string>` | Reads the full content of a subprocess's stdout or stderr log file. |
|
|
|
|
### Lifecycle
|
|
|
|
The service is registered in `GadgetDrone.startServices()` / `stopServices()`. During `stop()`, `killAll()` is called, ensuring no orphaned processes remain when the drone shuts down.
|
|
|
|
### Process Termination
|
|
|
|
Termination follows a graceful escalation:
|
|
1. Send `SIGINT` to the process.
|
|
2. Wait up to 5 seconds for the process to exit.
|
|
3. If still alive, send `SIGKILL`.
|
|
4. Wait up to another 5 seconds.
|
|
5. Remove stdout/stderr listeners.
|
|
6. Close log file streams.
|
|
|
|
For `kill`, log files are deleted after termination. For `halt`, log files are preserved.
|
|
|
|
## SubprocessTool
|
|
|
|
[Source](../src/tools/system/subprocess.ts)
|
|
|
|
The `subprocess` tool is registered in the agent's toolbox for modes: `Build`, `Test`, `Ship`, `Develop`. It provides a single function with a `cmd` parameter that dispatches to the appropriate operation.
|
|
|
|
### Tool Definition
|
|
|
|
**Name:** `subprocess`
|
|
**Category:** `system`
|
|
|
|
**Parameters:**
|
|
|
|
| Parameter | Type | Required | Description |
|
|
|---|---|---|---|
|
|
| `cmd` | string (enum) | yes | One of: `create`, `list`, `kill`, `killAll`, `halt`, `log` |
|
|
| `command` | string | for `create` | The executable to spawn (e.g., `node`, `npm`, `python`) |
|
|
| `args` | string[] | for `create` | Command-line arguments for the spawned process |
|
|
| `pid` | number | for `kill`, `halt`, `log` | The managed process ID |
|
|
| `which` | string (enum) | for `log` | `"stdout"` or `"stderr"` |
|
|
|
|
### Commands
|
|
|
|
#### `create`
|
|
Spawns a new child process in the project directory. Logs stdout and stderr to `<gadgetDir>/subprocess-logs/`.
|
|
|
|
Response:
|
|
```
|
|
SUBPROCESS CREATED
|
|
pid: 12345
|
|
stdout: /path/to/.gadget/subprocess-logs/subproc-12345.stdout.log
|
|
stderr: /path/to/.gadget/subprocess-logs/subproc-12345.stderr.log
|
|
```
|
|
|
|
#### `list`
|
|
Returns all currently managed subprocesses with their PIDs, project slugs, and timestamps.
|
|
|
|
Response:
|
|
```
|
|
SUBPROCESS LIST
|
|
pid: 12345 | project: my-project | created: ... | updated: ...
|
|
```
|
|
|
|
#### `kill`
|
|
Terminates a subprocess and removes its log files. Uses SIGINT with graceful escalation.
|
|
|
|
Response:
|
|
```
|
|
SUBPROCESS KILLED
|
|
pid: 12345
|
|
```
|
|
|
|
#### `killAll`
|
|
Terminates all managed subprocesses and removes all log files.
|
|
|
|
Response:
|
|
```
|
|
SUBPROCESS KILLALL
|
|
All managed subprocesses have been terminated and their logs removed.
|
|
```
|
|
|
|
#### `halt`
|
|
Terminates a subprocess but retains its log files. Useful for inspecting logs after process exit.
|
|
|
|
Response:
|
|
```
|
|
SUBPROCESS HALTED
|
|
pid: 12345
|
|
The process has been stopped. Log files are retained for inspection.
|
|
```
|
|
|
|
#### `log`
|
|
Reads stdout or stderr output from a subprocess's log file.
|
|
|
|
Response:
|
|
```
|
|
SUBPROCESS LOG (stdout)
|
|
pid: 12345
|
|
---
|
|
<log content>
|
|
```
|
|
|
|
## Integration Points
|
|
|
|
### System Prompts
|
|
|
|
All agent modes include a **Process Management** block in their system prompt. The block is loaded from [process-management-block.md](../../gadget-code/data/prompts/common/process-management-block.md) and rendered via `{{process_management_block}}` in the mode templates. This tells the agent about the `subprocess` tool and how to use it.
|
|
|
|
### Shutdown
|
|
|
|
`gadget-drone.ts` calls `SubProcessService.stop()` during shutdown, which triggers `killAll()` to clean up any remaining child processes.
|
|
|
|
## Tests
|
|
|
|
| File | What it covers |
|
|
|---|---|
|
|
| `src/services/subprocess.test.ts` | SubProcessService: create, ps, killPid, killAll, haltPid, getLog, error cases |
|
|
| `src/tools/system/subprocess.test.ts` | SubprocessTool: all 6 commands, parameter validation, service error handling |
|