Spaces:
Running
Running
| import { afterEach, beforeEach, describe, expect, mock, test } from "bun:test" | |
| import type { | |
| ResponsesPayload, | |
| ResponsesResult, | |
| } from "../src/services/copilot/create-responses" | |
| import { | |
| copilotHeaders, | |
| copilotWebSocketHeaders, | |
| prepareForCompact, | |
| prepareInteractionHeaders, | |
| } from "../src/lib/api-config" | |
| import { COMPACT_REQUEST } from "../src/lib/compact" | |
| import { state } from "../src/lib/state" | |
| import { | |
| buildResponsesWebSocketPoolKey, | |
| buildResponsesWebSocketPayload, | |
| buildResponsesWebSocketUrl, | |
| createResponses, | |
| prepareResponsesWebSocketRequest, | |
| } from "../src/services/copilot/create-responses" | |
| const originalFetch = globalThis.fetch | |
| const originalOauthApp = process.env.COPILOT_API_OAUTH_APP | |
| const originalState = { | |
| accountType: state.accountType, | |
| copilotToken: state.copilotToken, | |
| macMachineId: state.macMachineId, | |
| vsCodeDeviceId: state.vsCodeDeviceId, | |
| vsCodeSessionId: state.vsCodeSessionId, | |
| vsCodeVersion: state.vsCodeVersion, | |
| } | |
| const createResponsesResult = (model: string): ResponsesResult => ({ | |
| created_at: 0, | |
| error: null, | |
| id: "resp-test", | |
| incomplete_details: null, | |
| instructions: null, | |
| metadata: null, | |
| model, | |
| object: "response", | |
| output: [], | |
| output_text: "", | |
| parallel_tool_calls: false, | |
| status: "completed", | |
| temperature: null, | |
| tool_choice: "auto", | |
| tools: [], | |
| top_p: null, | |
| usage: null, | |
| }) | |
| const fetchMock = mock((_url: string | URL | Request, _init?: RequestInit) => | |
| Promise.resolve( | |
| new Response(JSON.stringify(createResponsesResult("gpt-test")), { | |
| headers: { | |
| "content-type": "application/json", | |
| }, | |
| status: 200, | |
| }), | |
| ), | |
| ) | |
| beforeEach(() => { | |
| delete process.env.COPILOT_API_OAUTH_APP | |
| state.accountType = "individual" | |
| state.copilotToken = "test-token" | |
| state.macMachineId = "machine-1" | |
| state.vsCodeDeviceId = "device-1" | |
| state.vsCodeSessionId = "session-1" | |
| state.vsCodeVersion = "1.120.0" | |
| fetchMock.mockClear() | |
| ;(globalThis as unknown as { fetch: typeof fetch }).fetch = | |
| fetchMock as unknown as typeof fetch | |
| }) | |
| afterEach(() => { | |
| if (originalOauthApp === undefined) { | |
| delete process.env.COPILOT_API_OAUTH_APP | |
| } else { | |
| process.env.COPILOT_API_OAUTH_APP = originalOauthApp | |
| } | |
| state.accountType = originalState.accountType | |
| state.copilotToken = originalState.copilotToken | |
| state.macMachineId = originalState.macMachineId | |
| state.vsCodeDeviceId = originalState.vsCodeDeviceId | |
| state.vsCodeSessionId = originalState.vsCodeSessionId | |
| state.vsCodeVersion = originalState.vsCodeVersion | |
| ;(globalThis as unknown as { fetch: typeof fetch }).fetch = originalFetch | |
| }) | |
| describe("createResponses", () => { | |
| test("keeps HTTP responses requests using x-initiator header", async () => { | |
| const payload: ResponsesPayload = { | |
| input: "hello", | |
| model: "gpt-test", | |
| } | |
| await createResponses(payload, { | |
| initiator: "user", | |
| requestId: "request-1", | |
| vision: false, | |
| }) | |
| expect(fetchMock).toHaveBeenCalledTimes(1) | |
| const [, init] = fetchMock.mock.calls[0] | |
| const requestInit = init as RequestInit & { | |
| headers: Record<string, string> | |
| } | |
| expect(requestInit.headers["x-initiator"]).toBe("user") | |
| expect(requestInit.headers["X-Request-Id"]).toBeUndefined() | |
| expect(typeof requestInit.body).toBe("string") | |
| const body = JSON.parse(requestInit.body as string) as Record< | |
| string, | |
| unknown | |
| > | |
| expect(body.initiator).toBeUndefined() | |
| expect(body.type).toBeUndefined() | |
| }) | |
| test("builds the first websocket frame as response.create", () => { | |
| const payload = { | |
| background: true, | |
| input: "hello", | |
| model: "gpt-test", | |
| service_tier: "auto", | |
| stream: true, | |
| } as ResponsesPayload | |
| const websocketPayload = buildResponsesWebSocketPayload(payload, "agent") | |
| expect(websocketPayload).toEqual({ | |
| initiator: "agent", | |
| input: "hello", | |
| model: "gpt-test", | |
| type: "response.create", | |
| }) | |
| expect("stream" in websocketPayload).toBe(false) | |
| expect("background" in websocketPayload).toBe(false) | |
| expect("service_tier" in websocketPayload).toBe(false) | |
| }) | |
| test("builds websocket URLs from the Copilot base URL", () => { | |
| expect(buildResponsesWebSocketUrl("https://api.githubcopilot.com")).toBe( | |
| "wss://api.githubcopilot.com/responses", | |
| ) | |
| expect(buildResponsesWebSocketUrl("http://localhost:3000/")).toBe( | |
| "ws://localhost:3000/responses", | |
| ) | |
| }) | |
| test("builds capture-style websocket headers without x-initiator", () => { | |
| const preparedHeaders = { | |
| ...copilotHeaders(state, "request-1", true), | |
| "x-initiator": "user", | |
| } | |
| prepareInteractionHeaders("interaction-1", false, preparedHeaders) | |
| const headers = copilotWebSocketHeaders(preparedHeaders) | |
| expect(headers).toMatchObject({ | |
| Authorization: "Bearer test-token", | |
| "Copilot-Integration-Id": "vscode-chat", | |
| "Copilot-Vision-Request": "true", | |
| "Editor-Device-Id": "device-1", | |
| "Editor-Plugin-Version": "copilot-chat/0.47.1", | |
| "Editor-Version": "vscode/1.120.0", | |
| "OpenAI-Intent": "conversation-agent", | |
| "VScode-SessionId": "session-1", | |
| "VScode-MachineId": "machine-1", | |
| "X-Agent-Task-Id": "request-1", | |
| "X-GitHub-Api-Version": "2026-01-09", | |
| "X-Interaction-Id": "interaction-1", | |
| "X-Interaction-Type": "conversation-agent", | |
| "X-Request-Id": "request-1", | |
| "user-agent": "node", | |
| }) | |
| const headerNames = Object.keys(headers) | |
| const agentTaskIdIndex = headerNames.indexOf("X-Agent-Task-Id") | |
| expect( | |
| headerNames.slice(agentTaskIdIndex + 1, agentTaskIdIndex + 3), | |
| ).toEqual(["VScode-SessionId", "VScode-MachineId"]) | |
| expect(headerNames[headerNames.length - 1]).toBe("user-agent") | |
| expect(headers.accept).toBeUndefined() | |
| expect(headers["accept-encoding"]).toBeUndefined() | |
| expect(headers["accept-language"]).toBeUndefined() | |
| expect(headers["cache-control"]).toBeUndefined() | |
| expect(headers.pragma).toBeUndefined() | |
| expect(headers["sec-fetch-mode"]).toBeUndefined() | |
| expect(headers["x-initiator"]).toBeUndefined() | |
| expect(headers["sec-websocket-key"]).toBeUndefined() | |
| }) | |
| test("websocket request uses prepared compact and interaction headers", () => { | |
| const preparedHeaders = { | |
| ...copilotHeaders(state, "request-1", false), | |
| "x-initiator": "user", | |
| } | |
| prepareInteractionHeaders("interaction-1", true, preparedHeaders) | |
| prepareForCompact(preparedHeaders, COMPACT_REQUEST) | |
| const request = prepareResponsesWebSocketRequest( | |
| { | |
| input: "hello", | |
| model: "gpt-test", | |
| stream: true, | |
| }, | |
| preparedHeaders, | |
| { | |
| requestId: "request-1", | |
| subagentMarker: { | |
| agent_id: "agent-1", | |
| agent_type: "Explore", | |
| session_id: "sub-session", | |
| }, | |
| }, | |
| ) | |
| expect(request.payload).toMatchObject({ | |
| initiator: "agent", | |
| input: "hello", | |
| model: "gpt-test", | |
| type: "response.create", | |
| }) | |
| expect(request.headers["OpenAI-Intent"]).toBe("conversation-agent") | |
| expect(request.headers["X-Interaction-Id"]).toBe("interaction-1") | |
| expect(request.headers["X-Interaction-Type"]).toBe( | |
| "conversation-compaction", | |
| ) | |
| expect(request.headers["x-initiator"]).toBeUndefined() | |
| }) | |
| test("websocket request keeps opencode headers and moves x-initiator into body", () => { | |
| process.env.COPILOT_API_OAUTH_APP = "opencode" | |
| const preparedHeaders = { | |
| ...copilotHeaders(state, "request-1", false), | |
| "x-initiator": "user", | |
| } | |
| prepareInteractionHeaders("interaction-1", true, preparedHeaders) | |
| prepareForCompact(preparedHeaders, COMPACT_REQUEST) | |
| const request = prepareResponsesWebSocketRequest( | |
| { | |
| input: "hello", | |
| model: "gpt-test", | |
| stream: true, | |
| }, | |
| preparedHeaders, | |
| { | |
| requestId: "request-1", | |
| }, | |
| ) | |
| expect(request.payload.initiator).toBe("agent") | |
| expect(request.headers).toMatchObject({ | |
| Authorization: "Bearer test-token", | |
| "Openai-Intent": "conversation-edits", | |
| }) | |
| expect(request.headers["User-Agent"]).toStartWith("opencode/") | |
| expect(request.headers["x-initiator"]).toBeUndefined() | |
| expect(request.headers["X-Request-Id"]).toBeUndefined() | |
| expect(request.headers["x-interaction-id"]).toBeUndefined() | |
| }) | |
| test("websocket pool key separates model request and subagent context", () => { | |
| const basePayload: ResponsesPayload = { | |
| input: "hello", | |
| model: "gpt-test", | |
| } | |
| const mainKey = buildResponsesWebSocketPoolKey(basePayload, { | |
| requestId: "request-1", | |
| }) | |
| const subagentKey = buildResponsesWebSocketPoolKey(basePayload, { | |
| requestId: "request-1", | |
| subagentMarker: { | |
| agent_id: "agent-1", | |
| agent_type: "Explore", | |
| session_id: "sub-session", | |
| }, | |
| }) | |
| const otherModelKey = buildResponsesWebSocketPoolKey( | |
| { | |
| ...basePayload, | |
| model: "gpt-other", | |
| }, | |
| { | |
| requestId: "request-1", | |
| }, | |
| ) | |
| expect(new Set([mainKey, subagentKey, otherModelKey]).size).toBe(3) | |
| expect(mainKey).toContain("gpt-test") | |
| expect(mainKey).toContain("request-1") | |
| }) | |
| }) | |