fix(codex): restore shared sessions after heartbeat and archive

This commit is contained in:
weishu
2026-09-12 18:29:57 +08:00
parent 4d92afaf36
commit d1f4972079
4 changed files with 36 additions and 14 deletions
@@ -318,6 +318,16 @@ describe.skipIf(process.env.HAPI_RUN_SHARED_CODEX_TESTS !== '1' || process.platf
await client.request('hapi/stopSession', { sessionId });
await api(`/sessions/${webId}/archive`, {});
await eventually(webDetail, value => record(value.session).active === false, 'end independent Web session');
const reopened = await api(`/sessions/${webId}/reopen`, {});
expect(reopened.sessionId).toBe(webId);
const reopenedSession = record((await webDetail()).session);
expect(record(reopenedSession.metadata).codexSessionId).toBe(record(record(independent.session).metadata).codexSessionId);
runtimePids.add(Number(record(reopenedSession.metadata).hostPid));
await api(`/sessions/${webId}/messages`, { text: 'AFTER_ARCHIVE_REOPEN', localId: randomUUID() });
await eventually(() => api(`/sessions/${webId}/messages`), value => hasReply(value, 'AFTER_ARCHIVE_REOPEN'), 'Web reply after reopening archived native thread');
expect(hasReply(await api(`/sessions/${webId}/messages`), 'HELLO_WEB_CREATED')).toBe(true);
await api(`/sessions/${webId}/archive`, {});
await eventually(webDetail, value => record(value.session).active === false, 'end reopened Web session');
} catch (error) {
await writeFile('/tmp/hapi-shared-e2e-terminal.log', output + '\nRUNNER:\n' + runnerOutput);
throw error;
+13 -1
View File
@@ -356,7 +356,19 @@ export async function runSharedRuntime(options: SharedLaunchOptions, onReady?: (
const root = await prepare(launch.cwd, existing);
await reserveRecord(root, threadId); return root;
});
const response = record(await root.client.request('thread/resume', root.config({ ...launch.threadParams, threadId })));
const params = root.config({ ...launch.threadParams, threadId });
let response: Record<string, unknown>;
try {
response = record(await root.client.request('thread/resume', params));
} catch (error) {
// Reopening a HAPI binding also restores its native archive.
// Only retry an explicit archived rejection, never a transport
// failure whose resume outcome may be unknown.
if (!options.existingSessionId || !(error instanceof Error)
|| !error.message.startsWith(`session ${threadId} is archived.`)) throw error;
await root.client.request('thread/unarchive', { threadId });
response = record(await root.client.request('thread/resume', params));
}
await bind(root, response, false, options);
} else root = await create('thread/start', { ...launch.threadParams, cwd: launch.cwd }, undefined, options);
assertRunning();