Observe native provider admission failures without crashing the sidecar
This commit is contained in:
parent
5c8974657a
commit
cb425d35bc
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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),
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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" },
|
||||
|
|
|
|||
Loading…
Reference in New Issue