Skip to content

[codex] support disabling SDK budget limit#270

Merged
lishuceo merged 1 commit into
mainfrom
codex/disable-max-budget-usd
Jul 6, 2026
Merged

[codex] support disabling SDK budget limit#270
lishuceo merged 1 commit into
mainfrom
codex/disable-max-budget-usd

Conversation

@lishuceo

@lishuceo lishuceo commented Jul 6, 2026

Copy link
Copy Markdown
Owner

Summary

  • allow maxBudgetUsd: null in agent config to disable the SDK budget option
  • support CLAUDE_MAX_BUDGET_USD=off/false/none/disabled/0
  • omit maxBudgetUsd from Claude SDK options when disabled

Validation

  • npm run typecheck
  • npx vitest run src/__tests__/config.test.ts src/__tests__/config-loader.test.ts src/agent/__tests__/config-loader.test.ts src/claude/__tests__/executor.test.ts

@lishuceo lishuceo marked this pull request as ready for review July 6, 2026 09:51
@github-actions

github-actions Bot commented Jul 6, 2026

Copy link
Copy Markdown
Contributor

Claude finished @lishuceo's task in 2m 38s —— View job


Review: support disabling SDK budget limit

  • Read the full diff
  • Review config parsing (src/config.ts)
  • Review agent config schema/loader
  • Review executor changes
  • Check test coverage

Verdict: ✅ Approved — clean, correct, and well-tested. No blocking issues found.

The design uses a consistent "omit the key = use default, present-but-undefined = disabled" contract that is threaded correctly through every layer. A few things I specifically verified:

Executor hasOwnProperty contract is sound (src/claude/executor.ts:651)

const hasMaxBudgetUsdOverride = Object.prototype.hasOwnProperty.call(input, 'maxBudgetUsd');
const effectiveMaxBudgetUsd = hasMaxBudgetUsdOverride ? maxBudgetUsd : config.claude.maxBudgetUsd;

This distinguishes "key absent → fall back to config" from "key present with undefined → disable". Because execute() is always called with an inline object literal (event-handler.ts:2795, :3362), the key presence is preserved (no JSON.stringify / reconstruction in the path that would drop undefined keys). ✅

The event-handler spread change is necessary, not cosmetic (event-handler.ts:2802, :2953)

...(agentCfg ? { maxBudgetUsd: agentCfg.maxBudgetUsd } : {}),

Previously this was maxBudgetUsd: agentCfg?.maxBudgetUsd, which passed maxBudgetUsd: undefined (key present) when agentCfg was null. Under the new executor semantics that would have disabled the budget instead of falling back to config. Guarding the key with the spread preserves the old "no agentCfg → config default" behavior. Good catch by the author — worth keeping in mind that these two forms are no longer interchangeable. Line 3369 (maxBudgetUsd: agentCfg.maxBudgetUsd) is safe because agentCfg is non-null there.

Config + schema + loader precedence is correct

  • parseOptionalMaxBudgetUsd (config.ts:29): empty→fallback, off/false/none/disabled/disable/0undefined, negative/NaN→fallback. Reasonable.
  • resolveMaxBudgetUsd (config-loader.ts:78) correctly implements input(null→off) → defaults(null→off) → config.claude.

Minor (non-blocking, no action required)

  • 🟡 (confidence ~70, below report threshold — noting only) parseOptionalMaxBudgetUsd also accepts disable as a disable keyword, but the README/.env.example doc only lists off/false/none/disabled/0. Harmless, but the accepted set and the documented set differ by one word. Consider aligning the docs or dropping disable.
  • The cron path (src/cron/store.ts:420) still coerces input.maxBudgetUsd ?? 5, so a budget-disabled agent that schedules a cron task will get a $5 cap rather than "disabled". This is pre-existing (cron's schema requires a number) and out of scope for this PR — just flagging in case disabling budget for cron is desired later.

Test coverage is solid: config parsing (off/0/number), schema null acceptance, loader file-level + agent-level null override, and executor omission for both config-disabled and caller-disabled cases.

@lishuceo lishuceo merged commit 7cc6f51 into main Jul 6, 2026
5 checks passed
@lishuceo lishuceo deleted the codex/disable-max-budget-usd branch July 6, 2026 10:08
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant