Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 44 additions & 0 deletions src/__tests__/local-agent-provider.test.ts
Original file line number Diff line number Diff line change
@@ -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']),
);
});
});
6 changes: 6 additions & 0 deletions src/config/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -867,6 +867,12 @@ class ConfigManager {
case 'codex':
actualModel = model || this.config.get('codex').defaultModel;
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':
actualModel = 'mock-model';
break;
Expand Down