fix: close OpenCode event streams before ending requests

Completed parallel conversations could leave buffered event connections open.
Chat and native context compaction now cancel their own subscriptions before
ending SDK iteration, without cancelling another conversation or its Server.

Strengthen cleanup-order regressions and verify real OpenCode parallel runs,
cancellation and compaction. The remote Agent uses ACP stdio and transcript
polling rather than this SDK event stream and needs no corresponding change.

Validation: 99 focused tests passed; the final candidate passed 4089 tests
with 66 skipped, typecheck and lint. No external model calls were made.
This commit is contained in:
mesalogo
2026-09-13 17:28:40 +08:00
parent a44de1441a
commit 59a1624396
4 changed files with 81 additions and 13 deletions
+16 -8
View File
@@ -1737,6 +1737,7 @@ describe("OpenCodeRuntime embedded launcher", () => {
const setup = runClient([]);
const controller = new AbortController();
let subscriptionSignal: AbortSignal | undefined;
let abortedBeforeIteratorClose: boolean | undefined;
const subscribe = setup.event.subscribe as unknown as ReturnType<
typeof vi.fn
>;
@@ -1745,15 +1746,19 @@ describe("OpenCodeRuntime embedded launcher", () => {
subscriptionSignal = options.signal;
return {
stream: (async function* () {
if (outcome === "cancelled") {
controller.abort(new DOMException("Cancelled", "AbortError"));
return;
try {
if (outcome === "cancelled") {
controller.abort(new DOMException("Cancelled", "AbortError"));
return;
}
if (outcome === "failed") return;
yield {
type: "session.idle",
properties: { sessionID: "session-1" },
};
} finally {
abortedBeforeIteratorClose = subscriptionSignal?.aborted;
}
if (outcome === "failed") return;
yield {
type: "session.idle",
properties: { sessionID: "session-1" },
};
})(),
};
},
@@ -1784,6 +1789,9 @@ describe("OpenCodeRuntime embedded launcher", () => {
await collect();
}
expect(subscriptionSignal?.aborted).toBe(true);
if (outcome !== "failed") {
expect(abortedBeforeIteratorClose).toBe(true);
}
expect(controller.signal.aborted).toBe(outcome === "cancelled");
} finally {
await runtime.dispose();