Saoud Rizwan 1 год назад
Родитель
Сommit
843ef29a07

+ 0 - 1
CHANGELOG.md

@@ -6,7 +6,6 @@ All notable changes to the "claude-dev" extension will be documented in this fil
 
 
 ## [1.4.0]
 ## [1.4.0]
 
 
-- Add Kodu provider with credit tracking (+ $20 free credits!)
 - Add "Always allow read-only operations" setting to let Claude read files and view directories without needing approval (off by default)
 - Add "Always allow read-only operations" setting to let Claude read files and view directories without needing approval (off by default)
 - Implement sliding window context management to keep tasks going past 200k tokens
 - Implement sliding window context management to keep tasks going past 200k tokens
 
 

+ 3 - 78
package-lock.json

@@ -1,18 +1,17 @@
 {
 {
   "name": "claude-dev",
   "name": "claude-dev",
-  "version": "1.4.12",
+  "version": "1.4.15",
   "lockfileVersion": 3,
   "lockfileVersion": 3,
   "requires": true,
   "requires": true,
   "packages": {
   "packages": {
     "": {
     "": {
       "name": "claude-dev",
       "name": "claude-dev",
-      "version": "1.4.12",
+      "version": "1.4.15",
       "license": "MIT",
       "license": "MIT",
       "dependencies": {
       "dependencies": {
         "@anthropic-ai/bedrock-sdk": "^0.10.2",
         "@anthropic-ai/bedrock-sdk": "^0.10.2",
         "@anthropic-ai/sdk": "^0.26.0",
         "@anthropic-ai/sdk": "^0.26.0",
         "@anthropic-ai/tokenizer": "^0.0.4",
         "@anthropic-ai/tokenizer": "^0.0.4",
-        "@kodu-ai/cloud-api": "^1.0.1",
         "@types/clone-deep": "^4.0.4",
         "@types/clone-deep": "^4.0.4",
         "@vscode/codicons": "^0.0.36",
         "@vscode/codicons": "^0.0.36",
         "axios": "^1.7.4",
         "axios": "^1.7.4",
@@ -29,8 +28,7 @@
         "serialize-error": "^11.0.3",
         "serialize-error": "^11.0.3",
         "tree-kill": "^1.2.2",
         "tree-kill": "^1.2.2",
         "tree-sitter-wasms": "^0.1.11",
         "tree-sitter-wasms": "^0.1.11",
-        "web-tree-sitter": "^0.22.6",
-        "zod": "^3.23.8"
+        "web-tree-sitter": "^0.22.6"
       },
       },
       "devDependencies": {
       "devDependencies": {
         "@types/diff": "^5.2.1",
         "@types/diff": "^5.2.1",
@@ -2790,16 +2788,6 @@
         "@jridgewell/sourcemap-codec": "^1.4.14"
         "@jridgewell/sourcemap-codec": "^1.4.14"
       }
       }
     },
     },
-    "node_modules/@kodu-ai/cloud-api": {
-      "version": "1.0.1",
-      "resolved": "https://registry.npmjs.org/@kodu-ai/cloud-api/-/cloud-api-1.0.1.tgz",
-      "integrity": "sha512-CnmZP4Gm72ReqSO8qZgRYgbuGbo6A7RBbG/kNjMSS9KsDmtXCQu8OIEj/ZBVdaW+vm32t633r7q4nLKv3SaUHQ==",
-      "dependencies": {
-        "@trpc/client": "^11.0.0-rc.485",
-        "@trpc/server": "^11.0.0-rc.485",
-        "superjson": "^2.2.1"
-      }
-    },
     "node_modules/@nodelib/fs.scandir": {
     "node_modules/@nodelib/fs.scandir": {
       "version": "2.1.5",
       "version": "2.1.5",
       "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
       "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
@@ -4512,25 +4500,6 @@
       "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.3.tgz",
       "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.3.tgz",
       "integrity": "sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ=="
       "integrity": "sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ=="
     },
     },
-    "node_modules/@trpc/client": {
-      "version": "11.0.0-rc.485",
-      "resolved": "https://registry.npmjs.org/@trpc/client/-/client-11.0.0-rc.485.tgz",
-      "integrity": "sha512-Ld1gQjdYyrku0rjP/4QMg/SdsKgujr0P5XNoWkCyPRjdw3PuJbZFebauQPRC17cbbqGcpJrR+T3vnkhjMb1sgw==",
-      "funding": [
-        "https://trpc.io/sponsor"
-      ],
-      "peerDependencies": {
-        "@trpc/server": "11.0.0-rc.485+1c1d824cd"
-      }
-    },
-    "node_modules/@trpc/server": {
-      "version": "11.0.0-rc.485",
-      "resolved": "https://registry.npmjs.org/@trpc/server/-/server-11.0.0-rc.485.tgz",
-      "integrity": "sha512-U9SK9jbqCjR8S9wGSe4UBu2e0fqxhQWriZiDb5BLzdxXzls4Jv+XhAkI65yBzlcTbt6VqXegZDAXB3IARPhUCg==",
-      "funding": [
-        "https://trpc.io/sponsor"
-      ]
-    },
     "node_modules/@types/clone-deep": {
     "node_modules/@types/clone-deep": {
       "version": "4.0.4",
       "version": "4.0.4",
       "resolved": "https://registry.npmjs.org/@types/clone-deep/-/clone-deep-4.0.4.tgz",
       "resolved": "https://registry.npmjs.org/@types/clone-deep/-/clone-deep-4.0.4.tgz",
@@ -5468,20 +5437,6 @@
       "dev": true,
       "dev": true,
       "license": "MIT"
       "license": "MIT"
     },
     },
-    "node_modules/copy-anything": {
-      "version": "3.0.5",
-      "resolved": "https://registry.npmjs.org/copy-anything/-/copy-anything-3.0.5.tgz",
-      "integrity": "sha512-yCEafptTtb4bk7GLEQoM8KVJpxAfdBJYaXyzQEgQQQgYrZiDp8SJmGKlYza6CYjEDNstAdNdKA3UuoULlEbS6w==",
-      "dependencies": {
-        "is-what": "^4.1.8"
-      },
-      "engines": {
-        "node": ">=12.13"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/mesqueeb"
-      }
-    },
     "node_modules/core-util-is": {
     "node_modules/core-util-is": {
       "version": "1.0.3",
       "version": "1.0.3",
       "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz",
       "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz",
@@ -7306,17 +7261,6 @@
         "url": "https://github.com/sponsors/ljharb"
         "url": "https://github.com/sponsors/ljharb"
       }
       }
     },
     },
-    "node_modules/is-what": {
-      "version": "4.1.16",
-      "resolved": "https://registry.npmjs.org/is-what/-/is-what-4.1.16.tgz",
-      "integrity": "sha512-ZhMwEosbFJkA0YhFnNDgTM4ZxDRsS6HqTo7qsZM08fehyRYIYa0yHu5R6mgo1n/8MgaPBXiPimPD77baVFYg+A==",
-      "engines": {
-        "node": ">=12.13"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/mesqueeb"
-      }
-    },
     "node_modules/isarray": {
     "node_modules/isarray": {
       "version": "1.0.0",
       "version": "1.0.0",
       "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
       "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
@@ -9471,17 +9415,6 @@
       "resolved": "https://registry.npmjs.org/strnum/-/strnum-1.0.5.tgz",
       "resolved": "https://registry.npmjs.org/strnum/-/strnum-1.0.5.tgz",
       "integrity": "sha512-J8bbNyKKXl5qYcR36TIO8W3mVGVHrmmxsd5PAItGkmyzwJvybiw2IVq5nqd0i4LSNSkB/sx9VHllbfFdr9k1JA=="
       "integrity": "sha512-J8bbNyKKXl5qYcR36TIO8W3mVGVHrmmxsd5PAItGkmyzwJvybiw2IVq5nqd0i4LSNSkB/sx9VHllbfFdr9k1JA=="
     },
     },
-    "node_modules/superjson": {
-      "version": "2.2.1",
-      "resolved": "https://registry.npmjs.org/superjson/-/superjson-2.2.1.tgz",
-      "integrity": "sha512-8iGv75BYOa0xRJHK5vRLEjE2H/i4lulTjzpUXic3Eg8akftYjkmQDa8JARQ42rlczXyFR3IeRoeFCc7RxHsYZA==",
-      "dependencies": {
-        "copy-anything": "^3.0.2"
-      },
-      "engines": {
-        "node": ">=16"
-      }
-    },
     "node_modules/supports-color": {
     "node_modules/supports-color": {
       "version": "9.4.0",
       "version": "9.4.0",
       "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-9.4.0.tgz",
       "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-9.4.0.tgz",
@@ -10254,14 +10187,6 @@
       "funding": {
       "funding": {
         "url": "https://github.com/sponsors/sindresorhus"
         "url": "https://github.com/sponsors/sindresorhus"
       }
       }
-    },
-    "node_modules/zod": {
-      "version": "3.23.8",
-      "resolved": "https://registry.npmjs.org/zod/-/zod-3.23.8.tgz",
-      "integrity": "sha512-XBx9AXhXktjUqnepgTiE5flcKIYWi/rme0Eaj+5Y0lftuGBq+jyRu/md4WnuxqgP1ubdpNCsYEYPxrzVHD8d6g==",
-      "funding": {
-        "url": "https://github.com/sponsors/colinhacks"
-      }
     }
     }
   }
   }
 }
 }

+ 2 - 4
package.json

@@ -2,7 +2,7 @@
   "name": "claude-dev",
   "name": "claude-dev",
   "displayName": "Claude Dev",
   "displayName": "Claude Dev",
   "description": "Autonomous coding agent right in your IDE, capable of creating/editing files, executing commands, and more with your permission every step of the way.",
   "description": "Autonomous coding agent right in your IDE, capable of creating/editing files, executing commands, and more with your permission every step of the way.",
-  "version": "1.4.15",
+  "version": "1.4.16",
   "icon": "icon.png",
   "icon": "icon.png",
   "engines": {
   "engines": {
     "vscode": "^1.84.0"
     "vscode": "^1.84.0"
@@ -135,7 +135,6 @@
     "@anthropic-ai/bedrock-sdk": "^0.10.2",
     "@anthropic-ai/bedrock-sdk": "^0.10.2",
     "@anthropic-ai/sdk": "^0.26.0",
     "@anthropic-ai/sdk": "^0.26.0",
     "@anthropic-ai/tokenizer": "^0.0.4",
     "@anthropic-ai/tokenizer": "^0.0.4",
-    "@kodu-ai/cloud-api": "^1.0.1",
     "@types/clone-deep": "^4.0.4",
     "@types/clone-deep": "^4.0.4",
     "@vscode/codicons": "^0.0.36",
     "@vscode/codicons": "^0.0.36",
     "axios": "^1.7.4",
     "axios": "^1.7.4",
@@ -152,7 +151,6 @@
     "serialize-error": "^11.0.3",
     "serialize-error": "^11.0.3",
     "tree-kill": "^1.2.2",
     "tree-kill": "^1.2.2",
     "tree-sitter-wasms": "^0.1.11",
     "tree-sitter-wasms": "^0.1.11",
-    "web-tree-sitter": "^0.22.6",
-    "zod": "^3.23.8"
+    "web-tree-sitter": "^0.22.6"
   }
   }
 }
 }

+ 2 - 2
src/ClaudeDev.ts

@@ -1261,8 +1261,8 @@ ${this.customInstructions.trim()}
 			)
 			)
 			const { message, userCredits } = await this.api.createMessage(systemPrompt, adjustedMessages, tools)
 			const { message, userCredits } = await this.api.createMessage(systemPrompt, adjustedMessages, tools)
 			if (userCredits !== undefined) {
 			if (userCredits !== undefined) {
-				console.log("Updating kodu credits", userCredits)
-				this.providerRef.deref()?.updateKoduCredits(userCredits)
+				console.log("Updating credits", userCredits)
+				// TODO: update credits
 			}
 			}
 			return message
 			return message
 		} catch (error) {
 		} catch (error) {

+ 0 - 3
src/api/index.ts

@@ -3,7 +3,6 @@ import { ApiConfiguration, ApiModelId, ModelInfo } from "../shared/api"
 import { AnthropicHandler } from "./anthropic"
 import { AnthropicHandler } from "./anthropic"
 import { AwsBedrockHandler } from "./bedrock"
 import { AwsBedrockHandler } from "./bedrock"
 import { OpenRouterHandler } from "./openrouter"
 import { OpenRouterHandler } from "./openrouter"
-import { KoduHandler } from "./kodu"
 
 
 export interface ApiHandlerMessageResponse {
 export interface ApiHandlerMessageResponse {
 	message: Anthropic.Messages.Message
 	message: Anthropic.Messages.Message
@@ -38,8 +37,6 @@ export function buildApiHandler(configuration: ApiConfiguration): ApiHandler {
 			return new OpenRouterHandler(options)
 			return new OpenRouterHandler(options)
 		case "bedrock":
 		case "bedrock":
 			return new AwsBedrockHandler(options)
 			return new AwsBedrockHandler(options)
-		case "kodu":
-			return new KoduHandler(options)
 		default:
 		default:
 			return new AnthropicHandler(options)
 			return new AnthropicHandler(options)
 	}
 	}

+ 0 - 186
src/api/kodu.ts

@@ -1,186 +0,0 @@
-import { Anthropic } from "@anthropic-ai/sdk"
-import axios from "axios"
-import { ApiHandler, ApiHandlerMessageResponse, withoutImageData } from "."
-import { ApiHandlerOptions, koduDefaultModelId, KoduModelId, koduModels, ModelInfo } from "../shared/api"
-import { getKoduCreditsUrl, getKoduInferenceUrl } from "../shared/kodu"
-
-export async function fetchKoduCredits({ apiKey }: { apiKey: string }) {
-	const response = await axios.get(getKoduCreditsUrl(), {
-		headers: {
-			"x-api-key": apiKey,
-		},
-	})
-	return (response.data.credits as number) || 0
-}
-
-export class KoduHandler implements ApiHandler {
-	private options: ApiHandlerOptions
-
-	constructor(options: ApiHandlerOptions) {
-		this.options = options
-	}
-
-	async createMessage(
-		systemPrompt: string,
-		messages: Anthropic.Messages.MessageParam[],
-		tools: Anthropic.Messages.Tool[]
-	): Promise<ApiHandlerMessageResponse> {
-		const modelId = this.getModel().id
-		let requestBody: Anthropic.Beta.PromptCaching.Messages.MessageCreateParamsNonStreaming
-		switch (modelId) {
-			case "claude-3-5-sonnet-20240620":
-			case "claude-3-opus-20240229":
-			case "claude-3-haiku-20240307":
-				const userMsgIndices = messages.reduce(
-					(acc, msg, index) => (msg.role === "user" ? [...acc, index] : acc),
-					[] as number[]
-				)
-				const lastUserMsgIndex = userMsgIndices[userMsgIndices.length - 1] ?? -1
-				const secondLastMsgUserIndex = userMsgIndices[userMsgIndices.length - 2] ?? -1
-				requestBody = {
-					model: modelId,
-					max_tokens: this.getModel().info.maxTokens,
-					system: [{ text: systemPrompt, type: "text", cache_control: { type: "ephemeral" } }],
-					messages: messages.map((message, index) => {
-						if (index === lastUserMsgIndex || index === secondLastMsgUserIndex) {
-							return {
-								...message,
-								content:
-									typeof message.content === "string"
-										? [
-												{
-													type: "text",
-													text: message.content,
-													cache_control: { type: "ephemeral" },
-												},
-										  ]
-										: message.content.map((content, contentIndex) =>
-												contentIndex === message.content.length - 1
-													? { ...content, cache_control: { type: "ephemeral" } }
-													: content
-										  ),
-							}
-						}
-						return message
-					}),
-					tools,
-					tool_choice: { type: "auto" },
-				}
-				break
-			default:
-				requestBody = {
-					model: modelId,
-					max_tokens: this.getModel().info.maxTokens,
-					system: [{ text: systemPrompt, type: "text" }],
-					messages,
-					tools,
-					tool_choice: { type: "auto" },
-				}
-		}
-
-		// const response = await axios.post(getKoduInferenceUrl(), requestBody, {
-		// 	headers: {
-		// 		"x-api-key": this.options.koduApiKey,
-		// 	},
-		// })
-		// const message = response.data
-		// const userCredits = response.headers["user-credits"]
-		// return { message, userCredits: userCredits !== undefined ? parseFloat(userCredits) : undefined }
-		// const thing = {
-		// 	method: "POST",
-		// 	headers: {
-		// 		"Content-Type": "application/json",
-		// 		"x-api-key": this.options.koduApiKey || "",
-		// 	},
-		// 	body: JSON.stringify(requestBody),
-		// }
-
-		const response = await axios.post(getKoduInferenceUrl(), requestBody, {
-			headers: {
-				"Content-Type": "application/json",
-				"x-api-key": this.options.koduApiKey || "",
-			},
-			responseType: "stream",
-		})
-
-		if (response.data) {
-			const reader = response.data
-			const decoder = new TextDecoder("utf-8")
-			let finalResponse: any = null
-			let buffer = ""
-
-			for await (const chunk of reader) {
-				buffer += decoder.decode(chunk, { stream: true })
-				const lines = buffer.split("\n\n")
-				buffer = lines.pop() || ""
-
-				for (const line of lines) {
-					if (line.startsWith("data: ")) {
-						const eventData = JSON.parse(line.slice(6))
-
-						console.log("eventData", eventData)
-
-						if (eventData.code === 0) {
-							console.log("Health check received")
-						} else if (eventData.code === 1) {
-							finalResponse = eventData.body
-							console.log("finalResponse", finalResponse)
-							break
-						} else if (eventData.code === -1) {
-							throw new Error(`Error in SSE stream: ${JSON.stringify(eventData.json)}`)
-						}
-					}
-				}
-
-				if (finalResponse) {
-					break
-				}
-			}
-
-			if (!finalResponse) {
-				throw new Error("No final response received from the SSE stream")
-			}
-
-			const message: {
-				anthropic: Anthropic.Messages.Message
-				internal: {
-					userCredits: number
-				}
-			} = finalResponse
-			console.log("message", message)
-			return {
-				message: message.anthropic,
-				userCredits: message.internal?.userCredits,
-			}
-		} else {
-			throw new Error("No response data received")
-		}
-	}
-
-	createUserReadableRequest(
-		userContent: Array<
-			| Anthropic.TextBlockParam
-			| Anthropic.ImageBlockParam
-			| Anthropic.ToolUseBlockParam
-			| Anthropic.ToolResultBlockParam
-		>
-	): any {
-		return {
-			model: this.getModel().id,
-			max_tokens: this.getModel().info.maxTokens,
-			system: "(see SYSTEM_PROMPT in src/ClaudeDev.ts)",
-			messages: [{ conversation_history: "..." }, { role: "user", content: withoutImageData(userContent) }],
-			tools: "(see tools in src/ClaudeDev.ts)",
-			tool_choice: { type: "auto" },
-		}
-	}
-
-	getModel(): { id: KoduModelId; info: ModelInfo } {
-		const modelId = this.options.apiModelId
-		if (modelId && modelId in koduModels) {
-			const id = modelId as KoduModelId
-			return { id, info: koduModels[id] }
-		}
-		return { id: koduDefaultModelId, info: koduModels[koduDefaultModelId] }
-	}
-}

+ 10 - 10
src/extension.ts

@@ -109,16 +109,16 @@ export function activate(context: vscode.ExtensionContext) {
 		vscode.workspace.registerTextDocumentContentProvider("claude-dev-diff", diffContentProvider)
 		vscode.workspace.registerTextDocumentContentProvider("claude-dev-diff", diffContentProvider)
 	)
 	)
 
 
-	// URI Handler
-	const handleUri = async (uri: vscode.Uri) => {
-		const query = new URLSearchParams(uri.query.replace(/\+/g, "%2B"))
-		const token = query.get("token")
-		const email = query.get("email")
-		if (token) {
-			await sidebarProvider.saveKoduApiKey(token, email || undefined)
-		}
-	}
-	context.subscriptions.push(vscode.window.registerUriHandler({ handleUri }))
+	// // URI Handler
+	// const handleUri = async (uri: vscode.Uri) => {
+	// 	const query = new URLSearchParams(uri.query.replace(/\+/g, "%2B"))
+	// 	const token = query.get("token")
+	// 	const email = query.get("email")
+	// 	if (token) {
+	// 		await sidebarProvider.saveKoduApiKey(token, email || undefined)
+	// 	}
+	// }
+	// context.subscriptions.push(vscode.window.registerUriHandler({ handleUri }))
 }
 }
 
 
 // This method is called when your extension is deactivated
 // This method is called when your extension is deactivated

