Skip to content

feat(agents): configurable default runTimeoutSeconds for subagent spawns#24594

Merged
steipete merged 3 commits intoopenclaw:mainfrom
mitchmcalister:feat/configurable-subagent-timeout
Feb 24, 2026
Merged

feat(agents): configurable default runTimeoutSeconds for subagent spawns#24594
steipete merged 3 commits intoopenclaw:mainfrom
mitchmcalister:feat/configurable-subagent-timeout

Conversation

@mitchmcalister
Copy link
Contributor

@mitchmcalister mitchmcalister commented Feb 23, 2026

When sessions_spawn is called without runTimeoutSeconds, subagents previously defaulted to 0 (no timeout). This adds a config key at agents.defaults.subagents.runTimeoutSeconds so operators can set a global default timeout for all subagent runs.

The agent-provided value still takes precedence when explicitly passed. When neither the agent nor the config specifies a timeout, behavior is unchanged (0 = no timeout), preserving backwards compatibility.

Closes #19288

Summary

  • Problem: sessions_spawn defaults to no timeout (0) when the agent omits runTimeoutSeconds, and there's no config key to change this default
  • Why it matters: subagents can run indefinitely with no progress signal — a coding subagent ran 2+ hours with no timeout, no progress, and no way for the parent to detect the stall
  • What changed: added agents.defaults.subagents.runTimeoutSeconds config key; moved loadConfig() above the timeout resolution block in sessions-spawn-tool.ts so the config value is available; added zod schema + TypeScript type for the new field
  • What did NOT change (scope boundary): explicit runTimeoutSeconds passed by the agent still wins; deployments without the config key behave identically to before (0 = no timeout)

Change Type (select all)

  • Bug fix
  • Feature
  • Refactor
  • Docs
  • Security hardening
  • Chore/infra

Scope (select all touched areas)

  • Gateway / orchestration
  • Skills / tool execution
  • Auth / tokens
  • Memory / storage
  • Integrations
  • API / contracts
  • UI / DX
  • CI/CD / infra

Linked Issue/PR

User-visible / Behavior Changes

  • New config key: agents.defaults.subagents.runTimeoutSeconds (number, seconds, default: unset = 0 = no timeout)
  • When set, all sessions_spawn calls that omit runTimeoutSeconds will use this value instead of 0
  • Agent-provided explicit runTimeoutSeconds still takes precedence

Security Impact (required)

  • New permissions/capabilities? No
  • Secrets/tokens handling changed? No
  • New/changed network calls? No
  • Command/tool execution surface changed? No
  • Data access scope changed? No

Repro + Verification

Environment

  • OS: Ubuntu 24.04 (Linux 6.8.0-100-generic)
  • Runtime/container: Node 25 / Docker (openclaw:local)
  • Model/provider: Codex gpt-5.3 / Gemini Flash Lite
  • Integration/channel: Discord
  • Relevant config (redacted):
    "agents": {
      "defaults": {
        "subagents": {
          "maxConcurrent": 8,
          "runTimeoutSeconds": 900
        }
      }
    }

Steps

  1. Set agents.defaults.subagents.runTimeoutSeconds: 900 in openclaw.json
  2. Restart gateway
  3. Trigger a sessions_spawn call without explicit runTimeoutSeconds (e.g., ask Johnny5 to spawn sports-analyst)
  4. Inspect session log — the agent gateway call should show timeout: 900

Expected

  • Gateway call includes timeout: 900 (from config)

Actual

  • Before this change: timeout: 0 (hardcoded fallback)
  • After this change: timeout: 900 (config default applied)

Evidence

  • Failing test/log before + passing after
  • Trace/log snippets
  • Screenshot/recording
  • Perf numbers (if relevant)

Three new unit tests (all passing, pnpm test:fast 621/622 — 1 pre-existing failure in browser/server.post-tabs-open-profile-unknown):

  1. sessions_spawn default runTimeoutSeconds > uses config default when agent omits runTimeoutSeconds — config set to 900, no param passed, asserts timeout: 900
  2. sessions_spawn default runTimeoutSeconds > explicit runTimeoutSeconds wins over config default — config 900, param 300, asserts timeout: 300
  3. sessions_spawn default runTimeoutSeconds (config absent) > falls back to 0 when config key absent — no config key, asserts timeout: 0

Human Verification (required)

  • Verified scenarios: build passes, all 3 new tests pass, deployed locally with runTimeoutSeconds: 900, container starts cleanly
  • Edge cases checked: config key absent (backwards compat), explicit param overrides config, non-finite/negative values sanitized by Math.max(0, Math.floor(...))
  • What you did not verify: end-to-end subagent timeout triggering (would need a long-running subagent to actually hit the 900s mark); per-agent override of runTimeoutSeconds (only global default tested)

Compatibility / Migration

  • Backward compatible? Yes
  • Config/env changes? Yes — new optional config key agents.defaults.subagents.runTimeoutSeconds
  • Migration needed? No — when absent, behavior is identical to before (0 = no timeout)

