Skip to content

Commit 7ee919a

Browse files
connor4312Copilot
andcommitted
fix(node): preserve first sendAndWait outcome
Use a shared terminal latch so a later shutdown cannot override an earlier idle or error while the send RPC is still pending. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent 82772b5 commit 7ee919a

2 files changed

Lines changed: 21 additions & 9 deletions

File tree

nodejs/src/session.ts

Lines changed: 20 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -754,6 +754,17 @@ export class CopilotSession {
754754
const shutdownPromise = new Promise<Error>((resolve) => {
755755
resolveShutdown = resolve;
756756
});
757+
let outcomeSettled = false;
758+
const settleOutcome = (outcome: SessionOutcome, shutdown = false): void => {
759+
if (outcomeSettled) {
760+
return;
761+
}
762+
outcomeSettled = true;
763+
resolveOutcome(outcome);
764+
if (shutdown && outcome.kind === "error") {
765+
resolveShutdown(outcome.error);
766+
}
767+
};
757768

758769
let lastAssistantMessage: AssistantMessageEvent | undefined;
759770

@@ -763,29 +774,29 @@ export class CopilotSession {
763774
if (event.type === "assistant.message") {
764775
lastAssistantMessage = event;
765776
} else if (event.type === "session.idle") {
766-
resolveOutcome({ kind: "idle" });
777+
settleOutcome({ kind: "idle" });
767778
} else if (event.type === "session.error") {
768779
const error = new Error(event.data.message);
769780
error.stack = event.data.stack;
770-
resolveOutcome({ kind: "error", error });
781+
settleOutcome({ kind: "error", error });
771782
} else if (event.type === "session.shutdown") {
772783
const reason = event.data.errorReason ? `: ${event.data.errorReason}` : "";
773784
const error = new Error(
774785
`Session ${this.sessionId} shut down before becoming idle${reason}`
775786
);
776-
resolveOutcome({ kind: "error", error });
777-
resolveShutdown(error);
787+
settleOutcome({ kind: "error", error }, true);
778788
}
779789
});
780790

781791
let timeoutId: ReturnType<typeof setTimeout> | undefined;
782792
try {
783-
await Promise.race([
784-
this.send(options),
785-
shutdownPromise.then((error) => {
786-
throw error;
787-
}),
793+
const sendResult = await Promise.race([
794+
this.send(options).then(() => ({ kind: "sent" }) as const),
795+
shutdownPromise.then((error) => ({ kind: "shutdown", error }) as const),
788796
]);
797+
if (sendResult.kind === "shutdown") {
798+
throw sendResult.error;
799+
}
789800

790801
const timeoutPromise = new Promise<never>((_, reject) => {
791802
timeoutId = setTimeout(

nodejs/test/session-send-and-wait.test.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,7 @@ describe("sendAndWait", () => {
112112
await sendStarted;
113113

114114
session._dispatchEvent(sessionEvent("session.idle"));
115+
session._dispatchEvent(shutdownEvent("later shutdown"));
115116

116117
const stateBeforeSend = await Promise.race([
117118
pending.then(() => "settled"),

0 commit comments

Comments
 (0)