refactor: centralize agent concurrency defaults

This commit is contained in:
Peter Steinberger
2026-01-20 10:27:58 +00:00
parent d88b239d3c
commit 213d9b47b0
8 changed files with 79 additions and 11 deletions

View File

@@ -7,6 +7,7 @@ Docs: https://docs.clawd.bot
### Changes ### Changes
- Repo: remove the Peekaboo git submodule now that the SPM release is used. - Repo: remove the Peekaboo git submodule now that the SPM release is used.
- Gateway: raise default lane concurrency for main and sub-agent runs. - Gateway: raise default lane concurrency for main and sub-agent runs.
- Config: centralize default agent concurrency limits.
### Fixes ### Fixes
- Web search: infer Perplexity base URL from API key source (direct vs OpenRouter). - Web search: infer Perplexity base URL from API key source (direct vs OpenRouter).

View File

@@ -0,0 +1,2 @@
export const DEFAULT_AGENT_MAX_CONCURRENT = 4;
export const DEFAULT_SUBAGENT_MAX_CONCURRENT = 8;

View File

@@ -1,5 +1,6 @@
import { resolveTalkApiKey } from "./talk.js"; import { resolveTalkApiKey } from "./talk.js";
import type { ClawdbotConfig } from "./types.js"; import type { ClawdbotConfig } from "./types.js";
import { DEFAULT_AGENT_MAX_CONCURRENT, DEFAULT_SUBAGENT_MAX_CONCURRENT } from "./agent-limits.js";
type WarnState = { warned: boolean }; type WarnState = { warned: boolean };
@@ -105,6 +106,43 @@ export function applyModelDefaults(cfg: ClawdbotConfig): ClawdbotConfig {
}; };
} }
export function applyAgentDefaults(cfg: ClawdbotConfig): ClawdbotConfig {
const agents = cfg.agents;
const defaults = agents?.defaults;
const hasMax =
typeof defaults?.maxConcurrent === "number" && Number.isFinite(defaults.maxConcurrent);
const hasSubMax =
typeof defaults?.subagents?.maxConcurrent === "number" &&
Number.isFinite(defaults.subagents.maxConcurrent);
if (hasMax && hasSubMax) return cfg;
let mutated = false;
const nextDefaults = defaults ? { ...defaults } : {};
if (!hasMax) {
nextDefaults.maxConcurrent = DEFAULT_AGENT_MAX_CONCURRENT;
mutated = true;
}
const nextSubagents = defaults?.subagents ? { ...defaults.subagents } : {};
if (!hasSubMax) {
nextSubagents.maxConcurrent = DEFAULT_SUBAGENT_MAX_CONCURRENT;
mutated = true;
}
if (!mutated) return cfg;
return {
...cfg,
agents: {
...agents,
defaults: {
...nextDefaults,
subagents: nextSubagents,
},
},
};
}
export function applyLoggingDefaults(cfg: ClawdbotConfig): ClawdbotConfig { export function applyLoggingDefaults(cfg: ClawdbotConfig): ClawdbotConfig {
const logging = cfg.logging; const logging = cfg.logging;
if (!logging) return cfg; if (!logging) return cfg;

View File

@@ -15,6 +15,7 @@ import { DuplicateAgentDirError, findDuplicateAgentDirs } from "./agent-dirs.js"
import { import {
applyCompactionDefaults, applyCompactionDefaults,
applyContextPruningDefaults, applyContextPruningDefaults,
applyAgentDefaults,
applyLoggingDefaults, applyLoggingDefaults,
applyMessageDefaults, applyMessageDefaults,
applyModelDefaults, applyModelDefaults,
@@ -244,11 +245,13 @@ export function createConfigIO(overrides: ConfigIoDeps = {}) {
const cfg = applyModelDefaults( const cfg = applyModelDefaults(
applyCompactionDefaults( applyCompactionDefaults(
applyContextPruningDefaults( applyContextPruningDefaults(
applyAgentDefaults(
applySessionDefaults( applySessionDefaults(
applyLoggingDefaults(applyMessageDefaults(validated.data as ClawdbotConfig)), applyLoggingDefaults(applyMessageDefaults(validated.data as ClawdbotConfig)),
), ),
), ),
), ),
),
); );
normalizeConfigPaths(cfg); normalizeConfigPaths(cfg);
@@ -291,7 +294,9 @@ export function createConfigIO(overrides: ConfigIoDeps = {}) {
const config = applyTalkApiKey( const config = applyTalkApiKey(
applyModelDefaults( applyModelDefaults(
applyCompactionDefaults( applyCompactionDefaults(
applyContextPruningDefaults(applySessionDefaults(applyMessageDefaults({}))), applyContextPruningDefaults(
applyAgentDefaults(applySessionDefaults(applyMessageDefaults({}))),
),
), ),
), ),
); );
@@ -402,10 +407,12 @@ export function createConfigIO(overrides: ConfigIoDeps = {}) {
config: normalizeConfigPaths( config: normalizeConfigPaths(
applyTalkApiKey( applyTalkApiKey(
applyModelDefaults( applyModelDefaults(
applyAgentDefaults(
applySessionDefaults(applyLoggingDefaults(applyMessageDefaults(validated.config))), applySessionDefaults(applyLoggingDefaults(applyMessageDefaults(validated.config))),
), ),
), ),
), ),
),
hash, hash,
issues: [], issues: [],
legacyIssues, legacyIssues,

View File

@@ -1,5 +1,5 @@
import { findDuplicateAgentDirs, formatDuplicateAgentDirError } from "./agent-dirs.js"; import { findDuplicateAgentDirs, formatDuplicateAgentDirError } from "./agent-dirs.js";
import { applyModelDefaults, applySessionDefaults } from "./defaults.js"; import { applyAgentDefaults, applyModelDefaults, applySessionDefaults } from "./defaults.js";
import { findLegacyConfigIssues } from "./legacy.js"; import { findLegacyConfigIssues } from "./legacy.js";
import type { ClawdbotConfig, ConfigValidationIssue } from "./types.js"; import type { ClawdbotConfig, ConfigValidationIssue } from "./types.js";
import { ClawdbotSchema } from "./zod-schema.js"; import { ClawdbotSchema } from "./zod-schema.js";
@@ -41,6 +41,8 @@ export function validateConfigObject(
} }
return { return {
ok: true, ok: true,
config: applyModelDefaults(applySessionDefaults(validated.data as ClawdbotConfig)), config: applyModelDefaults(
applyAgentDefaults(applySessionDefaults(validated.data as ClawdbotConfig)),
),
}; };
} }

View File

@@ -1,8 +1,18 @@
import type { loadConfig } from "../config/config.js"; import type { loadConfig } from "../config/config.js";
import {
DEFAULT_AGENT_MAX_CONCURRENT,
DEFAULT_SUBAGENT_MAX_CONCURRENT,
} from "../config/agent-limits.js";
import { setCommandLaneConcurrency } from "../process/command-queue.js"; import { setCommandLaneConcurrency } from "../process/command-queue.js";
export function applyGatewayLaneConcurrency(cfg: ReturnType<typeof loadConfig>) { export function applyGatewayLaneConcurrency(cfg: ReturnType<typeof loadConfig>) {
setCommandLaneConcurrency("cron", cfg.cron?.maxConcurrentRuns ?? 1); setCommandLaneConcurrency("cron", cfg.cron?.maxConcurrentRuns ?? 1);
setCommandLaneConcurrency("main", cfg.agents?.defaults?.maxConcurrent ?? 4); setCommandLaneConcurrency(
setCommandLaneConcurrency("subagent", cfg.agents?.defaults?.subagents?.maxConcurrent ?? 8); "main",
cfg.agents?.defaults?.maxConcurrent ?? DEFAULT_AGENT_MAX_CONCURRENT,
);
setCommandLaneConcurrency(
"subagent",
cfg.agents?.defaults?.subagents?.maxConcurrent ?? DEFAULT_SUBAGENT_MAX_CONCURRENT,
);
} }

View File

@@ -8,6 +8,10 @@ import {
setGatewaySigusr1RestartPolicy, setGatewaySigusr1RestartPolicy,
} from "../infra/restart.js"; } from "../infra/restart.js";
import { setCommandLaneConcurrency } from "../process/command-queue.js"; import { setCommandLaneConcurrency } from "../process/command-queue.js";
import {
DEFAULT_AGENT_MAX_CONCURRENT,
DEFAULT_SUBAGENT_MAX_CONCURRENT,
} from "../config/agent-limits.js";
import { isTruthyEnvValue } from "../infra/env.js"; import { isTruthyEnvValue } from "../infra/env.js";
import type { ChannelKind, GatewayReloadPlan } from "./config-reload.js"; import type { ChannelKind, GatewayReloadPlan } from "./config-reload.js";
import { resolveHooksConfig } from "./hooks.js"; import { resolveHooksConfig } from "./hooks.js";
@@ -127,10 +131,13 @@ export function createGatewayReloadHandlers(params: {
} }
setCommandLaneConcurrency("cron", nextConfig.cron?.maxConcurrentRuns ?? 1); setCommandLaneConcurrency("cron", nextConfig.cron?.maxConcurrentRuns ?? 1);
setCommandLaneConcurrency("main", nextConfig.agents?.defaults?.maxConcurrent ?? 4); setCommandLaneConcurrency(
"main",
nextConfig.agents?.defaults?.maxConcurrent ?? DEFAULT_AGENT_MAX_CONCURRENT,
);
setCommandLaneConcurrency( setCommandLaneConcurrency(
"subagent", "subagent",
nextConfig.agents?.defaults?.subagents?.maxConcurrent ?? 8, nextConfig.agents?.defaults?.subagents?.maxConcurrent ?? DEFAULT_SUBAGENT_MAX_CONCURRENT,
); );
if (plan.hotReasons.length > 0) { if (plan.hotReasons.length > 0) {

View File

@@ -1,6 +1,7 @@
import { type RunOptions, run } from "@grammyjs/runner"; import { type RunOptions, run } from "@grammyjs/runner";
import type { ClawdbotConfig } from "../config/config.js"; import type { ClawdbotConfig } from "../config/config.js";
import { loadConfig } from "../config/config.js"; import { loadConfig } from "../config/config.js";
import { DEFAULT_AGENT_MAX_CONCURRENT } from "../config/agent-limits.js";
import { computeBackoff, sleepWithAbort } from "../infra/backoff.js"; import { computeBackoff, sleepWithAbort } from "../infra/backoff.js";
import { formatDurationMs } from "../infra/format-duration.js"; import { formatDurationMs } from "../infra/format-duration.js";
import type { RuntimeEnv } from "../runtime.js"; import type { RuntimeEnv } from "../runtime.js";
@@ -28,7 +29,7 @@ export type MonitorTelegramOpts = {
export function createTelegramRunnerOptions(cfg: ClawdbotConfig): RunOptions<unknown> { export function createTelegramRunnerOptions(cfg: ClawdbotConfig): RunOptions<unknown> {
return { return {
sink: { sink: {
concurrency: cfg.agents?.defaults?.maxConcurrent ?? 1, concurrency: cfg.agents?.defaults?.maxConcurrent ?? DEFAULT_AGENT_MAX_CONCURRENT,
}, },
runner: { runner: {
fetch: { fetch: {