fix(runner): restore ACPX backend identity fallback
This commit is contained in:
parent
9611da2b38
commit
6cba271e65
|
|
@ -1428,26 +1428,45 @@ describe("Codex ACPX runtime adapter", () => {
|
|||
},
|
||||
);
|
||||
|
||||
it("fails closed and closes the session when ACPX omits recovery identity", async () => {
|
||||
it("uses the real ACP session when no second agent identity is advertised", async () => {
|
||||
const runtime = fakeRuntime({ ...HANDLE, agentSessionId: undefined });
|
||||
await expect(
|
||||
openCodexAcpxRuntime(openOptions(fakeCommand()), {
|
||||
createRegistry: () => registry(),
|
||||
createStore: () => store(),
|
||||
createRuntime: () => runtime,
|
||||
}),
|
||||
).rejects.toThrow("ACPX runtime omitted agentSessionId");
|
||||
expect(runtime.close).toHaveBeenCalledWith({
|
||||
handle: { ...HANDLE, agentSessionId: undefined },
|
||||
reason: "ACPX runtime identity validation failed",
|
||||
discardPersistentState: false,
|
||||
const durableStore: AcpSessionStore = {
|
||||
load: vi.fn(async () =>
|
||||
structuredClone({
|
||||
acpxRecordId: "record-1",
|
||||
acpSessionId: "backend-1",
|
||||
acpx: { current_model_id: "gpt-5.6-sol" },
|
||||
} as never),
|
||||
),
|
||||
save: vi.fn(),
|
||||
};
|
||||
const port = await openCodexAcpxRuntime(openOptions(fakeCommand()), {
|
||||
createRegistry: () => registry(),
|
||||
createStore: () => durableStore,
|
||||
createRuntime: () => runtime,
|
||||
});
|
||||
|
||||
await expect(port.identity()).resolves.toEqual({
|
||||
acpxRecordId: "record-1",
|
||||
backendSessionId: "backend-1",
|
||||
agentSessionId: "backend-1",
|
||||
});
|
||||
await expect(port.getStatus()).resolves.toMatchObject({
|
||||
backendSessionId: "backend-1",
|
||||
agentSessionId: "backend-1",
|
||||
models: { currentModelId: "gpt-5.6-sol" },
|
||||
});
|
||||
expect(runtime.close).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("bounds invalid-identity cleanup before terminating the provider", async () => {
|
||||
vi.useFakeTimers();
|
||||
try {
|
||||
const runtime = fakeRuntime({ ...HANDLE, agentSessionId: undefined });
|
||||
const runtime = fakeRuntime({
|
||||
...HANDLE,
|
||||
backendSessionId: undefined,
|
||||
agentSessionId: undefined,
|
||||
});
|
||||
vi.mocked(runtime.close).mockImplementation(
|
||||
() => new Promise<void>(() => undefined),
|
||||
);
|
||||
|
|
@ -1466,7 +1485,11 @@ describe("Codex ACPX runtime adapter", () => {
|
|||
args: ["--stdio"],
|
||||
options: {},
|
||||
});
|
||||
return { ...HANDLE, agentSessionId: undefined };
|
||||
return {
|
||||
...HANDLE,
|
||||
backendSessionId: undefined,
|
||||
agentSessionId: undefined,
|
||||
};
|
||||
}),
|
||||
}),
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1048,10 +1048,12 @@ async function persistedRuntimeStatus(
|
|||
{ code: "ACPX_PERSISTED_SESSION_MISSING" },
|
||||
);
|
||||
}
|
||||
const persistedAgentSessionId =
|
||||
nonEmptyRuntimeIdentity(record.agentSessionId) ?? record.acpSessionId;
|
||||
if (
|
||||
record.acpxRecordId !== identity.acpxRecordId ||
|
||||
record.acpSessionId !== identity.backendSessionId ||
|
||||
record.agentSessionId !== identity.agentSessionId
|
||||
persistedAgentSessionId !== identity.agentSessionId
|
||||
) {
|
||||
throw Object.assign(
|
||||
new Error("The persisted ACPX session identity changed after admission"),
|
||||
|
|
@ -1064,12 +1066,12 @@ async function persistedRuntimeStatus(
|
|||
summary: [
|
||||
`session=${record.acpxRecordId}`,
|
||||
`backendSessionId=${record.acpSessionId}`,
|
||||
`agentSessionId=${record.agentSessionId}`,
|
||||
`agentSessionId=${persistedAgentSessionId}`,
|
||||
record.closed === true ? "closed" : "open",
|
||||
].join(" "),
|
||||
acpxRecordId: record.acpxRecordId,
|
||||
backendSessionId: record.acpSessionId,
|
||||
agentSessionId: record.agentSessionId,
|
||||
agentSessionId: persistedAgentSessionId,
|
||||
...(currentModelId === undefined && !availableModelIds?.length
|
||||
? {}
|
||||
: {
|
||||
|
|
@ -1491,17 +1493,21 @@ function pushUnique(errors: unknown[], error: unknown): void {
|
|||
}
|
||||
|
||||
function requireIdentity(handle: AcpRuntimeHandle): AcpxRuntimePortIdentity {
|
||||
const identity = {
|
||||
acpxRecordId: handle.acpxRecordId,
|
||||
backendSessionId: handle.backendSessionId,
|
||||
agentSessionId: handle.agentSessionId,
|
||||
};
|
||||
for (const [name, value] of Object.entries(identity)) {
|
||||
if (typeof value !== "string" || value.length === 0) {
|
||||
throw new Error(`ACPX runtime omitted ${name}`);
|
||||
}
|
||||
const acpxRecordId = nonEmptyRuntimeIdentity(handle.acpxRecordId);
|
||||
if (!acpxRecordId) throw new Error("ACPX runtime omitted acpxRecordId");
|
||||
const backendSessionId = nonEmptyRuntimeIdentity(handle.backendSessionId);
|
||||
if (!backendSessionId) {
|
||||
throw new Error("ACPX runtime omitted backendSessionId");
|
||||
}
|
||||
return identity as AcpxRuntimePortIdentity;
|
||||
return {
|
||||
acpxRecordId,
|
||||
backendSessionId,
|
||||
// ACPX agents do not all advertise a distinct native thread identity.
|
||||
// In that case the backend ID is the real ACP protocol session, so retain
|
||||
// it explicitly rather than inventing a Paperclip-owned identifier.
|
||||
agentSessionId:
|
||||
nonEmptyRuntimeIdentity(handle.agentSessionId) ?? backendSessionId,
|
||||
};
|
||||
}
|
||||
|
||||
function definedEnvironment(
|
||||
|
|
|
|||
Loading…
Reference in New Issue