fix(cliproxy): correct pool routing version capability map (#1726)

This commit is contained in:
Tam Nhu Tran committed 2026-09-02 11:14:57 -04:00
1 parent 87f1d9b4e8
commit b9601cb913
2 files changed
+114 -15

No files matched your search

@@ -2,7 +2,7 @@ import { afterEach, beforeEach, describe, expect, it, mock } from 'bun:test';
import * as fs from 'fs';
import * as os from 'os';
import * as path from 'path';
import type { CLIProxyBackend } from '../../types';
describe('cliproxy routing strategy service', () => {
let tempHome = '';
let scopedConfigDir = '';
@@ -15,6 +15,10 @@ describe('cliproxy routing strategy service', () => {
protocol: 'http' as const,
isRemote: false,
};
let binaryBackend: CLIProxyBackend = 'original';
let binaryVersion = '7.2.127-7';
let installedVersionQueriedBackend: CLIProxyBackend | undefined = undefined;
let loggedWarnings: Array<{ event: string; message: string; context?: Record<string, unknown> }> = [];
let responseFactory: (() => Promise<Response>) | null = null;
beforeEach(async () => {
@@ -28,6 +32,10 @@ describe('cliproxy routing strategy service', () => {
};
responseFactory = null;
originalCcsDir = process.env.CCS_DIR;
binaryBackend = 'original';
binaryVersion = '7.2.127-7';
installedVersionQueriedBackend = undefined;
loggedWarnings = [];
originalCcsHome = process.env.CCS_HOME;
process.env.CCS_DIR = scopedConfigDir;
process.env.CCS_HOME = tempHome;
@@ -74,6 +82,26 @@ describe('cliproxy routing strategy service', () => {
},
}));
mock.module('../../binary-manager', () => ({
getConfiguredBackend: () => binaryBackend,
getInstalledCliproxyVersion: (backend?: CLIProxyBackend) => {
installedVersionQueriedBackend = backend;
return binaryVersion;
},
}));
mock.module('../../../services/logging', () => ({
createLogger: () => ({
debug: () => {},
info: () => {},
warn: (event: string, message: string, context?: Record<string, unknown>) => {
loggedWarnings.push({ event, message, context });
},
error: () => {},
child: () => ({ warn: () => {} }),
}),
}));
return import(`../routing-strategy?test=${Date.now()}-${Math.random()}`);
}
@@ -365,4 +393,65 @@ describe('cliproxy routing strategy service', () => {
expect(result.message).not.toContain('Pool routing is active');
});
});
describe('pool routing version compatibility (#1726)', () => {
it('defines backend-specific minimum versions for pool routing', async () => {
const mod = await loadRoutingModule();
expect(mod.POOL_ROUTING_MIN_VERSION).toEqual({
original: '6.8.34',
plus: '6.8.34-0',
});
});
it('evaluates isPoolRoutingSupported correctly across original and plus thresholds', async () => {
const mod = await loadRoutingModule();
expect(mod.isPoolRoutingSupported('original', '6.8.33')).toBe(false);
expect(mod.isPoolRoutingSupported('original', '6.8.34')).toBe(true);
expect(mod.isPoolRoutingSupported('original', '6.9.0')).toBe(true);
expect(mod.isPoolRoutingSupported('plus', '6.8.33-9')).toBe(false);
expect(mod.isPoolRoutingSupported('plus', '6.8.34-0')).toBe(true);
expect(mod.isPoolRoutingSupported('plus', '7.2.127-7')).toBe(true);
expect(mod.isPoolRoutingSupported('original', '')).toBe(false);
expect(mod.isPoolRoutingSupported('plus', 'not-a-version')).toBe(false);
});
it('warns with backend-specific metadata when plus binary is below minimum', async () => {
await withScopedConfig(async () => {
binaryBackend = 'plus';
binaryVersion = '6.8.33-9';
loggedWarnings = [];
const mod = await loadRoutingModule();
const result = mod.enablePoolRouting(8317);
expect(result.changed).toBe(true);
expect(installedVersionQueriedBackend).toBe('plus');
expect(loggedWarnings).toHaveLength(1);
expect(loggedWarnings[0].event).toBe('pool_routing.binary_below_minimum');
expect(loggedWarnings[0].message).toContain('CLIProxy Plus');
expect(loggedWarnings[0].context).toEqual({
backend: 'plus',
installedVersion: '6.8.33-9',
minimumVersion: '6.8.34-0',
});
});
});
it('does not warn when plus binary meets the minimum version', async () => {
await withScopedConfig(async () => {
binaryBackend = 'plus';
binaryVersion = '6.8.34-0';
loggedWarnings = [];
const mod = await loadRoutingModule();
const result = mod.enablePoolRouting(8317);
expect(result.changed).toBe(true);
expect(installedVersionQueriedBackend).toBe('plus');
expect(loggedWarnings).toHaveLength(0);
});
});
});
});
+24 -14
View File
@@ -7,10 +7,13 @@ import {
getCliproxyRoutingTarget,
getRoutingErrorMessage,
} from './routing-strategy-http';
import type { CliproxyRoutingStrategy } from '../types';
import type { CLIProxyBackend, CliproxyRoutingStrategy } from '../types';
import { loadOrCreateUnifiedConfig, mutateConfig } from '../../config/config-loader-facade';
import { getInstalledCliproxyVersion } from '../binary-manager';
import { compareVersions } from '../../utils/update-checker';
import { getConfiguredBackend, getInstalledCliproxyVersion } from '../binary-manager';
import {
type CLIProxyBackendMinVersions,
meetsBackendMinimumVersion,
} from '../binary/version-checker';
import { getConfigYamlPath } from '../../config/loader/io-locks';
import { createLogger } from '../../services/logging';
@@ -46,12 +49,18 @@ export const POOL_ROUTING_VERIFIED_PROVIDERS = new Set(['claude', 'agy']);
* max-retry-credentials and the cooling flip.
* Older binaries silently ignore unknown keys — pool rails would appear active
* but have no effect. Warn the user at enable time if below this version.
*
* NOTE: Update this constant when upstream first ships these keys.
* Current best estimate based on spec; adjust after spike Test D confirms.
*/
export const POOL_ROUTING_MIN_VERSION = '6.9.45';
export const POOL_ROUTING_MIN_VERSION: CLIProxyBackendMinVersions = {
original: '6.8.34',
plus: '6.8.34-0',
};
export function isPoolRoutingSupported(
backend: CLIProxyBackend,
installedVersion: string
): boolean {
return meetsBackendMinimumVersion(installedVersion, backend, POOL_ROUTING_MIN_VERSION);
}
/**
* Pool-active override warning text. When pool routing is enabled the generator
* forces fill-first/affinity/cooling and ignores the stored strategy/affinity, so
@@ -205,20 +214,21 @@ export function enablePoolRouting(
const preservedExplicitSetting = hasExplicitRoutingStrategy() || hasExplicitSessionAffinity();
// Spec step 3 / architecture: assert minimum CLIProxy version at enable time.
// Stale binaries silently ignore max-retry-credentials and the cooling flip,
// so pool rails would appear active but have no effect. Warn and proceed.
try {
const installedVersion = getInstalledCliproxyVersion();
if (compareVersions(installedVersion, POOL_ROUTING_MIN_VERSION) < 0) {
const backend = getConfiguredBackend();
const installedVersion = getInstalledCliproxyVersion(backend);
if (!isPoolRoutingSupported(backend, installedVersion)) {
const minimumVersion = POOL_ROUTING_MIN_VERSION[backend];
const backendLabel = backend === 'plus' ? 'CLIProxy Plus' : 'CLIProxy';
logger.warn(
'pool_routing.binary_below_minimum',
`CLIProxy v${installedVersion} is older than the pool routing minimum (v${POOL_ROUTING_MIN_VERSION}). ` +
`${backendLabel} v${installedVersion} is older than the pool routing minimum (v${minimumVersion}). ` +
`The max-retry-credentials and cooling keys may be silently ignored by the running binary. ` +
`Run 'ccs cliproxy --latest' to update CLIProxy, then restart with 'ccs cliproxy restart'.`,
{
backend,
installedVersion,
minimumVersion: POOL_ROUTING_MIN_VERSION,
minimumVersion,
}
);
}