mirror of
https://github.com/openclaw/openclaw.git
synced 2026-04-03 05:12:15 +00:00
fix: keep active ACP runs alive after reconnect timeout
This commit is contained in:
@@ -295,6 +295,49 @@ describe("acp translator stop reason mapping", () => {
|
||||
}
|
||||
});
|
||||
|
||||
it("keeps accepted prompts pending when the deadline recheck still reports timeout", async () => {
|
||||
vi.useFakeTimers();
|
||||
try {
|
||||
const sessionId = "session-1";
|
||||
const sessionKey = "agent:main:main";
|
||||
const request = vi.fn(async (method: string) => {
|
||||
if (method === "chat.send") {
|
||||
return {};
|
||||
}
|
||||
if (method === "agent.wait") {
|
||||
return { status: "timeout" };
|
||||
}
|
||||
return {};
|
||||
}) as GatewayClient["request"];
|
||||
const sessionStore = createInMemorySessionStore();
|
||||
sessionStore.createSession({
|
||||
sessionId,
|
||||
sessionKey,
|
||||
cwd: "/tmp",
|
||||
});
|
||||
const agent = new AcpGatewayAgent(createAcpConnection(), createAcpGateway(request), {
|
||||
sessionStore,
|
||||
});
|
||||
const promptPromise = agent.prompt({
|
||||
sessionId,
|
||||
prompt: [{ type: "text", text: "hello" }],
|
||||
_meta: {},
|
||||
} as unknown as PromptRequest);
|
||||
|
||||
await Promise.resolve();
|
||||
agent.handleGatewayDisconnect("1006: connection lost");
|
||||
agent.handleGatewayReconnect();
|
||||
await Promise.resolve();
|
||||
await vi.advanceTimersByTimeAsync(5_000);
|
||||
|
||||
await expect(Promise.race([promptPromise, Promise.resolve("pending")])).resolves.toBe(
|
||||
"pending",
|
||||
);
|
||||
} finally {
|
||||
vi.useRealTimers();
|
||||
}
|
||||
});
|
||||
|
||||
it("does not clear a newer disconnect deadline while reconnect reconciliation is still running", async () => {
|
||||
vi.useFakeTimers();
|
||||
try {
|
||||
@@ -353,7 +396,10 @@ describe("acp translator stop reason mapping", () => {
|
||||
expect(settleSpy).not.toHaveBeenCalled();
|
||||
|
||||
await vi.advanceTimersByTimeAsync(1);
|
||||
await expect(promptPromise).rejects.toThrow("Gateway disconnected: 1006: second disconnect");
|
||||
expect(request).toHaveBeenCalledTimes(3);
|
||||
await expect(Promise.race([promptPromise, Promise.resolve("pending")])).resolves.toBe(
|
||||
"pending",
|
||||
);
|
||||
} finally {
|
||||
vi.useRealTimers();
|
||||
}
|
||||
|
||||
@@ -1052,6 +1052,11 @@ export class AcpGatewayAgent implements Agent {
|
||||
pending.reject(error);
|
||||
}
|
||||
|
||||
private clearPendingDisconnectState(pending: PendingPrompt): void {
|
||||
pending.disconnectGeneration = undefined;
|
||||
pending.disconnectReason = undefined;
|
||||
}
|
||||
|
||||
private async reconcilePendingPrompts(
|
||||
observedDisconnectGeneration: number,
|
||||
deadlineExpired: boolean,
|
||||
@@ -1116,10 +1121,7 @@ export class AcpGatewayAgent implements Agent {
|
||||
} catch (err) {
|
||||
this.log(`agent.wait reconcile failed for ${pending.idempotencyKey}: ${String(err)}`);
|
||||
if (deadlineExpired) {
|
||||
this.rejectPendingPrompt(
|
||||
pending,
|
||||
new Error(`Gateway disconnected: ${pending.disconnectReason}`),
|
||||
);
|
||||
this.clearPendingDisconnectState(pending);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
@@ -1138,10 +1140,7 @@ export class AcpGatewayAgent implements Agent {
|
||||
return false;
|
||||
}
|
||||
if (deadlineExpired) {
|
||||
this.rejectPendingPrompt(
|
||||
currentPending,
|
||||
new Error(`Gateway disconnected: ${currentPending.disconnectReason}`),
|
||||
);
|
||||
this.clearPendingDisconnectState(currentPending);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
|
||||
Reference in New Issue
Block a user