fix(vscode): stabilize SDK e2e login flow (#11441)

This commit is contained in:
Robin Newhouse
2026-06-24 14:11:33 +09:00
committed by Dominic Cooney
parent 072e237d01
commit 268462ddcf
5 changed files with 91 additions and 12 deletions
@@ -272,6 +272,22 @@ describe("normalizeProviderReasoningSettings", () => {
// ---------------------------------------------------------------------------
describe("buildSessionConfig", () => {
it("resolves Cline OAuth credentials after defaulting to the Cline provider", async () => {
mocks.stateManager.getApiConfiguration.mockReturnValue({} as any)
mocks.providerSettingsManager.getProviderSettings.mockReturnValue({
provider: "cline",
auth: {
accessToken: "workos:test-access-token",
refreshToken: "test-refresh-token",
},
} as any)
const config = await buildSessionConfig({ cwd: "/tmp/workspace" })
expect(config.providerId).toBe("cline")
expect(config.apiKey).toBe("workos:test-access-token")
})
it("enables basic SDK compaction when global useAutoCondense is true", async () => {
mocks.stateManager.getGlobalSettingsKey.mockImplementation((key: string) => {
if (key === "useAutoCondense") {
+5 -1
View File
@@ -462,6 +462,7 @@ export async function buildSessionConfig(input: SessionConfigInput): Promise<Cor
let modelId: string | undefined
let apiKey: string | undefined
let baseUrl: string | undefined
let apiConfig: ApiConfiguration | undefined
// Cloud-provider structured options. The core runtime reads these from
// CoreSessionConfig.providerConfig; without them the SDK gateway never receives
// region/project/auth fields for inference calls.
@@ -470,7 +471,7 @@ export async function buildSessionConfig(input: SessionConfigInput): Promise<Cor
try {
const stateManager = StateManager.get()
const apiConfig = stateManager.getApiConfiguration()
apiConfig = stateManager.getApiConfiguration()
// Resolve the provider for the current mode
const modeProvider = mode === "plan" ? apiConfig.planModeApiProvider : apiConfig.actModeApiProvider
@@ -531,6 +532,9 @@ export async function buildSessionConfig(input: SessionConfigInput): Promise<Cor
// session factory share one source of truth for default models.
providerId = providerId ?? DEFAULT_PROVIDER_ID
modelId = modelId ?? getDefaultModelIdForProvider(providerId) ?? getDefaultModelIdForProvider(DEFAULT_PROVIDER_ID) ?? ""
if (!apiKey && apiConfig) {
apiKey = resolveApiKey(providerId, apiConfig)
}
apiKey = apiKey ?? ""
const reasoningConfig = resolveProviderReasoningConfig(providerId)
+5 -10
View File
@@ -5,7 +5,7 @@ e2e.describe("Diff Editor", () => {
E2E_WORKSPACE_TYPES.forEach(({ title, workspaceType }) => {
e2e.extend({
workspaceType,
})(title, async ({ helper, page, sidebar }) => {
})(title, async ({ helper, sidebar }) => {
await helper.signin(sidebar)
const inputbox = sidebar.getByTestId("chat-input")
@@ -37,15 +37,10 @@ e2e.describe("Diff Editor", () => {
// Wait for the sidebar to load the file edit request
await sidebar.waitForSelector('span:has-text("Cline wants to edit this file:")')
// Cline Diff Editor should open with the file name and diff
await expect(page.getByText("test.ts: Original ↔ Cline's")).toBeVisible()
// Diff editor should show the original and modified content
const diffEditor = page.locator(
".monaco-editor.modified-in-monaco-diff-editor > .overflow-guard > .monaco-scrollable-element.editor-scrollable > .lines-content > div:nth-child(4)",
)
await diffEditor.click()
await expect(diffEditor).toBeVisible()
// The SDK-backed path renders a pending edit approval before the user saves it.
await expect(sidebar.getByText(/\/test\.ts/)).toBeVisible()
await expect(sidebar.getByRole("button", { name: "Save" })).toBeVisible()
await expect(sidebar.getByRole("button", { name: "Reject" })).toBeVisible()
})
})
})
@@ -474,10 +474,15 @@ export class ClineApiServerMock {
const parsed = JSON.parse(body)
const { _messages, model = "claude-3-5-sonnet-20241022", stream = true } = parsed
let responseText = E2E_MOCK_API_RESPONSES.DEFAULT
const isEditRequest = body.includes("edit_request")
log("Chat completion mock selection:", {
isEditRequest,
isReplaceResult: body.includes("[replace_in_file for 'test.ts'] Result:"),
})
if (body.includes("[replace_in_file for 'test.ts'] Result:")) {
responseText = E2E_MOCK_API_RESPONSES.REPLACE_REQUEST
}
if (body.includes("edit_request")) {
if (isEditRequest) {
responseText = E2E_MOCK_API_RESPONSES.EDIT_REQUEST
}
if (body.includes("[diff.test.ts] Hello, Cline!")) {
@@ -498,6 +503,63 @@ export class ClineApiServerMock {
const randomUUID = uuidv4()
if (isEditRequest) {
const toolCallId = `call_${randomUUID}`
const toolCallChunk = {
id: generationId,
object: "chat.completion.chunk",
created: Math.floor(Date.now() / 1000),
model,
choices: [
{
index: 0,
delta: {
tool_calls: [
{
index: 0,
id: toolCallId,
type: "function",
function: {
name: "editor",
arguments: JSON.stringify({
path: "test.ts",
old_text: 'export const name = "john"',
new_text: 'export const name = "cline"',
}),
},
},
],
},
finish_reason: null,
},
],
}
const finalChunk = {
id: generationId,
object: "chat.completion.chunk",
created: Math.floor(Date.now() / 1000),
model,
choices: [
{
index: 0,
delta: {},
finish_reason: "tool_calls",
},
],
usage: {
prompt_tokens: 140,
completion_tokens: responseText.length,
total_tokens: 140 + responseText.length,
cost: (140 + responseText.length) * 0.00015,
},
}
res.write(`data: ${JSON.stringify(toolCallChunk)}\n\n`)
res.write(`data: ${JSON.stringify(finalChunk)}\n\n`)
res.write("data: [DONE]\n\n")
res.end()
return
}
responseText += `\n\nGenerated UUID: ${randomUUID}`
const chunks = responseText.split(" ")
@@ -378,6 +378,7 @@ export const e2e = test
await use(async (workspacePath: string) => {
// Create isolated Cline data directory for this test
const clineTestDir = mkdtempSync(path.join(os.tmpdir(), "cline-e2e-"))
const clineDataDir = path.join(clineTestDir, "data")
const app = await _electron.launch({
executablePath,
@@ -387,6 +388,7 @@ export const e2e = test
E2E_TEST: "true",
CLINE_ENVIRONMENT: "local",
CLINE_DIR: clineTestDir, // Isolate test data from user's ~/.cline
CLINE_DATA_DIR: clineDataDir, // Keep SDK/shared storage off the user's real Cline data dir
GRPC_RECORDER_FILE_NAME: E2ETestHelper.generateTestFileName(testInfo.title, testInfo.project.name),
// GRPC_RECORDER_ENABLED: "true",
// GRPC_RECORDER_TESTS_FILTERS_ENABLED: "true"