+ 26 - 68
src/providers/ClaudeDevProvider.ts

@@ -8,7 +8,6 @@ import { downloadTask, getNonce, getUri, selectImages } from "../utils"
 import * as path from "path"
 import * as path from "path"
 import fs from "fs/promises"
 import fs from "fs/promises"
 import { HistoryItem } from "../shared/HistoryItem"
 import { HistoryItem } from "../shared/HistoryItem"
-import { fetchKoduCredits } from "../api/kodu"
 
 
 /*
 /*
 https://github.com/microsoft/vscode-webview-ui-toolkit-samples/blob/main/default/weather-webview/src/providers/WeatherViewProvider.ts
 https://github.com/microsoft/vscode-webview-ui-toolkit-samples/blob/main/default/weather-webview/src/providers/WeatherViewProvider.ts
@@ -16,19 +15,16 @@ https://github.com/microsoft/vscode-webview-ui-toolkit-samples/blob/main/default
 https://github.com/KumarVariable/vscode-extension-sidebar-html/blob/master/src/customSidebarViewProvider.ts
 https://github.com/KumarVariable/vscode-extension-sidebar-html/blob/master/src/customSidebarViewProvider.ts
 */
 */
 
 
-type SecretKey = "apiKey" | "openRouterApiKey" | "awsAccessKey" | "awsSecretKey" | "koduApiKey"
+type SecretKey = "apiKey" | "openRouterApiKey" | "awsAccessKey" | "awsSecretKey"
 type GlobalStateKey =
 type GlobalStateKey =
 	| "apiProvider"
 	| "apiProvider"
 	| "apiModelId"
 	| "apiModelId"
 	| "awsRegion"
 	| "awsRegion"
-	| "koduEmail"
-	| "koduCredits"
 	| "maxRequestsPerTask"
 	| "maxRequestsPerTask"
 	| "lastShownAnnouncementId"
 	| "lastShownAnnouncementId"
 	| "customInstructions"
 	| "customInstructions"
 	| "alwaysAllowReadOnly"
 	| "alwaysAllowReadOnly"
 	| "taskHistory"
 	| "taskHistory"
