Skip to content

feat: expose nested lane maxConcurrent as config option#51622

Open
momo890709 wants to merge 3 commits intoopenclaw:mainfrom
momo890709:feat/nested-lane-concurrency-config
Open

feat: expose nested lane maxConcurrent as config option#51622
momo890709 wants to merge 3 commits intoopenclaw:mainfrom
momo890709:feat/nested-lane-concurrency-config

Conversation

@momo890709
Copy link
Copy Markdown

@momo890709 momo890709 commented Mar 21, 2026

Summary

The CommandLane.Nested lane (used by sessions_send and similar nested invocations) was hardcoded to maxConcurrent: 1 at gateway startup. This forced all nested session calls to run serially, causing severe bottlenecks in multi-agent architectures.

The Problem

When an orchestrator agent broadcasts to multiple sub-agents via sessions_send, each call queues behind the previous one. With maxConcurrent: 1:

  • Sending to 3 agents takes 3× the time (serial queue)
  • sessions_send timeout starts from call time, not delivery time — so queue wait eats into the timeout budget
  • Agents later in the queue reliably hit timeout even when they respond quickly

Real-world impact: A cron job sending sessions_send to 2 agents with 60s timeout would fail because:

  1. Agent A queues, waits ~30s, executes
  2. Agent B queues behind A, starts at T+30s, only has 30s left of the original 60s timeout
  3. Agent B times out even though it responds in 5 seconds

This issue was reported in #14214 but marked stale without resolution.

The Fix

Adds a new config key agents.defaults.nested.maxConcurrent (default: 4) so users can tune nested lane parallelism, consistent with how agents.defaults.maxConcurrent and agents.defaults.subagents.maxConcurrent already work.

The new setting is applied both at gateway startup and on hot-reload.

Config example

agents:
  defaults:
    nested:
      maxConcurrent: 8   # default 4

Files changed

File What
src/config/agent-limits.ts DEFAULT_NESTED_MAX_CONCURRENT, resolveNestedMaxConcurrent()
src/config/types.agent-defaults.ts nested?: { maxConcurrent?: number } on AgentDefaultsConfig
src/config/zod-schema.agent-defaults.ts Zod schema for the new key
src/config/defaults.ts Inject default in applyAgentDefaults()
src/gateway/server-lanes.ts Set nested lane at startup
src/gateway/server-reload-handlers.ts Set nested lane on hot-reload
src/config/config.agent-concurrency-defaults.test.ts Tests for defaults, clamping, config read

Test plan

  • ✅ All pre-commit checks pass (tsgo, oxlint, oxfmt, boundary lints)
  • pnpm test passes (unit tests for resolveNestedMaxConcurrent)
  • ✅ Gateway starts with default nested concurrency = 4
  • ✅ Setting agents.defaults.nested.maxConcurrent: 8 in config is respected
  • ✅ Hot-reload picks up nested concurrency changes without restart

Related


🤖 Generated with Claude Code

@openclaw-barnacle openclaw-barnacle bot added gateway Gateway runtime size: S labels Mar 21, 2026
@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps bot commented Mar 21, 2026

Greptile Summary

This PR exposes agents.defaults.nested.maxConcurrent as a new config option (default: 4), fixing a gap where the CommandLane.Nested lane was left at its hard-coded default of 1 and therefore serialized all nested sessions_send calls even when the gateway had capacity to spare. The implementation follows the exact established patterns for maxConcurrent and subagents.maxConcurrent end-to-end.

  • resolveNestedMaxConcurrent() in agent-limits.ts mirrors resolveSubagentMaxConcurrent() with identical clamping (Math.max(1, Math.floor(raw))) and fallback logic.
  • The early-return guard in applyAgentDefaults is correctly extended from hasMax && hasSubMaxhasMax && hasSubMax && hasNestedMax, so the default is injected whenever any of the three is missing.
  • Both startup (server-lanes.ts) and hot-reload (server-reload-handlers.ts) paths are updated consistently; CommandLane.Nested already existed in the lanes enum.
  • Zod schema and TypeScript types are updated in lockstep, with the JSDoc default value (Default: 4) matching DEFAULT_NESTED_MAX_CONCURRENT.
  • Unit tests cover the three key scenarios: unset → default, invalid → clamped to 1, and explicit value respected, plus an integration-level loadConfig() assertion.

Confidence Score: 5/5

  • This PR is safe to merge — it is a small, additive config option that follows every established pattern in the codebase with no breaking changes.
  • The change is purely additive (new optional config key), mirrors the existing subagents.maxConcurrent implementation exactly, has thorough test coverage, and both the startup and hot-reload paths are kept in sync. No behavioral regressions are possible for users who don't set the new key.
  • No files require special attention.

Last reviewed commit: "feat: expose nested ..."

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: bbdbfe6fb6

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment on lines +167 to +171
nested: z
.object({
maxConcurrent: z.number().int().positive().optional(),
})
.optional(),
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Enforce strict validation for agents.defaults.nested

The new agents.defaults.nested schema is not marked .strict(), so unknown keys under this object are silently dropped instead of raising a config error. In practice, a typo like maxConcurent will be ignored and the gateway will fall back to the default nested lane concurrency, which makes misconfiguration hard to detect; subagents already uses .strict() and does not have this problem.

Useful? React with 👍 / 👎.

@openclaw-barnacle openclaw-barnacle bot added the docs Improvements or additions to documentation label Mar 21, 2026
Ciel and others added 3 commits March 21, 2026 21:42
The nested lane (used by sessions_send) was hardcoded to maxConcurrent: 1,
forcing all nested session invocations to run serially. This adds a new
config key `agents.defaults.nested.maxConcurrent` (default: 4) so users
can tune nested lane parallelism the same way they can for subagent and
main lanes.

Co-Authored-By: Claude Opus 4.6 <[email protected]>
Matches the existing subagents schema behavior — unknown keys like
`maxConcurent` (missing an 'r') now raise a config validation error
instead of being silently dropped.

Co-Authored-By: Claude Opus 4.6 <[email protected]>
@momo890709 momo890709 force-pushed the feat/nested-lane-concurrency-config branch from 0900d92 to ccf4b3f Compare March 21, 2026 13:43
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

docs Improvements or additions to documentation gateway Gateway runtime size: S

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: nested lane hardcoded to maxConcurrent: 1 — sessions_send broadcasts cause cascading timeouts

1 participant