agent-ecosystem/test/main/services/runtime/ClaudeMultimodelBridgeService.test.ts
2026-05-18 01:57:16 +03:00

2106 lines
70 KiB
TypeScript

// @vitest-environment node
import type { PathLike } from 'fs';
import { readFile as readFileFixture, writeFile } from 'fs/promises';
import * as path from 'path';
import { beforeEach, describe, expect, it, vi } from 'vitest';
import {
getProviderConnectionModeSummary,
getProviderCurrentRuntimeSummary,
isConnectionManagedRuntimeProvider,
} from '@renderer/components/runtime/providerConnectionUi';
const execCliMock = vi.fn();
const buildProviderAwareCliEnvMock = vi.fn();
const resolveInteractiveShellEnvMock = vi.fn<() => Promise<NodeJS.ProcessEnv>>();
const readFileMock = vi.fn<(path: PathLike, encoding: BufferEncoding) => Promise<string>>();
const enrichProviderStatusMock = vi.fn(
(provider, _options?: { hydrateModelCatalog?: boolean }) => Promise.resolve(provider)
);
const enrichProviderStatusesMock = vi.fn((providers) => Promise.resolve(providers));
vi.mock('@main/utils/childProcess', () => ({
execCli: (...args: Parameters<typeof execCliMock>) => execCliMock(...args),
}));
vi.mock('@main/utils/shellEnv', () => ({
resolveInteractiveShellEnv: () => resolveInteractiveShellEnvMock(),
resolveInteractiveShellEnvBestEffort: () => resolveInteractiveShellEnvMock(),
}));
vi.mock('fs', () => ({
default: {
readFileSync: () => {
throw Object.assign(new Error('ENOENT'), { code: 'ENOENT' });
},
promises: {
readFile: (filePath: PathLike, encoding: BufferEncoding) => readFileMock(filePath, encoding),
},
},
readFileSync: () => {
throw Object.assign(new Error('ENOENT'), { code: 'ENOENT' });
},
promises: {
readFile: (filePath: PathLike, encoding: BufferEncoding) => readFileMock(filePath, encoding),
},
}));
vi.mock('@main/services/runtime/ProviderConnectionService', () => ({
providerConnectionService: {
enrichProviderStatus: (...args: Parameters<typeof enrichProviderStatusMock>) =>
enrichProviderStatusMock(...args),
enrichProviderStatuses: (...args: Parameters<typeof enrichProviderStatusesMock>) =>
enrichProviderStatusesMock(...args),
},
}));
vi.mock('@main/services/runtime/providerAwareCliEnv', () => ({
buildProviderAwareCliEnv: (...args: Parameters<typeof buildProviderAwareCliEnvMock>) =>
buildProviderAwareCliEnvMock(...args),
}));
describe('ClaudeMultimodelBridgeService', () => {
beforeEach(() => {
vi.resetModules();
vi.clearAllMocks();
resolveInteractiveShellEnvMock.mockResolvedValue({});
buildProviderAwareCliEnvMock.mockImplementation(
({ providerId }: { providerId?: string } = {}) =>
Promise.resolve({
env: {
HOME: '/Users/tester',
...(providerId ? { CLAUDE_CODE_ENTRY_PROVIDER: providerId } : {}),
},
connectionIssues: {},
})
);
readFileMock.mockImplementation((filePath) => {
if (String(filePath) === path.join('/Users/tester', '.claude.json')) {
return Promise.resolve(
JSON.stringify({
geminiResolvedBackend: 'cli',
geminiLastAuthMethod: 'cli_oauth_personal',
geminiProjectId: 'demo-project',
})
);
}
return Promise.reject(Object.assign(new Error('ENOENT'), { code: 'ENOENT' }));
});
});
it('keeps Gemini out of frontend aggregate fallback while explicit Gemini status still works', async () => {
execCliMock.mockImplementation((_binaryPath, args, options) => {
const normalizedArgs = Array.isArray(args) ? args.join(' ') : '';
const env = options?.env ?? {};
if (
normalizedArgs.startsWith('runtime status --json --provider ') &&
normalizedArgs.endsWith(' --summary')
) {
return Promise.reject(new Error('unknown option --summary'));
}
if (normalizedArgs === 'auth status --json --provider all') {
return Promise.resolve({
stdout: JSON.stringify({
providers: {
anthropic: {
supported: true,
authenticated: true,
authMethod: 'oauth_token',
verificationState: 'verified',
canLoginFromUi: true,
capabilities: {
teamLaunch: true,
oneShot: true,
extensions: {
plugins: { status: 'supported', ownership: 'shared', reason: null },
mcp: { status: 'supported', ownership: 'shared', reason: null },
skills: { status: 'supported', ownership: 'shared', reason: null },
apiKeys: { status: 'supported', ownership: 'shared', reason: null },
},
},
backend: { kind: 'anthropic', label: 'Anthropic' },
},
codex: {
supported: true,
authenticated: false,
verificationState: 'verified',
canLoginFromUi: true,
statusMessage: 'Not connected',
capabilities: {
teamLaunch: true,
oneShot: true,
extensions: {
plugins: {
status: 'unsupported',
ownership: 'shared',
reason: 'Anthropic only',
},
mcp: { status: 'supported', ownership: 'shared', reason: null },
skills: { status: 'supported', ownership: 'shared', reason: null },
apiKeys: { status: 'supported', ownership: 'shared', reason: null },
},
},
backend: { kind: 'openai', label: 'OpenAI' },
},
},
}),
stderr: '',
exitCode: 0,
});
}
if (
normalizedArgs === 'model list --json --provider all' &&
env.CLAUDE_CODE_ENTRY_PROVIDER === 'gemini'
) {
return Promise.resolve({
stdout: JSON.stringify({
providers: {
gemini: {
models: [{ id: 'gemini-2.5-pro', label: 'Gemini 2.5 Pro' }],
},
},
}),
stderr: '',
exitCode: 0,
});
}
if (normalizedArgs === 'model list --json --provider all') {
return Promise.resolve({
stdout: JSON.stringify({
providers: {
anthropic: {
models: [{ id: 'claude-sonnet-4-5', label: 'Claude Sonnet 4.5' }],
},
codex: {
models: [{ id: 'gpt-5-codex', label: 'GPT-5 Codex' }],
},
},
}),
stderr: '',
exitCode: 0,
});
}
return Promise.reject(new Error(`Unexpected execCli call: ${normalizedArgs}`));
});
const { ClaudeMultimodelBridgeService } =
await import('@main/services/runtime/ClaudeMultimodelBridgeService');
const service = new ClaudeMultimodelBridgeService();
const providers = await service.getProviderStatuses('/mock/agent_teams_orchestrator');
expect(providers).toHaveLength(3);
expect(providers.map((provider) => provider.providerId)).toEqual([
'anthropic',
'codex',
'opencode',
]);
expect(providers[0]).toMatchObject({
providerId: 'anthropic',
authenticated: true,
models: ['claude-sonnet-4-5'],
});
expect(providers[1]).toMatchObject({
providerId: 'codex',
authenticated: false,
models: ['gpt-5-codex'],
statusMessage: 'Not connected',
capabilities: {
extensions: {
plugins: {
status: 'unsupported',
ownership: 'shared',
reason: 'Anthropic only',
},
},
},
});
expect(providers[2]).toMatchObject({
providerId: 'opencode',
displayName: 'OpenCode (200+ models)',
supported: false,
authenticated: false,
models: [],
canLoginFromUi: false,
capabilities: {
teamLaunch: false,
oneShot: false,
},
});
const gemini = await service.getProviderStatus('/mock/agent_teams_orchestrator', 'gemini');
expect(gemini).toMatchObject({
providerId: 'gemini',
displayName: 'Gemini',
supported: true,
authenticated: true,
models: ['gemini-2.5-pro'],
canLoginFromUi: true,
authMethod: 'cli_oauth_personal',
backend: {
kind: 'cli',
label: 'Gemini CLI',
endpointLabel: 'Code Assist (cloudcode-pa.googleapis.com/v1internal)',
projectId: 'demo-project',
},
});
});
it('falls back to provider-scoped full runtime status without probing Gemini', async () => {
const providerPayloads = {
anthropic: {
supported: true,
authenticated: true,
authMethod: 'oauth_token',
verificationState: 'verified',
canLoginFromUi: true,
models: ['claude-sonnet-4-5'],
capabilities: { teamLaunch: true, oneShot: true },
},
codex: {
supported: true,
authenticated: false,
verificationState: 'unknown',
canLoginFromUi: true,
models: ['gpt-5-codex'],
capabilities: { teamLaunch: true, oneShot: true },
},
opencode: {
supported: true,
authenticated: true,
authMethod: 'opencode_managed',
verificationState: 'verified',
canLoginFromUi: false,
models: ['openai/gpt-5.4-mini'],
capabilities: { teamLaunch: true, oneShot: false },
},
} as const;
execCliMock.mockImplementation((_binaryPath, args) => {
const normalizedArgs = Array.isArray(args) ? args.join(' ') : '';
const providerArgIndex = Array.isArray(args) ? args.indexOf('--provider') : -1;
const providerId =
providerArgIndex >= 0 && Array.isArray(args)
? (args[providerArgIndex + 1] as keyof typeof providerPayloads)
: null;
if (
normalizedArgs.startsWith('runtime status --json --provider ') &&
normalizedArgs.endsWith(' --summary')
) {
return Promise.reject(new Error('unknown option --summary'));
}
if (
normalizedArgs.startsWith('runtime status --json --provider ') &&
providerId &&
providerPayloads[providerId]
) {
return Promise.resolve({
stdout: JSON.stringify({
schemaVersion: 2,
providers: {
[providerId]: providerPayloads[providerId],
},
}),
stderr: '',
exitCode: 0,
});
}
return Promise.reject(new Error(`Unexpected execCli call: ${normalizedArgs}`));
});
const { ClaudeMultimodelBridgeService } =
await import('@main/services/runtime/ClaudeMultimodelBridgeService');
const service = new ClaudeMultimodelBridgeService();
const providers = await service.getProviderStatuses('/mock/agent_teams_orchestrator');
const calls = execCliMock.mock.calls.map((call) => call[1].join(' '));
expect(providers.map((provider) => provider.providerId)).toEqual([
'anthropic',
'codex',
'opencode',
]);
expect(calls).toEqual(
expect.arrayContaining([
'runtime status --json --provider anthropic --summary',
'runtime status --json --provider codex --summary',
'runtime status --json --provider opencode --summary',
'runtime status --json --provider anthropic',
'runtime status --json --provider codex',
'runtime status --json --provider opencode',
])
);
expect(calls).not.toContain('runtime status --json --provider gemini');
expect(calls).not.toContain('runtime status --json');
expect(calls).not.toContain('auth status --json --provider all');
expect(calls).not.toContain('model list --json --provider all');
});
it('loads frontend providers with parallel provider-scoped runtime status probes', async () => {
const providerPayloads = {
anthropic: {
supported: true,
authenticated: true,
authMethod: 'oauth_token',
verificationState: 'verified',
canLoginFromUi: true,
models: ['claude-sonnet-4-5'],
capabilities: { teamLaunch: true, oneShot: true },
backend: { kind: 'anthropic', label: 'Anthropic' },
},
codex: {
supported: true,
authenticated: true,
authMethod: 'api_key',
verificationState: 'verified',
canLoginFromUi: false,
models: ['gpt-5-codex'],
capabilities: { teamLaunch: true, oneShot: true },
backend: { kind: 'codex-native', label: 'Codex native' },
},
gemini: {
supported: true,
authenticated: false,
verificationState: 'unknown',
canLoginFromUi: true,
statusMessage: 'No Gemini runtime backend is ready',
models: ['gemini-2.5-pro'],
capabilities: { teamLaunch: true, oneShot: true },
},
opencode: {
supported: true,
authenticated: true,
authMethod: 'opencode_managed',
verificationState: 'verified',
canLoginFromUi: false,
models: ['openai/gpt-5.4-mini'],
capabilities: { teamLaunch: true, oneShot: false },
backend: { kind: 'opencode-cli', label: 'OpenCode CLI' },
},
} as const;
execCliMock.mockImplementation((_binaryPath, args) => {
const normalizedArgs = Array.isArray(args) ? args.join(' ') : '';
const providerArgIndex = Array.isArray(args) ? args.indexOf('--provider') : -1;
const providerId =
providerArgIndex >= 0 && Array.isArray(args)
? (args[providerArgIndex + 1] as keyof typeof providerPayloads)
: null;
if (
normalizedArgs.startsWith('runtime status --json --provider ') &&
providerId &&
providerPayloads[providerId]
) {
return Promise.resolve({
stdout: JSON.stringify({
schemaVersion: 2,
providers: {
[providerId]: providerPayloads[providerId],
},
}),
stderr: '',
exitCode: 0,
});
}
return Promise.reject(new Error(`Unexpected execCli call: ${normalizedArgs}`));
});
const { ClaudeMultimodelBridgeService } =
await import('@main/services/runtime/ClaudeMultimodelBridgeService');
const service = new ClaudeMultimodelBridgeService();
const onUpdate = vi.fn();
const providers = await service.getProviderStatuses('/mock/agent_teams_orchestrator', onUpdate);
expect(execCliMock).toHaveBeenCalledTimes(3);
expect(execCliMock.mock.calls.map((call) => call[1].join(' '))).toEqual(
expect.arrayContaining([
'runtime status --json --provider anthropic --summary',
'runtime status --json --provider codex --summary',
'runtime status --json --provider opencode --summary',
])
);
expect(execCliMock.mock.calls.map((call) => call[1].join(' '))).not.toContain(
'runtime status --json --provider gemini --summary'
);
expect(
execCliMock.mock.calls
.filter((call) => call[1].join(' ').startsWith('runtime status --json --provider '))
.map((call) => call[2]?.maxBuffer)
).toEqual([8 * 1024 * 1024, 8 * 1024 * 1024, 8 * 1024 * 1024]);
expect(enrichProviderStatusMock).toHaveBeenCalledTimes(3);
expect(
enrichProviderStatusMock.mock.calls.every(
(call) => call[1]?.hydrateModelCatalog === false
)
).toBe(true);
expect(providers.map((provider) => provider.providerId)).toEqual([
'anthropic',
'codex',
'opencode',
]);
expect(providers.find((provider) => provider.providerId === 'codex')).toMatchObject({
authenticated: true,
models: ['gpt-5-codex'],
backend: { kind: 'codex-native' },
});
expect(onUpdate).toHaveBeenCalled();
expect(onUpdate.mock.calls.at(-1)?.[0]).toEqual(providers);
});
it('hydrates model catalogs without overwriting live summary auth state', async () => {
const summaryPayloads = {
anthropic: {
supported: true,
authenticated: true,
authMethod: 'oauth_token',
verificationState: 'verified',
canLoginFromUi: true,
models: ['sonnet'],
capabilities: { teamLaunch: true, oneShot: true },
},
codex: {
supported: true,
authenticated: true,
authMethod: 'api_key',
verificationState: 'verified',
canLoginFromUi: false,
statusMessage: null,
models: ['gpt-5.4'],
capabilities: { teamLaunch: true, oneShot: true },
runtimeCapabilities: { modelCatalog: { dynamic: true, source: 'app-server' } },
},
gemini: {
supported: true,
authenticated: false,
verificationState: 'unknown',
canLoginFromUi: true,
models: ['gemini-2.5-pro'],
capabilities: { teamLaunch: true, oneShot: true },
},
opencode: {
supported: true,
authenticated: true,
authMethod: 'opencode_managed',
verificationState: 'verified',
canLoginFromUi: false,
models: ['opencode/big-pickle'],
capabilities: { teamLaunch: true, oneShot: false },
},
} as const;
execCliMock.mockImplementation((_binaryPath, args) => {
const normalizedArgs = Array.isArray(args) ? args.join(' ') : '';
const providerArgIndex = Array.isArray(args) ? args.indexOf('--provider') : -1;
const providerId =
providerArgIndex >= 0 && Array.isArray(args)
? (args[providerArgIndex + 1] as keyof typeof summaryPayloads)
: null;
if (
normalizedArgs === 'runtime status --json --provider codex' &&
providerId === 'codex'
) {
return Promise.resolve({
stdout: JSON.stringify({
schemaVersion: 2,
providers: {
codex: {
...summaryPayloads.codex,
authenticated: false,
authMethod: null,
statusMessage: 'stale full status should not win',
modelCatalog: {
schemaVersion: 1,
providerId: 'codex',
source: 'app-server',
status: 'ready',
fetchedAt: '2026-05-17T00:00:00.000Z',
staleAt: '2026-05-17T00:10:00.000Z',
defaultModelId: 'gpt-5.4',
defaultLaunchModel: 'gpt-5.4',
models: [
{
id: 'gpt-5.4',
launchModel: 'gpt-5.4',
displayName: 'GPT-5.4',
hidden: false,
supportedReasoningEfforts: ['low', 'medium', 'high', 'xhigh'],
defaultReasoningEffort: 'medium',
inputModalities: ['text'],
supportsPersonality: true,
isDefault: true,
upgrade: false,
source: 'app-server',
},
],
diagnostics: {
configReadState: 'skipped',
appServerState: 'healthy',
},
},
},
},
}),
stderr: '',
exitCode: 0,
});
}
if (
normalizedArgs.startsWith('runtime status --json --provider ') &&
normalizedArgs.endsWith(' --summary') &&
providerId &&
summaryPayloads[providerId]
) {
return Promise.resolve({
stdout: JSON.stringify({
schemaVersion: 2,
providers: {
[providerId]: summaryPayloads[providerId],
},
}),
stderr: '',
exitCode: 0,
});
}
return Promise.reject(new Error(`Unexpected execCli call: ${normalizedArgs}`));
});
const { ClaudeMultimodelBridgeService } =
await import('@main/services/runtime/ClaudeMultimodelBridgeService');
const service = new ClaudeMultimodelBridgeService();
type ProviderStatuses = Awaited<ReturnType<typeof service.getProviderStatuses>>;
let resolveHydrated!: (providers: ProviderStatuses) => void;
const hydrated = new Promise<ProviderStatuses>((resolve) => {
resolveHydrated = resolve;
});
const onUpdate = vi.fn((providers: ProviderStatuses) => {
if (providers.find((provider) => provider.providerId === 'codex')?.modelCatalog) {
resolveHydrated(providers);
}
});
const providers = await service.getProviderStatuses('/mock/agent_teams_orchestrator', onUpdate);
expect(providers.find((provider) => provider.providerId === 'codex')).toMatchObject({
authenticated: true,
authMethod: 'api_key',
modelCatalogRefreshState: 'loading',
});
const hydratedProviders = await hydrated;
const hydratedCodex = hydratedProviders.find((provider) => provider.providerId === 'codex');
expect(hydratedCodex).toMatchObject({
authenticated: true,
authMethod: 'api_key',
statusMessage: null,
modelCatalogRefreshState: 'ready',
});
expect(hydratedCodex?.modelCatalog?.models.map((model) => model.id)).toEqual(['gpt-5.4']);
});
it('hydrates a single provider catalog after summary refresh', async () => {
execCliMock.mockImplementation((_binaryPath, args) => {
const normalizedArgs = Array.isArray(args) ? args.join(' ') : '';
if (normalizedArgs === 'runtime status --json --provider codex --summary') {
return Promise.resolve({
stdout: JSON.stringify({
schemaVersion: 2,
providers: {
codex: {
providerId: 'codex',
displayName: 'Codex',
supported: true,
authenticated: true,
authMethod: 'api_key',
verificationState: 'verified',
canLoginFromUi: false,
statusMessage: null,
models: ['gpt-5.4'],
capabilities: { teamLaunch: true, oneShot: true },
runtimeCapabilities: { modelCatalog: { dynamic: true, source: 'app-server' } },
},
},
}),
stderr: '',
exitCode: 0,
});
}
if (normalizedArgs === 'runtime status --json --provider codex') {
return Promise.resolve({
stdout: JSON.stringify({
schemaVersion: 2,
providers: {
codex: {
providerId: 'codex',
displayName: 'Codex',
supported: true,
authenticated: false,
authMethod: 'oauth_token',
verificationState: 'unknown',
canLoginFromUi: false,
statusMessage: 'full status should not overwrite live summary',
models: ['gpt-5.4'],
capabilities: { teamLaunch: true, oneShot: true },
runtimeCapabilities: { modelCatalog: { dynamic: true, source: 'app-server' } },
modelCatalog: {
schemaVersion: 1,
providerId: 'codex',
source: 'app-server',
status: 'ready',
fetchedAt: '2026-05-17T00:00:00.000Z',
staleAt: '2026-05-17T00:10:00.000Z',
defaultModelId: 'gpt-5.4',
defaultLaunchModel: 'gpt-5.4',
models: [],
diagnostics: {
configReadState: 'skipped',
appServerState: 'healthy',
},
},
},
},
}),
stderr: '',
exitCode: 0,
});
}
return Promise.reject(new Error(`Unexpected execCli call: ${normalizedArgs}`));
});
const { ClaudeMultimodelBridgeService } =
await import('@main/services/runtime/ClaudeMultimodelBridgeService');
const service = new ClaudeMultimodelBridgeService();
const onCatalogUpdate = vi.fn();
const provider = await service.getProviderStatus(
'/mock/agent_teams_orchestrator',
'codex',
onCatalogUpdate
);
expect(provider).toMatchObject({
authenticated: true,
authMethod: 'api_key',
modelCatalogRefreshState: 'loading',
});
await vi.waitFor(() => {
expect(onCatalogUpdate).toHaveBeenCalledTimes(1);
});
expect(onCatalogUpdate.mock.calls[0]?.[0]).toMatchObject({
authenticated: true,
authMethod: 'api_key',
statusMessage: null,
modelCatalogRefreshState: 'ready',
modelCatalog: {
defaultModelId: 'gpt-5.4',
},
});
});
it('hydrates Anthropic subscription rate limits after the live summary status', async () => {
execCliMock.mockImplementation((_binaryPath, args) => {
const normalizedArgs = Array.isArray(args) ? args.join(' ') : '';
if (normalizedArgs === 'runtime status --json --provider anthropic --summary') {
return Promise.resolve({
stdout: JSON.stringify({
schemaVersion: 2,
providers: {
anthropic: {
providerId: 'anthropic',
displayName: 'Anthropic',
supported: true,
authenticated: true,
authMethod: 'oauth_token',
verificationState: 'verified',
canLoginFromUi: true,
statusMessage: null,
models: ['sonnet'],
capabilities: { teamLaunch: true, oneShot: true },
runtimeCapabilities: {
modelCatalog: { dynamic: true, source: 'anthropic-models-api' },
},
subscriptionRateLimits: null,
},
},
}),
stderr: '',
exitCode: 0,
});
}
if (normalizedArgs === 'runtime status --json --provider anthropic') {
return Promise.resolve({
stdout: JSON.stringify({
schemaVersion: 2,
providers: {
anthropic: {
providerId: 'anthropic',
displayName: 'Anthropic',
supported: true,
authenticated: false,
authMethod: 'oauth_token',
verificationState: 'unknown',
canLoginFromUi: true,
statusMessage: 'full status should not overwrite live summary',
models: ['sonnet'],
capabilities: { teamLaunch: true, oneShot: true },
runtimeCapabilities: {
modelCatalog: { dynamic: true, source: 'anthropic-models-api' },
},
subscriptionRateLimits: {
primary: { usedPercent: 42, windowDurationMins: 300, resetsAt: 1_800 },
secondary: null,
},
modelCatalog: {
schemaVersion: 1,
providerId: 'anthropic',
source: 'anthropic-models-api',
status: 'ready',
fetchedAt: '2026-05-17T00:00:00.000Z',
staleAt: '2026-05-17T00:10:00.000Z',
defaultModelId: 'sonnet',
defaultLaunchModel: 'sonnet',
models: [],
diagnostics: {
configReadState: 'ready',
appServerState: 'healthy',
},
},
},
},
}),
stderr: '',
exitCode: 0,
});
}
return Promise.reject(new Error(`Unexpected execCli call: ${normalizedArgs}`));
});
const { ClaudeMultimodelBridgeService } =
await import('@main/services/runtime/ClaudeMultimodelBridgeService');
const service = new ClaudeMultimodelBridgeService();
const onCatalogUpdate = vi.fn();
const provider = await service.getProviderStatus(
'/mock/agent_teams_orchestrator',
'anthropic',
onCatalogUpdate
);
expect(provider).toMatchObject({
authenticated: true,
authMethod: 'oauth_token',
subscriptionRateLimits: null,
modelCatalogRefreshState: 'loading',
});
await vi.waitFor(() => {
expect(onCatalogUpdate).toHaveBeenCalledTimes(1);
});
expect(onCatalogUpdate.mock.calls[0]?.[0]).toMatchObject({
authenticated: true,
authMethod: 'oauth_token',
statusMessage: null,
subscriptionRateLimits: {
primary: { usedPercent: 42, windowDurationMins: 300, resetsAt: 1_800 },
secondary: null,
},
modelCatalogRefreshState: 'ready',
});
});
it('does not cancel one provider catalog hydration when another provider refresh starts', async () => {
let resolveCodexHydration!: (value: {
stdout: string;
stderr: string;
exitCode: number;
}) => void;
const codexHydration = new Promise<{
stdout: string;
stderr: string;
exitCode: number;
}>((resolve) => {
resolveCodexHydration = resolve;
});
execCliMock.mockImplementation((_binaryPath, args) => {
const normalizedArgs = Array.isArray(args) ? args.join(' ') : '';
if (normalizedArgs === 'runtime status --json --provider codex --summary') {
return Promise.resolve({
stdout: JSON.stringify({
schemaVersion: 2,
providers: {
codex: {
providerId: 'codex',
displayName: 'Codex',
supported: true,
authenticated: true,
authMethod: 'api_key',
verificationState: 'verified',
canLoginFromUi: false,
statusMessage: null,
models: ['gpt-5.4'],
capabilities: { teamLaunch: true, oneShot: true },
runtimeCapabilities: { modelCatalog: { dynamic: true, source: 'app-server' } },
},
},
}),
stderr: '',
exitCode: 0,
});
}
if (normalizedArgs === 'runtime status --json --provider anthropic --summary') {
return Promise.resolve({
stdout: JSON.stringify({
schemaVersion: 2,
providers: {
anthropic: {
providerId: 'anthropic',
displayName: 'Anthropic',
supported: true,
authenticated: false,
authMethod: null,
verificationState: 'unknown',
canLoginFromUi: true,
statusMessage: 'Not connected',
models: ['sonnet'],
capabilities: { teamLaunch: true, oneShot: true },
},
},
}),
stderr: '',
exitCode: 0,
});
}
if (normalizedArgs === 'runtime status --json --provider codex') {
return codexHydration;
}
return Promise.reject(new Error(`Unexpected execCli call: ${normalizedArgs}`));
});
const { ClaudeMultimodelBridgeService } =
await import('@main/services/runtime/ClaudeMultimodelBridgeService');
const service = new ClaudeMultimodelBridgeService();
const onCodexCatalogUpdate = vi.fn();
const codex = await service.getProviderStatus(
'/mock/agent_teams_orchestrator',
'codex',
onCodexCatalogUpdate
);
expect(codex.modelCatalogRefreshState).toBe('loading');
const anthropic = await service.getProviderStatus(
'/mock/agent_teams_orchestrator',
'anthropic'
);
expect(anthropic.statusMessage).toBe('Not connected');
resolveCodexHydration({
stdout: JSON.stringify({
schemaVersion: 2,
providers: {
codex: {
...codex,
authenticated: false,
authMethod: null,
statusMessage: 'full status should not overwrite live summary',
modelCatalog: {
schemaVersion: 1,
providerId: 'codex',
source: 'app-server',
status: 'ready',
fetchedAt: '2026-05-17T00:00:00.000Z',
staleAt: '2026-05-17T00:10:00.000Z',
defaultModelId: 'gpt-5.4',
defaultLaunchModel: 'gpt-5.4',
models: [],
diagnostics: {
configReadState: 'skipped',
appServerState: 'healthy',
},
},
},
},
}),
stderr: '',
exitCode: 0,
});
await vi.waitFor(() => {
expect(onCodexCatalogUpdate).toHaveBeenCalledTimes(1);
});
expect(onCodexCatalogUpdate.mock.calls[0]?.[0]).toMatchObject({
authenticated: true,
authMethod: 'api_key',
statusMessage: null,
modelCatalogRefreshState: 'ready',
});
});
it('ignores stale catalog hydration from an older provider status refresh', async () => {
buildProviderAwareCliEnvMock.mockResolvedValue({
env: { HOME: '/Users/tester' },
connectionIssues: {},
});
const codexSummaryConnected = {
providerId: 'codex',
displayName: 'Codex',
supported: true,
authenticated: true,
authMethod: 'api_key',
verificationState: 'verified',
canLoginFromUi: false,
statusMessage: null,
models: ['gpt-5.4'],
capabilities: { teamLaunch: true, oneShot: true },
runtimeCapabilities: { modelCatalog: { dynamic: true, source: 'app-server' } },
};
const codexSummaryDisconnected = {
...codexSummaryConnected,
authenticated: false,
authMethod: null,
statusMessage: 'Not connected',
};
const staticSummaryPayloads = {
anthropic: {
supported: true,
authenticated: false,
verificationState: 'unknown',
canLoginFromUi: true,
models: ['sonnet'],
capabilities: { teamLaunch: true, oneShot: true },
},
gemini: {
supported: true,
authenticated: false,
verificationState: 'unknown',
canLoginFromUi: true,
models: ['gemini-2.5-pro'],
capabilities: { teamLaunch: true, oneShot: true },
},
opencode: {
supported: true,
authenticated: true,
authMethod: 'opencode_managed',
verificationState: 'verified',
canLoginFromUi: false,
models: ['opencode/big-pickle'],
capabilities: { teamLaunch: true, oneShot: false },
},
} as const;
let codexSummaryCalls = 0;
let codexFullCalls = 0;
let firstHydrationStarted = false;
let resolveFirstHydration!: (value: {
stdout: string;
stderr: string;
exitCode: number;
}) => void;
const firstHydration = new Promise<{
stdout: string;
stderr: string;
exitCode: number;
}>((resolve) => {
resolveFirstHydration = resolve;
});
execCliMock.mockImplementation((_binaryPath, args) => {
const normalizedArgs = Array.isArray(args) ? args.join(' ') : '';
const providerArgIndex = Array.isArray(args) ? args.indexOf('--provider') : -1;
const providerId =
providerArgIndex >= 0 && Array.isArray(args)
? (args[providerArgIndex + 1] as keyof typeof staticSummaryPayloads | 'codex')
: null;
if (
normalizedArgs.startsWith('runtime status --json --provider ') &&
normalizedArgs.endsWith(' --summary') &&
providerId
) {
const payload =
providerId === 'codex'
? ++codexSummaryCalls === 1
? codexSummaryConnected
: codexSummaryDisconnected
: staticSummaryPayloads[providerId];
return Promise.resolve({
stdout: JSON.stringify({
schemaVersion: 2,
providers: {
[providerId]: payload,
},
}),
stderr: '',
exitCode: 0,
});
}
if (normalizedArgs === 'runtime status --json --provider codex') {
codexFullCalls += 1;
if (codexFullCalls === 1) {
firstHydrationStarted = true;
return firstHydration;
}
return Promise.resolve({
stdout: JSON.stringify({
schemaVersion: 2,
providers: {
codex: {
...codexSummaryDisconnected,
authenticated: true,
authMethod: 'api_key',
statusMessage: 'fresh full status should not overwrite live summary',
modelCatalog: {
schemaVersion: 1,
providerId: 'codex',
source: 'app-server',
status: 'ready',
fetchedAt: '2026-05-17T00:01:00.000Z',
staleAt: '2026-05-17T00:11:00.000Z',
defaultModelId: 'fresh-model',
defaultLaunchModel: 'fresh-model',
models: [],
diagnostics: {
configReadState: 'skipped',
appServerState: 'healthy',
},
},
},
},
}),
stderr: '',
exitCode: 0,
});
}
return Promise.reject(new Error(`Unexpected execCli call: ${normalizedArgs}`));
});
const { ClaudeMultimodelBridgeService } =
await import('@main/services/runtime/ClaudeMultimodelBridgeService');
const service = new ClaudeMultimodelBridgeService();
type ProviderStatuses = Awaited<ReturnType<typeof service.getProviderStatuses>>;
const firstUpdates = vi.fn((_: ProviderStatuses) => undefined);
const secondUpdates = vi.fn((_: ProviderStatuses) => undefined);
const firstProviders = await service.getProviderStatuses(
'/mock/agent_teams_orchestrator',
firstUpdates
);
expect(firstProviders.find((provider) => provider.providerId === 'codex')).toMatchObject({
authenticated: true,
authMethod: 'api_key',
});
for (let attempt = 0; attempt < 10 && !firstHydrationStarted; attempt += 1) {
await new Promise((resolve) => setTimeout(resolve, 0));
}
expect(firstHydrationStarted).toBe(true);
const secondProviders = await service.getProviderStatuses(
'/mock/agent_teams_orchestrator',
secondUpdates
);
expect(secondProviders.find((provider) => provider.providerId === 'codex')).toMatchObject({
authenticated: false,
authMethod: null,
statusMessage: 'Not connected',
});
resolveFirstHydration({
stdout: JSON.stringify({
schemaVersion: 2,
providers: {
codex: {
...codexSummaryConnected,
statusMessage: 'old catalog hydration',
modelCatalog: {
schemaVersion: 1,
providerId: 'codex',
source: 'app-server',
status: 'ready',
fetchedAt: '2026-05-17T00:00:00.000Z',
staleAt: '2026-05-17T00:10:00.000Z',
defaultModelId: 'old-model',
defaultLaunchModel: 'old-model',
models: [],
diagnostics: {
configReadState: 'skipped',
appServerState: 'healthy',
},
},
},
},
}),
stderr: '',
exitCode: 0,
});
await new Promise((resolve) => setTimeout(resolve, 0));
const hasOldCatalogUpdate = [...firstUpdates.mock.calls, ...secondUpdates.mock.calls].some(
([providers]) =>
providers
.find((provider) => provider.providerId === 'codex')
?.modelCatalog?.defaultModelId === 'old-model'
);
expect(hasOldCatalogUpdate).toBe(false);
});
it('overrides provider auth status when provider-aware env reports a missing API key', async () => {
buildProviderAwareCliEnvMock.mockResolvedValue({
env: { HOME: '/Users/tester' },
connectionIssues: {
anthropic: 'Anthropic API key mode is enabled, but no ANTHROPIC_API_KEY is configured.',
},
});
execCliMock.mockResolvedValue({
stdout: JSON.stringify({
providers: {
anthropic: {
supported: true,
authenticated: true,
authMethod: 'oauth_token',
verificationState: 'verified',
canLoginFromUi: true,
capabilities: { teamLaunch: true, oneShot: true },
},
},
}),
stderr: '',
exitCode: 0,
});
const { ClaudeMultimodelBridgeService } =
await import('@main/services/runtime/ClaudeMultimodelBridgeService');
const service = new ClaudeMultimodelBridgeService();
const provider = await service.getProviderStatus('/mock/agent_teams_orchestrator', 'anthropic');
expect(provider).toMatchObject({
providerId: 'anthropic',
authenticated: false,
authMethod: null,
verificationState: 'error',
});
expect(provider.statusMessage).toContain('ANTHROPIC_API_KEY');
});
it('falls back conservatively when the runtime omits extension capability metadata', async () => {
execCliMock.mockResolvedValue({
stdout: JSON.stringify({
providers: {
codex: {
supported: true,
authenticated: true,
verificationState: 'verified',
canLoginFromUi: true,
capabilities: {
teamLaunch: true,
oneShot: true,
},
},
},
}),
stderr: '',
exitCode: 0,
});
const { ClaudeMultimodelBridgeService } =
await import('@main/services/runtime/ClaudeMultimodelBridgeService');
const service = new ClaudeMultimodelBridgeService();
const provider = await service.getProviderStatus('/mock/agent_teams_orchestrator', 'codex');
expect(provider).toMatchObject({
providerId: 'codex',
capabilities: {
extensions: {
plugins: { status: 'unsupported' },
mcp: { status: 'read-only' },
skills: { status: 'supported' },
apiKeys: { status: 'supported' },
},
},
});
});
it('maps anthropic runtime model catalog metadata through the bridge', async () => {
execCliMock.mockResolvedValue({
stdout: JSON.stringify({
schemaVersion: 2,
providers: {
anthropic: {
supported: true,
authenticated: true,
authMethod: 'oauth_token',
verificationState: 'verified',
canLoginFromUi: true,
models: ['opus', 'claude-opus-4-6', 'sonnet', 'haiku'],
modelCatalog: {
schemaVersion: 1,
providerId: 'anthropic',
source: 'anthropic-models-api',
status: 'ready',
fetchedAt: '2026-04-21T00:00:00.000Z',
staleAt: '2026-04-21T00:10:00.000Z',
defaultModelId: 'opus[1m]',
defaultLaunchModel: 'opus[1m]',
models: [
{
id: 'opus',
launchModel: 'opus',
displayName: 'Opus 4.8',
hidden: false,
supportedReasoningEfforts: ['low', 'medium', 'high'],
defaultReasoningEffort: null,
inputModalities: ['text', 'image'],
supportsPersonality: false,
isDefault: false,
upgrade: false,
source: 'anthropic-models-api',
badgeLabel: 'Opus 4.8',
metadata: {
cost: { input: 0, output: 0 },
context: 200000,
limits: { context: 200000, output: 32000 },
free: true,
},
},
{
id: 'opus[1m]',
launchModel: 'opus[1m]',
displayName: 'Opus 4.8 (1M)',
hidden: true,
supportedReasoningEfforts: ['low', 'medium', 'high'],
defaultReasoningEffort: null,
inputModalities: ['text', 'image'],
supportsPersonality: false,
isDefault: true,
upgrade: false,
source: 'anthropic-models-api',
},
],
diagnostics: {
configReadState: 'ready',
appServerState: 'healthy',
message: null,
code: null,
},
},
capabilities: {
teamLaunch: true,
oneShot: true,
extensions: {
plugins: { status: 'supported', ownership: 'shared', reason: null },
mcp: { status: 'supported', ownership: 'shared', reason: null },
skills: { status: 'supported', ownership: 'shared', reason: null },
apiKeys: { status: 'supported', ownership: 'shared', reason: null },
},
},
runtimeCapabilities: {
modelCatalog: {
dynamic: true,
source: 'anthropic-models-api',
},
reasoningEffort: {
supported: true,
values: ['low', 'medium', 'high'],
configPassthrough: false,
},
},
backend: {
kind: 'anthropic',
label: 'Anthropic',
},
},
},
}),
stderr: '',
exitCode: 0,
});
const { ClaudeMultimodelBridgeService } =
await import('@main/services/runtime/ClaudeMultimodelBridgeService');
const service = new ClaudeMultimodelBridgeService();
const provider = await service.getProviderStatus('/mock/agent_teams_orchestrator', 'anthropic');
expect(provider).toMatchObject({
providerId: 'anthropic',
authenticated: true,
models: ['opus', 'claude-opus-4-6', 'sonnet', 'haiku'],
modelCatalog: {
providerId: 'anthropic',
source: 'anthropic-models-api',
status: 'ready',
defaultModelId: 'opus[1m]',
defaultLaunchModel: 'opus[1m]',
},
runtimeCapabilities: {
modelCatalog: {
dynamic: true,
source: 'anthropic-models-api',
},
reasoningEffort: {
supported: true,
values: ['low', 'medium', 'high'],
configPassthrough: false,
},
},
});
expect(provider.modelCatalog?.models).toEqual(
expect.arrayContaining([
expect.objectContaining({
launchModel: 'opus',
displayName: 'Opus 4.8',
hidden: false,
source: 'anthropic-models-api',
badgeLabel: 'Opus 4.8',
metadata: {
cost: { input: 0, output: 0 },
context: 200000,
limits: { context: 200000, output: 32000 },
free: true,
},
}),
expect.objectContaining({
launchModel: 'opus[1m]',
displayName: 'Opus 4.8 (1M)',
hidden: true,
source: 'anthropic-models-api',
}),
])
);
});
it('keeps codex-native lane truth honest from unified runtime status through renderer summaries', async () => {
execCliMock.mockResolvedValue({
stdout: JSON.stringify({
providers: {
anthropic: {
supported: true,
authenticated: true,
authMethod: 'oauth_token',
verificationState: 'verified',
canLoginFromUi: true,
models: ['claude-sonnet-4-5'],
capabilities: {
teamLaunch: true,
oneShot: true,
extensions: {
plugins: { status: 'supported', ownership: 'shared', reason: null },
mcp: { status: 'supported', ownership: 'shared', reason: null },
skills: { status: 'supported', ownership: 'shared', reason: null },
apiKeys: { status: 'supported', ownership: 'shared', reason: null },
},
},
backend: { kind: 'anthropic', label: 'Anthropic' },
},
codex: {
supported: true,
authenticated: true,
authMethod: 'api_key',
verificationState: 'verified',
canLoginFromUi: false,
statusMessage: 'Codex native runtime ready',
detailMessage: 'Codex native runtime is ready through the local codex exec seam.',
selectedBackendId: 'codex-native',
resolvedBackendId: 'codex-native',
availableBackends: [
{
id: 'codex-native',
label: 'Codex native',
selectable: true,
recommended: true,
available: true,
state: 'ready',
audience: 'general',
statusMessage: 'Ready',
detailMessage: 'Codex native runtime is ready through the local codex exec seam.',
},
],
externalRuntimeDiagnostics: [
{
id: 'codex-cli',
label: 'Codex CLI',
detected: true,
statusMessage: 'Detected',
detailMessage: 'System codex binary available.',
},
],
capabilities: {
teamLaunch: true,
oneShot: true,
extensions: {
plugins: {
status: 'unsupported',
ownership: 'shared',
reason:
'Plugins are not currently guaranteed for codex-native sessions in the multimodel runtime.',
},
mcp: {
status: 'unsupported',
ownership: 'shared',
reason: 'Headless-limited lane',
},
skills: {
status: 'unsupported',
ownership: 'shared',
reason: 'Headless-limited lane',
},
apiKeys: { status: 'supported', ownership: 'shared', reason: null },
},
},
backend: {
kind: 'codex-native',
label: 'Codex native',
authMethodDetail: 'API key',
},
},
gemini: {
supported: false,
authenticated: false,
},
},
}),
stderr: '',
exitCode: 0,
});
const { ClaudeMultimodelBridgeService } =
await import('@main/services/runtime/ClaudeMultimodelBridgeService');
const service = new ClaudeMultimodelBridgeService();
const providers = await service.getProviderStatuses('/mock/agent_teams_orchestrator');
const codex = providers.find((provider) => provider.providerId === 'codex');
expect(codex).toMatchObject({
providerId: 'codex',
authenticated: true,
selectedBackendId: 'codex-native',
resolvedBackendId: 'codex-native',
backend: {
kind: 'codex-native',
label: 'Codex native',
},
availableBackends: [
expect.objectContaining({
id: 'codex-native',
selectable: true,
available: true,
state: 'ready',
audience: 'general',
statusMessage: 'Ready',
}),
],
externalRuntimeDiagnostics: [
expect.objectContaining({
id: 'codex-cli',
detected: true,
}),
],
});
expect(codex?.capabilities.extensions.plugins).toMatchObject({
status: 'unsupported',
});
expect(isConnectionManagedRuntimeProvider(codex!)).toBe(true);
expect(getProviderConnectionModeSummary(codex!)).toBeNull();
expect(getProviderCurrentRuntimeSummary(codex!)).toBe('Current runtime: Codex native');
});
it('preserves codex-native ready truth from runtime status payloads', async () => {
execCliMock.mockResolvedValue({
stdout: JSON.stringify({
providers: {
codex: {
supported: true,
authenticated: true,
authMethod: 'api_key',
verificationState: 'verified',
canLoginFromUi: false,
selectedBackendId: 'codex-native',
resolvedBackendId: 'codex-native',
availableBackends: [
{
id: 'codex-native',
label: 'Codex native',
selectable: true,
recommended: true,
available: true,
state: 'ready',
audience: 'general',
statusMessage: 'Ready',
detailMessage: 'Codex native runtime is ready through the local codex exec seam.',
},
],
capabilities: {
teamLaunch: true,
oneShot: true,
extensions: {
plugins: { status: 'unsupported', ownership: 'shared', reason: 'Phase 1' },
mcp: { status: 'unsupported', ownership: 'shared', reason: 'Phase 1' },
skills: { status: 'unsupported', ownership: 'shared', reason: 'Phase 1' },
apiKeys: { status: 'supported', ownership: 'shared', reason: null },
},
},
backend: {
kind: 'codex-native',
label: 'Codex native',
authMethodDetail: 'api_key',
},
},
},
}),
stderr: '',
exitCode: 0,
});
const { ClaudeMultimodelBridgeService } =
await import('@main/services/runtime/ClaudeMultimodelBridgeService');
const service = new ClaudeMultimodelBridgeService();
const codex = await service.getProviderStatus('/mock/agent_teams_orchestrator', 'codex');
expect(codex.availableBackends?.find((backend) => backend.id === 'codex-native')).toMatchObject(
{
id: 'codex-native',
selectable: true,
available: true,
state: 'ready',
audience: 'general',
statusMessage: 'Ready',
}
);
});
it('preserves codex-native runtime-missing rollout states from runtime status payloads', async () => {
execCliMock.mockResolvedValue({
stdout: JSON.stringify({
providers: {
codex: {
supported: true,
authenticated: false,
authMethod: null,
verificationState: 'unknown',
canLoginFromUi: false,
statusMessage: 'Codex native runtime unavailable',
detailMessage:
'Codex native runtime requires the codex CLI binary to be installed and discoverable.',
selectedBackendId: 'codex-native',
resolvedBackendId: null,
availableBackends: [
{
id: 'codex-native',
label: 'Codex native',
selectable: false,
recommended: false,
available: false,
state: 'runtime-missing',
audience: 'general',
statusMessage: 'Codex CLI not found',
detailMessage:
'Codex native runtime requires the codex CLI binary to be installed and discoverable.',
},
],
capabilities: {
teamLaunch: true,
oneShot: true,
extensions: {
plugins: { status: 'unsupported', ownership: 'shared', reason: 'Phase 1' },
mcp: { status: 'unsupported', ownership: 'shared', reason: 'Phase 1' },
skills: { status: 'unsupported', ownership: 'shared', reason: 'Phase 1' },
apiKeys: { status: 'supported', ownership: 'shared', reason: null },
},
},
backend: null,
},
},
}),
stderr: '',
exitCode: 0,
});
const { ClaudeMultimodelBridgeService } =
await import('@main/services/runtime/ClaudeMultimodelBridgeService');
const service = new ClaudeMultimodelBridgeService();
const codex = await service.getProviderStatus('/mock/agent_teams_orchestrator', 'codex');
expect(codex.availableBackends?.find((backend) => backend.id === 'codex-native')).toMatchObject(
{
id: 'codex-native',
selectable: false,
available: false,
state: 'runtime-missing',
audience: 'general',
statusMessage: 'Codex CLI not found',
}
);
});
it('uses live OpenCode verification on explicit provider verify', async () => {
execCliMock.mockImplementation((_binaryPath, args) => {
const normalizedArgs = Array.isArray(args) ? args.join(' ') : '';
if (
normalizedArgs === 'runtime status --json --provider opencode' ||
normalizedArgs === 'runtime status --json --provider opencode --summary'
) {
return Promise.resolve({
stdout: JSON.stringify({
providers: {
opencode: {
supported: true,
authenticated: true,
authMethod: 'opencode_managed',
verificationState: 'verified',
canLoginFromUi: false,
statusMessage: null,
detailMessage: 'version 1.4.0 - connected openai',
capabilities: {
teamLaunch: false,
oneShot: false,
extensions: {
plugins: { status: 'read-only', ownership: 'provider-scoped', reason: null },
mcp: { status: 'read-only', ownership: 'provider-scoped', reason: null },
skills: { status: 'read-only', ownership: 'provider-scoped', reason: null },
apiKeys: { status: 'read-only', ownership: 'provider-scoped', reason: null },
},
},
models: ['openai/gpt-5.4-mini'],
backend: { kind: 'opencode-cli', label: 'OpenCode CLI' },
externalRuntimeDiagnostics: [],
},
},
}),
stderr: '',
exitCode: 0,
});
}
if (normalizedArgs === 'runtime verify --json --provider opencode') {
return Promise.resolve({
stdout: JSON.stringify({
schemaVersion: 1,
providerId: 'opencode',
snapshot: {
detected: true,
hostHealthy: true,
probeError: null,
diagnostics: [],
host: {
version: '1.4.0',
resolvedConfigFingerprint: 'resolved-fingerprint-123456',
},
profile: {
profileRootKey: 'profile-root',
projectBehaviorFingerprint: 'behavior-fingerprint-123456',
managedConfigFingerprint: 'managed-fingerprint-123456',
},
config: {
default_agent: 'teammate',
share: 'disabled',
snapshot: false,
autoupdate: false,
},
},
}),
stderr: '',
exitCode: 0,
});
}
return Promise.reject(new Error(`Unexpected execCli call: ${normalizedArgs}`));
});
const { ClaudeMultimodelBridgeService } =
await import('@main/services/runtime/ClaudeMultimodelBridgeService');
const service = new ClaudeMultimodelBridgeService();
const provider = await service.verifyProviderStatus(
'/mock/agent_teams_orchestrator',
'opencode'
);
expect(provider).toMatchObject({
providerId: 'opencode',
verificationState: 'verified',
detailMessage: expect.stringContaining('live resolved-fin'),
capabilities: {
extensions: {
plugins: {
status: 'unsupported',
},
mcp: {
status: 'read-only',
},
},
},
backend: {
kind: 'opencode-cli',
authMethodDetail: 'managed teammate agent',
},
});
expect(provider.externalRuntimeDiagnostics).toEqual(
expect.arrayContaining([
expect.objectContaining({
id: 'opencode-live-host',
detected: true,
statusMessage: 'Healthy',
}),
expect.objectContaining({
id: 'opencode-managed-runtime',
detected: true,
statusMessage: 'Managed runtime verified',
}),
])
);
});
it('loads projected OpenCode transcript data through the runtime transcript command', async () => {
execCliMock.mockImplementation(async (_binaryPath, args) => {
const normalizedArgs = Array.isArray(args) ? args.join(' ') : '';
if (
normalizedArgs.startsWith(
'runtime transcript --json --provider opencode --team team-a --member alice --projection-only --limit 20 --output '
)
) {
const outputIndex = Array.isArray(args) ? args.indexOf('--output') : -1;
const outputPath =
outputIndex >= 0 && Array.isArray(args) ? String(args[outputIndex + 1] ?? '') : '';
await writeFile(
outputPath,
JSON.stringify({
schemaVersion: 1,
providerId: 'opencode',
transcript: {
sessionId: 'session-1',
durableState: 'idle',
messageCount: 2,
toolCallCount: 1,
errorCount: 0,
latestAssistantText: '/Users/tester/project',
latestAssistantPreview: '/Users/tester/project',
messages: [],
diagnostics: [],
logProjection: {
sessionId: 'session-1',
durableState: 'idle',
sourceMessageCount: 2,
projectedMessageCount: 3,
syntheticMessageCount: 1,
toolCallCount: 1,
errorCount: 0,
diagnostics: [],
messages: [
{
uuid: 'msg-assistant-1',
type: 'assistant',
toolCalls: [{ id: 'call_pwd', name: 'bash' }],
},
{
uuid: 'msg-assistant-1::tool_results',
type: 'user',
isMeta: true,
toolResults: [{ toolUseId: 'call_pwd', isError: false }],
},
],
},
},
}),
'utf8'
);
return Promise.resolve({
stdout: '',
stderr: '',
exitCode: 0,
});
}
return Promise.reject(new Error(`Unexpected execCli call: ${normalizedArgs}`));
});
const { ClaudeMultimodelBridgeService } =
await import('@main/services/runtime/ClaudeMultimodelBridgeService');
const service = new ClaudeMultimodelBridgeService();
const transcript = await service.getOpenCodeTranscript('/mock/agent_teams_orchestrator', {
teamId: 'team-a',
memberName: 'alice',
limit: 20,
});
expect(transcript).toMatchObject({
sessionId: 'session-1',
durableState: 'idle',
toolCallCount: 1,
logProjection: {
projectedMessageCount: 3,
syntheticMessageCount: 1,
messages: expect.arrayContaining([
expect.objectContaining({
uuid: 'msg-assistant-1',
type: 'assistant',
}),
expect.objectContaining({
uuid: 'msg-assistant-1::tool_results',
type: 'user',
isMeta: true,
}),
]),
},
});
});
it('passes OpenCode lane and popup timeout to the runtime transcript command', async () => {
execCliMock.mockImplementation(async (_binaryPath, args) => {
const normalizedArgs = Array.isArray(args) ? args.join(' ') : '';
if (
normalizedArgs.startsWith(
'runtime transcript --json --provider opencode --team team-a --member alice --projection-only --limit 20 --lane secondary:opencode:alice --output '
)
) {
const outputIndex = Array.isArray(args) ? args.indexOf('--output') : -1;
const outputPath =
outputIndex >= 0 && Array.isArray(args) ? String(args[outputIndex + 1] ?? '') : '';
await writeFile(
outputPath,
JSON.stringify({
schemaVersion: 1,
providerId: 'opencode',
transcript: {
sessionId: 'session-lane',
durableState: 'idle',
messages: [],
diagnostics: [],
logProjection: {
messages: [],
},
},
}),
'utf8'
);
return Promise.resolve({
stdout: '',
stderr: '',
exitCode: 0,
});
}
return Promise.reject(new Error(`Unexpected execCli call: ${normalizedArgs}`));
});
const { ClaudeMultimodelBridgeService } =
await import('@main/services/runtime/ClaudeMultimodelBridgeService');
const service = new ClaudeMultimodelBridgeService();
const transcript = await service.getOpenCodeTranscript('/mock/agent_teams_orchestrator', {
teamId: 'team-a',
memberName: 'alice',
limit: 20,
laneId: ' secondary:opencode:alice ',
timeoutMs: 1_234,
});
expect(transcript?.sessionId).toBe('session-lane');
expect(execCliMock).toHaveBeenCalledWith(
'/mock/agent_teams_orchestrator',
expect.arrayContaining(['--lane', 'secondary:opencode:alice']),
expect.objectContaining({ timeout: 1_234 })
);
});
it('passes exact OpenCode session id to the runtime transcript command', async () => {
execCliMock.mockImplementation(async (_binaryPath, args) => {
const normalizedArgs = Array.isArray(args) ? args.join(' ') : '';
if (
normalizedArgs.startsWith(
'runtime transcript --json --provider opencode --team team-a --member alice --projection-only --limit 20 --session-id session-exact --output '
)
) {
const outputIndex = Array.isArray(args) ? args.indexOf('--output') : -1;
const outputPath =
outputIndex >= 0 && Array.isArray(args) ? String(args[outputIndex + 1] ?? '') : '';
await writeFile(
outputPath,
JSON.stringify({
schemaVersion: 1,
providerId: 'opencode',
transcript: {
sessionId: 'session-exact',
durableState: 'idle',
messages: [],
diagnostics: [],
logProjection: {
sessionId: 'session-exact',
messages: [],
},
},
}),
'utf8'
);
return Promise.resolve({
stdout: '',
stderr: '',
exitCode: 0,
});
}
return Promise.reject(new Error(`Unexpected execCli call: ${normalizedArgs}`));
});
const { ClaudeMultimodelBridgeService } =
await import('@main/services/runtime/ClaudeMultimodelBridgeService');
const service = new ClaudeMultimodelBridgeService();
const transcript = await service.getOpenCodeTranscript('/mock/agent_teams_orchestrator', {
teamId: 'team-a',
memberName: 'alice',
limit: 20,
sessionId: ' session-exact ',
});
expect(transcript?.sessionId).toBe('session-exact');
expect(execCliMock).toHaveBeenCalledWith(
'/mock/agent_teams_orchestrator',
expect.arrayContaining(['--session-id', 'session-exact']),
expect.any(Object)
);
});
it('loads a large real OpenCode projection fixture through output-file transcript delivery', async () => {
const fixturePath = path.resolve(
process.cwd(),
'test/fixtures/team/opencode/relay-works-10-jack-projection-transcript.json'
);
const fixtureRaw = await readFileFixture(fixturePath, 'utf8');
execCliMock.mockImplementation(async (_binaryPath, args) => {
const normalizedArgs = Array.isArray(args) ? args.join(' ') : '';
if (
normalizedArgs.startsWith(
'runtime transcript --json --provider opencode --team relay-works-10 --member jack --projection-only --limit 200 --output '
)
) {
const outputIndex = Array.isArray(args) ? args.indexOf('--output') : -1;
const outputPath =
outputIndex >= 0 && Array.isArray(args) ? String(args[outputIndex + 1] ?? '') : '';
await writeFile(outputPath, fixtureRaw, 'utf8');
return Promise.resolve({
stdout: '',
stderr: '',
exitCode: 0,
});
}
return Promise.reject(new Error(`Unexpected execCli call: ${normalizedArgs}`));
});
const { ClaudeMultimodelBridgeService } =
await import('@main/services/runtime/ClaudeMultimodelBridgeService');
const service = new ClaudeMultimodelBridgeService();
const transcript = await service.getOpenCodeTranscript('/mock/agent_teams_orchestrator', {
teamId: 'relay-works-10',
memberName: 'jack',
limit: 200,
});
const projectedMessages = transcript?.logProjection?.messages ?? [];
const toolNames = projectedMessages.flatMap((message) =>
message.toolCalls.map((toolCall) => toolCall.name)
);
expect(fixtureRaw.length).toBeGreaterThan(64_000);
expect(transcript?.sessionId).toBe('ses_23edf9243ffeSNYPWObDloBJyQ');
expect(transcript?.messageCount).toBe(65);
expect(transcript?.toolCallCount).toBe(36);
expect(transcript?.messages).toEqual([]);
expect(projectedMessages).toHaveLength(101);
expect(toolNames).toEqual(
expect.arrayContaining([
'agent-teams_runtime_bootstrap_checkin',
'agent-teams_member_briefing',
'agent-teams_message_send',
'agent-teams_task_start',
'agent-teams_task_add_comment',
'agent-teams_task_complete',
'bash',
'read',
])
);
expect(toolNames).not.toContain('SendMessage');
});
it('keeps OpenCode model verification catalog-only in the bridge', async () => {
execCliMock.mockImplementation((_binaryPath, args) => {
const normalizedArgs = Array.isArray(args) ? args.join(' ') : '';
return Promise.reject(new Error(`Unexpected execCli call: ${normalizedArgs}`));
});
const { ClaudeMultimodelBridgeService } =
await import('@main/services/runtime/ClaudeMultimodelBridgeService');
const service = new ClaudeMultimodelBridgeService();
const provider = await service.verifyOpenCodeModels('/mock/agent_teams_orchestrator', {
providerId: 'opencode',
displayName: 'OpenCode',
supported: true,
authenticated: true,
authMethod: 'opencode_managed',
verificationState: 'verified',
modelVerificationState: 'idle',
statusMessage: null,
detailMessage: null,
models: ['openai/gpt-5.4-mini', 'openrouter/moonshotai/kimi-k2', 'opencode/big-pickle'],
modelAvailability: [],
canLoginFromUi: false,
capabilities: {
teamLaunch: false,
oneShot: false,
extensions: {
plugins: { status: 'read-only', ownership: 'provider-scoped', reason: null },
mcp: { status: 'read-only', ownership: 'provider-scoped', reason: null },
skills: { status: 'read-only', ownership: 'provider-scoped', reason: null },
apiKeys: { status: 'read-only', ownership: 'provider-scoped', reason: null },
},
},
selectedBackendId: null,
resolvedBackendId: null,
availableBackends: [],
externalRuntimeDiagnostics: [],
backend: { kind: 'opencode-cli', label: 'OpenCode CLI' },
connection: null,
});
expect(execCliMock).not.toHaveBeenCalled();
expect(provider.modelVerificationState).toBe('idle');
expect(provider.modelAvailability).toEqual([]);
});
});