Observe native provider admission failures without crashing the sidecar

This commit is contained in:
Dotta 2026-09-11 14:37:05 -05:00
parent 5c8974657a
commit cb425d35bc
3 changed files with 63 additions and 15 deletions

View File

@ -1311,6 +1311,32 @@ describe("Codex ACPX runtime adapter", () => {
});
});
it("observes prompt admission rejection when the sidecar consumes only events and the result", async () => {
const runtime = fakeRuntime();
const failure = new Error("Recovered provider could not start the prompt");
vi.mocked(runtime.startTurn).mockImplementation(() => ({
requestId: "turn-recovered-failure",
promptStarted: Promise.reject(failure),
events: { async *[Symbol.asyncIterator]() { throw failure; } },
result: Promise.reject(failure),
cancel: vi.fn(),
closeStream: vi.fn(),
}));
const port = await openCodexAcpxRuntime(openOptions(fakeCommand()), {
createRegistry: () => registry(), createStore: () => store(), createRuntime: () => runtime,
});
const turn = port.startTurn({ text: "Resume", requestId: "turn-recovered-failure" });
const eventDrain = (async () => { for await (const _event of turn.events) { /* drain */ } })();
await expect(eventDrain).rejects.toBe(failure);
// The sidecar does not await promptStarted. Leave it unconsumed across a
// full event-loop turn so an unobserved derived rejection fails this test.
await new Promise<void>((resolve) => setImmediate(resolve));
// Observing internally must not replace failure with successful admission.
await expect(turn.result).rejects.toBe(failure);
await expect(turn.promptStarted).rejects.toBe(failure);
await port.close({ reason: "test complete" });
});
it("verifies a lazy recovered provider spawned by model selection before returning", async () => {
const runtime = fakeRuntime();
const command = fakeCommand();

View File

@ -1221,11 +1221,20 @@ function turnWithVerifiedLifetimeOwnership(
finishOwnershipAdmission(),
);
void ownershipVerified.catch(() => undefined);
const promptStarted = ownershipVerified.then(() => turn.promptStarted);
const result = ownershipVerified.then(() => turn.result);
// Some consumers (including the sidecar) drain events and await the result
// without awaiting this optional admission signal. Observe its rejection
// immediately so a failed cold start cannot terminate the host process as an
// unhandled rejection. Keep the original rejected promise for consumers.
void promptStarted.catch(() => undefined);
// Event drains can fail before their caller reaches the result promise.
void result.catch(() => undefined);
return {
requestId: turn.requestId,
promptStarted: ownershipVerified.then(() => turn.promptStarted),
promptStarted,
events: eventsAfterLifetimeOwnership(turn.events, ownershipVerified),
result: ownershipVerified.then(() => turn.result),
result,
cancel: (input) => turn.cancel(input),
closeStream: (input) => turn.closeStream(input),
};

View File

@ -3135,19 +3135,32 @@ describe("native session same-turn steering", () => {
});
describe("native warm session supervision", () => {
it.each([true, false])("preserves chat reply grace for per-turn providers: chat=%s", async (conversationMode) => {
state.execute.mockReset().mockImplementationOnce(async (options) => {
expect(options.semanticResultTerminalGraceMs).toBe(conversationMode ? 30_000 : undefined);
return {
result: { summary: "Reply completed" },
terminal: { runTerminalState: "succeeded" },
turnId: "turn-grace", normalizedSessionId: execution.session.normalizedSessionId,
providerSessionId: "provider-grace", driverKind: "test", driverVersion: "1",
nativeEventCount: 1, highestContiguousSourceSeq: 1,
};
});
await executePaperclipNativeSession({ db: leaseDb(), execution, runnerInstanceId: "runner", conversationMode });
});
it.each([true, false])(
"preserves chat reply grace for per-turn providers: chat=%s",
async (conversationMode) => {
state.execute.mockReset().mockImplementationOnce(async (options) => {
expect(options.semanticResultTerminalGraceMs).toBe(conversationMode ? 30_000 : undefined);
return {
result: { summary: "Reply completed" },
terminal: { runTerminalState: "succeeded" },
turnId: "turn-grace",
normalizedSessionId: execution.session.normalizedSessionId,
providerSessionId: "provider-grace",
driverKind: "test",
driverVersion: "1",
nativeEventCount: 1,
highestContiguousSourceSeq: 1,
};
});
await executePaperclipNativeSession({
db: leaseDb(),
execution,
runnerInstanceId: "runner",
conversationMode,
});
},
);
it("persists agent-created goal continuity before a per-turn runner settles", async () => {
const goalCheckpoint = {
identity: { runId: execution.binding.runId, sessionId: "session" },