feat(scheduler): ScriptRunner through the process spawn policy

Spec: on Windows the command goes through Arguments like the bash tool, since cmd.exe doesn't parse ArgumentList quoting; the workspace is created if missing, and a timeout vs cancellation outcome is spelled out.
This commit is contained in:
EugeneTes
2026-09-27 10:14:33 +02:00
parent 1cc60267f9
commit 51bc0cedf1
3 changed files with 434 additions and 4 deletions

View File

@@ -163,16 +163,20 @@ Invalid jobs are dropped with a warning. `SaveAsync(jobs)` writes `jobs.json.<pi
- The shell matches the built-in `bash` tool, so a script the agent tested with `bash` behaves the same when it's
scheduled: `/bin/sh -c <command>` on Unix and `cmd.exe /d /s /c` on Windows. It's overridable through
`SchedulerOptions.Shell`. The command is passed through `ArgumentList`, not quoted by hand.
- The working directory is the target's workspace. Stdin is closed.
`SchedulerOptions.Shell`. On Unix the command is passed through `ArgumentList`, not quoted by hand. On Windows
`cmd.exe` doesn't parse `ArgumentList`'s quoting, so the command goes through `Arguments` as
`/d /s /c "<command>"`, exactly as the `bash` tool does. A configured `SchedulerOptions.Shell` always uses
`ArgumentList`.
- The working directory is the target's workspace (created if missing). Stdin is closed.
- The command, cwd and environment come from `IProcessSpawnPolicy.Prepare` (DI singleton, registered by
`AddAgenticCore`) with `Purpose = ScheduledScript`, `Scope = target`, `Origin = job id` and `TimeZoneId` = the
target's zone. So the script sees the same filtered environment as `bash`, with `TZ` set to the chat's zone, and
host hooks apply. There's no scheduler-specific scrub list any more (`SchedulerOptions.ScrubbedEnvironment` is
removed). A policy that throws is `Failed(null, "process environment hook failed: …")`. Session-level
`ProcessSpawnEvent` handlers don't run: a job fires without a live session.
- Timeout or cancellation → `Process.Kill(entireProcessTree: true)`. A shell that fails to start is `Failed(null, "failed to start <shell>: …")`,
never an exception.
- Timeout or cancellation → `Process.Kill(entireProcessTree: true)`. A timeout is `Failed(null, stderrTail, timedOut: true)`;
a cancellation throws `OperationCanceledException` after the kill. A shell that fails to start (or a workspace that
can't be created) is `Failed(null, "failed to start <shell>: …")`, never an exception.
- Output capture as in the reference, then `Trim()` on stdout.
### 4. `JobScheduler` (process-wide, the slice's entry type)