Skip to content

Commit 7be7a7f

Browse files
committed
docs(agent): document the host deadline bound and how to raise a timeout
`nemoclaw <name> agent` gained a host-side deadline and a nonzero exit for a timed-out turn, and neither was described anywhere. The recovery question #8723 raised was also unanswered: a reader who hit a timeout had no documented way to give the next attempt more time. Describe the host bound in the command reference, including the three argv forms that leave the OpenShell wait unbounded, and record that a turn whose deadline fired now exits 1 rather than the upstream 0. Add `timeoutPhase` to the JSON completion markers that already produce that exit. Explain in the inference timeout page that the two configuration keys bound different deadlines. `agents.defaults.timeoutSeconds` bounds the run and `agent --timeout` overrides it for a single run; the provider request keeps `models.providers.<provider-id>.timeoutSeconds`, which no flag overrides. Every timeout measured for #8723 fired in the provider phase, so a reader who raises only the run deadline is not helped. Add the in-place procedure beside the existing rebuild instruction. Name those same commands in the failure text, so an operator reads the documented `shields down` and `config set --restart` pair instead of an instruction to edit a file. Signed-off-by: Hung Le <hple@nvidia.com>
1 parent 3106809 commit 7be7a7f

4 files changed

Lines changed: 44 additions & 10 deletions

File tree

docs/inference/configure-inference-timeouts.mdx

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,18 @@ $$nemoclaw onboard
4646
This setting is baked into the sandbox image.
4747
Recreate an existing sandbox to apply a new value.
4848

49+
Each key bounds a different deadline.
50+
`agents.defaults.timeoutSeconds` bounds one agent run, and `$$nemoclaw <name> agent --timeout <seconds>` overrides it for a single run.
51+
`models.providers.<provider-id>.timeoutSeconds` bounds one provider request, and no flag overrides it.
52+
Raise the provider key when a turn times out while waiting for the model server, because a longer `--timeout` does not extend the provider request.
53+
54+
To change a deadline on an existing sandbox instead of recreating it, lower shields first and write the key directly.
55+
56+
```bash
57+
$$nemoclaw <sandbox-name> shields down
58+
$$nemoclaw <sandbox-name> config set --key agents.defaults.timeoutSeconds --value 1800 --restart
59+
```
60+
4961
</AgentOnly>
5062

5163
<AgentOnly variant="hermes">

docs/reference/commands.mdx

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1288,6 +1288,17 @@ Because a delivered turn always writes to one of the two streams, the wrapper re
12881288
The wrapper prints recovery guidance to `stderr` and exits with status `1`.
12891289
Pressing `Ctrl+C` interrupts the OpenShell child, and sending `SIGTERM` to the host wrapper forwards `SIGTERM` to that child.
12901290
NemoClaw waits for OpenShell to stop the in-sandbox turn, replays captured output, and returns status `130` for `SIGINT` or `143` for `SIGTERM`.
1291+
When the forwarded argv sets `openclaw agent --timeout <seconds>`, both captured paths bound the OpenShell command at that value plus 30 seconds.
1292+
The extra seconds let the in-sandbox turn report its own timeout first, so the host bound catches only a turn that stops answering.
1293+
1294+
These leave the OpenShell wait unbounded:
1295+
1296+
- `--timeout 0`.
1297+
- A value NemoClaw cannot read as a positive whole number of seconds.
1298+
- An argv without `--timeout`.
1299+
1300+
When the captured output reports that the turn's deadline fired, the wrapper replays the partial output and writes deadline guidance to `stderr`.
1301+
It exits with status `1` instead of the upstream status `0`.
12911302
The diagnostic shell-quotes the sandbox name and forwarded arguments, then redacts detected credential values before writing the recovery command to `stderr`.
12921303
If redaction changes the recovery command, the diagnostic tells you not to replay it; otherwise, it labels the command as runnable inside the sandbox.
12931304
For a registered sandbox, both captured paths pin the sandbox's recorded gateway with an explicit `-g`.
@@ -1297,11 +1308,12 @@ Raw `stderr`, including structured JSON diagnostics, is forwarded unchanged.
12971308
NemoClaw appends failed-tool or untrusted-child provenance only from the `stdout` JSON.
12981309
The wrapper reads completion markers only from the final matching OpenClaw response envelope: a local `{ payloads, meta }` response or a gateway `{ status, result: { payloads, meta } }` response.
12991310
It ignores earlier JSON progress or log records.
1300-
It exits with status `1` when that metadata contains `error.kind: "incomplete_turn"`, `livenessState: "abandoned"`, or `replayInvalid: true`, even when the envelope reports success.
1311+
It exits with status `1` when that metadata contains `error.kind: "incomplete_turn"`, `livenessState: "abandoned"`, `replayInvalid: true`, or a `timeoutPhase` value, even when the envelope reports success.
13011312
Marker-shaped values inside tool results, tool-call arguments, or other descendants do not change the exit status.
13021313
A turn can run every tool successfully and still become abandoned before it produces a reply.
13031314
The wrapper writes the unchanged JSON trace to `stdout` before it reports the incomplete turn, so the partial tool trace remains available.
13041315
The wrapper writes the verdict, the detected markers, and verify-before-retry guidance to `stderr`.
1316+
A `timeoutPhase` value names the phase the deadline fired in, so the wrapper writes deadline guidance in place of the generic incomplete-turn text.
13051317
Tool calls in a partial trace may have already applied side effects, so verify what the turn changed before you retry it.
13061318
The wrapper passes through an upstream non-zero exit status unchanged.
13071319
Literal `--json` values consumed by flags such as `-m` or `--reply-channel`, or arguments after `--`, stay on the normal passthrough path.