Failure Recovery (if this breaks)

  • How to disable/revert this change quickly: remove runTimeoutSeconds from config and restart — reverts to 0 (no timeout)
  • Files/config to restore: openclaw.json (remove key from agents.defaults.subagents)
  • Known bad symptoms reviewers should watch for: subagents timing out unexpectedly early (config value too low); loadConfig() being called before params are available (shouldn't happen — params comes from args which is available at function entry)

Risks and Mitigations

  • Risk: operator sets a very low timeout (e.g., 30s) and all subagents start timing out
    • Mitigation: value of 0 explicitly means "no timeout" (documented); zod schema allows min: 0 so operators can always disable; agent can override per-spawn

Greptile Summary

Adds a new config key agents.defaults.subagents.runTimeoutSeconds so operators can set a global default timeout for all subagent runs spawned via sessions_spawn. When an agent omits runTimeoutSeconds, the config default is used; when both are absent, behavior is unchanged (0 = no timeout). The implementation moves loadConfig() earlier in the execute function to make the config available for timeout resolution, adds Zod schema + TypeScript type for the new field, and includes three well-structured unit tests covering the key scenarios (config applied, explicit override wins, config absent fallback).

  • The priority chain is correct: explicit agent param > config default > 0 (no timeout)
  • Sanitization via Number.isFinite() + Math.max(0, Math.floor(...)) protects against invalid config values
  • The loadConfig() move is safe — it was previously called a few lines later, and all variables it depends on are already available
  • Other consumers of runTimeoutSeconds (subagents-tool.ts, commands-subagents.ts) read from the registry entry, which is populated at spawn time, so the config default flows through correctly
  • Minor style note: the Zod schema is missing .int() compared to all other *Seconds timeout fields in the codebase

Confidence Score: 4/5

  • This PR is safe to merge — it adds a backwards-compatible config key with correct fallback behavior and good test coverage.
  • The changes are well-scoped and backwards compatible. The priority chain logic is correct, edge cases (NaN, negative, non-finite) are handled defensively, and three targeted tests validate the key scenarios. The only finding is a minor schema consistency issue (missing .int() on the Zod validator). No logic bugs or security concerns.
  • Minor: src/config/zod-schema.agent-defaults.ts — missing .int() constraint for consistency with other timeout fields

Last reviewed commit: f63d23b

(2/5) Greptile learns from your feedback when you react with thumbs up/down!

@openclaw-barnacle openclaw-barnacle bot added agents Agent runtime and tooling size: S labels Feb 23, 2026
Copy link
Contributor

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

5 files reviewed, 1 comment

Edit Code Review Agent Settings | Greptile

@mitchmcalister mitchmcalister force-pushed the feat/configurable-subagent-timeout branch from f63d23b to 863418a Compare February 23, 2026 17:25
mitchmcalister and others added 3 commits February 24, 2026 04:20
When sessions_spawn is called without runTimeoutSeconds, subagents
previously defaulted to 0 (no timeout). This adds a config key at
agents.defaults.subagents.runTimeoutSeconds so operators can set a
global default timeout for all subagent runs.

The agent-provided value still takes precedence when explicitly passed.
When neither the agent nor the config specifies a timeout, behavior is
unchanged (0 = no timeout), preserving backwards compatibility.

Updated for the subagent-spawn.ts refactor (logic moved from
sessions-spawn-tool.ts to spawnSubagentDirect).

Closes #19288

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Matches convention used by all other *Seconds/*Ms timeout fields.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@steipete steipete force-pushed the feat/configurable-subagent-timeout branch from e5810e5 to 1497658 Compare February 24, 2026 04:22
@steipete steipete merged commit 8c5cf2d into openclaw:main Feb 24, 2026
8 of 9 checks passed
@steipete
Copy link
Contributor

Landed via temp rebase onto main.

Gate run:

  • pnpm test src/agents/openclaw-tools.subagents.sessions-spawn-default-timeout-absent.test.ts src/agents/openclaw-tools.subagents.sessions-spawn-default-timeout.test.ts
  • pnpm test src/agents/openclaw-tools.subagents.sessions-spawn.model.test.ts src/agents/openclaw-tools.subagents.sessions-spawn.allowlist.test.ts

Land commit: 1497658
Merge commit: 8c5cf2d

Post-approval follow-up included docs + changelog updates for:

  • agents.defaults.subagents.runTimeoutSeconds
  • sessions_spawn timeout default precedence

Thanks @mitchmcalister!

@openclaw-barnacle openclaw-barnacle bot added docs Improvements or additions to documentation gateway Gateway runtime labels Feb 24, 2026
plgs2005 pushed a commit to plgs2005/openclaw that referenced this pull request Feb 24, 2026
margulans pushed a commit to margulans/Neiron-AI-assistant that referenced this pull request Feb 25, 2026
brianleach pushed a commit to brianleach/openclaw that referenced this pull request Feb 26, 2026
mylukin pushed a commit to mylukin/openclaw that referenced this pull request Feb 26, 2026
r4jiv007 pushed a commit to r4jiv007/openclaw that referenced this pull request Feb 28, 2026
hughdidit pushed a commit to hughdidit/DAISy-Agency that referenced this pull request Mar 1, 2026
…24594) (thanks @mitchmcalister)

(cherry picked from commit 8c5cf2d)

# Conflicts:
#	docs/tools/subagents.md
hughdidit pushed a commit to hughdidit/DAISy-Agency that referenced this pull request Mar 3, 2026
…24594) (thanks @mitchmcalister)

(cherry picked from commit 8c5cf2d)

# Conflicts:
#	docs/tools/subagents.md
zooqueen pushed a commit to hanzoai/bot that referenced this pull request Mar 6, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

agents Agent runtime and tooling docs Improvements or additions to documentation gateway Gateway runtime size: S

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Feature: default runTimeoutSeconds for subagent spawns in config

2 participants