Skip to content

Commit 4e58fc9

Browse files
committed
Fix triggers in default config, needs operator intervention to recover
1 parent 4ecf032 commit 4e58fc9

11 files changed

Lines changed: 1698 additions & 66 deletions

File tree

‎README.md‎

Lines changed: 35 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -219,7 +219,23 @@ The runner is selected by `PAW_RUNNER`:
219219
- **locked down** — read-only rootfs, dropped Linux capabilities,
220220
non-root user, `no-new-privileges`, and memory/CPU/PID ceilings.
221221

222-
Idle sandboxes are reaped after `PAW_SANDBOX__TTL_SECONDS`.
222+
Idle sandboxes are reaped after `PAW_SANDBOX__TTL_SECONDS` by a
223+
background sweep (`PAW_SANDBOX__REAP_INTERVAL_SECONDS`). The sweep
224+
skips any sandbox with a live run, so the TTL bounds idleness and never
225+
shortens a run — `last_used` is only touched when a run starts, so a
226+
long run looks increasingly idle while it is working. The idle/stale
227+
decision is made inside the same lock that removes the sandbox, so a
228+
run claiming a stale sandbox cannot lose it to a concurrent sweep.
229+
230+
**Sandboxes are per-instance.** A sandbox is a process (or container)
231+
held by one web instance, so its registry row carries an
232+
`owner_instance` like runs do. A restart retires its own rows at
233+
startup; a row whose runner no longer holds it is retired lazily on the
234+
next run. If a conversation is routed to a *different* instance, that
235+
instance cannot reach the resident process holding the history, so it
236+
rebuilds — the agent's context restarts. That is logged and counted
237+
(`paw_sandbox_instance_migrations_total`), and the fix is session
238+
affinity: route a conversation to the same instance.
223239

224240
**Secret injection.** User secrets are Fernet-encrypted at rest and
225241
never returned by the API. For the `docker` runner they are decrypted
@@ -339,9 +355,18 @@ groups use a double underscore (e.g. `PAW_HARNESS__CMD`).
339355
| `PAW_HARNESS__CMD` | `python-agent-harness` | harness binary |
340356
| `PAW_HARNESS__CWD` | `""` | override the agent workspace dir per sandbox |
341357
| `PAW_HARNESS__TIMEOUT` | unset | host-side wall-clock budget for one run |
358+
| `PAW_HARNESS__MAX_ROUNDS` | unset | cap each run at N LLM rounds (`serve --max-rounds`); unset = unlimited |
359+
| `PAW_HARNESS__SANDBOX_TIMEOUT` | unset | per-run wall-clock budget enforced *inside* the sandbox (`serve --timeout`); unset = unlimited |
360+
| `PAW_HARNESS__ANSWER_TIMEOUT` | unset | how long the sandbox waits for an answer to a mid-run question; unset = wait forever |
342361
| `PAW_WORKSPACE_ROOT` | `./workspaces` | base dir for per-conversation workspaces |
343362
| `PAW_RUNNER` | `server` | `server` (host subprocess) or `docker` (isolated container) |
344-
| `PAW_SANDBOX__TTL_SECONDS` | `300` | idle sandbox reaper TTL |
363+
| `PAW_SANDBOX__TTL_SECONDS` | `300` | idle sandbox reaper TTL (never applied to a sandbox with a live run) |
364+
| `PAW_SANDBOX__REAP_INTERVAL_SECONDS` | `60` | how often the background reaper sweeps; `0` disables it |
365+
366+
The three budget vars are spawn-time arguments to `harness serve`, not
367+
fields on the submit op: the harness refuses to take a budget off the
368+
wire so a run cannot raise its own ceiling. Each is omitted from the
369+
command line entirely when unset, so the default is an unbounded run.
345370

346371
### Docker runner
347372

@@ -383,8 +408,16 @@ groups use a double underscore (e.g. `PAW_HARNESS__CMD`).
383408
| var | default | note |
384409
|---|---|---|
385410
| `PAW_SHUTDOWN_DRAIN_SECONDS` | `25` | on SIGTERM, seconds to let in-flight runs finish before exit |
411+
| `PAW_CANCEL_GRACE_SECONDS` | `10` | after a *user* cancels, how long to wait for the harness to honour it before destroying the sandbox; `0` disables escalation |
386412
| `PAW_INSTANCE_ID` | host+pid | this instance's id (set to pod/task name); scopes run reconciliation |
387413

414+
`PAW_CANCEL_GRACE_SECONDS` is a recovery path, not a run budget: it is
415+
armed only by an explicit cancel request, never by a timer. The
416+
protocol `cancel` is cooperative, so a run wedged in an uninterruptible
417+
operation never reaches the check — and while its row stays `running`
418+
the partial unique index locks that conversation out of new runs.
419+
Escalation closes the sandbox so the run's own worker can finalize it.
420+
388421
---
389422

390423
## Design principles

0 commit comments

Comments
 (0)