fix(config): handle 'local-agent' provider in getLLMConfig (#118)
* fix(config): handle 'local-agent' provider in getLLMConfig
Routing a mission through a connected local CLI agent (Claude Code / Codex /
Hermes) sets the provider to `local-agent`, but getLLMConfig's switch had no
case for it, so it fell through to `default` and threw
`Unknown provider: local-agent`. This aborted every keyless mission with a
REFUSED, even though `local-agent` is a registered provider wired to
LocalAgentAdapter everywhere else.
Add a `case 'local-agent'` mirroring `codex`: keyless (no apiKey/baseUrl), with
the connected agent id (codex|claude|hermes) carried in `model` and a sensible
default. Verified getLLMConfig('local-agent','claude') now resolves to
{provider:'local-agent', model:'claude'} instead of throwing.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* test(config): regression coverage for local-agent provider (#118)
Addresses review feedback: the fix had no test, so removing the
`case 'local-agent'` would leave CI green while re-breaking keyless missions.
Adds focused assertions in src/__tests__/local-agent-provider.test.ts:
- local-agent + model `claude` resolves to provider/model with no apiKey/baseUrl
- any supported agent id (codex|claude|hermes) passed as model is preserved
- omitting the model falls back to the default agent id
- getLLMConfig('local-agent') no longer throws
- AVAILABLE_MODELS['local-agent'] surfaces the connected-agent ids
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
---------
Co-authored-by: hoptoad-off <mvzmysun@gmail.com>
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2 files changed
+50
No files matched your search
@@ -0,0 +1,44 @@
|
|||||||
|
import { describe, expect, it } from 'vitest';
|
||||||
|
import { config, AVAILABLE_MODELS } from '../config/index.js';
|
||||||
|
|
||||||
|
// Regression guard for #118: getLLMConfig had no `case 'local-agent'`, so it fell
|
||||||
|
// through to `default` and threw `Unknown provider: local-agent`, aborting every
|
||||||
|
// keyless (connected-agent) mission. These assertions fail if that case is removed.
|
||||||
|
describe("local-agent provider wiring (#118)", () => {
|
||||||
|
it('resolves a keyless config for local-agent with the agent id carried in model', () => {
|
||||||
|
const cfg = config.getLLMConfig('local-agent', 'claude');
|
||||||
|
expect(cfg.provider).toBe('local-agent');
|
||||||
|
expect(cfg.model).toBe('claude');
|
||||||
|
// Keyless: the connected CLI agent uses its own login — no API key or base URL.
|
||||||
|
expect(cfg.apiKey).toBeUndefined();
|
||||||
|
expect(cfg.baseUrl).toBeUndefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('preserves any supported agent id passed as the model', () => {
|
||||||
|
for (const agent of ['codex', 'claude', 'hermes']) {
|
||||||
|
const cfg = config.getLLMConfig('local-agent', agent);
|
||||||
|
expect(cfg.provider).toBe('local-agent');
|
||||||
|
expect(cfg.model).toBe(agent);
|
||||||
|
expect(cfg.apiKey).toBeUndefined();
|
||||||
|
expect(cfg.baseUrl).toBeUndefined();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
it('falls back to the default agent id when no model is given', () => {
|
||||||
|
const cfg = config.getLLMConfig('local-agent');
|
||||||
|
expect(cfg.provider).toBe('local-agent');
|
||||||
|
expect(cfg.model).toBe('claude');
|
||||||
|
expect(cfg.apiKey).toBeUndefined();
|
||||||
|
expect(cfg.baseUrl).toBeUndefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('does not throw "Unknown provider" for local-agent', () => {
|
||||||
|
expect(() => config.getLLMConfig('local-agent')).not.toThrow();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('surfaces the connected-agent ids as available local-agent models', () => {
|
||||||
|
expect(AVAILABLE_MODELS['local-agent']?.map(m => m.id)).toEqual(
|
||||||
|
expect.arrayContaining(['codex', 'claude', 'hermes']),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -867,6 +867,12 @@ class ConfigManager {
|
|||||||
case 'codex':
|
case 'codex':
|
||||||
actualModel = model || this.config.get('codex').defaultModel;
|
actualModel = model || this.config.get('codex').defaultModel;
|
||||||
break;
|
break;
|
||||||
|
case 'local-agent':
|
||||||
|
// Keyless backbone: the mission is routed through a connected local CLI agent
|
||||||
|
// (Claude Code / Codex / Hermes), each using its own login — no API key or base
|
||||||
|
// URL. The chosen agent id (codex|claude|hermes) travels in the `model` field.
|
||||||
|
actualModel = model || 'claude';
|
||||||
|
break;
|
||||||
case 'mock':
|
case 'mock':
|
||||||
actualModel = 'mock-model';
|
actualModel = 'mock-model';
|
||||||
break;
|
break;
|
||||||
|
|||||||
Reference in new issue
Block a user