resolveProviderContribution read only containerConfig.provider (from each
group's container.json) and ignored both agent_groups.agent_provider and
sessions.agent_provider. The provider-install skills (opencode, codex)
and CLAUDE.md document those DB columns as the source of truth with
session-overrides-group precedence, but the code never consulted them —
so setting `agent_provider = 'codex'` on a group had no effect, and the
only way to route to a non-default provider was to edit the per-group
JSON directly. Discovered while wiring up Codex: DB update landed but
the spawned container kept running Claude.
Extract a pure `resolveProviderName(session, group, containerConfig)`
with the documented precedence:
sessions.agent_provider
→ agent_groups.agent_provider
→ container.json `provider`
→ 'claude'
`resolveProviderContribution` now calls it. The container.json fallback
stays so existing installs that only set provider in JSON keep working.
Empty strings treated as unset to avoid footguns when a DB-backed form
writes '' for "no override."
Added unit tests covering precedence, null-fallthrough, empty-string
fallthrough, and case normalization.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
33 lines
1.2 KiB
TypeScript
33 lines
1.2 KiB
TypeScript
import { describe, expect, it } from 'vitest';
|
|
|
|
import { resolveProviderName } from './container-runner.js';
|
|
|
|
describe('resolveProviderName', () => {
|
|
it('prefers session over group and container.json', () => {
|
|
expect(resolveProviderName('codex', 'opencode', 'claude')).toBe('codex');
|
|
});
|
|
|
|
it('falls back to group when session is null', () => {
|
|
expect(resolveProviderName(null, 'codex', 'claude')).toBe('codex');
|
|
});
|
|
|
|
it('falls back to container.json when session and group are null', () => {
|
|
expect(resolveProviderName(null, null, 'opencode')).toBe('opencode');
|
|
});
|
|
|
|
it('defaults to claude when nothing is set', () => {
|
|
expect(resolveProviderName(null, null, undefined)).toBe('claude');
|
|
});
|
|
|
|
it('lowercases the resolved name', () => {
|
|
expect(resolveProviderName('CODEX', null, null)).toBe('codex');
|
|
expect(resolveProviderName(null, 'OpenCode', null)).toBe('opencode');
|
|
expect(resolveProviderName(null, null, 'Claude')).toBe('claude');
|
|
});
|
|
|
|
it('treats empty string as unset (falls through)', () => {
|
|
expect(resolveProviderName('', 'codex', null)).toBe('codex');
|
|
expect(resolveProviderName(null, '', 'opencode')).toBe('opencode');
|
|
});
|
|
});
|