diff --git a/Clients.md b/Clients.md
index 1b0142f..79421ce 100644
--- a/Clients.md
+++ b/Clients.md
@@ -449,7 +449,22 @@ for each flow.
# Extra Settings
Coyote also lets you customize some extra settings for interacting with APIs:
-| Setting | Description |
-|-------------------|-------------------------------------------------------|
-| `proxy` | Set a proxy to use |
-| `connect_timeout` | Set the timeout in seconds for connections to the API |
+| Setting | Description | Default |
+|-------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|---------|
+| `proxy` | Set a proxy to use | - |
+| `connect_timeout` | Timeout in seconds for *establishing* a connection to the API | `10` |
+| `read_timeout` | Stall detector: maximum seconds allowed between successive reads from the API before the request is aborted.
Streaming-safe; the clock resets on every chunk received, so long streaming responses are fine as long as data keeps flowing.
Set to `0` to disable entirely (e.g. for very slow local models or large non-streaming embedding calls). | `300` |
+
+```yaml
+clients:
+ - type: openai
+ extra:
+ proxy: socks5://127.0.0.1:1080
+ connect_timeout: 10
+ read_timeout: 300
+```
+
+> [!NOTE]
+> Before `read_timeout` existed, a connection that stalled *after* connecting (e.g. a streaming response that
+> silently stopped mid-stream) would hang Coyote indefinitely. The default now aborts any request that goes 300
+> seconds without receiving a single byte.
diff --git a/Custom-Bash-Tools.md b/Custom-Bash-Tools.md
index 2f51979..24bf16b 100644
--- a/Custom-Bash-Tools.md
+++ b/Custom-Bash-Tools.md
@@ -26,6 +26,17 @@ define a CLI.
* A `# @describe`
* And a `main` function that writes to `$LLM_OUTPUT`
+## Execution Environment
+
+A few things to know about how Coyote runs your tool's subprocess:
+
+* **`stdin` is `/dev/null`.** Tools must not read from standard input. Anything that blocks on stdin (interactive
+ editors, `read` without `This variable is ignored if a configuration file exists. |
| `COYOTE_PATCH_{client}_CHAT_COMPLETIONS` | Patch chat completion requests to models on the corresponding client; Can modify the URL, body,
or headers. |
| `COYOTE_SHELL` | Specify the shell that Coyote should be using when executing commands |
+| `COYOTE_TOOL_TIMEOUT` | Wall-clock cap in seconds for any single tool-call subprocess (built-in and custom tools alike).
When exceeded, the tool process is killed and a `tool_call_error` is returned to the model instead of hanging the session forever.
Set to `0` for unlimited. Default: `1800` (30 minutes). |
# Files and Directory Related Variables
You can also customize the files and directories that Coyote loads its configuration files from:
diff --git a/Graph-Agents.md b/Graph-Agents.md
index f0e9753..bd5ebe2 100644
--- a/Graph-Agents.md
+++ b/Graph-Agents.md
@@ -130,9 +130,15 @@ nodes:
graph-step cap. If the same node id is entered more than this many times,
execution aborts with `Node 'X' visited N times (max_loop_iterations=...)`.
Default: 100.
-- **`timeout`:** Wall-clock cap on the entire graph run. The executor
- checks this between every node transition; nodes that block longer than
- the timeout will still finish before the check fires.
+- **`timeout`:** Wall-clock cap on the entire graph run. Checked before each
+ super-step **and** enforced during super-steps: if the budget elapses while
+ nodes are still running, the in-flight branches are aborted and the run
+ fails with a `timed out after Ns during super-step` error. Note that a
+ graph-level timeout is a hard bail. It does **not** route through node
+ `fallback`s, so any cleanup/reporting nodes are skipped. Prefer per-node
+ `timeout`s on `llm` nodes (which *do* route via `fallback`) as the
+ operative bound, and treat the graph-level `timeout` as a generous
+ backstop.
- **`initial_state`:** A JSON-compatible object. Values are seeded into
state before any node runs and are referenced from any node via `{{key}}`
templates.
@@ -466,6 +472,12 @@ list; an unknown entry is a startup error.
downstream nodes run, so downstream `{{output}}` references never see error
strings; the upstream cause is reported instead.
+A node-level `timeout` counts as a failure like any other: it routes via
+`fallback` when one is set, and fails the graph otherwise. This makes
+per-node `timeout`s the preferred way to bound long-running `llm` nodes.
+Unlike the graph-level `settings.timeout`, a node timeout still gives the
+graph a chance to recover or report through its `fallback` route.
+
### Retries (`max_attempts`)
`max_attempts` retries the LLM call **only on transient errors**. The