Skip to content

Commit 2d714c5

Browse files
committed
updated settings tab
1 parent 313506f commit 2d714c5

File tree

18 files changed

+803
-551
lines changed

18 files changed

+803
-551
lines changed

extension-state-summary.md

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
# ExtensionStateContext State Management Summary
2+
3+
The ExtensionStateContext manages the state for the extension using Jotai, a state management library for React. Here's an overview of how the state is structured and managed:
4+
5+
## State Structure
6+
7+
The main state is contained in the `extensionStateAtom`, which is a derived atom combining various individual state atoms. The state includes:
8+
9+
1. version
10+
2. claudeMessages
11+
3. taskHistory
12+
4. useUdiff
13+
5. currentTask
14+
6. currentTaskId
15+
7. shouldShowAnnouncement
16+
8. shouldShowKoduPromo
17+
9. apiConfiguration
18+
10. uriScheme
19+
11. maxRequestsPerTask
20+
12. customInstructions
21+
13. fingerprint
22+
14. technicalBackground
23+
15. alwaysAllowReadOnly
24+
16. experimentalTerminal
25+
17. fpjsKey
26+
18. extensionName
27+
19. themeName
28+
20. user
29+
21. alwaysAllowWriteOnly
30+
22. creativeMode
31+
32+
## State Management
33+
34+
1. Individual atoms are created for each piece of state using `atom()` from Jotai.
35+
2. The `extensionStateAtom` combines all individual atoms into a single state object.
36+
3. The `ExtensionStateProvider` component manages the state updates:
37+
- It sets up event listeners for messages from the extension.
38+
- Updates the state based on received messages.
39+
- Provides the state to child components.
40+
4. The `useExtensionState` hook allows components to access the state and update functions:
41+
- It returns the current state and setter functions for various state properties.
42+
43+
## State Updates
44+
45+
State updates occur through:
46+
47+
1. Message events from the extension (e.g., 'claudeMessages', 'state', 'action').
48+
2. Setter functions provided by the `useExtensionState` hook.
49+
50+
This structure allows for centralized state management and easy access to state and update functions throughout the application.

extension/src/agent/v1/api-handler.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -159,9 +159,9 @@ ${this.customInstructions.trim()}
159159
} else {
160160
// reverse claude messages to find the last message that is typeof v1 and has apiMetrics
161161
const reversedClaudeMessages = claudeMessages?.slice().reverse()
162-
const lastV1Message = reversedClaudeMessages?.find((m) => isV1ClaudeMessage(m) && m.apiMetrics)
162+
const lastV1Message = reversedClaudeMessages?.find((m) => isV1ClaudeMessage(m) && m?.apiMetrics)
163163
if (lastV1Message) {
164-
apiMetrics = (lastV1Message as V1ClaudeMessage).apiMetrics!
164+
apiMetrics = (lastV1Message as V1ClaudeMessage)?.apiMetrics!
165165
}
166166
}
167167