-	| "shouldShowKoduPromo"
 
 
 export class ClaudeDevProvider implements vscode.WebviewViewProvider {
 export class ClaudeDevProvider implements vscode.WebviewViewProvider {
 	public static readonly sideBarId = "claude-dev.SidebarProvider" // used in package.json as the view's id. This value cannot be changed due to how vscode caches views based on their id, and updating the id would break existing instances of the extension.
 	public static readonly sideBarId = "claude-dev.SidebarProvider" // used in package.json as the view's id. This value cannot be changed due to how vscode caches views based on their id, and updating the id would break existing instances of the extension.
@@ -40,6 +36,28 @@ export class ClaudeDevProvider implements vscode.WebviewViewProvider {
 
 
 	constructor(readonly context: vscode.ExtensionContext, private readonly outputChannel: vscode.OutputChannel) {
 	constructor(readonly context: vscode.ExtensionContext, private readonly outputChannel: vscode.OutputChannel) {
 		this.outputChannel.appendLine("ClaudeDevProvider instantiated")
 		this.outputChannel.appendLine("ClaudeDevProvider instantiated")
+		this.revertKodu()
+	}
+
+	async revertKodu() {
+		const apiProvider = await this.getGlobalState("apiProvider")
+		if (apiProvider === "kodu") {
+			// switch back to previous provider
+			const anthropicKey = await this.getSecret("apiKey")
+			if (anthropicKey) {
+				await this.updateGlobalState("apiProvider", "anthropic" as ApiProvider)
+			} else {
+				const openRouterApiKey = await this.getSecret("openRouterApiKey")
+				if (openRouterApiKey) {
+					await this.updateGlobalState("apiProvider", "openrouter" as ApiProvider)
+				} else {
+					const awsAccessKey = await this.getSecret("awsAccessKey")
+					if (awsAccessKey) {
+						await this.updateGlobalState("apiProvider", "bedrock" as ApiProvider)
+					}
+				}
+			}
+		}
 	}
 	}
 
 
 	/*
 	/*
@@ -359,25 +377,6 @@ export class ClaudeDevProvider implements vscode.WebviewViewProvider {
 					case "exportTaskWithId":
 					case "exportTaskWithId":
 						this.exportTaskWithId(message.text!)
 						this.exportTaskWithId(message.text!)
 						break
 						break
-					case "didClickKoduSignOut":
-						await this.signOutKodu()
-						break
-					case "fetchKoduCredits":
-						const koduApiKey = await this.getSecret("koduApiKey")
-						if (koduApiKey) {
-							const credits = await fetchKoduCredits({ apiKey: koduApiKey })
-							await this.updateGlobalState("koduCredits", credits)
-							await this.postMessageToWebview({
-								type: "action",
-								action: "koduCreditsFetched",
-								state: await this.getStateToPostToWebview(),
-							})
-						}
-						break
-					case "didDismissKoduPromo":
-						await this.updateGlobalState("shouldShowKoduPromo", false)
-						await this.postStateToWebview()
-						break
 					case "resetState":
 					case "resetState":
 						await this.resetState()
 						await this.resetState()
 						break
 						break
@@ -390,27 +389,6 @@ export class ClaudeDevProvider implements vscode.WebviewViewProvider {
 		)
 		)
 	}
 	}
 
 
-	// Kodu
-
-	async saveKoduApiKey(apiKey: string, email?: string) {
-		await this.storeSecret("koduApiKey", apiKey)
-		await this.updateGlobalState("koduEmail", email)
-		await this.updateGlobalState("apiProvider", "kodu")
-		await this.updateGlobalState("shouldShowKoduPromo", false)
-		await this.postStateToWebview()
-		await this.postMessageToWebview({ type: "action", action: "koduAuthenticated" })
-		this.claudeDev?.updateApi({ apiProvider: "kodu", koduApiKey: apiKey })
-	}
-
-	async signOutKodu() {
-		await this.storeSecret("koduApiKey", undefined)
-		await this.updateGlobalState("koduEmail", undefined)
-		await this.updateGlobalState("koduCredits", undefined)
-		await this.updateGlobalState("apiProvider", "kodu")
-		this.claudeDev?.updateApi({ apiProvider: "kodu", koduApiKey: undefined })
-		await this.postStateToWebview()
-	}
-
 	// Task history
 	// Task history
 
 
 	async getTaskWithId(id: string): Promise<{
 	async getTaskWithId(id: string): Promise<{
@@ -510,8 +488,6 @@ export class ClaudeDevProvider implements vscode.WebviewViewProvider {
 			customInstructions,
 			customInstructions,
 			alwaysAllowReadOnly,
 			alwaysAllowReadOnly,
 			taskHistory,
 			taskHistory,
-			koduCredits,
-			shouldShowKoduPromo,
 		} = await this.getState()
 		} = await this.getState()
 		return {
 		return {
 			version: this.context.extension?.packageJSON?.version ?? "",
 			version: this.context.extension?.packageJSON?.version ?? "",
@@ -524,8 +500,6 @@ export class ClaudeDevProvider implements vscode.WebviewViewProvider {
 			claudeMessages: this.claudeDev?.claudeMessages || [],
 			claudeMessages: this.claudeDev?.claudeMessages || [],
 			taskHistory: (taskHistory || []).filter((item) => item.ts && item.task).sort((a, b) => b.ts - a.ts),
 			taskHistory: (taskHistory || []).filter((item) => item.ts && item.task).sort((a, b) => b.ts - a.ts),
 			shouldShowAnnouncement: lastShownAnnouncementId !== this.latestAnnouncementId,
 			shouldShowAnnouncement: lastShownAnnouncementId !== this.latestAnnouncementId,
-			koduCredits,
-			shouldShowKoduPromo,
 		}
 		}
 	}
 	}
 
 
@@ -624,15 +598,11 @@ export class ClaudeDevProvider implements vscode.WebviewViewProvider {
 			awsAccessKey,
 			awsAccessKey,
 			awsSecretKey,
 			awsSecretKey,
 			awsRegion,
 			awsRegion,
-			koduApiKey,
-			koduEmail,
-			koduCredits,
 			maxRequestsPerTask,
 			maxRequestsPerTask,
 			lastShownAnnouncementId,
 			lastShownAnnouncementId,
 			customInstructions,
 			customInstructions,
 			alwaysAllowReadOnly,
 			alwaysAllowReadOnly,
 			taskHistory,
 			taskHistory,
-			shouldShowKoduPromo,
 		] = await Promise.all([
 		] = await Promise.all([
 			this.getGlobalState("apiProvider") as Promise<ApiProvider | undefined>,
 			this.getGlobalState("apiProvider") as Promise<ApiProvider | undefined>,
 			this.getGlobalState("apiModelId") as Promise<ApiModelId | undefined>,
 			this.getGlobalState("apiModelId") as Promise<ApiModelId | undefined>,
@@ -641,15 +611,11 @@ export class ClaudeDevProvider implements vscode.WebviewViewProvider {
 			this.getSecret("awsAccessKey") as Promise<string | undefined>,
 			this.getSecret("awsAccessKey") as Promise<string | undefined>,
 			this.getSecret("awsSecretKey") as Promise<string | undefined>,
 			this.getSecret("awsSecretKey") as Promise<string | undefined>,
 			this.getGlobalState("awsRegion") as Promise<string | undefined>,
 			this.getGlobalState("awsRegion") as Promise<string | undefined>,
-			this.getSecret("koduApiKey") as Promise<string | undefined>,
-			this.getGlobalState("koduEmail") as Promise<string | undefined>,
-			this.getGlobalState("koduCredits") as Promise<number | undefined>,
 			this.getGlobalState("maxRequestsPerTask") as Promise<number | undefined>,
 			this.getGlobalState("maxRequestsPerTask") as Promise<number | undefined>,
 			this.getGlobalState("lastShownAnnouncementId") as Promise<string | undefined>,
 			this.getGlobalState("lastShownAnnouncementId") as Promise<string | undefined>,
 			this.getGlobalState("customInstructions") as Promise<string | undefined>,
 			this.getGlobalState("customInstructions") as Promise<string | undefined>,
 			this.getGlobalState("alwaysAllowReadOnly") as Promise<boolean | undefined>,
 			this.getGlobalState("alwaysAllowReadOnly") as Promise<boolean | undefined>,
 			this.getGlobalState("taskHistory") as Promise<HistoryItem[] | undefined>,
 			this.getGlobalState("taskHistory") as Promise<HistoryItem[] | undefined>,
-			this.getGlobalState("shouldShowKoduPromo") as Promise<boolean | undefined>,
 		])
 		])
 
 
 		let apiProvider: ApiProvider
 		let apiProvider: ApiProvider
@@ -661,8 +627,8 @@ export class ClaudeDevProvider implements vscode.WebviewViewProvider {
 			if (apiKey) {
 			if (apiKey) {
 				apiProvider = "anthropic"
 				apiProvider = "anthropic"
 			} else {
 			} else {
-				// New users should default to kodu
-				apiProvider = "kodu"
+				// New users should default to anthropic
+				apiProvider = "anthropic"
 			}
 			}
 		}
 		}
 
 
@@ -675,16 +641,12 @@ export class ClaudeDevProvider implements vscode.WebviewViewProvider {
 				awsAccessKey,
 				awsAccessKey,
 				awsSecretKey,
 				awsSecretKey,
 				awsRegion,
 				awsRegion,
-				koduApiKey,
-				koduEmail,
 			},
 			},
 			maxRequestsPerTask,
 			maxRequestsPerTask,
 			lastShownAnnouncementId,
 			lastShownAnnouncementId,
 			customInstructions,
 			customInstructions,
 			alwaysAllowReadOnly: alwaysAllowReadOnly ?? false,
 			alwaysAllowReadOnly: alwaysAllowReadOnly ?? false,
 			taskHistory,
 			taskHistory,
-			koduCredits,
-			shouldShowKoduPromo: shouldShowKoduPromo ?? true,
 		}
 		}
 	}
 	}
 
 
@@ -700,10 +662,6 @@ export class ClaudeDevProvider implements vscode.WebviewViewProvider {
 		return history
 		return history
 	}
 	}
 
 
-	async updateKoduCredits(credits: number) {
-		await this.updateGlobalState("koduCredits", credits)
-	}
-
 	// global
 	// global
 
 
 	private async updateGlobalState(key: GlobalStateKey, value: any) {
 	private async updateGlobalState(key: GlobalStateKey, value: any) {
@@ -755,7 +713,7 @@ export class ClaudeDevProvider implements vscode.WebviewViewProvider {
 		for (const key of this.context.globalState.keys()) {
 		for (const key of this.context.globalState.keys()) {
 			await this.context.globalState.update(key, undefined)
 			await this.context.globalState.update(key, undefined)
 		}
 		}
-		const secretKeys: SecretKey[] = ["apiKey", "openRouterApiKey", "awsAccessKey", "awsSecretKey", "koduApiKey"]
+		const secretKeys: SecretKey[] = ["apiKey", "openRouterApiKey", "awsAccessKey", "awsSecretKey"]
 		for (const key of secretKeys) {
 		for (const key of secretKeys) {
 			await this.storeSecret(key, undefined)
 			await this.storeSecret(key, undefined)
 		}
 		}

+ 1 - 9
src/shared/ExtensionMessage.ts

@@ -7,13 +7,7 @@ import { HistoryItem } from "./HistoryItem"
 export interface ExtensionMessage {
 export interface ExtensionMessage {
 	type: "action" | "state" | "selectedImages"
 	type: "action" | "state" | "selectedImages"
 	text?: string
 	text?: string
-	action?:
-		| "chatButtonTapped"
-		| "settingsButtonTapped"
-		| "historyButtonTapped"
-		| "didBecomeVisible"
-		| "koduAuthenticated"
-		| "koduCreditsFetched"
+	action?: "chatButtonTapped" | "settingsButtonTapped" | "historyButtonTapped" | "didBecomeVisible"
 	state?: ExtensionState
 	state?: ExtensionState
 	images?: string[]
 	images?: string[]
 }
 }
@@ -29,8 +23,6 @@ export interface ExtensionState {
 	claudeMessages: ClaudeMessage[]
 	claudeMessages: ClaudeMessage[]
 	taskHistory: HistoryItem[]
 	taskHistory: HistoryItem[]
 	shouldShowAnnouncement: boolean
 	shouldShowAnnouncement: boolean
-	koduCredits?: number
-	shouldShowKoduPromo: boolean
 }
 }
 
 
 export interface ClaudeMessage {
 export interface ClaudeMessage {

+ 0 - 3
src/shared/WebviewMessage.ts

@@ -16,9 +16,6 @@ export interface WebviewMessage {
 		| "showTaskWithId"
 		| "showTaskWithId"
 		| "deleteTaskWithId"
 		| "deleteTaskWithId"
 		| "exportTaskWithId"
 		| "exportTaskWithId"
-		| "didClickKoduSignOut"
-		| "fetchKoduCredits"
-		| "didDismissKoduPromo"
 		| "resetState"
 		| "resetState"
 	text?: string
 	text?: string
 	askResponse?: ClaudeAskResponse
 	askResponse?: ClaudeAskResponse

+ 1 - 10
src/shared/api.ts

@@ -1,4 +1,4 @@
-export type ApiProvider = "anthropic" | "openrouter" | "bedrock" | "kodu"
+export type ApiProvider = "anthropic" | "openrouter" | "bedrock"
 
 
 export interface ApiHandlerOptions {
 export interface ApiHandlerOptions {
 	apiModelId?: ApiModelId
 	apiModelId?: ApiModelId
@@ -7,8 +7,6 @@ export interface ApiHandlerOptions {
 	awsAccessKey?: string
 	awsAccessKey?: string
 	awsSecretKey?: string
 	awsSecretKey?: string
 	awsRegion?: string
 	awsRegion?: string
-	koduApiKey?: string
-	koduEmail?: string
 }
 }
 
 
 export type ApiConfiguration = ApiHandlerOptions & {
 export type ApiConfiguration = ApiHandlerOptions & {
@@ -252,10 +250,3 @@ export const openRouterModels = {
 	// 	outputPrice: 1.5,
 	// 	outputPrice: 1.5,
 	// },
 	// },
 } as const satisfies Record<string, ModelInfo>
 } as const satisfies Record<string, ModelInfo>
-
-// Kodu
-export type KoduModelId = keyof typeof koduModels
-export const koduDefaultModelId: KoduModelId = "claude-3-5-sonnet-20240620"
-export const koduModels = {
-	...anthropicModels,
-} as const satisfies Record<string, ModelInfo>

+ 0 - 21
src/shared/kodu.ts

@@ -1,21 +0,0 @@
-const KODU_BASE_URL = "https://kodu.ai"
-
-export function getKoduSignInUrl(uriScheme?: string) {
-	return `${KODU_BASE_URL}/auth/login?redirectTo=${uriScheme}://saoudrizwan.claude-dev&ext=1`
-}
-
-export function getKoduAddCreditsUrl(uriScheme?: string) {
-	return `${KODU_BASE_URL}/cloud/plan`
-}
-
-export function getKoduCreditsUrl() {
-	return `${KODU_BASE_URL}/api/credits`
-}
-
-export function getKoduInferenceUrl() {
-	return `${KODU_BASE_URL}/api/inference-stream`
-}
-
-export function getKoduHomepageUrl() {
-	return `${KODU_BASE_URL}`
-}

+ 34 - 48
webview-ui/src/App.tsx

@@ -15,54 +15,40 @@ const AppContent = () => {
 	const [showHistory, setShowHistory] = useState(false)
 	const [showHistory, setShowHistory] = useState(false)
 	const [showWelcome, setShowWelcome] = useState<boolean>(false)
 	const [showWelcome, setShowWelcome] = useState<boolean>(false)
 	const [showAnnouncement, setShowAnnouncement] = useState(false)
 	const [showAnnouncement, setShowAnnouncement] = useState(false)
-	const [didAuthKoduFromWelcome, setDidAuthKoduFromWelcome] = useState<boolean>(false)
 
 
-	const handleMessage = useCallback(
-		(e: MessageEvent) => {
-			const message: ExtensionMessage = e.data
-			switch (message.type) {
-				case "state":
-					const hasKey =
-						message.state!.apiConfiguration?.apiKey !== undefined ||
-						message.state!.apiConfiguration?.openRouterApiKey !== undefined ||
-						message.state!.apiConfiguration?.awsAccessKey !== undefined ||
-						message.state!.apiConfiguration?.koduApiKey !== undefined
-					setShowWelcome(!hasKey)
-					if (!hasKey) {
-						setDidAuthKoduFromWelcome(false)
-					}
-					// don't update showAnnouncement to false if shouldShowAnnouncement is false
-					if (message.state!.shouldShowAnnouncement) {
-						setShowAnnouncement(true)
-					}
-					break
-				case "action":
-					switch (message.action!) {
-						case "settingsButtonTapped":
-							setShowSettings(true)
-							setShowHistory(false)
-							break
-						case "historyButtonTapped":
-							setShowSettings(false)
-							setShowHistory(true)
-							break
-						case "chatButtonTapped":
-							setShowSettings(false)
-							setShowHistory(false)
-							break
-						case "koduAuthenticated":
-							if (!didAuthKoduFromWelcome) {
-								setShowSettings(true)
-								setShowHistory(false)
-							}
-							break
-					}
-					break
-			}
-			// (react-use takes care of not registering the same listener multiple times even if this callback is updated.)
-		},
-		[didAuthKoduFromWelcome]
-	)
+	const handleMessage = useCallback((e: MessageEvent) => {
+		const message: ExtensionMessage = e.data
+		switch (message.type) {
+			case "state":
+				const hasKey =
+					message.state!.apiConfiguration?.apiKey !== undefined ||
+					message.state!.apiConfiguration?.openRouterApiKey !== undefined ||
+					message.state!.apiConfiguration?.awsAccessKey !== undefined
+				setShowWelcome(!hasKey)
+				// don't update showAnnouncement to false if shouldShowAnnouncement is false
+				if (message.state!.shouldShowAnnouncement) {
+					setShowAnnouncement(true)
+				}
+				break
+			case "action":
+				switch (message.action!) {
+					case "settingsButtonTapped":
+						setShowSettings(true)
+						setShowHistory(false)
+						break
+					case "historyButtonTapped":
+						setShowSettings(false)
+						setShowHistory(true)
+						break
+					case "chatButtonTapped":
+						setShowSettings(false)
+						setShowHistory(false)
+						break
+				}
+				break
+		}
+		// (react-use takes care of not registering the same listener multiple times even if this callback is updated.)
+	}, [])
 
 
 	useEvent("message", handleMessage)
 	useEvent("message", handleMessage)
 
 
@@ -73,7 +59,7 @@ const AppContent = () => {
 	return (
 	return (
 		<>
 		<>
 			{showWelcome ? (
 			{showWelcome ? (
-				<WelcomeView setDidAuthKoduFromWelcome={setDidAuthKoduFromWelcome} />
+				<WelcomeView />
 			) : (
 			) : (
 				<>
 				<>
 					{showSettings && <SettingsView onDone={() => setShowSettings(false)} />}
 					{showSettings && <SettingsView onDone={() => setShowSettings(false)} />}

+ 0 - 19
webview-ui/src/components/Announcement.tsx

@@ -1,7 +1,5 @@
 import { VSCodeButton, VSCodeLink } from "@vscode/webview-ui-toolkit/react"
 import { VSCodeButton, VSCodeLink } from "@vscode/webview-ui-toolkit/react"
 import { ApiConfiguration } from "../../../src/shared/api"
 import { ApiConfiguration } from "../../../src/shared/api"
-import { getKoduSignInUrl } from "../../../src/shared/kodu"
-import VSCodeButtonLink from "./VSCodeButtonLink"
 
 
 interface AnnouncementProps {
 interface AnnouncementProps {
 	version: string
 	version: string
@@ -33,23 +31,6 @@ const Announcement = ({ version, hideAnnouncement, apiConfiguration, vscodeUriSc
 			</h3>
 			</h3>
 
 
 			<ul style={{ margin: "0 0 8px", paddingLeft: "12px" }}>
 			<ul style={{ margin: "0 0 8px", paddingLeft: "12px" }}>
-				<li>
-					Excited to announce that we've partnered with Anthropic and are offering <b>$20 free credits</b> to
-					help users get the most out of Claude Dev with increased rate limits and prompt caching! Stay tuned
-					for some exciting updates like easier billing, voice mode and one click deployment!
-					{apiConfiguration?.koduApiKey === undefined && (
-						<VSCodeButtonLink
-							appearance="secondary"
-							href={getKoduSignInUrl(vscodeUriScheme)}
-							style={{
-								transform: "scale(0.85)",
-								transformOrigin: "left center",
-								margin: "4px -30px 2px 0",
-							}}>
-							Claim $20 Credits on Kodu
-						</VSCodeButtonLink>
-					)}
-				</li>
 				<li>
 				<li>
 					Added "Always allow read-only operations" setting to let Claude read files and view directories
 					Added "Always allow read-only operations" setting to let Claude read files and view directories
 					without needing to approve (off by default).
 					without needing to approve (off by default).

+ 3 - 87
webview-ui/src/components/ApiOptions.tsx

@@ -1,6 +1,5 @@
 import { VSCodeDropdown, VSCodeLink, VSCodeOption, VSCodeTextField } from "@vscode/webview-ui-toolkit/react"
 import { VSCodeDropdown, VSCodeLink, VSCodeOption, VSCodeTextField } from "@vscode/webview-ui-toolkit/react"
-import React, { useCallback, useEffect, useMemo, useState } from "react"
-import { useEvent } from "react-use"
+import React, { useMemo } from "react"
 import {
 import {
 	ApiConfiguration,
 	ApiConfiguration,
 	ApiModelId,
 	ApiModelId,
@@ -9,26 +8,18 @@ import {
 	anthropicModels,
 	anthropicModels,
 	bedrockDefaultModelId,
 	bedrockDefaultModelId,
 	bedrockModels,
 	bedrockModels,
-	koduDefaultModelId,
-	koduModels,
 	openRouterDefaultModelId,
 	openRouterDefaultModelId,
 	openRouterModels,
 	openRouterModels,
 } from "../../../src/shared/api"
 } from "../../../src/shared/api"
-import { ExtensionMessage } from "../../../src/shared/ExtensionMessage"
-import { getKoduAddCreditsUrl, getKoduHomepageUrl, getKoduSignInUrl } from "../../../src/shared/kodu"
-import { vscode } from "../utils/vscode"
-import VSCodeButtonLink from "./VSCodeButtonLink"
 import { useExtensionState } from "../context/ExtensionStateContext"
 import { useExtensionState } from "../context/ExtensionStateContext"
 
 
 interface ApiOptionsProps {
 interface ApiOptionsProps {
 	showModelOptions: boolean
 	showModelOptions: boolean
 	apiErrorMessage?: string
 	apiErrorMessage?: string
-	setDidAuthKodu?: React.Dispatch<React.SetStateAction<boolean>>
 }
 }
 
 
-const ApiOptions: React.FC<ApiOptionsProps> = ({ showModelOptions, apiErrorMessage, setDidAuthKodu }) => {
-	const { apiConfiguration, setApiConfiguration, koduCredits, uriScheme } = useExtensionState()
-	const [, setDidFetchKoduCredits] = useState(false)
+const ApiOptions: React.FC<ApiOptionsProps> = ({ showModelOptions, apiErrorMessage }) => {
+	const { apiConfiguration, setApiConfiguration } = useExtensionState()
 	const handleInputChange = (field: keyof ApiConfiguration) => (event: any) => {
 	const handleInputChange = (field: keyof ApiConfiguration) => (event: any) => {
 		setApiConfiguration({ ...apiConfiguration, [field]: event.target.value })
 		setApiConfiguration({ ...apiConfiguration, [field]: event.target.value })
 	}
 	}
@@ -69,27 +60,6 @@ const ApiOptions: React.FC<ApiOptionsProps> = ({ showModelOptions, apiErrorMessa
 		)
 		)
 	}
 	}
 
 
-	useEffect(() => {
-		if (selectedProvider === "kodu" && apiConfiguration?.koduApiKey && koduCredits === undefined) {
-			setDidFetchKoduCredits(false)
-			vscode.postMessage({ type: "fetchKoduCredits" })
-		}
-	}, [selectedProvider, apiConfiguration?.koduApiKey, koduCredits])
-
-	const handleMessage = useCallback((e: MessageEvent) => {
-		const message: ExtensionMessage = e.data
-		switch (message.type) {
-			case "action":
-				switch (message.action) {
-					case "koduCreditsFetched":
-						setDidFetchKoduCredits(true)
-						break
-				}
-				break
-		}
-	}, [])
-	useEvent("message", handleMessage)
-
 	return (
 	return (
 		<div style={{ display: "flex", flexDirection: "column", gap: 5 }}>
 		<div style={{ display: "flex", flexDirection: "column", gap: 5 }}>
 			<div className="dropdown-container">
 			<div className="dropdown-container">
@@ -97,7 +67,6 @@ const ApiOptions: React.FC<ApiOptionsProps> = ({ showModelOptions, apiErrorMessa
 					<span style={{ fontWeight: 500 }}>API Provider</span>
 					<span style={{ fontWeight: 500 }}>API Provider</span>
 				</label>
 				</label>
 				<VSCodeDropdown id="api-provider" value={selectedProvider} onChange={handleInputChange("apiProvider")}>
 				<VSCodeDropdown id="api-provider" value={selectedProvider} onChange={handleInputChange("apiProvider")}>
-					<VSCodeOption value="kodu">Kodu</VSCodeOption>
 					<VSCodeOption value="anthropic">Anthropic</VSCodeOption>
 					<VSCodeOption value="anthropic">Anthropic</VSCodeOption>
 					<VSCodeOption value="bedrock">AWS Bedrock</VSCodeOption>
 					<VSCodeOption value="bedrock">AWS Bedrock</VSCodeOption>
 					<VSCodeOption value="openrouter">OpenRouter</VSCodeOption>
 					<VSCodeOption value="openrouter">OpenRouter</VSCodeOption>
@@ -154,56 +123,6 @@ const ApiOptions: React.FC<ApiOptionsProps> = ({ showModelOptions, apiErrorMessa
 				</div>
 				</div>
 			)}
 			)}
 
 
-			{selectedProvider === "kodu" && (
-				<div>
-					{apiConfiguration?.koduApiKey !== undefined ? (
-						<>
-							<div style={{ marginBottom: 5, marginTop: 3 }}>
-								<span style={{ color: "var(--vscode-descriptionForeground)" }}>
-									Signed in as {apiConfiguration?.koduEmail || "Unknown"}
-								</span>{" "}
-								<VSCodeLink
-									style={{ display: "inline" }}
-									onClick={() => vscode.postMessage({ type: "didClickKoduSignOut" })}>
-									(sign out?)
-								</VSCodeLink>
-							</div>
-							<div style={{ marginBottom: 7 }}>
-								Credits remaining:{" "}
-								<span style={{ fontWeight: 500, opacity: koduCredits !== undefined ? 1 : 0.6 }}>
-									{formatPrice(koduCredits || 0)}
-								</span>
-							</div>
-							<VSCodeButtonLink
-								href={getKoduAddCreditsUrl(uriScheme)}
-								style={{
-									width: "fit-content",
-								}}>
-								Add Credits
-							</VSCodeButtonLink>
-						</>
-					) : (
-						<div style={{ margin: "4px 0px" }}>
-							<VSCodeButtonLink href={getKoduSignInUrl(uriScheme)} onClick={() => setDidAuthKodu?.(true)}>
-								Sign in to Kodu
-							</VSCodeButtonLink>
-						</div>
-					)}
-					<p
-						style={{
-							fontSize: 12,
-							marginTop: 6,
-							color: "var(--vscode-descriptionForeground)",
-						}}>
-						Kodu is recommended for its high rate limits and access to the latest features like prompt
-						caching.
-						<VSCodeLink href={getKoduHomepageUrl()} style={{ display: "inline", fontSize: "12px" }}>
-							Learn more about Kodu here.
-						</VSCodeLink>
-					</p>
-				</div>
-			)}
-
 			{selectedProvider === "bedrock" && (
 			{selectedProvider === "bedrock" && (
 				<div style={{ display: "flex", flexDirection: "column", gap: 5 }}>
 				<div style={{ display: "flex", flexDirection: "column", gap: 5 }}>
 					<VSCodeTextField
 					<VSCodeTextField
@@ -289,7 +208,6 @@ const ApiOptions: React.FC<ApiOptionsProps> = ({ showModelOptions, apiErrorMessa
 						{selectedProvider === "anthropic" && createDropdown(anthropicModels)}
 						{selectedProvider === "anthropic" && createDropdown(anthropicModels)}
 						{selectedProvider === "openrouter" && createDropdown(openRouterModels)}
 						{selectedProvider === "openrouter" && createDropdown(openRouterModels)}
 						{selectedProvider === "bedrock" && createDropdown(bedrockModels)}
 						{selectedProvider === "bedrock" && createDropdown(bedrockModels)}
-						{selectedProvider === "kodu" && createDropdown(koduModels)}
 					</div>
 					</div>
 
 
 					<ModelInfoView modelInfo={selectedModelInfo} />
 					<ModelInfoView modelInfo={selectedModelInfo} />
@@ -393,8 +311,6 @@ export function normalizeApiConfiguration(apiConfiguration?: ApiConfiguration) {
 			return getProviderData(openRouterModels, openRouterDefaultModelId)
 			return getProviderData(openRouterModels, openRouterDefaultModelId)
 		case "bedrock":
 		case "bedrock":
 			return getProviderData(bedrockModels, bedrockDefaultModelId)
 			return getProviderData(bedrockModels, bedrockDefaultModelId)
-		case "kodu":
-			return getProviderData(koduModels, koduDefaultModelId)
 		default:
 		default:
 			return getProviderData(anthropicModels, anthropicDefaultModelId)
 			return getProviderData(anthropicModels, anthropicDefaultModelId)
 	}
 	}

+ 2 - 2
webview-ui/src/components/ChatRow.tsx

@@ -281,7 +281,7 @@ const ChatRow: React.FC<ChatRowProps> = ({
 										<p style={{ ...pStyle, color: "var(--vscode-errorForeground)" }}>
 										<p style={{ ...pStyle, color: "var(--vscode-errorForeground)" }}>
 											{apiRequestFailedMessage}
 											{apiRequestFailedMessage}
 										</p>
 										</p>
-										{apiProvider === "kodu" && (
+										{/* {apiProvider === "kodu" && (
 											<div
 											<div
 												style={{
 												style={{
 													display: "flex",
 													display: "flex",
@@ -312,7 +312,7 @@ const ChatRow: React.FC<ChatRowProps> = ({
 													.
 													.
 												</span>
 												</span>
 											</div>
 											</div>
-										)}
+										)} */}
 									</>
 									</>
 								)}
 								)}
 							</>
 							</>

+ 0 - 7
webview-ui/src/components/ChatView.tsx

@@ -14,7 +14,6 @@ import { vscode } from "../utils/vscode"
 import Announcement from "./Announcement"
 import Announcement from "./Announcement"
 import ChatRow from "./ChatRow"
 import ChatRow from "./ChatRow"
 import HistoryPreview from "./HistoryPreview"
 import HistoryPreview from "./HistoryPreview"
-import KoduPromo from "./KoduPromo"
 import TaskHeader from "./TaskHeader"
 import TaskHeader from "./TaskHeader"
 import Thumbnails from "./Thumbnails"
 import Thumbnails from "./Thumbnails"
 
 
@@ -44,8 +43,6 @@ const ChatView = ({
 		themeName: vscodeThemeName,
 		themeName: vscodeThemeName,
 		apiConfiguration,
 		apiConfiguration,
 		uriScheme,
 		uriScheme,
-		shouldShowKoduPromo,
-		koduCredits,
 	} = useExtensionState()
 	} = useExtensionState()
 
 
 	//const task = messages.length > 0 ? (messages[0].say === "task" ? messages[0] : undefined) : undefined) : undefined
 	//const task = messages.length > 0 ? (messages[0].say === "task" ? messages[0] : undefined) : undefined) : undefined
