mirror of
https://github.com/wu736139669/hapi.git
synced 2026-08-05 06:24:37 +00:00
fix(codex): keep local session timeout non-fatal (#304)
This commit is contained in:
@@ -1,7 +1,9 @@
|
||||
import { afterEach, describe, expect, it, vi } from 'vitest';
|
||||
|
||||
const harness = vi.hoisted(() => ({
|
||||
launches: [] as Array<Record<string, unknown>>
|
||||
launches: [] as Array<Record<string, unknown>>,
|
||||
sessionScannerCalls: [] as Array<Record<string, unknown>>,
|
||||
scannerFailureMessage: 'No Codex session found within 120000ms for cwd c:\\workspace\\project; refusing fallback.'
|
||||
}));
|
||||
|
||||
vi.mock('./codexLocal', () => ({
|
||||
@@ -21,10 +23,18 @@ vi.mock('./utils/buildHapiMcpBridge', () => ({
|
||||
}));
|
||||
|
||||
vi.mock('./utils/codexSessionScanner', () => ({
|
||||
createCodexSessionScanner: async () => ({
|
||||
cleanup: async () => {},
|
||||
onNewSession: () => {}
|
||||
})
|
||||
createCodexSessionScanner: async (opts: {
|
||||
onSessionMatchFailed?: (message: string) => void;
|
||||
}) => {
|
||||
harness.sessionScannerCalls.push(opts as Record<string, unknown>);
|
||||
return {
|
||||
cleanup: async () => {},
|
||||
onNewSession: () => {},
|
||||
triggerFailure: () => {
|
||||
opts.onSessionMatchFailed?.(harness.scannerFailureMessage);
|
||||
}
|
||||
};
|
||||
}
|
||||
}));
|
||||
|
||||
vi.mock('@/modules/common/launcher/BaseLocalLauncher', () => ({
|
||||
@@ -44,33 +54,55 @@ vi.mock('@/modules/common/launcher/BaseLocalLauncher', () => ({
|
||||
|
||||
import { codexLocalLauncher } from './codexLocalLauncher';
|
||||
|
||||
function createSessionStub(permissionMode: 'default' | 'read-only' | 'safe-yolo' | 'yolo', codexArgs?: string[]) {
|
||||
function createQueueStub() {
|
||||
return {
|
||||
sessionId: null,
|
||||
path: '/tmp/worktree',
|
||||
startedBy: 'terminal' as const,
|
||||
startingMode: 'local' as const,
|
||||
codexArgs,
|
||||
client: {
|
||||
rpcHandlerManager: {}
|
||||
size: () => 0,
|
||||
reset: () => {},
|
||||
setOnMessage: () => {}
|
||||
};
|
||||
}
|
||||
|
||||
function createSessionStub(permissionMode: 'default' | 'read-only' | 'safe-yolo' | 'yolo', codexArgs?: string[], path = '/tmp/worktree') {
|
||||
const sessionEvents: Array<{ type: string; message?: string }> = [];
|
||||
let localLaunchFailure: { message: string; exitReason: 'switch' | 'exit' } | null = null;
|
||||
|
||||
return {
|
||||
session: {
|
||||
sessionId: null,
|
||||
path,
|
||||
startedBy: 'terminal' as const,
|
||||
startingMode: 'local' as const,
|
||||
codexArgs,
|
||||
client: {
|
||||
rpcHandlerManager: {
|
||||
registerHandler: () => {}
|
||||
}
|
||||
},
|
||||
getPermissionMode: () => permissionMode,
|
||||
onSessionFound: () => {},
|
||||
sendSessionEvent: (event: { type: string; message?: string }) => {
|
||||
sessionEvents.push(event);
|
||||
},
|
||||
recordLocalLaunchFailure: (message: string, exitReason: 'switch' | 'exit') => {
|
||||
localLaunchFailure = { message, exitReason };
|
||||
},
|
||||
sendUserMessage: () => {},
|
||||
sendCodexMessage: () => {},
|
||||
queue: createQueueStub()
|
||||
},
|
||||
getPermissionMode: () => permissionMode,
|
||||
onSessionFound: () => {},
|
||||
sendSessionEvent: () => {},
|
||||
recordLocalLaunchFailure: () => {},
|
||||
sendUserMessage: () => {},
|
||||
sendCodexMessage: () => {},
|
||||
queue: {}
|
||||
sessionEvents,
|
||||
getLocalLaunchFailure: () => localLaunchFailure
|
||||
};
|
||||
}
|
||||
|
||||
describe('codexLocalLauncher', () => {
|
||||
afterEach(() => {
|
||||
harness.launches = [];
|
||||
harness.sessionScannerCalls = [];
|
||||
});
|
||||
|
||||
it('rebuilds approval and sandbox args from yolo mode', async () => {
|
||||
const session = createSessionStub('yolo', [
|
||||
const { session } = createSessionStub('yolo', [
|
||||
'--sandbox',
|
||||
'read-only',
|
||||
'--ask-for-approval',
|
||||
@@ -94,7 +126,7 @@ describe('codexLocalLauncher', () => {
|
||||
});
|
||||
|
||||
it('preserves raw Codex approval flags in default mode', async () => {
|
||||
const session = createSessionStub('default', [
|
||||
const { session } = createSessionStub('default', [
|
||||
'--ask-for-approval',
|
||||
'on-request',
|
||||
'--sandbox',
|
||||
@@ -117,7 +149,7 @@ describe('codexLocalLauncher', () => {
|
||||
});
|
||||
|
||||
it('keeps sandbox escalation available in safe-yolo mode', async () => {
|
||||
const session = createSessionStub('safe-yolo', [
|
||||
const { session } = createSessionStub('safe-yolo', [
|
||||
'--ask-for-approval',
|
||||
'never',
|
||||
'--sandbox',
|
||||
@@ -138,4 +170,20 @@ describe('codexLocalLauncher', () => {
|
||||
'o3'
|
||||
]);
|
||||
});
|
||||
|
||||
it('warns on session match failure without aborting local Codex launch', async () => {
|
||||
const { session, sessionEvents, getLocalLaunchFailure } = createSessionStub('default', undefined, 'c:\\workspace\\project');
|
||||
|
||||
await codexLocalLauncher(session as never);
|
||||
|
||||
const scannerCall = harness.sessionScannerCalls[0] as { onSessionMatchFailed?: (message: string) => void } | undefined;
|
||||
scannerCall?.onSessionMatchFailed?.(harness.scannerFailureMessage);
|
||||
|
||||
expect(harness.launches).toHaveLength(1);
|
||||
expect(getLocalLaunchFailure()).toBeNull();
|
||||
expect(sessionEvents).toContainEqual({
|
||||
type: 'message',
|
||||
message: `${harness.scannerFailureMessage} Keeping local Codex running; remote transcript sync may be unavailable for this launch.`
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -60,8 +60,10 @@ export async function codexLocalLauncher(session: CodexSession): Promise<'switch
|
||||
|
||||
const handleSessionMatchFailed = (message: string) => {
|
||||
logger.warn(`[codex-local]: ${message}`);
|
||||
session.sendSessionEvent({ type: 'message', message });
|
||||
launcher.control.requestExit();
|
||||
session.sendSessionEvent({
|
||||
type: 'message',
|
||||
message: `${message} Keeping local Codex running; remote transcript sync may be unavailable for this launch.`
|
||||
});
|
||||
};
|
||||
|
||||
scanner = await createCodexSessionScanner({
|
||||
|
||||
Reference in New Issue
Block a user