Skip to content

Bound Codex bootstrap thread limits for token control #145

Description

@johnnyisme

Problem Situation

Codex token optimization lowered local agent thread limits to 4, but LazyCodex/OMO bootstrap provisioning could restore high defaults. The shipped OMO bundles still set CODEX_SUBAGENT_THREAD_LIMIT = 1000 and CODEX_MULTI_AGENT_V2_THREAD_LIMIT = 16, so a session-start/worker provisioning pass can undo local token-control settings.

Reproduction Logs

RED regression before the fix, from a fresh code-yeongyu/lazycodex worktree at 895b70c:

node --test test/codex-token-thread-limits.test.mjs
fails because shipped bundles do not contain `var CODEX_SUBAGENT_THREAD_LIMIT = 4;` or `var CODEX_MULTI_AGENT_V2_THREAD_LIMIT = 4;`.

Manual smoke before completing the fix also showed the bootstrap worker lowered [agents].max_threads but left existing [features.multi_agent_v2].max_concurrent_threads_per_session = 16, because the helper returned early when that setting already existed.

Root Cause

The generated OMO bootstrap/install helper has two token-cost problems:

  • It ships high concurrency defaults: CODEX_SUBAGENT_THREAD_LIMIT = 1000 and CODEX_MULTI_AGENT_V2_THREAD_LIMIT = 16.
  • It skips rewriting features.multi_agent_v2.max_concurrent_threads_per_session when the setting already exists, so existing installs can remain at 16 even after lowering the constant.

Verified Fix

Lower both shipped defaults to 4, remove the early return so existing features.multi_agent_v2.max_concurrent_threads_per_session values are replaced with the bounded value, and add a Node test that runs the bootstrap worker against a temporary Codex home with 1000/16 configured.

diff --git a/plugins/omo/components/bootstrap/dist/cli.js b/plugins/omo/components/bootstrap/dist/cli.js
index 43fc26a..6fa7ac1 100755
--- a/plugins/omo/components/bootstrap/dist/cli.js
+++ b/plugins/omo/components/bootstrap/dist/cli.js
@@ -3006,8 +3006,8 @@ import { dirname as dirname7, isAbsolute as isAbsolute5, join as join17 } from "
 var CODEX_AGENTS_HEADER = "agents";
 var CODEX_MULTI_AGENT_V2_HEADER = "features.multi_agent_v2";
 var CODEX_MULTI_AGENT_V2_THREAD_LIMIT_KEY = `${CODEX_MULTI_AGENT_V2_HEADER}.max_concurrent_threads_per_session`;