@@ -483,7 +480,6 @@ const ChatView = ({
 					totalCost={apiMetrics.totalCost}
 					totalCost={apiMetrics.totalCost}
 					onClose={handleTaskCloseButtonClick}
 					onClose={handleTaskCloseButtonClick}
 					isHidden={isHidden}
 					isHidden={isHidden}
-					koduCredits={koduCredits}
 					vscodeUriScheme={uriScheme}
 					vscodeUriScheme={uriScheme}
 					apiProvider={apiConfiguration?.apiProvider}
 					apiProvider={apiConfiguration?.apiProvider}
 				/>
 				/>
@@ -497,9 +493,6 @@ const ChatView = ({
 							vscodeUriScheme={uriScheme}
 							vscodeUriScheme={uriScheme}
 						/>
 						/>
 					)}
 					)}
-					{apiConfiguration?.koduApiKey === undefined && !showAnnouncement && shouldShowKoduPromo && (
-						<KoduPromo style={{ margin: "10px 15px -10px 15px" }} />
-					)}
 					<div style={{ padding: "0 20px", flexGrow: taskHistory.length > 0 ? undefined : 1 }}>
 					<div style={{ padding: "0 20px", flexGrow: taskHistory.length > 0 ? undefined : 1 }}>
 						<h2>What can I do for you?</h2>
 						<h2>What can I do for you?</h2>
 						<p>
 						<p>

