Skip to content

Commit 3b51a13

Browse files
committed
fix: let the subagent timeout environment variable disable the timeout
`[subagent] timeout_ms = 0` already means "no timeout", and the sibling dynamic-workflow variable accepts the same value, but the subagent environment variable rejected `0` and silently fell back to the configured value. Accept a non-negative integer, and ignore a blank value rather than reading it as zero.
1 parent 31e229f commit 3b51a13

4 files changed

Lines changed: 20 additions & 2 deletions

File tree

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@pymodel/pythinker-code": patch
3+
---
4+
5+
Accept `0` from the subagent timeout environment variable to disable the timeout, matching the config file.

docs/configuration/env-vars.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -135,7 +135,7 @@ Switches that control the behavior of subsystems such as telemetry, background t
135135
| `PYTHINKER_CODE_PLUGIN_MARKETPLACE_URL` | Override the plugin marketplace JSON loaded by `/plugins`; useful for dev loopback servers, staging CDN files, or alternate marketplace directories | Unset (no default catalog; unset means only built-in entries are shown); accepts `http://`, `file://` URLs, and local paths |
136136
| `PYTHINKER_CODE_AGENT_DYNAMIC_WORKFLOW_MAX_CONCURRENCY` | Cap how many AgentDynamicWorkflow subagents run concurrently during the initial ramp; takes higher priority than `[dynamic_workflow] max_concurrency` in `config.toml` (unset means no cap) | Positive integer; invalid values fail fast |
137137
| `PYTHINKER_CODE_AGENT_DYNAMIC_WORKFLOW_TIMEOUT_MS` | Maximum wall-clock time (ms) for one `AgentDynamicWorkflow` subagent; takes higher priority than `[dynamic_workflow] timeout_ms` in `config.toml` (default `7200000`, or 2 hours) | Non-negative integer (`0` means no timeout); invalid values fall back to the config or default |
138-
| `PYTHINKER_SUBAGENT_TIMEOUT_MS` | Maximum wall-clock time (ms) a single `Agent` subagent may run, and the same limit for tower workers and reviewers; takes higher priority than `[subagent] timeout_ms` in `config.toml` (default `7200000`, i.e. 2 hours) | Positive integer; invalid values fall back to the config or default |
138+
| `PYTHINKER_SUBAGENT_TIMEOUT_MS` | Maximum wall-clock time (ms) a single `Agent` subagent may run, and the same limit for tower workers and reviewers; takes higher priority than `[subagent] timeout_ms` in `config.toml` (default `7200000`, i.e. 2 hours) | Non-negative integer (`0` means no timeout); invalid values fall back to the config or default |
139139
| `PYTHINKER_CODE_IDENTITY_NAME` | Display name the agent calls itself in the system prompt; takes higher priority than `[identity] name` in `config.toml` and is never written back to it | Any non-empty string; blank values read as unset |
140140
| `PYTHINKER_CODE_IDENTITY_SLUG` | Protocol identifier for the `User-Agent` product token sent to third-party providers and the MCP client name; takes higher priority than `[identity] slug`. Derived from the name when unset | Any non-empty string; normalized to lowercase with non-alphanumeric runs folded to `-` |
141141
| `PYTHINKER_CODE_BUILTIN_PRODUCT_SKILLS` | Whether the built-in skills documenting Pythinker Code itself are offered to the model; takes higher priority than `builtin_product_skills` in `config.toml` (default enabled) | Truthy: `1`/`true`/`yes`/`on`; falsy: `0`/`false`/`no`/`off` |

packages/agent-core-v2/src/session/subagent/configSection.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ export const SUBAGENT_TIMEOUT_ENV = 'PYTHINKER_SUBAGENT_TIMEOUT_MS';
6363

6464
function parseTimeoutMsEnv(raw: string): number | undefined {
6565
const parsed = Number(raw);
66-
return Number.isInteger(parsed) && parsed >= 1 ? parsed : undefined;
66+
return raw.trim() !== '' && Number.isInteger(parsed) && parsed >= 0 ? parsed : undefined;
6767
}
6868

6969
export const subagentEnvBindings: EnvBindings<SubagentConfig> = envBindings(

packages/agent-core-v2/test/app/config/config.test.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2065,6 +2065,19 @@ describe('subagent config section', () => {
20652065
disposables.dispose();
20662066
});
20672067

2068+
it('accepts 0 from the env var as "no timeout" and ignores a blank value', async () => {
2069+
const env: Record<string, string> = {};
2070+
const { config, disposables } = await createConfig(env, '[subagent]\ntimeout_ms = 5000\n');
2071+
2072+
env[SUBAGENT_TIMEOUT_ENV] = '0';
2073+
expect(resolveSubagentTimeoutMs(config)).toBe(0);
2074+
2075+
env[SUBAGENT_TIMEOUT_ENV] = ' ';
2076+
expect(resolveSubagentTimeoutMs(config)).toBe(5000);
2077+
2078+
disposables.dispose();
2079+
});
2080+
20682081
it('reads timeout_ms from config.toml and lets the env var win', async () => {
20692082
const env: Record<string, string> = {};
20702083
const { config, disposables } = await createConfig(env, '[subagent]\ntimeout_ms = 5000\n');

0 commit comments

Comments
 (0)