fix(config): accept partial cliBackends overrides by making command optional#5
Merged
Merged
Conversation
…ptional
`agents.defaults.cliBackends.<backendId>` rejects partial overrides today:
supplying only `{ modelAliases: { ... } }` fails schema validation with
"command: expected string, received undefined", even though the runtime
merge (`mergeBackendConfig`) already composes with base `command` via
object spread and `resolveCliBackendConfig` guards on
`config.command?.trim()` returning null when unresolvable.
Make `command` optional at the schema and TS-type level so config-only
alias remaps (e.g. pinning `claude-opus-4-7` past the shared family-alias
table in `cli-shared.ts`) no longer require duplicating the full backend
block. Every other field on the schema is already optional; this aligns
`command` with the rest and with the runtime's actual contract.
Changes:
- `CliBackendSchema.command` → `.optional()` in zod-schema.core.ts
- `CliBackendConfig.command` → optional in types.agent-defaults.ts
- New `ResolvedCliBackendConfig` type narrows `command: string` so
downstream consumers don't lose the non-null guarantee
- `cli-runner/execute.ts` guards once at the spawn site and reuses the
narrowed `backendCommand` for argv[0] and the argv log line
- Regression test: partial `modelAliases`-only override parses, resolves
non-null, and surfaces the user's alias entry
3 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
agents.defaults.cliBackends.<backendId>.commandoptional at both the zod schema level (CliBackendSchema) and the TS-type level (CliBackendConfig).ResolvedCliBackendConfigwithcommand: stringso downstream consumers don't lose the non-null guarantee afterresolveCliBackendConfigvalidates it.cli-runner/execute.ts) at the spawn site instead of propagating the stricter type up through the whole config plumbing.Why
Users cannot currently write a partial
cliBackendsoverride — e.g. pinningclaude-opus-4-7past the shared family-alias table inextensions/anthropic/cli-shared.ts:{ "agents": { "defaults": { "cliBackends": { "claude-cli": { "modelAliases": { "claude-opus-4-7": "claude-opus-4-7" } } } } } }This fails validation with
command: expected string, received undefined, forcing users to duplicate the entire backend block (every arg, every clearEnv entry, etc.) just to override an alias. The runtime merge (mergeBackendConfigatsrc/agents/cli-backends.ts:130-142) already handles partials correctly via object spread, andresolveCliBackendConfigguards onconfig.command?.trim()returning null when unresolvable — so the validation constraint is out of sync with the actual runtime contract.Every other field on
CliBackendSchemais already optional; this change alignscommandwith the rest.Why this shape was picked
Two alternatives were weighed:
CliBackendConfig.commandrequired and introduce a separatePartialCliBackendConfiginput type. Cleaner on paper but requires a wide refactor: every caller currently takes the same type for both input and resolved backends. Lots of churn for the same behavior.ResolvedCliBackendConfig) exported for anyone who wants the tightened contract, and a single runtime guard inexecute.tsthat throws with a clear message if the invariant is ever violated. No changes to existing callers; no new optional chaining anywhere else.Test plan
pnpm exec vitest run src/agents/cli-backends.test.ts→ 19/19 passing (new regression test + existing 18)pnpm exec vitest run src/config/ src/agents/→ exit 0pnpm tsgo→ cleanpnpm check:import-cycles/pnpm check:madge-import-cycles→ 0 cyclesmodelAliases-only override parses, resolves non-null, inherits basecommand, and surfaces the user's alias entryContext
Hit while switching Aurelius (one of Bench's OpenClaw-hosted agents) from Opus 4.6 → Opus 4.7. The CLI's
opusalias on v2.1.87 still resolves to 4.6, so the only way to force 4.7 through OpenClaw today is either a bundle patch oncli-shared-*.js(brittle — file hashes change on every publish) or a partialmodelAliasesconfig override (blocked by this validation bug). This PR unblocks the latter.