-var CODEX_SUBAGENT_THREAD_LIMIT = 1000;
-var CODEX_MULTI_AGENT_V2_THREAD_LIMIT = 16;
+var CODEX_SUBAGENT_THREAD_LIMIT = 4;
+var CODEX_MULTI_AGENT_V2_THREAD_LIMIT = 4;
 function ensureCodexMultiAgentV2Config(config, options = {}) {
   const featureFlag = removeFeatureFlagSetting(config, "multi_agent_v2");
   const v2Preferred = options.multiAgentVersion === "v2";
@@ -3015,8 +3015,6 @@ function ensureCodexMultiAgentV2Config(config, options = {}) {
   const agentsConfig = v2Preferred ? removeAgentsMaxThreads(featureFlag.config) : modelKnown ? ensureAgentsMaxThreads(featureFlag.config) : raiseExistingAgentsMaxThreads(featureFlag.config);
   const preserveDisable = featureFlag.value === false && !v2Preferred;
   const featureConfig = preserveDisable ? setMultiAgentV2Disable(agentsConfig) : v2Preferred ? removeMultiAgentV2Disable(agentsConfig) : agentsConfig;
-  if (hasTomlSetting(featureConfig, CODEX_MULTI_AGENT_V2_THREAD_LIMIT_KEY))
-    return featureConfig;
   const section = findTomlSection(featureConfig, CODEX_MULTI_AGENT_V2_HEADER);
   if (!section) {
     const enabledSetting = preserveDisable ? `enabled = false
diff --git a/plugins/omo/dist/cli-node/index.js b/plugins/omo/dist/cli-node/index.js
index c41f475..e971294 100755
--- a/plugins/omo/dist/cli-node/index.js
+++ b/plugins/omo/dist/cli-node/index.js
@@ -98168,8 +98168,8 @@ import { dirname as dirname14, isAbsolute as isAbsolute7, join as join38 } from
 var CODEX_AGENTS_HEADER = "agents";
 var CODEX_MULTI_AGENT_V2_HEADER = "features.multi_agent_v2";
 var CODEX_MULTI_AGENT_V2_THREAD_LIMIT_KEY = `${CODEX_MULTI_AGENT_V2_HEADER}.max_concurrent_threads_per_session`;
-var CODEX_SUBAGENT_THREAD_LIMIT = 1000;
-var CODEX_MULTI_AGENT_V2_THREAD_LIMIT = 16;
+var CODEX_SUBAGENT_THREAD_LIMIT = 4;
+var CODEX_MULTI_AGENT_V2_THREAD_LIMIT = 4;
 function ensureCodexMultiAgentV2Config(config3, options = {}) {
   const featureFlag = removeFeatureFlagSetting(config3, "multi_agent_v2");
   const v2Preferred = options.multiAgentVersion === "v2";
@@ -98177,8 +98177,6 @@ function ensureCodexMultiAgentV2Config(config3, options = {}) {
   const agentsConfig = v2Preferred ? removeAgentsMaxThreads(featureFlag.config) : modelKnown ? ensureAgentsMaxThreads(featureFlag.config) : raiseExistingAgentsMaxThreads(featureFlag.config);
   const preserveDisable = featureFlag.value === false && !v2Preferred;
   const featureConfig = preserveDisable ? setMultiAgentV2Disable(agentsConfig) : v2Preferred ? removeMultiAgentV2Disable(agentsConfig) : agentsConfig;
-  if (hasTomlSetting(featureConfig, CODEX_MULTI_AGENT_V2_THREAD_LIMIT_KEY))
-    return featureConfig;
   const section = findTomlSection(featureConfig, CODEX_MULTI_AGENT_V2_HEADER);
   if (!section) {
     const enabledSetting = preserveDisable ? `enabled = false
diff --git a/plugins/omo/dist/cli/index.js b/plugins/omo/dist/cli/index.js
index a2e6bbb..a128de5 100755
--- a/plugins/omo/dist/cli/index.js
+++ b/plugins/omo/dist/cli/index.js
@@ -98112,8 +98112,8 @@ import { dirname as dirname14, isAbsolute as isAbsolute7, join as join38 } from
 var CODEX_AGENTS_HEADER = "agents";
 var CODEX_MULTI_AGENT_V2_HEADER = "features.multi_agent_v2";
 var CODEX_MULTI_AGENT_V2_THREAD_LIMIT_KEY = `${CODEX_MULTI_AGENT_V2_HEADER}.max_concurrent_threads_per_session`;
-var CODEX_SUBAGENT_THREAD_LIMIT = 1000;
-var CODEX_MULTI_AGENT_V2_THREAD_LIMIT = 16;
+var CODEX_SUBAGENT_THREAD_LIMIT = 4;
+var CODEX_MULTI_AGENT_V2_THREAD_LIMIT = 4;
 function ensureCodexMultiAgentV2Config(config3, options = {}) {
   const featureFlag = removeFeatureFlagSetting(config3, "multi_agent_v2");
   const v2Preferred = options.multiAgentVersion === "v2";
@@ -98121,8 +98121,6 @@ function ensureCodexMultiAgentV2Config(config3, options = {}) {
   const agentsConfig = v2Preferred ? removeAgentsMaxThreads(featureFlag.config) : modelKnown ? ensureAgentsMaxThreads(featureFlag.config) : raiseExistingAgentsMaxThreads(featureFlag.config);
   const preserveDisable = featureFlag.value === false && !v2Preferred;
   const featureConfig = preserveDisable ? setMultiAgentV2Disable(agentsConfig) : v2Preferred ? removeMultiAgentV2Disable(agentsConfig) : agentsConfig;
-  if (hasTomlSetting(featureConfig, CODEX_MULTI_AGENT_V2_THREAD_LIMIT_KEY))
-    return featureConfig;
   const section = findTomlSection(featureConfig, CODEX_MULTI_AGENT_V2_HEADER);
   if (!section) {
     const enabledSetting = preserveDisable ? `enabled = false
diff --git a/test/codex-token-thread-limits.test.mjs b/test/codex-token-thread-limits.test.mjs
new file mode 100644
index 0000000..a13311b
--- /dev/null
+++ b/test/codex-token-thread-limits.test.mjs
@@ -0,0 +1,71 @@
+import { mkdirSync, mkdtempSync, readFileSync, writeFileSync } from "node:fs";
+import assert from "node:assert/strict";
+import { tmpdir } from "node:os";
+import { join } from "node:path";
+import { spawnSync } from "node:child_process";
+import { describe, test } from "node:test";
+
+const repoRoot = new URL("..", import.meta.url).pathname;
+
+const shippedBundles = [
+	"plugins/omo/components/bootstrap/dist/cli.js",
+	"plugins/omo/dist/cli/index.js",
+	"plugins/omo/dist/cli-node/index.js",
+];
+
+describe("Codex token-control thread limits", () => {
+	for (const relativePath of shippedBundles) {
+		test(`${relativePath} keeps bootstrap thread limits bounded`, () => {
+			const source = readFileSync(join(repoRoot, relativePath), "utf8");
+
+			assert.match(source, /var CODEX_SUBAGENT_THREAD_LIMIT = 4;/);
+			assert.match(source, /var CODEX_MULTI_AGENT_V2_THREAD_LIMIT = 4;/);
+		});
+	}
+
+	test("bootstrap worker lowers existing Codex thread limits", () => {
+		const smokeRoot = mkdtempSync(join(tmpdir(), "lazycodex-thread-limit-"));
+		const codexHome = join(smokeRoot, "codex");
+		const home = join(smokeRoot, "home");
+		mkdirSync(codexHome);
+		mkdirSync(home);
+		writeFileSync(
+			join(codexHome, "config.toml"),
+			[
+				'model = "gpt-5.5"',
+				'model_provider = "ly-chatai"',
+				"",
+				"[features]",
+				"multi_agent = true",
+				"",
+				"[agents]",
+				"enabled = true",
+				"max_threads = 1000",
+				"",
+				"[features.multi_agent_v2]",
+				"max_concurrent_threads_per_session = 16",
+				"",
+			].join("\n"),
+		);
+
+		const result = spawnSync(
+			process.execPath,
+			[
+				join(repoRoot, "plugins/omo/components/bootstrap/dist/cli.js"),
+				"worker",
+				"--codex-home",
+				codexHome,
+				"--once",
+			],
+			{
+				encoding: "utf8",
+				env: { ...process.env, HOME: home },
+			},
+		);
+
+		assert.equal(result.status, 0, `${result.stdout}\n${result.stderr}`);
+		const config = readFileSync(join(codexHome, "config.toml"), "utf8");
+		assert.match(config, /^max_threads = 4$/m);
+		assert.match(config, /^max_concurrent_threads_per_session = 4$/m);
+	});
+});

Verification

  • RED: node --test test/codex-token-thread-limits.test.mjs failed before the fix because all three shipped bundles still used high thread limits.
  • GREEN focused: node --test test/codex-token-thread-limits.test.mjs -> 4 tests passed.
  • GREEN suite: npm test -> 13 tests passed.
  • Manual QA: node plugins/omo/components/bootstrap/dist/cli.js worker --codex-home <temp-codex-home> --once rewrote a temporary config from agents.max_threads = 1000 and features.multi_agent_v2.max_concurrent_threads_per_session = 16 to 4 and 4.

This fix was debugged, implemented, and verified with LazyCodex.
Tag: lazycodex-generated

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions