feat(backends): add retry/backoff controls for external CLI execution

This commit is contained in:
William Valentin
2026-02-17 09:49:06 -08:00
parent 1f3d35726b
commit 4400bdfcc9
5 changed files with 64 additions and 8 deletions
+34
View File
@@ -15,6 +15,19 @@ vi.mock('child_process', () => ({
const mockExecFile = vi.mocked(execFile);
function makeFailChild(stderrText = 'failed'): FakeChild {
const child = new EventEmitter() as FakeChild;
child.stdout = new EventEmitter();
child.stderr = new EventEmitter();
child.stdin = { end: vi.fn() };
child.kill = vi.fn();
setImmediate(() => {
child.stderr.emit('data', Buffer.from(stderrText));
child.emit('close', 1);
});
return child;
}
describe('ExternalCliBackend', () => {
beforeEach(() => {
vi.clearAllMocks();
@@ -104,4 +117,25 @@ describe('ExternalCliBackend', () => {
expect(opencode.name).toBe('opencode');
expect(gemini.name).toBe('gemini');
});
it('retries failed backend calls when retries are configured', async () => {
spawnMock
.mockImplementationOnce(() => makeFailChild('transient'))
.mockImplementationOnce(() => makeChild('recovered'));
const backend = new ExternalCliBackend({
name: 'codex',
command: 'codex',
retries: 1,
retryDelayMs: 0,
});
const result = await backend.process({
systemPrompt: 'sys',
history: [],
message: 'retry me',
});
expect(result).toBe('recovered');
expect(spawnMock).toHaveBeenCalledTimes(2);
});
});