fix(copilot): preserve replacement session reuse

This commit is contained in:
Vincent Koc
2026-06-21 01:34:33 +08:00
committed by Vincent Koc
parent 6084442ab6
commit 54dddda68d
3 changed files with 49 additions and 4 deletions

View File

@@ -1551,6 +1551,51 @@ describe("createCopilotAgentHarness", () => {
await flushAsyncWork();
});
it("clears the reset block when storing a replacement session fails", async () => {
const cleanup = createDeferred<"aborted" | "completed" | "deadline">();
const sessionStore = makeSessionStoreMock();
sessionStore.store.register.mockImplementation(() => {
throw new Error("sqlite register failed");
});
mocks.runCopilotAttempt.mockImplementation(async (_params, deps) => {
const sdkSessionId =
mocks.runCopilotAttempt.mock.calls.length === 1
? "sdk-sess-background"
: "sdk-sess-replacement";
deps.onSessionEstablished?.({
sdkSessionId,
pooledClient: { key: {} as any, client: {} as any },
sessionConfig: TEST_SESSION_CONFIG,
});
if (sdkSessionId === "sdk-sess-background") {
deps.onDeferredCompaction?.({
abort: () => undefined,
cleanup: cleanup.promise,
sdkSessionId,
});
}
return ATTEMPT_RESULT;
});
const harness = createCopilotAgentHarness({
pool: makePoolMock(),
sessionStore: sessionStore.store,
});
const params = makeCompactParams({ sessionId: "oc-sess-store-failure" });
await harness.runAttempt(params);
await harness.runAttempt(params);
await harness.runAttempt(params);
expect(mocks.runCopilotAttempt.mock.calls[1]?.[0]).not.toMatchObject({
initialReplayState: expect.objectContaining({ sdkSessionId: "sdk-sess-background" }),
});
expect(mocks.runCopilotAttempt.mock.calls[2]?.[0]).toMatchObject({
initialReplayState: expect.objectContaining({ sdkSessionId: "sdk-sess-replacement" }),
});
cleanup.resolve("completed");
await flushAsyncWork();
});
it("calls the SDK history compaction RPC without requiring a workspace sidecar", async () => {
const beforeCompaction = vi.fn();
const afterCompaction = vi.fn();

View File

@@ -621,7 +621,7 @@ export function createCopilotAgentHarness(
sessionConfig,
...sessionAuthFields(poolAcquire.auth),
});
const persisted = registerStoredBinding(options?.sessionStore, openclawSessionId, {
registerStoredBinding(options?.sessionStore, openclawSessionId, {
schemaVersion: 2,
sdkSessionId,
compatKey: currentCompatKey,
@@ -629,9 +629,7 @@ export function createCopilotAgentHarness(
...sessionAuthFields(poolAcquire.auth),
updatedAt: Date.now(),
});
if (persisted) {
resetBlockedStoredSessions.delete(openclawSessionId);
}
resetBlockedStoredSessions.delete(openclawSessionId);
}
: undefined,
onDeferredCompaction: openclawSessionId

View File

@@ -837,6 +837,8 @@ export async function runCopilotAttempt(
}
params.abortSignal?.removeEventListener("abort", onAbort);
} else {
// `sendAndWait` resolves on `session.idle`, which the SDK defines as
// no background agents in flight. Only an observed compaction needs retention.
await bridge?.awaitCompactionChain();
bridge?.detach();
params.abortSignal?.removeEventListener("abort", onAbort);