Skip to content

Commit b517c2b

Browse files
committed
fix(github-copilot): strip encrypted_content from reasoning replay items
1 parent 83785a6 commit b517c2b

2 files changed

Lines changed: 18 additions & 14 deletions

File tree

extensions/github-copilot/connection-bound-ids.test.ts

Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ describe("github-copilot connection-bound response IDs", () => {
3737
expect(input[4]?.id).toMatch(/^msg_[a-f0-9]{16}$/);
3838
});
3939

40-
it("preserves valid reasoning IDs regardless of encrypted_content", () => {
40+
it("preserves valid reasoning IDs but strips encrypted_content", () => {
4141
const withEncrypted = Buffer.from(`reasoning-${"e".repeat(24)}`).toString("base64");
4242
const withNull = Buffer.from(`reasoning-${"n".repeat(24)}`).toString("base64");
4343
const withoutField = Buffer.from(`reasoning-${"a".repeat(24)}`).toString("base64");
@@ -47,25 +47,26 @@ describe("github-copilot connection-bound response IDs", () => {
4747
{ id: withoutField, type: "reasoning" },
4848
];
4949

50-
expect(rewriteCopilotConnectionBoundResponseIds(input)).toBe(false);
51-
expect(input[0]?.id).toBe(withEncrypted);
52-
expect(input[1]?.id).toBe(withNull);
53-
expect(input[2]?.id).toBe(withoutField);
50+
expect(rewriteCopilotConnectionBoundResponseIds(input)).toBe(true);
51+
expect(input[0]).toEqual({ id: withEncrypted, type: "reasoning" });
52+
expect(input[1]).toEqual({ id: withNull, type: "reasoning" });
53+
expect(input[2]).toEqual({ id: withoutField, type: "reasoning" });
5454
});
5555

56-
it("preserves valid base64-ish reasoning IDs with and without encrypted content", () => {
56+
it("strips encrypted_content from valid reasoning IDs at payload send time", () => {
5757
const withEncrypted = "abcDEF0123+/=";
5858
const withoutEncrypted = "reasoning/abc+123=";
5959
const input = [
6060
{ id: withEncrypted, type: "reasoning", encrypted_content: "opaque-encrypted-payload" },
6161
{ id: withoutEncrypted, type: "reasoning" },
6262
];
6363

64-
expect(sanitizeCopilotReplayResponseIds(input)).toBe(false);
65-
expect(input.map((item) => item.id)).toEqual([withEncrypted, withoutEncrypted]);
64+
expect(sanitizeCopilotReplayResponseIds(input)).toBe(true);
65+
expect(input[0]).toEqual({ id: withEncrypted, type: "reasoning" });
66+
expect(input[1]).toEqual({ id: withoutEncrypted, type: "reasoning" });
6667
});
6768

68-
it("drops unsafe reasoning replay item IDs while keeping idless reasoning replay", () => {
69+
it("drops unsafe reasoning IDs and strips encrypted_content from kept items", () => {
6970
const overlongId = `5PX6gLHXT5wE+Y2tPmUV4gn+${"B".repeat(384)}`;
7071
const input = [
7172
{
@@ -81,8 +82,8 @@ describe("github-copilot connection-bound response IDs", () => {
8182

8283
expect(sanitizeCopilotReplayResponseIds(input)).toBe(true);
8384
expect(input).toEqual([
84-
{ type: "reasoning", encrypted_content: "missing-id", summary: [] },
85-
{ id: "rs_valid", type: "reasoning", encrypted_content: "valid", summary: [] },
85+
{ type: "reasoning", summary: [] },
86+
{ id: "rs_valid", type: "reasoning", summary: [] },
8687
]);
8788
});
8889

extensions/github-copilot/connection-bound-ids.ts

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -45,13 +45,16 @@ export function sanitizeCopilotReplayResponseIds(input: unknown): boolean {
4545
continue;
4646
}
4747
const id = item.id;
48-
// Reasoning items with replay IDs reference server-side encrypted state
49-
// bound to that ID. Drop unsafe IDs, but keep the store-disabled idless
50-
// replay form produced by core Responses conversion.
48+
// Reasoning encrypted_content is tied to the Copilot connection token,
49+
// which rotates per request. Drop items with unsafe IDs; strip
50+
// encrypted_content from kept items so summary-only replay is sent.
5151
if (item.type === "reasoning") {
5252
if (id !== undefined && !isValidReasoningReplayId(id)) {
5353
input.splice(index, 1);
5454
rewrote = true;
55+
} else if ("encrypted_content" in item) {
56+
delete (item as Record<string, unknown>)["encrypted_content"];
57+
rewrote = true;
5558
}
5659
continue;
5760
}

0 commit comments

Comments
 (0)