extension/src/agent/v1/prompts/base-system.ts

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,16 @@ export const BASE_SYSTEM_PROMPT = async (
2424
- in your thinking, always write current step and next step, think like engineer so explain your thoughts in a way that is clear and concise, from technical perspective (ex: "I will read the file to understand the current state of the project, then I will use the execute_command tool to run the project locally").
2525
- if it logically make sense to do multiple tool calls in one request do it, but if it doesn't make sense just do one tool call per request.
2626
27+
====
28+
29+
RESPONSE FORMAT
30+
31+
You must always respond with the following format
32+
<format>
33+
... best instructions for Chain of Thought, with best practices and guidelines to prevent the ai
34+
</format>
35+
36+
2737
====
2838
2939
TOOL USE
@@ -263,7 +273,7 @@ RULES
263273
- You are STRICTLY FORBIDDEN from starting your messages with "Great", "Certainly", "Okay", "Sure". You should NOT be conversational in your responses, but rather direct and to the point. For example you should NOT say "Great, I've updated the CSS" but instead something like "I've updated the CSS". It is important you be clear and technical in your messages.
264274
- When presented with images, utilize your vision capabilities to thoroughly examine them and extract meaningful information. Incorporate these insights into your thought process as you accomplish the user's task.
265275
- At the end of each user message, you will automatically receive environment_details. This information is not written by the user themselves, but is auto-generated to provide potentially relevant context about the project structure and environment. While this information can be valuable for understanding the project context, do not treat it as a direct part of the user's request or response. Use it to inform your actions and decisions, but don't assume the user is explicitly asking about or referring to this information unless they clearly do so in their message. When using environment_details, explain your actions clearly to ensure the user understands, as they may not be aware of these details.
266-
- Before executing commands, check the "Actively Running Terminals" section in environment_details. If present, consider how these active processes might impact your task. For example, if a local development server is already running, you wouldn't need to start it again. If no active terminals are listed, proceed with command execution as normal.
276+
- Before executing commands, check the "Actively Running Terminals" section in <environment_details>. If present, consider how these active processes might impact your task. For example, if a local development server is already running, you wouldn't need to start it again. If no active terminals are listed, proceed with command execution as normal.
267277
268278
====
269279
@@ -337,7 +347,6 @@ Key notes:
337347
- you should never apologize to the user more than twice in a row, if you find yourself apologizing to the user more than twice in a row, it's a red flag that you are stuck in a loop.
338348
- Linting errors might presist in the chat, they aren't refreshed automatically, the only way to get the linting errors is by writing back to the file, only do it if it's absolutely necessary. otherwise ignore the linting errors and go forward with the task.
339349
</error_handling>
340-
341350
Critical instructions for using multiple tool calls in one request:
342351
<multiple_tool_calls>
343352
multiple tool calls in one request are allowed, but only if it logically makes sense to do so.

extension/src/agent/v1/tools/runners/execute-command.tool.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,7 @@ export class ExecuteCommandTool extends BaseAgentTool {
107107
const terminalInfo = await terminalManager.getOrCreateTerminal(this.cwd)
108108
console.log("Terminal created")
109109
terminalInfo.terminal.show() // weird visual bug when creating new terminals (even manually) where there's an empty space at the top.
110-
const process = terminalManager.runCommand(terminalInfo, command)
110+
const process = terminalManager.runCommand(terminalInfo, command, { autoClose: false })
111111
await delay(100)
112112

113113
let userFeedback: { text?: string; images?: string[] } | undefined

extension/src/api/index.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { Anthropic } from "@anthropic-ai/sdk"
2-
import { ApiModelId, ModelInfo } from "../shared/api"
2+
import { ApiModelId, KoduModelId, ModelInfo } from "../shared/api"
33
import { KoduHandler } from "./kodu"
44
import { AskConsultantResponseDto, SummaryResponseDto, WebSearchResponseDto } from "./interfaces"
55
import { z } from "zod"
@@ -14,7 +14,7 @@ export interface ApiHandlerMessageResponse {
1414

1515
export type ApiConfiguration = {
1616
koduApiKey?: string
17-
apiModelId?: ApiModelId
17+
apiModelId?: KoduModelId
1818
}
1919
export const bugReportSchema = z.object({
2020
description: z.string(),

extension/src/api/kodu.ts

Lines changed: 131 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,137 @@ export class KoduHandler implements ApiHandler {
9797
}
9898
}
9999

100+
async *createBaseMessageStream(
101+
systemPrompt: string,
102+
messages: Anthropic.Messages.MessageParam[],
103+
abortSignal?: AbortSignal | null,
104+
tempature?: number,
105+
top_p?: number
106+
): AsyncIterableIterator<koduSSEResponse> {
107+
const modelId = this.getModel().id
108+
let requestBody: Anthropic.Beta.PromptCaching.Messages.MessageCreateParamsNonStreaming
109+
110+
switch (modelId) {
111+
case "claude-3-5-sonnet-20240620":
112+
case "claude-3-opus-20240229":
113+
case "claude-3-haiku-20240307":
114+
console.log("Matched anthropic cache model")
115+
const userMsgIndices = messages.reduce(
116+
(acc, msg, index) => (msg.role === "user" ? [...acc, index] : acc),
117+
[] as number[]
118+
)
119+
const lastUserMsgIndex = userMsgIndices[userMsgIndices.length - 1] ?? -1
120+
const secondLastMsgUserIndex = userMsgIndices[userMsgIndices.length - 2] ?? -1
121+
requestBody = {
122+
model: modelId,
123+
max_tokens: this.getModel().info.maxTokens,
124+
system: systemPrompt,
125+
messages: messages.map((message, index) => {
126+
if (index === lastUserMsgIndex || index === secondLastMsgUserIndex) {
127+
return {
128+
...message,
129+
content:
130+
typeof message.content === "string"
131+
? [
132+
{
133+
type: "text",
134+
text: message.content,
135+
cache_control: { type: "ephemeral" },
136+
},
137+
]
138+
: message.content.map((content, contentIndex) =>
139+
contentIndex === message.content.length - 1
140+
? { ...content, cache_control: { type: "ephemeral" } }
141+
: content
142+
),
143+
}
144+
}
145+
return message
146+
}),
147+
}
148+
break
149+
default:
150+
console.log("Matched default model")
151+
requestBody = {
152+
model: modelId,
153+
max_tokens: this.getModel().info.maxTokens,
154+
system: [{ text: systemPrompt, type: "text" }],
155+
messages,
156+
temperature: tempature ?? 0.2,
157+
top_p: top_p ?? 0.8,
158+
}
159+
}
160+
this.cancelTokenSource = axios.CancelToken.source()
161+
162+
const response = await axios.post(
163+
getKoduInferenceUrl(),
164+
{
165+
...requestBody,
166+
},
167+
{
168+
headers: {
169+
"Content-Type": "application/json",
170+
"x-api-key": this.options.koduApiKey || "",
171+
},
172+
responseType: "stream",
173+
signal: abortSignal ?? undefined,
174+
timeout: 60_000,
175+
}
176+
)
177+
178+
if (response.status !== 200) {
179+
if (response.status in koduErrorMessages) {
180+
throw new KoduError({
181+
code: response.status as keyof typeof koduErrorMessages,
182+
})
183+
}
184+
throw new KoduError({
185+
code: KODU_ERROR_CODES.NETWORK_REFUSED_TO_CONNECT,
186+
})
187+
}
188+
189+
if (response.data) {
190+
const reader = response.data
191+
const decoder = new TextDecoder("utf-8")
192+
let finalResponse: Extract<koduSSEResponse, { code: 1 }> | null = null
193+
let partialResponse: Extract<koduSSEResponse, { code: 2 }> | null = null
194+
let buffer = ""
195+
196+
for await (const chunk of reader) {
197+
buffer += decoder.decode(chunk, { stream: true })
198+
const lines = buffer.split("\n\n")
199+
buffer = lines.pop() || ""
200+
for (const line of lines) {
201+
if (line.startsWith("data: ")) {
202+
const eventData = JSON.parse(line.slice(6)) as koduSSEResponse
203+
if (eventData.code === 2) {
204+
// -> Happens to the current message
205+
// We have a partial response, so we need to add it to the message shown to the user and refresh the UI
206+
}
207+
if (eventData.code === 0) {
208+
} else if (eventData.code === 1) {
209+
finalResponse = eventData
210+
} else if (eventData.code === -1) {
211+
console.error("Network / API ERROR")
212+
// we should yield the error and not throw it
213+
}
214+
yield eventData
215+
}
216+
}
217+
218+
if (finalResponse) {
219+
break
220+
}
221+
}
222+
223+
if (!finalResponse) {
224+
throw new KoduError({
225+
code: KODU_ERROR_CODES.NETWORK_REFUSED_TO_CONNECT,
226+
})
227+
}
228+
}
229+
}
230+
100231
async *createMessageStream(
101232
systemPrompt: string,
102233
messages: Anthropic.Messages.MessageParam[],
@@ -136,13 +267,6 @@ export class KoduHandler implements ApiHandler {
136267
console.error(`Length difference: ${previousSystemPrompt.length - systemPrompt.length}`)
137268
}
138269
previousSystemPrompt = systemPrompt
139-
// if (dotKoduFileContent) {
140-
// system.push({
141-
// text: dotKoduFileContent,
142-
// type: "text",
143-
// // cache_control: { type: "ephemeral" },
144-
// })
145-
// }
146270
if (customInstructions && customInstructions.trim()) {
147271
system.push({
148272
text: customInstructions,
@@ -152,20 +276,6 @@ export class KoduHandler implements ApiHandler {
152276
} else {
153277
system[0].cache_control = { type: "ephemeral" }
154278
}
155-
// if (environmentDetails) {
156-
// system.push({
157-
// text: environmentDetails,
158-
// type: "text",
159-
// })
160-
// }
161-
/**
162-
* push it last to not break the cache
163-
*/
164-
// system.push({
165-
// text: USER_TASK_HISTORY_PROMPT(userMemory),
166-
// type: "text",
167-
// cache_control: { type: "ephemeral" },
168-
// })
169279

170280
switch (modelId) {
171281
case "claude-3-5-sonnet-20240620":

extension/src/extension.ts

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ import * as dotenv from "dotenv"
77
import * as path from "path"
88
import { extensionName } from "./shared/Constants"
99
import "./utils/path-helpers"
10+
import { TerminalManager } from "./integrations/terminal/terminal-manager"
11+
import { getCwd } from "./agent/v1/utils"
1012

1113
/*
1214
Built using https://github.com/microsoft/vscode-webview-ui-toolkit
@@ -234,6 +236,49 @@ export function activate(context: vscode.ExtensionContext) {
234236
},
235237
})
236238
)
239+
240+
// Test the terminal manager
241+
// testTerminalManager()
242+
}
243+
244+
async function testTerminalManager() {
245+
console.log(`Testing terminal manager`)
246+
const terminalManager = new TerminalManager()
247+
248+
// Run a command
249+
const terminalInfo = await terminalManager.getOrCreateTerminal(getCwd())
250+
const process = terminalManager.runCommand(terminalInfo, "echo 25", { autoClose: false })
251+
252+
// Wait for the process to complete
253+
await process
254+
console.log(`CWD: ${terminalInfo.terminal.shellIntegration?.cwd}`)
255+
256+
// Retrieve all output
257+
const fullOutput = terminalManager.getFullOutput(terminalInfo.id)
258+
259+
// Retrieve unretrieved output without updating the retrieved index
260+
const unretrievedOutput = terminalManager.getUnretrievedOutput(terminalInfo.id, false)
261+
262+
// Retrieve partial output
263+
const partialOutput = terminalManager.getPartialOutput(terminalInfo.id, 2, 5)
264+
265+
// is terminal hot?
266+
267+
// try sending another 50 + 50 echo to the terminal
268+
const terminalInfo2 = await terminalManager.getOrCreateTerminal(getCwd())
269+
const process2 = terminalManager.runCommand(terminalInfo2, "echo 50", { autoClose: false })
270+
await process2
271+
console.log(`CWD2: ${terminalInfo.terminal.shellIntegration?.cwd}`)
272+
const fullOutput2 = terminalManager.getFullOutput(terminalInfo.id)
273+
274+
// Retrieve unretrieved output without updating the retrieved index
275+
const unretrievedOutput2 = terminalManager.getUnretrievedOutput(terminalInfo.id, false)
276+
277+
// Retrieve partial output
278+
const partialOutput2 = terminalManager.getPartialOutput(terminalInfo.id, 2, 5)
279+
280+
// is terminal hot?
281+
console.log("Is terminal hot?", terminalInfo.busy)
237282
}
238283

239284
// This method is called when your extension is deactivated

0 commit comments

Comments
 (0)