mirror of
https://github.com/cline/cline.git
synced 2026-09-01 15:11:04 +08:00
fix(core): handle hub abort cleanup failures (#10918)
This commit is contained in:
@@ -1329,6 +1329,27 @@ describe("HubRuntimeHost", () => {
|
||||
);
|
||||
});
|
||||
|
||||
it("serializes error abort reasons for hub abort commands", async () => {
|
||||
commandMock.mockResolvedValue({ ok: true, payload: { applied: true } });
|
||||
|
||||
const { HubRuntimeHost } = await import("./hub-runtime-host");
|
||||
const host = new HubRuntimeHost({ url: "ws://127.0.0.1:25463/hub" });
|
||||
|
||||
await host.abort(
|
||||
"sess-1",
|
||||
new Error("Interactive runtime abort requested"),
|
||||
);
|
||||
|
||||
expect(commandMock).toHaveBeenCalledWith(
|
||||
"run.abort",
|
||||
{
|
||||
sessionId: "sess-1",
|
||||
reason: "Interactive runtime abort requested",
|
||||
},
|
||||
"sess-1",
|
||||
);
|
||||
});
|
||||
|
||||
it("reads messages through the hub instead of dereferencing client-local artifact paths", async () => {
|
||||
const messages = [
|
||||
{
|
||||
|
||||
@@ -308,12 +308,12 @@ function buildClientContributionRegistration(
|
||||
return registration;
|
||||
}
|
||||
|
||||
function abortReasonMessage(value: unknown): string {
|
||||
function messageFromUnknown(value: unknown): string | undefined {
|
||||
if (typeof value === "string" && value.trim()) {
|
||||
return value.trim();
|
||||
}
|
||||
if (value instanceof Error) {
|
||||
return value.message;
|
||||
return value.message.trim() || undefined;
|
||||
}
|
||||
if (value && typeof value === "object" && "message" in value) {
|
||||
const message = (value as { message?: unknown }).message;
|
||||
@@ -321,7 +321,11 @@ function abortReasonMessage(value: unknown): string {
|
||||
return message.trim();
|
||||
}
|
||||
}
|
||||
return "Capability request was cancelled.";
|
||||
return undefined;
|
||||
}
|
||||
|
||||
function abortReasonMessage(value: unknown): string {
|
||||
return messageFromUnknown(value) ?? "Capability request was cancelled.";
|
||||
}
|
||||
|
||||
function parseApprovalInput(value: unknown): unknown {
|
||||
@@ -1144,7 +1148,7 @@ export class HubRuntimeHost implements RuntimeHost {
|
||||
async abort(sessionId: string, reason?: unknown): Promise<void> {
|
||||
await this.client.command(
|
||||
"run.abort",
|
||||
{ sessionId, reason: typeof reason === "string" ? reason : undefined },
|
||||
{ sessionId, reason: messageFromUnknown(reason) },
|
||||
sessionId,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@ import type { HubEventEnvelope } from "@cline/shared";
|
||||
import { afterEach, describe, expect, it, vi } from "vitest";
|
||||
import type { RuntimeHost } from "../../../runtime/host/runtime-host";
|
||||
import { buildHubEvent, type HubTransportContext } from "./context";
|
||||
import { handleSessionInput } from "./run-handlers";
|
||||
import { handleRunAbort, handleSessionInput } from "./run-handlers";
|
||||
|
||||
function createContext(
|
||||
overrides: Partial<RuntimeHost> = {},
|
||||
@@ -160,4 +160,27 @@ describe("run handlers", () => {
|
||||
resolveRun?.(undefined);
|
||||
await expect(promise).resolves.toMatchObject({ ok: true });
|
||||
});
|
||||
|
||||
it("treats abort as applied when the runtime abort hook rejects", async () => {
|
||||
const abort = vi.fn().mockRejectedValue(new Error("Run aborted"));
|
||||
const ctx = createContext({ abort });
|
||||
|
||||
const reply = await handleRunAbort(ctx, {
|
||||
version: "v1",
|
||||
command: "run.abort",
|
||||
requestId: "req-abort",
|
||||
clientId: "client-1",
|
||||
sessionId: "session-1",
|
||||
payload: {
|
||||
sessionId: "session-1",
|
||||
reason: "user cancelled",
|
||||
},
|
||||
});
|
||||
|
||||
expect(reply).toMatchObject({
|
||||
ok: true,
|
||||
payload: { applied: true },
|
||||
});
|
||||
expect(abort).toHaveBeenCalledWith("session-1", "user cancelled");
|
||||
});
|
||||
});
|
||||
|
||||
@@ -281,12 +281,23 @@ export async function handleRunAbort(
|
||||
(approval) => approval.sessionId === sessionId,
|
||||
reason,
|
||||
);
|
||||
await ctx.sessionHost.abort(sessionId, envelope.payload?.reason);
|
||||
cancelPendingCapabilityRequests(
|
||||
ctx,
|
||||
(request) => request.sessionId === sessionId,
|
||||
reason,
|
||||
);
|
||||
try {
|
||||
await ctx.sessionHost.abort(sessionId, envelope.payload?.reason);
|
||||
} catch (error) {
|
||||
logHubMessage("warn", "run.abort_failed", {
|
||||
command: envelope.command,
|
||||
requestId: envelope.requestId,
|
||||
clientId: envelope.clientId,
|
||||
sessionId,
|
||||
error,
|
||||
});
|
||||
} finally {
|
||||
cancelPendingCapabilityRequests(
|
||||
ctx,
|
||||
(request) => request.sessionId === sessionId,
|
||||
reason,
|
||||
);
|
||||
}
|
||||
return okReply(envelope, { applied: true });
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user