+ 0 - 71
webview-ui/src/components/KoduPromo.tsx

@@ -1,71 +0,0 @@
-import React from "react"
-import { getKoduSignInUrl } from "../../../src/shared/kodu"
-import { vscode } from "../utils/vscode"
-import { useExtensionState } from "../context/ExtensionStateContext"
-
-interface KoduPromoProps {
-	style?: React.CSSProperties
-}
-
-const KoduPromo: React.FC<KoduPromoProps> = ({ style }) => {
-	const { uriScheme } = useExtensionState()
-
-	function onClose() {
-		vscode.postMessage({ type: "didDismissKoduPromo" })
-	}
-
-	return (
-		<div style={{ ...style }}>
-			<div
-				style={{
-					display: "flex",
-					alignItems: "center",
-					justifyContent: "space-between",
-					backgroundColor: "var(--vscode-editor-inactiveSelectionBackground)",
-					color: "var(--vscode-textLink-foreground)",
-					padding: "6px 8px",
-					borderRadius: "3px",
-					margin: "0 0 8px 0px",
-					fontSize: "12px",
-					cursor: "pointer",
-				}}>
-				<a
-					href={getKoduSignInUrl(uriScheme)}
-					style={{
-						textDecoration: "none",
-						color: "inherit",
-						outline: "none",
-						display: "flex",
-						alignItems: "center",
-					}}>
-					<i
-						className="codicon codicon-info"
-						style={{
-							marginRight: 6,
-							fontSize: 16,
-						}}></i>
-					<span>Claim $20 free credits from Kodu</span>
-				</a>
-				<button
-					onClick={onClose}
-					style={{
-						background: "none",
-						border: "none",
-						color: "var(--vscode-textLink-foreground)",
-						cursor: "pointer",
-						fontSize: "12px",
-						opacity: 0.7,
-						padding: 0,
-						marginLeft: 4,
-						marginTop: 2,
-					}}
-					onMouseEnter={(e) => (e.currentTarget.style.opacity = "1")}
-					onMouseLeave={(e) => (e.currentTarget.style.opacity = "0.7")}>
-					<i className="codicon codicon-close"></i>
-				</button>
-			</div>
-		</div>
-	)
-}
-
-export default KoduPromo