src/lib/actions/sandbox/agent/passthrough-help.test.ts

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -189,14 +189,20 @@ describe("writeTimedOutAgentTurnFailure", () => {
189189
expect(written).toContain("before retrying");
190190
});
191191

192-
it("offers the transcript export as the runnable recovery path (#8723)", () => {
192+
it("offers the documented commands that read the trace and raise a deadline (#8723)", () => {
193193
const { lines, proc } = collectStderr();
194194

195195
writeTimedOutAgentTurnFailure(proc, "my-assistant");
196196

197197
const written = lines.join("");
198198
expect(written).toContain("'my-assistant' sessions list");
199199
expect(written).toContain("'my-assistant' sessions export <key>");
200+
expect(written).toContain(
201+
"'my-assistant' config set --key <deadline-key> --value <seconds> --restart",
202+
);
203+
// Writing the config fails while shields are up, so the order is part of
204+
// the guidance rather than a detail the reader has to discover.
205+
expect(written.indexOf("shields down")).toBeLessThan(written.indexOf("config set"));
200206
});
201207

202208
it("names both deadlines instead of offering --timeout as the fix (#8723)", () => {
@@ -205,11 +211,9 @@ describe("writeTimedOutAgentTurnFailure", () => {
205211
writeTimedOutAgentTurnFailure(proc, "my-assistant", "provider");
206212

207213
const written = lines.join("");
208-
expect(written).toContain("agents.defaults.timeoutSeconds sets the run deadline");
209-
expect(written).toContain(
210-
"models.providers.<id>.timeoutSeconds sets the provider request deadline",
211-
);
212-
expect(written).toContain("never changes");
214+
expect(written).toContain("agents.defaults.timeoutSeconds bounds the run");
215+
expect(written).toContain("models.providers.<id>.timeoutSeconds");
216+
expect(written).toContain("no flag overrides it");
213217
// A provider-phase timeout does not respond to the flag, so it is never
214218
// presented as a runnable recovery command.
215219
expect(written).not.toMatch(/^ {4}\S*nemoclaw.* agent --timeout/m);

src/lib/actions/sandbox/agent/passthrough-help.ts

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -120,13 +120,19 @@ export function writeTimedOutAgentTurnFailure(
120120
proc.stderr.write(
121121
` ${CLI_NAME} ${target} sessions export <key> — export the partial transcript\n`,
122122
);
123-
proc.stderr.write(" A longer deadline is an openclaw.json change, not a flag.\n");
124123
proc.stderr.write(
125-
" agents.defaults.timeoutSeconds sets the run deadline, which `agent --timeout` overrides for one run.\n",
124+
` ${CLI_NAME} ${target} shields down — unlock configuration writes\n`,
126125
);
127126
proc.stderr.write(
128-
" models.providers.<id>.timeoutSeconds sets the provider request deadline, which `agent --timeout` never changes.\n",
127+
` ${CLI_NAME} ${target} config set --key <deadline-key> --value <seconds> --restart — raise the deadline\n`,
129128
);
129+
proc.stderr.write(
130+
" Two keys carry a deadline. agents.defaults.timeoutSeconds bounds the run, and\n",
131+
);
132+
proc.stderr.write(
133+
" `agent --timeout <seconds>` overrides it for a single run. models.providers.<id>.timeoutSeconds\n",
134+
);
135+
proc.stderr.write(" bounds the provider request, and no flag overrides it.\n");
130136
proc.stderr.write(" Inspect the partial output and affected resources before retrying.\n");
131137
}
132138

0 commit comments

Comments
 (0)