Skip to content
Closed
13 changes: 13 additions & 0 deletions docs/content/docs/(configuration)/config.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,12 @@ eager_embedding_load = true
refresh_secs = 900
startup_delay_secs = 5

# Worker watchdog settings (channel-spawned workers).
[defaults.worker]
hard_timeout_secs = 0 # optional hard cap; 0 disables hard timeout
idle_timeout_secs = 120
max_tool_timeout_secs = 3600 # cap for shell/exec timeout_seconds

# Browser automation for workers.
[defaults.browser]
enabled = true
Expand All @@ -123,6 +129,12 @@ user_timezone = "America/Los_Angeles" # optional per-agent timezone override fo
[agents.routing]
channel = "anthropic/claude-opus-4-20250514"

# Per-agent worker watchdog overrides.
[agents.worker]
hard_timeout_secs = 0
idle_timeout_secs = 90
max_tool_timeout_secs = 1800

# Per-agent sandbox configuration.
[agents.sandbox]
mode = "enabled" # "enabled" (default) or "disabled"
Expand Down Expand Up @@ -230,6 +242,7 @@ Most config values are hot-reloaded when their files change. Spacebot watches `c
| `max_turns` | Yes | Next channel message uses new limit |
| `context_window` | Yes | Next compaction/worker check uses new size |
| `max_concurrent_branches` | Yes | Next branch spawn checks new limit |
| Worker watchdog (`[agents.worker]`) | Yes | Next watchdog pass uses new hard/idle values; new worker spawns use new tool timeout cap. Existing worker/cortex chat sessions keep their prior cap until recreated (or agent restart). |
| Browser config | Yes | Next worker spawn uses new config |
| Warmup config | Yes | Next warmup pass uses new values |
| Identity files (SOUL.md, etc.) | Yes | Next channel message renders new identity |
Expand Down
2 changes: 2 additions & 0 deletions docs/content/docs/(core)/architecture.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,8 @@ When a branch or worker completes, the channel doesn't poll for results. The com

Retrigger events are debounced. If multiple branches complete within a short window, the channel batches them into a single turn. A retrigger limit (default: 3 per turn) prevents infinite cascades where a branch result triggers a new branch that triggers another retrigger.

Completion relays are also mirrored to a durable SQLite outbox. If the channel self-queue is full or closed during retrigger enqueue, pending relays are retried and replayed from that outbox instead of being dropped.

### Status Block

Every turn, the channel receives a live snapshot of all active processes:
Expand Down
2 changes: 2 additions & 0 deletions docs/content/docs/(features)/opencode.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,8 @@ route: worker_id=abc, message="now add the database layer"

The OpenCode session accumulates context across follow-ups, so subsequent messages benefit from everything the agent learned during earlier work.

Like builtin interactive workers, OpenCode workers suspend watchdog timeouts while they are in `WaitingForInput`. Hard/idle watchdog limits apply only when the worker is actively running.

## Model Override

You can override the model used by OpenCode workers:
Expand Down
14 changes: 13 additions & 1 deletion docs/content/docs/(features)/workers.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ Channel: "route: looks good, run the tests"
→ Worker runs tests, returns result
```

Interactive workers stay alive until the input channel is dropped, a follow-up fails, or the channel cancels them.
Interactive workers stay alive while waiting for follow-up input. They stop when the input channel is dropped, a follow-up fails, or the channel cancels them.

## Tools

Expand Down Expand Up @@ -70,6 +70,18 @@ WaitingForInput ──→ Failed (follow-up processing failed)

`Done` and `Failed` are terminal. Illegal transitions are runtime errors.

## Watchdog Timeouts

Channel-owned workers are monitored by a watchdog with three limits (configured in `[defaults.worker]` / `[agents.worker]`):

- `hard_timeout_secs` applies while the worker is actively running (`0` disables hard-timeout enforcement).
- `idle_timeout_secs` applies while the worker is actively running. The idle timer starts when the worker is registered and is refreshed by `set_status` and tool activity events.
- `max_tool_timeout_secs` caps per-call `timeout_seconds` for `shell` and `exec` tool invocations.

When a worker enters `WaitingForInput`, watchdog timeouts are suspended until it resumes running.

Worker completion relays are durable: branch/worker completion notifications are written to a channel retrigger outbox in SQLite before enqueue. If the channel retrigger queue is unavailable, relays are replayed from the outbox when the channel loop is healthy again.

## Context and History

Workers start with a **fresh empty history**. They have no access to the channel's conversation. Their only context is:
Expand Down
20 changes: 20 additions & 0 deletions migrations/20260228000001_channel_retrigger_outbox.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
-- Durable outbox for branch/worker completion retriggers.
-- Ensures completion relays can be replayed after queue failures or restarts.

CREATE TABLE IF NOT EXISTS channel_retrigger_outbox (
id TEXT PRIMARY KEY,
agent_id TEXT NOT NULL,
channel_id TEXT NOT NULL,
result_payload TEXT NOT NULL,
attempt_count INTEGER NOT NULL DEFAULT 0,
next_attempt_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
last_error TEXT,
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
delivered_at TIMESTAMP
);

CREATE INDEX IF NOT EXISTS idx_retrigger_outbox_pending
ON channel_retrigger_outbox(agent_id, channel_id, delivered_at, next_attempt_at);

CREATE INDEX IF NOT EXISTS idx_retrigger_outbox_created
ON channel_retrigger_outbox(created_at);
Loading