+ 4 - 8
webview-ui/src/components/TaskHeader.tsx

@@ -1,12 +1,10 @@
-import { VSCodeButton, VSCodeLink } from "@vscode/webview-ui-toolkit/react"
+import { VSCodeButton } from "@vscode/webview-ui-toolkit/react"
 import React, { useEffect, useRef, useState } from "react"
 import React, { useEffect, useRef, useState } from "react"
 import { useWindowSize } from "react-use"
 import { useWindowSize } from "react-use"
+import { ApiProvider } from "../../../src/shared/api"
 import { ClaudeMessage } from "../../../src/shared/ExtensionMessage"
 import { ClaudeMessage } from "../../../src/shared/ExtensionMessage"
 import { vscode } from "../utils/vscode"
 import { vscode } from "../utils/vscode"
 import Thumbnails from "./Thumbnails"
 import Thumbnails from "./Thumbnails"
-import { formatPrice } from "./ApiOptions"
-import { getKoduAddCreditsUrl } from "../../../src/shared/kodu"
-import { ApiProvider } from "../../../src/shared/api"
 
 
 interface TaskHeaderProps {
 interface TaskHeaderProps {
 	task: ClaudeMessage
 	task: ClaudeMessage
@@ -18,7 +16,6 @@ interface TaskHeaderProps {
 	totalCost: number
 	totalCost: number
 	onClose: () => void
 	onClose: () => void
 	isHidden: boolean
 	isHidden: boolean
-	koduCredits?: number
 	vscodeUriScheme?: string
 	vscodeUriScheme?: string
 	apiProvider?: ApiProvider
 	apiProvider?: ApiProvider
 }
 }
@@ -33,7 +30,6 @@ const TaskHeader: React.FC<TaskHeaderProps> = ({
 	totalCost,
 	totalCost,
 	onClose,
 	onClose,
 	isHidden,
 	isHidden,
-	koduCredits,
 	vscodeUriScheme,
 	vscodeUriScheme,
 	apiProvider,
 	apiProvider,
 }) => {
 }) => {
@@ -258,7 +254,7 @@ const TaskHeader: React.FC<TaskHeaderProps> = ({
 					</div>
 					</div>
 				</div>
 				</div>
 			</div>
 			</div>
-			{apiProvider === "kodu" && (
+			{/* {apiProvider === "kodu" && (
 				<div
 				<div
 					style={{
 					style={{
 						backgroundColor: "color-mix(in srgb, var(--vscode-badge-background) 50%, transparent)",
 						backgroundColor: "color-mix(in srgb, var(--vscode-badge-background) 50%, transparent)",
@@ -285,7 +281,7 @@ const TaskHeader: React.FC<TaskHeaderProps> = ({
 						)}
 						)}
 					</div>
 					</div>
 				</div>
 				</div>
-			)}
+			)} */}
 		</div>
 		</div>
 	)
 	)
 }
 }

+ 7 - 38
webview-ui/src/components/WelcomeView.tsx

@@ -1,17 +1,14 @@
 import { VSCodeButton, VSCodeLink } from "@vscode/webview-ui-toolkit/react"
 import { VSCodeButton, VSCodeLink } from "@vscode/webview-ui-toolkit/react"
 import React, { useEffect, useState } from "react"
 import React, { useEffect, useState } from "react"
-import { getKoduSignInUrl } from "../../../src/shared/kodu"
 import { useExtensionState } from "../context/ExtensionStateContext"
 import { useExtensionState } from "../context/ExtensionStateContext"
 import { validateApiConfiguration } from "../utils/validate"
 import { validateApiConfiguration } from "../utils/validate"
 import { vscode } from "../utils/vscode"
 import { vscode } from "../utils/vscode"
 import ApiOptions from "./ApiOptions"
 import ApiOptions from "./ApiOptions"
 
 
-interface WelcomeViewProps {
-	setDidAuthKoduFromWelcome: React.Dispatch<React.SetStateAction<boolean>>
-}
+interface WelcomeViewProps {}
 
 
-const WelcomeView: React.FC<WelcomeViewProps> = ({ setDidAuthKoduFromWelcome }) => {
-	const { apiConfiguration, uriScheme } = useExtensionState()
+const WelcomeView: React.FC<WelcomeViewProps> = () => {
+	const { apiConfiguration } = useExtensionState()
 
 
 	const [apiErrorMessage, setApiErrorMessage] = useState<string | undefined>(undefined)
 	const [apiErrorMessage, setApiErrorMessage] = useState<string | undefined>(undefined)
 
 
@@ -41,39 +38,11 @@ const WelcomeView: React.FC<WelcomeViewProps> = ({ setDidAuthKoduFromWelcome })
 
 
 			<b>To get started, this extension needs an API provider for Claude 3.5 Sonnet.</b>
 			<b>To get started, this extension needs an API provider for Claude 3.5 Sonnet.</b>
 
 
-			<div
-				style={{
-					display: "flex",
-					alignItems: "center",
-					backgroundColor: "var(--vscode-editor-inactiveSelectionBackground)",
-					color: "var(--vscode-editor-foreground)",
-					padding: "6px 8px",
-					borderRadius: "3px",
-					margin: "8px 0px",
-					fontSize: "12px",
-				}}>
-				<i
-					className="codicon codicon-info"
-					style={{
-						marginRight: "6px",
-						fontSize: "16px",
-						color: "var(--vscode-infoIcon-foreground)",
-					}}></i>
-				<span>
-					Explore Claude's capabilities with $20 free credits from{" "}
-					<VSCodeLink href={getKoduSignInUrl(uriScheme)} style={{ display: "inline" }}>
-						Kodu
-					</VSCodeLink>
-				</span>
-			</div>
-
 			<div style={{ marginTop: "10px" }}>
 			<div style={{ marginTop: "10px" }}>
-				<ApiOptions showModelOptions={false} setDidAuthKodu={setDidAuthKoduFromWelcome} />
-				{apiConfiguration?.apiProvider !== "kodu" && (
-					<VSCodeButton onClick={handleSubmit} disabled={disableLetsGoButton} style={{ marginTop: "3px" }}>
-						Let's go!
-					</VSCodeButton>
-				)}
+				<ApiOptions showModelOptions={false} />
+				<VSCodeButton onClick={handleSubmit} disabled={disableLetsGoButton} style={{ marginTop: "3px" }}>
+					Let's go!
+				</VSCodeButton>
 			</div>
 			</div>
 		</div>
 		</div>
 	)
 	)

+ 0 - 9
webview-ui/src/context/ExtensionStateContext.tsx

@@ -20,7 +20,6 @@ export const ExtensionStateContextProvider: React.FC<{ children: React.ReactNode
 		claudeMessages: [],
 		claudeMessages: [],
 		taskHistory: [],
 		taskHistory: [],
 		shouldShowAnnouncement: false,
 		shouldShowAnnouncement: false,
-		shouldShowKoduPromo: false,
 	})
 	})
 	const [didHydrateState, setDidHydrateState] = useState(false)
 	const [didHydrateState, setDidHydrateState] = useState(false)
 
 
@@ -30,14 +29,6 @@ export const ExtensionStateContextProvider: React.FC<{ children: React.ReactNode
 			setState(message.state)
 			setState(message.state)
 			setDidHydrateState(true)
 			setDidHydrateState(true)
 		}
 		}
-		if (message.type === "action" && message.action) {
-			switch (message.action) {
-				case "koduCreditsFetched":
-					// special case where we only want to update one part of state in case user is in the middle of modifying settings
-					setState((prevState) => ({ ...prevState, koduCredits: message.state?.koduCredits }))
-					break
-			}
-		}
 	}, [])
 	}, [])
 
 
 	useEvent("message", handleMessage)
 	useEvent("message", handleMessage)

+ 0 - 5
webview-ui/src/utils/validate.ts

@@ -18,11 +18,6 @@ export function validateApiConfiguration(apiConfiguration?: ApiConfiguration): s
 					return "You must provide a valid API key or choose a different provider."
 					return "You must provide a valid API key or choose a different provider."
 				}
 				}
 				break
 				break
-			case "kodu":
-				if (!apiConfiguration.koduApiKey) {
-					return "You must sign in to Kodu to use it as an API provider."
-				}
-				break
 		}
 		}
 	}
 	}
 	return undefined
 	return undefined