[codex] Remove Codex SDK canary runtime (#220)
* Revert "fix: drain sdk follow-up turns (#219)" This reverts commit807828e0ce. * Revert "feat: feature-flag Codex SDK runner (#218)" This reverts commit879d16235f.
This commit is contained in:
@@ -1,64 +0,0 @@
|
||||
import { describe, expect, it } from 'vitest';
|
||||
|
||||
import { resolveCodexRuntimeMode } from '../src/runtime-mode.js';
|
||||
|
||||
describe('codex runner runtime mode selection', () => {
|
||||
it('keeps app-server as the default runtime', () => {
|
||||
expect(
|
||||
resolveCodexRuntimeMode({}, { codexGoals: false }, 'do work'),
|
||||
).toEqual({
|
||||
mode: 'app-server',
|
||||
reason: 'default',
|
||||
});
|
||||
});
|
||||
|
||||
it('selects SDK mode when CODEX_RUNTIME=sdk', () => {
|
||||
expect(
|
||||
resolveCodexRuntimeMode(
|
||||
{ CODEX_RUNTIME: 'sdk' },
|
||||
{ codexGoals: false },
|
||||
'do work',
|
||||
),
|
||||
).toEqual({ mode: 'sdk', reason: 'CODEX_RUNTIME=sdk' });
|
||||
});
|
||||
|
||||
it('canary-limits SDK mode to configured paired roles', () => {
|
||||
expect(
|
||||
resolveCodexRuntimeMode(
|
||||
{ CODEX_RUNTIME: 'sdk', CODEX_RUNTIME_SDK_ROLES: 'owner,arbiter' },
|
||||
{ codexGoals: false, roomRole: 'owner' },
|
||||
'do work',
|
||||
),
|
||||
).toEqual({ mode: 'sdk', reason: 'CODEX_RUNTIME=sdk' });
|
||||
expect(
|
||||
resolveCodexRuntimeMode(
|
||||
{ CODEX_RUNTIME: 'sdk', CODEX_RUNTIME_SDK_ROLES: 'owner,arbiter' },
|
||||
{ codexGoals: false, roomRole: 'reviewer' },
|
||||
'do work',
|
||||
),
|
||||
).toEqual({ mode: 'app-server', reason: 'sdk-role-not-enabled' });
|
||||
});
|
||||
|
||||
it('falls back to app-server for /compact because SDK lacks compaction', () => {
|
||||
expect(
|
||||
resolveCodexRuntimeMode(
|
||||
{ CODEX_RUNTIME: 'sdk' },
|
||||
{ codexGoals: false },
|
||||
'/compact',
|
||||
),
|
||||
).toEqual({
|
||||
mode: 'app-server',
|
||||
reason: 'sdk-unsupported-session-command',
|
||||
});
|
||||
});
|
||||
|
||||
it('falls back to app-server when Codex goals are enabled', () => {
|
||||
expect(
|
||||
resolveCodexRuntimeMode(
|
||||
{ CODEX_RUNTIME: 'sdk' },
|
||||
{ codexGoals: true },
|
||||
'do work',
|
||||
),
|
||||
).toEqual({ mode: 'app-server', reason: 'sdk-unsupported-goals' });
|
||||
});
|
||||
});
|
||||
@@ -1,194 +0,0 @@
|
||||
import { describe, expect, it } from 'vitest';
|
||||
|
||||
import {
|
||||
CodexSdkClient,
|
||||
createInitialCodexSdkTurnState,
|
||||
reduceCodexSdkTurnState,
|
||||
toCodexSdkInput,
|
||||
type CodexSdkThreadPort,
|
||||
} from '../src/codex-sdk-client.js';
|
||||
|
||||
const usage = {
|
||||
input_tokens: 1,
|
||||
cached_input_tokens: 0,
|
||||
output_tokens: 2,
|
||||
reasoning_output_tokens: 0,
|
||||
};
|
||||
|
||||
async function* events(items: unknown[]): AsyncGenerator<unknown> {
|
||||
for (const item of items) {
|
||||
yield item;
|
||||
}
|
||||
}
|
||||
|
||||
class FakeThread implements CodexSdkThreadPort {
|
||||
id: string | null = null;
|
||||
lastInput: unknown = null;
|
||||
lastSignal: AbortSignal | undefined;
|
||||
|
||||
constructor(private readonly scriptedEvents: unknown[]) {}
|
||||
|
||||
async runStreamed(input: unknown, options?: { signal?: AbortSignal }) {
|
||||
this.lastInput = input;
|
||||
this.lastSignal = options?.signal;
|
||||
return { events: events(this.scriptedEvents) };
|
||||
}
|
||||
}
|
||||
|
||||
class FakeCodex {
|
||||
readonly started: unknown[] = [];
|
||||
readonly resumed: unknown[] = [];
|
||||
readonly threads: FakeThread[] = [];
|
||||
|
||||
constructor(private readonly scriptedEvents: unknown[]) {}
|
||||
|
||||
startThread(options?: unknown): FakeThread {
|
||||
this.started.push(options);
|
||||
const thread = new FakeThread(this.scriptedEvents);
|
||||
this.threads.push(thread);
|
||||
return thread;
|
||||
}
|
||||
|
||||
resumeThread(id: string, options?: unknown): FakeThread {
|
||||
this.resumed.push({ id, options });
|
||||
const thread = new FakeThread(this.scriptedEvents);
|
||||
thread.id = id;
|
||||
this.threads.push(thread);
|
||||
return thread;
|
||||
}
|
||||
}
|
||||
|
||||
describe('Codex SDK runner spike', () => {
|
||||
it('maps existing app-server text/image input into Codex SDK input shape', () => {
|
||||
expect(
|
||||
toCodexSdkInput([
|
||||
{ type: 'text', text: 'inspect this' },
|
||||
{ type: 'localImage', path: '/tmp/screenshot.png' },
|
||||
]),
|
||||
).toEqual([
|
||||
{ type: 'text', text: 'inspect this' },
|
||||
{ type: 'local_image', path: '/tmp/screenshot.png' },
|
||||
]);
|
||||
});
|
||||
|
||||
it('reduces SDK thread events into the runner final result and thread id', () => {
|
||||
let state = createInitialCodexSdkTurnState();
|
||||
state = reduceCodexSdkTurnState(state, {
|
||||
type: 'thread.started',
|
||||
thread_id: 'thread-1',
|
||||
});
|
||||
state = reduceCodexSdkTurnState(state, { type: 'turn.started' });
|
||||
state = reduceCodexSdkTurnState(state, {
|
||||
type: 'item.completed',
|
||||
item: {
|
||||
id: 'msg-1',
|
||||
type: 'agent_message',
|
||||
text: 'STEP_DONE\n검증 완료',
|
||||
},
|
||||
});
|
||||
state = reduceCodexSdkTurnState(state, {
|
||||
type: 'turn.completed',
|
||||
usage,
|
||||
});
|
||||
|
||||
expect(state).toMatchObject({
|
||||
status: 'completed',
|
||||
threadId: 'thread-1',
|
||||
finalAnswer: 'STEP_DONE\n검증 완료',
|
||||
usage,
|
||||
});
|
||||
});
|
||||
|
||||
it('runs a streamed SDK turn and returns the first real thread id', async () => {
|
||||
const codex = new FakeCodex([
|
||||
{ type: 'thread.started', thread_id: 'thread-42' },
|
||||
{ type: 'turn.started' },
|
||||
{
|
||||
type: 'item.completed',
|
||||
item: { id: 'msg-1', type: 'agent_message', text: 'TASK_DONE\nSDK_OK' },
|
||||
},
|
||||
{ type: 'turn.completed', usage },
|
||||
]);
|
||||
const client = new CodexSdkClient({
|
||||
codex,
|
||||
env: { PATH: '/usr/bin' },
|
||||
log: () => undefined,
|
||||
});
|
||||
|
||||
const handle = await client.startOrResumeThread(undefined, {
|
||||
cwd: '/repo',
|
||||
model: 'gpt-5.5',
|
||||
effort: 'high',
|
||||
});
|
||||
const turn = await client.startTurn(
|
||||
handle,
|
||||
[{ type: 'text', text: 'respond' }],
|
||||
{ cwd: '/repo', model: 'gpt-5.5', effort: 'high' },
|
||||
);
|
||||
|
||||
await expect(turn.wait()).resolves.toMatchObject({
|
||||
threadId: 'thread-42',
|
||||
result: 'TASK_DONE\nSDK_OK',
|
||||
state: { status: 'completed' },
|
||||
});
|
||||
expect(codex.started).toEqual([
|
||||
{
|
||||
approvalPolicy: 'never',
|
||||
model: 'gpt-5.5',
|
||||
modelReasoningEffort: 'high',
|
||||
networkAccessEnabled: true,
|
||||
sandboxMode: 'danger-full-access',
|
||||
workingDirectory: '/repo',
|
||||
},
|
||||
]);
|
||||
expect(codex.threads[0].lastInput).toEqual([
|
||||
{ type: 'text', text: 'respond' },
|
||||
]);
|
||||
});
|
||||
|
||||
it('coerces minimal effort to low because Codex SDK exec keeps tools enabled', async () => {
|
||||
const codex = new FakeCodex([
|
||||
{ type: 'thread.started', thread_id: 'thread-minimal' },
|
||||
{ type: 'turn.started' },
|
||||
{
|
||||
type: 'item.completed',
|
||||
item: { id: 'msg-1', type: 'agent_message', text: 'SDK_OK' },
|
||||
},
|
||||
{ type: 'turn.completed', usage },
|
||||
]);
|
||||
const client = new CodexSdkClient({ codex, log: () => undefined });
|
||||
|
||||
await client.startOrResumeThread(undefined, {
|
||||
cwd: '/repo',
|
||||
effort: 'minimal',
|
||||
});
|
||||
|
||||
expect(codex.started[0]).toMatchObject({ modelReasoningEffort: 'low' });
|
||||
});
|
||||
|
||||
it('documents the SDK gap: mid-turn steering is not available', async () => {
|
||||
const codex = new FakeCodex([
|
||||
{ type: 'thread.started', thread_id: 'thread-1' },
|
||||
{ type: 'turn.started' },
|
||||
{ type: 'turn.completed', usage },
|
||||
]);
|
||||
const client = new CodexSdkClient({ codex, log: () => undefined });
|
||||
const handle = await client.startOrResumeThread('thread-1', {
|
||||
cwd: '/repo',
|
||||
});
|
||||
const turn = await client.startTurn(
|
||||
handle,
|
||||
[{ type: 'text', text: 'start' }],
|
||||
{
|
||||
cwd: '/repo',
|
||||
},
|
||||
);
|
||||
|
||||
await expect(
|
||||
turn.steer([{ type: 'text', text: 'follow-up' }]),
|
||||
).rejects.toThrow('Codex SDK runner does not support mid-turn steering');
|
||||
await expect(turn.wait()).resolves.toMatchObject({
|
||||
state: { status: 'completed' },
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user