mirror of
https://github.com/RooVetGit/Roo-Code.git
synced 2026-09-06 08:18:39 +00:00
Merge main
This commit is contained in:
commit
3950949058
45 changed files with 2180 additions and 1750 deletions
5
.changeset/great-mice-turn.md
Normal file
5
.changeset/great-mice-turn.md
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
"roo-cline": patch
|
||||
---
|
||||
|
||||
Remove redundant zod schemas
|
||||
5
.changeset/sweet-bugs-glow.md
Normal file
5
.changeset/sweet-bugs-glow.md
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
"roo-cline": patch
|
||||
---
|
||||
|
||||
Automatically generate .d.ts from zod schemas
|
||||
5
.changeset/two-months-drop.md
Normal file
5
.changeset/two-months-drop.md
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
"roo-cline": patch
|
||||
---
|
||||
|
||||
Add config getters to RooCodeAPI
|
||||
|
|
@ -12,4 +12,12 @@ else
|
|||
npx_cmd="npx"
|
||||
fi
|
||||
|
||||
"$npx_cmd" lint-staged
|
||||
npm run generate-types
|
||||
|
||||
if [ -n "$(git diff --name-only src/exports/roo-code.d.ts)" ]; then
|
||||
echo "Error: There are unstaged changes to roo-code.d.ts after running 'npm run generate-types'."
|
||||
echo "Please review and stage the changes before committing."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
"$npx_cmd" lint-staged
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ type WaitForOptions = {
|
|||
|
||||
export const waitFor = (
|
||||
condition: (() => Promise<boolean>) | (() => boolean),
|
||||
{ timeout = 30_000, interval = 250 }: WaitForOptions = {},
|
||||
{ timeout = 60_000, interval = 250 }: WaitForOptions = {},
|
||||
) => {
|
||||
let timeoutId: NodeJS.Timeout | undefined = undefined
|
||||
|
||||
|
|
|
|||
|
|
@ -348,10 +348,12 @@
|
|||
"clean:extension": "rimraf bin dist out",
|
||||
"clean:webview": "cd webview-ui && npm run clean",
|
||||
"clean:e2e": "cd e2e && npm run clean",
|
||||
"clean:benchmark": "cd benchmark && npm run clean",
|
||||
"vscode-test": "npm-run-all -l -p vscode-test:*",
|
||||
"vscode-test:extension": "tsc -p . --outDir out && node esbuild.js",
|
||||
"vscode-test:webview": "cd webview-ui && npm run build",
|
||||
"update-contributors": "node scripts/update-contributors.js"
|
||||
"update-contributors": "node scripts/update-contributors.js",
|
||||
"generate-types": "tsx scripts/generate-types.mts"
|
||||
},
|
||||
"dependencies": {
|
||||
"@anthropic-ai/bedrock-sdk": "^0.10.2",
|
||||
|
|
|
|||
26
scripts/generate-types.mts
Normal file
26
scripts/generate-types.mts
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
import fs from "fs/promises"
|
||||
|
||||
import { zodToTs, createTypeAlias, printNode } from "zod-to-ts"
|
||||
import { $ } from "execa"
|
||||
|
||||
import { typeDefinitions } from "../src/schemas"
|
||||
|
||||
async function main() {
|
||||
const types: string[] = [
|
||||
"// This file is automatically generated by running `npm run generate-types`\n// Do not edit it directly.",
|
||||
]
|
||||
|
||||
for (const { schema, identifier } of typeDefinitions) {
|
||||
types.push(printNode(createTypeAlias(zodToTs(schema, identifier).node, identifier)))
|
||||
types.push(`export type { ${identifier} }`)
|
||||
}
|
||||
|
||||
await fs.writeFile("src/exports/types.ts", types.join("\n\n"))
|
||||
|
||||
await $`npx tsup src/exports/interface.ts --dts-only -d out`
|
||||
await fs.copyFile('out/interface.d.ts', 'src/exports/roo-code.d.ts')
|
||||
|
||||
await $`npx prettier --write src/exports/types.ts src/exports/roo-code.d.ts`
|
||||
}
|
||||
|
||||
main()
|
||||
|
|
@ -1,14 +1,20 @@
|
|||
function pWaitFor(condition, options = {}) {
|
||||
return new Promise((resolve, reject) => {
|
||||
let timeout
|
||||
|
||||
const interval = setInterval(() => {
|
||||
if (condition()) {
|
||||
if (timeout) {
|
||||
clearTimeout(timeout)
|
||||
}
|
||||
|
||||
clearInterval(interval)
|
||||
resolve()
|
||||
}
|
||||
}, options.interval || 20)
|
||||
|
||||
if (options.timeout) {
|
||||
setTimeout(() => {
|
||||
timeout = setTimeout(() => {
|
||||
clearInterval(interval)
|
||||
reject(new Error("Timed out"))
|
||||
}, options.timeout)
|
||||
|
|
|
|||
|
|
@ -11,9 +11,8 @@ import pWaitFor from "p-wait-for"
|
|||
import getFolderSize from "get-folder-size"
|
||||
import { serializeError } from "serialize-error"
|
||||
import * as vscode from "vscode"
|
||||
import { isPathOutsideWorkspace } from "../utils/pathUtils"
|
||||
|
||||
import { TokenUsage } from "../exports/roo-code"
|
||||
import { TokenUsage } from "../schemas"
|
||||
import { ApiHandler, buildApiHandler } from "../api"
|
||||
import { ApiStream } from "../api/transform/stream"
|
||||
import { DIFF_VIEW_URI_SCHEME, DiffViewProvider } from "../integrations/editor/DiffViewProvider"
|
||||
|
|
@ -65,6 +64,7 @@ import { defaultModeSlug, getModeBySlug, getFullModeDetails } from "../shared/mo
|
|||
import { EXPERIMENT_IDS, experiments as Experiments, ExperimentId } from "../shared/experiments"
|
||||
import { calculateApiCostAnthropic } from "../utils/cost"
|
||||
import { fileExistsAtPath } from "../utils/fs"
|
||||
import { isPathOutsideWorkspace } from "../utils/pathUtils"
|
||||
import { arePathsEqual, getReadablePath } from "../utils/path"
|
||||
import { parseMentions } from "./mentions"
|
||||
import { RooIgnoreController } from "./ignore/RooIgnoreController"
|
||||
|
|
@ -123,10 +123,7 @@ export type ClineOptions = {
|
|||
export class Cline extends EventEmitter<ClineEvents> {
|
||||
readonly taskId: string
|
||||
readonly instanceId: string
|
||||
get cwd() {
|
||||
return getWorkspacePath(path.join(os.homedir(), "Desktop"))
|
||||
}
|
||||
// Subtasks
|
||||
|
||||
readonly rootTask: Cline | undefined = undefined
|
||||
readonly parentTask: Cline | undefined = undefined
|
||||
readonly taskNumber: number
|
||||
|
|
@ -268,6 +265,10 @@ export class Cline extends EventEmitter<ClineEvents> {
|
|||
return [instance, promise]
|
||||
}
|
||||
|
||||
get cwd() {
|
||||
return getWorkspacePath(path.join(os.homedir(), "Desktop"))
|
||||
}
|
||||
|
||||
// Add method to update diffStrategy
|
||||
async updateDiffStrategy(experimentalDiffStrategy?: boolean, multiSearchReplaceDiffStrategy?: boolean) {
|
||||
// If not provided, get from current state
|
||||
|
|
@ -334,6 +335,7 @@ export class Cline extends EventEmitter<ClineEvents> {
|
|||
|
||||
private async getSavedClineMessages(): Promise<ClineMessage[]> {
|
||||
const filePath = path.join(await this.ensureTaskDirectoryExists(), GlobalFileNames.uiMessages)
|
||||
|
||||
if (await fileExistsAtPath(filePath)) {
|
||||
return JSON.parse(await fs.readFile(filePath, "utf8"))
|
||||
} else {
|
||||
|
|
@ -1222,11 +1224,12 @@ export class Cline extends EventEmitter<ClineEvents> {
|
|||
}
|
||||
return { role, content }
|
||||
})
|
||||
|
||||
const stream = this.api.createMessage(systemPrompt, cleanConversationHistory)
|
||||
const iterator = stream[Symbol.asyncIterator]()
|
||||
|
||||
try {
|
||||
// awaiting first chunk to see if it will throw an error
|
||||
// Awaiting first chunk to see if it will throw an error.
|
||||
this.isWaitingForFirstChunk = true
|
||||
const firstChunk = await iterator.next()
|
||||
yield firstChunk.value
|
||||
|
|
@ -3392,6 +3395,7 @@ export class Cline extends EventEmitter<ClineEvents> {
|
|||
? `This may indicate a failure in his thought process or inability to use a tool properly, which can be mitigated with some user guidance (e.g. "Try breaking down the task into smaller steps").`
|
||||
: "Roo Code uses complex prompts and iterative task execution that may be challenging for less capable models. For best results, it's recommended to use Claude 3.7 Sonnet for its advanced agentic coding capabilities.",
|
||||
)
|
||||
|
||||
if (response === "messageResponse") {
|
||||
userContent.push(
|
||||
...[
|
||||
|
|
@ -3455,9 +3459,11 @@ export class Cline extends EventEmitter<ClineEvents> {
|
|||
|
||||
// since we sent off a placeholder api_req_started message to update the webview while waiting to actually start the API request (to load potential details for example), we need to update the text of that message
|
||||
const lastApiReqIndex = findLastIndex(this.clineMessages, (m) => m.say === "api_req_started")
|
||||
|
||||
this.clineMessages[lastApiReqIndex].text = JSON.stringify({
|
||||
request: userContent.map((block) => formatContentBlockToMarkdown(block)).join("\n\n"),
|
||||
} satisfies ClineApiReqInfo)
|
||||
|
||||
await this.saveClineMessages()
|
||||
await this.providerRef.deref()?.postStateToWebview()
|
||||
|
||||
|
|
@ -3499,6 +3505,7 @@ export class Cline extends EventEmitter<ClineEvents> {
|
|||
|
||||
// if last message is a partial we need to update and save it
|
||||
const lastMessage = this.clineMessages.at(-1)
|
||||
|
||||
if (lastMessage && lastMessage.partial) {
|
||||
// lastMessage.ts = Date.now() DO NOT update ts since it is used as a key for virtuoso list
|
||||
lastMessage.partial = false
|
||||
|
|
@ -3544,7 +3551,10 @@ export class Cline extends EventEmitter<ClineEvents> {
|
|||
this.presentAssistantMessageHasPendingUpdates = false
|
||||
await this.diffViewProvider.reset()
|
||||
|
||||
const stream = this.attemptApiRequest(previousApiReqIndex) // yields only if the first chunk is successful, otherwise will allow the user to retry the request (most likely due to rate limit error, which gets thrown on the first chunk)
|
||||
// Yields only if the first chunk is successful, otherwise will
|
||||
// allow the user to retry the request (most likely due to rate
|
||||
// limit error, which gets thrown on the first chunk).
|
||||
const stream = this.attemptApiRequest(previousApiReqIndex)
|
||||
let assistantMessage = ""
|
||||
let reasoningMessage = ""
|
||||
this.isStreaming = true
|
||||
|
|
@ -3552,9 +3562,10 @@ export class Cline extends EventEmitter<ClineEvents> {
|
|||
try {
|
||||
for await (const chunk of stream) {
|
||||
if (!chunk) {
|
||||
// Sometimes chunk is undefined, no idea that can cause it, but this workaround seems to fix it
|
||||
// Sometimes chunk is undefined, no idea that can cause it, but this workaround seems to fix it.
|
||||
continue
|
||||
}
|
||||
|
||||
switch (chunk.type) {
|
||||
case "reasoning":
|
||||
reasoningMessage += chunk.text
|
||||
|
|
@ -3610,11 +3621,14 @@ export class Cline extends EventEmitter<ClineEvents> {
|
|||
// abandoned happens when extension is no longer waiting for the cline instance to finish aborting (error is thrown here when any function in the for loop throws due to this.abort)
|
||||
if (!this.abandoned) {
|
||||
this.abortTask() // if the stream failed, there's various states the task could be in (i.e. could have streamed some tools the user may have executed), so we just resort to replicating a cancel task
|
||||
|
||||
await abortStream(
|
||||
"streaming_failed",
|
||||
error.message ?? JSON.stringify(serializeError(error), null, 2),
|
||||
)
|
||||
|
||||
const history = await this.providerRef.deref()?.getTaskWithId(this.taskId)
|
||||
|
||||
if (history) {
|
||||
await this.providerRef.deref()?.initClineWithHistoryItem(history.historyItem)
|
||||
// await this.providerRef.deref()?.postStateToWebview()
|
||||
|
|
@ -4092,7 +4106,9 @@ export class Cline extends EventEmitter<ClineEvents> {
|
|||
})
|
||||
|
||||
service.initShadowGit().catch((err) => {
|
||||
log("[Cline#initializeCheckpoints] caught unexpected error in initShadowGit, disabling checkpoints")
|
||||
log(
|
||||
`[Cline#initializeCheckpoints] caught unexpected error in initShadowGit, disabling checkpoints (${err.message})`,
|
||||
)
|
||||
console.error(err)
|
||||
this.enableCheckpoints = false
|
||||
})
|
||||
|
|
|
|||
|
|
@ -1,67 +1,21 @@
|
|||
// npx jest src/core/__tests__/Cline.test.ts
|
||||
|
||||
import * as os from "os"
|
||||
import * as path from "path"
|
||||
|
||||
import pWaitFor from "p-wait-for"
|
||||
import * as vscode from "vscode"
|
||||
import { Anthropic } from "@anthropic-ai/sdk"
|
||||
|
||||
import { GlobalState } from "../../schemas"
|
||||
import { Cline } from "../Cline"
|
||||
import { ClineProvider } from "../webview/ClineProvider"
|
||||
import { ApiConfiguration, ModelInfo } from "../../shared/api"
|
||||
import { ApiStreamChunk } from "../../api/transform/stream"
|
||||
import { Anthropic } from "@anthropic-ai/sdk"
|
||||
import * as vscode from "vscode"
|
||||
import * as os from "os"
|
||||
import * as path from "path"
|
||||
|
||||
// Mock RooIgnoreController
|
||||
jest.mock("../ignore/RooIgnoreController")
|
||||
|
||||
// Mock all MCP-related modules
|
||||
jest.mock(
|
||||
"@modelcontextprotocol/sdk/types.js",
|
||||
() => ({
|
||||
CallToolResultSchema: {},
|
||||
ListResourcesResultSchema: {},
|
||||
ListResourceTemplatesResultSchema: {},
|
||||
ListToolsResultSchema: {},
|
||||
ReadResourceResultSchema: {},
|
||||
ErrorCode: {
|
||||
InvalidRequest: "InvalidRequest",
|
||||
MethodNotFound: "MethodNotFound",
|
||||
InternalError: "InternalError",
|
||||
},
|
||||
McpError: class McpError extends Error {
|
||||
code: string
|
||||
constructor(code: string, message: string) {
|
||||
super(message)
|
||||
this.code = code
|
||||
this.name = "McpError"
|
||||
}
|
||||
},
|
||||
}),
|
||||
{ virtual: true },
|
||||
)
|
||||
|
||||
jest.mock(
|
||||
"@modelcontextprotocol/sdk/client/index.js",
|
||||
() => ({
|
||||
Client: jest.fn().mockImplementation(() => ({
|
||||
connect: jest.fn().mockResolvedValue(undefined),
|
||||
close: jest.fn().mockResolvedValue(undefined),
|
||||
listTools: jest.fn().mockResolvedValue({ tools: [] }),
|
||||
callTool: jest.fn().mockResolvedValue({ content: [] }),
|
||||
})),
|
||||
}),
|
||||
{ virtual: true },
|
||||
)
|
||||
|
||||
jest.mock(
|
||||
"@modelcontextprotocol/sdk/client/stdio.js",
|
||||
() => ({
|
||||
StdioClientTransport: jest.fn().mockImplementation(() => ({
|
||||
connect: jest.fn().mockResolvedValue(undefined),
|
||||
close: jest.fn().mockResolvedValue(undefined),
|
||||
})),
|
||||
}),
|
||||
{ virtual: true },
|
||||
)
|
||||
|
||||
// Mock fileExistsAtPath
|
||||
jest.mock("../../utils/fs", () => ({
|
||||
fileExistsAtPath: jest.fn().mockImplementation((filePath) => {
|
||||
|
|
@ -174,6 +128,7 @@ jest.mock("vscode", () => {
|
|||
stat: jest.fn().mockResolvedValue({ type: 1 }), // FileType.File = 1
|
||||
},
|
||||
onDidSaveTextDocument: jest.fn(() => mockDisposable),
|
||||
getConfiguration: jest.fn(() => ({ get: (key: string, defaultValue: any) => defaultValue })),
|
||||
},
|
||||
env: {
|
||||
uriScheme: "vscode",
|
||||
|
|
@ -193,40 +148,6 @@ jest.mock("p-wait-for", () => ({
|
|||
default: jest.fn().mockImplementation(async () => Promise.resolve()),
|
||||
}))
|
||||
|
||||
jest.mock("delay", () => ({
|
||||
__esModule: true,
|
||||
default: jest.fn().mockImplementation(async () => Promise.resolve()),
|
||||
}))
|
||||
|
||||
jest.mock("serialize-error", () => ({
|
||||
__esModule: true,
|
||||
serializeError: jest.fn().mockImplementation((error) => ({
|
||||
name: error.name,
|
||||
message: error.message,
|
||||
stack: error.stack,
|
||||
})),
|
||||
}))
|
||||
|
||||
jest.mock("strip-ansi", () => ({
|
||||
__esModule: true,
|
||||
default: jest.fn().mockImplementation((str) => str.replace(/\u001B\[\d+m/g, "")),
|
||||
}))
|
||||
|
||||
jest.mock("globby", () => ({
|
||||
__esModule: true,
|
||||
globby: jest.fn().mockImplementation(async () => []),
|
||||
}))
|
||||
|
||||
jest.mock("os-name", () => ({
|
||||
__esModule: true,
|
||||
default: jest.fn().mockReturnValue("Mock OS Name"),
|
||||
}))
|
||||
|
||||
jest.mock("default-shell", () => ({
|
||||
__esModule: true,
|
||||
default: "/bin/bash", // Mock default shell path
|
||||
}))
|
||||
|
||||
describe("Cline", () => {
|
||||
let mockProvider: jest.Mocked<ClineProvider>
|
||||
let mockApiConfig: ApiConfiguration
|
||||
|
|
@ -238,9 +159,10 @@ describe("Cline", () => {
|
|||
const storageUri = {
|
||||
fsPath: path.join(os.tmpdir(), "test-storage"),
|
||||
}
|
||||
|
||||
mockExtensionContext = {
|
||||
globalState: {
|
||||
get: jest.fn().mockImplementation((key) => {
|
||||
get: jest.fn().mockImplementation((key: keyof GlobalState) => {
|
||||
if (key === "taskHistory") {
|
||||
return [
|
||||
{
|
||||
|
|
@ -256,6 +178,7 @@ describe("Cline", () => {
|
|||
},
|
||||
]
|
||||
}
|
||||
|
||||
return undefined
|
||||
}),
|
||||
update: jest.fn().mockImplementation((key, value) => Promise.resolve()),
|
||||
|
|
@ -336,80 +259,69 @@ describe("Cline", () => {
|
|||
|
||||
describe("constructor", () => {
|
||||
it("should respect provided settings", async () => {
|
||||
const [cline, task] = Cline.create({
|
||||
const cline = new Cline({
|
||||
provider: mockProvider,
|
||||
apiConfiguration: mockApiConfig,
|
||||
customInstructions: "custom instructions",
|
||||
fuzzyMatchThreshold: 0.95,
|
||||
task: "test task",
|
||||
startTask: false,
|
||||
})
|
||||
|
||||
expect(cline.customInstructions).toBe("custom instructions")
|
||||
expect(cline.diffEnabled).toBe(false)
|
||||
|
||||
await cline.abortTask(true)
|
||||
await task.catch(() => {})
|
||||
})
|
||||
|
||||
it("should use default fuzzy match threshold when not provided", async () => {
|
||||
const [cline, task] = await Cline.create({
|
||||
const cline = new Cline({
|
||||
provider: mockProvider,
|
||||
apiConfiguration: mockApiConfig,
|
||||
customInstructions: "custom instructions",
|
||||
enableDiff: true,
|
||||
fuzzyMatchThreshold: 0.95,
|
||||
task: "test task",
|
||||
startTask: false,
|
||||
})
|
||||
|
||||
expect(cline.diffEnabled).toBe(true)
|
||||
// The diff strategy should be created with default threshold (1.0)
|
||||
expect(cline.diffStrategy).toBeDefined()
|
||||
|
||||
await cline.abortTask(true)
|
||||
await task.catch(() => {})
|
||||
// The diff strategy should be created with default threshold (1.0).
|
||||
expect(cline.diffStrategy).toBeDefined()
|
||||
})
|
||||
|
||||
it("should use provided fuzzy match threshold", async () => {
|
||||
const getDiffStrategySpy = jest.spyOn(require("../diff/DiffStrategy"), "getDiffStrategy")
|
||||
|
||||
const [cline, task] = await Cline.create({
|
||||
const cline = new Cline({
|
||||
provider: mockProvider,
|
||||
apiConfiguration: mockApiConfig,
|
||||
customInstructions: "custom instructions",
|
||||
enableDiff: true,
|
||||
fuzzyMatchThreshold: 0.9,
|
||||
task: "test task",
|
||||
startTask: false,
|
||||
})
|
||||
|
||||
expect(cline.diffEnabled).toBe(true)
|
||||
expect(cline.diffStrategy).toBeDefined()
|
||||
expect(getDiffStrategySpy).toHaveBeenCalledWith("claude-3-5-sonnet-20241022", 0.9, false, false)
|
||||
|
||||
getDiffStrategySpy.mockRestore()
|
||||
|
||||
await cline.abortTask(true)
|
||||
await task.catch(() => {})
|
||||
})
|
||||
|
||||
it("should pass default threshold to diff strategy when not provided", async () => {
|
||||
const getDiffStrategySpy = jest.spyOn(require("../diff/DiffStrategy"), "getDiffStrategy")
|
||||
|
||||
const [cline, task] = Cline.create({
|
||||
const cline = new Cline({
|
||||
provider: mockProvider,
|
||||
apiConfiguration: mockApiConfig,
|
||||
customInstructions: "custom instructions",
|
||||
enableDiff: true,
|
||||
task: "test task",
|
||||
startTask: false,
|
||||
})
|
||||
|
||||
expect(cline.diffEnabled).toBe(true)
|
||||
expect(cline.diffStrategy).toBeDefined()
|
||||
expect(getDiffStrategySpy).toHaveBeenCalledWith("claude-3-5-sonnet-20241022", 1.0, false, false)
|
||||
|
||||
getDiffStrategySpy.mockRestore()
|
||||
|
||||
await cline.abortTask(true)
|
||||
await task.catch(() => {})
|
||||
})
|
||||
|
||||
it("should require either task or historyItem", () => {
|
||||
|
|
@ -464,22 +376,20 @@ describe("Cline", () => {
|
|||
})
|
||||
|
||||
it("should include timezone information in environment details", async () => {
|
||||
const [cline, task] = Cline.create({
|
||||
const cline = new Cline({
|
||||
provider: mockProvider,
|
||||
apiConfiguration: mockApiConfig,
|
||||
task: "test task",
|
||||
startTask: false,
|
||||
})
|
||||
|
||||
const details = await cline["getEnvironmentDetails"](false)
|
||||
|
||||
// Verify timezone information is present and formatted correctly
|
||||
// Verify timezone information is present and formatted correctly.
|
||||
expect(details).toContain("America/Los_Angeles")
|
||||
expect(details).toMatch(/UTC-7:00/) // Fixed offset for America/Los_Angeles
|
||||
expect(details).toMatch(/UTC-7:00/) // Fixed offset for America/Los_Angeles.
|
||||
expect(details).toContain("# Current Time")
|
||||
expect(details).toMatch(/1\/1\/2024.*5:00:00 AM.*\(America\/Los_Angeles, UTC-7:00\)/) // Full time string format
|
||||
|
||||
await cline.abortTask(true)
|
||||
await task.catch(() => {})
|
||||
expect(details).toMatch(/1\/1\/2024.*5:00:00 AM.*\(America\/Los_Angeles, UTC-7:00\)/) // Full time string format.
|
||||
})
|
||||
|
||||
describe("API conversation handling", () => {
|
||||
|
|
@ -493,24 +403,22 @@ describe("Cline", () => {
|
|||
cline.abandoned = true
|
||||
await task
|
||||
|
||||
// Mock the API's createMessage method to capture the conversation history
|
||||
const createMessageSpy = jest.fn()
|
||||
// Set up mock stream
|
||||
// Set up mock stream.
|
||||
const mockStreamForClean = (async function* () {
|
||||
yield { type: "text", text: "test response" }
|
||||
})()
|
||||
|
||||
// Set up spy
|
||||
// Set up spy.
|
||||
const cleanMessageSpy = jest.fn().mockReturnValue(mockStreamForClean)
|
||||
jest.spyOn(cline.api, "createMessage").mockImplementation(cleanMessageSpy)
|
||||
|
||||
// Mock getEnvironmentDetails to return empty details
|
||||
// Mock getEnvironmentDetails to return empty details.
|
||||
jest.spyOn(cline as any, "getEnvironmentDetails").mockResolvedValue("")
|
||||
|
||||
// Mock loadContext to return unmodified content
|
||||
// Mock loadContext to return unmodified content.
|
||||
jest.spyOn(cline as any, "loadContext").mockImplementation(async (content) => [content, ""])
|
||||
|
||||
// Add test message to conversation history
|
||||
// Add test message to conversation history.
|
||||
cline.apiConversationHistory = [
|
||||
{
|
||||
role: "user" as const,
|
||||
|
|
@ -533,6 +441,7 @@ describe("Cline", () => {
|
|||
ts: Date.now(),
|
||||
extraProp: "should be removed",
|
||||
}
|
||||
|
||||
cline.apiConversationHistory = [messageWithExtra]
|
||||
|
||||
// Trigger an API request
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
import { Mode, isToolAllowedForMode, getModeConfig, modes } from "../../shared/modes"
|
||||
import { validateToolUse } from "../mode-validator"
|
||||
import { isToolAllowedForMode, getModeConfig, modes, ModeConfig } from "../../shared/modes"
|
||||
import { TOOL_GROUPS } from "../../shared/tool-groups"
|
||||
import { validateToolUse } from "../mode-validator"
|
||||
|
||||
const [codeMode, architectMode, askMode] = modes.map((mode) => mode.slug)
|
||||
|
||||
describe("mode-validator", () => {
|
||||
|
|
@ -49,7 +50,7 @@ describe("mode-validator", () => {
|
|||
|
||||
describe("custom modes", () => {
|
||||
it("allows tools from custom mode configuration", () => {
|
||||
const customModes = [
|
||||
const customModes: ModeConfig[] = [
|
||||
{
|
||||
slug: "custom-mode",
|
||||
name: "Custom Mode",
|
||||
|
|
@ -65,7 +66,7 @@ describe("mode-validator", () => {
|
|||
})
|
||||
|
||||
it("allows custom mode to override built-in mode", () => {
|
||||
const customModes = [
|
||||
const customModes: ModeConfig[] = [
|
||||
{
|
||||
slug: codeMode,
|
||||
name: "Custom Code Mode",
|
||||
|
|
@ -80,7 +81,7 @@ describe("mode-validator", () => {
|
|||
})
|
||||
|
||||
it("respects tool requirements in custom modes", () => {
|
||||
const customModes = [
|
||||
const customModes: ModeConfig[] = [
|
||||
{
|
||||
slug: "custom-mode",
|
||||
name: "Custom Mode",
|
||||
|
|
|
|||
|
|
@ -1,25 +1,27 @@
|
|||
import * as vscode from "vscode"
|
||||
|
||||
import { logger } from "../../utils/logging"
|
||||
import type {
|
||||
ProviderSettings,
|
||||
RooCodeSettings,
|
||||
RooCodeSettingsKey,
|
||||
GlobalStateKey,
|
||||
GlobalState,
|
||||
SecretStateKey,
|
||||
SecretState,
|
||||
GlobalSettings,
|
||||
} from "../../exports/roo-code"
|
||||
import {
|
||||
PROVIDER_SETTINGS_KEYS,
|
||||
GLOBAL_STATE_KEYS,
|
||||
SECRET_STATE_KEYS,
|
||||
isSecretStateKey,
|
||||
isPassThroughStateKey,
|
||||
globalSettingsSchema,
|
||||
ProviderSettings,
|
||||
providerSettingsSchema,
|
||||
} from "../../shared/globalState"
|
||||
GlobalSettings,
|
||||
globalSettingsSchema,
|
||||
RooCodeSettings,
|
||||
SECRET_STATE_KEYS,
|
||||
SecretState,
|
||||
isSecretStateKey,
|
||||
GLOBAL_STATE_KEYS,
|
||||
GlobalState,
|
||||
} from "../../schemas"
|
||||
import { logger } from "../../utils/logging"
|
||||
|
||||
type GlobalStateKey = keyof GlobalState
|
||||
type SecretStateKey = keyof SecretState
|
||||
type RooCodeSettingsKey = keyof RooCodeSettings
|
||||
|
||||
const PASS_THROUGH_STATE_KEYS = ["taskHistory"]
|
||||
|
||||
export const isPassThroughStateKey = (key: string) => PASS_THROUGH_STATE_KEYS.includes(key)
|
||||
|
||||
const globalSettingsExportSchema = globalSettingsSchema.omit({
|
||||
taskHistory: true,
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import * as vscode from "vscode"
|
||||
import * as path from "path"
|
||||
import * as fs from "fs/promises"
|
||||
import { CustomModesSettingsSchema } from "./CustomModesSchema"
|
||||
import { customModesSettingsSchema } from "../../schemas"
|
||||
import { ModeConfig } from "../../shared/modes"
|
||||
import { fileExistsAtPath } from "../../utils/fs"
|
||||
import { arePathsEqual, getWorkspacePath } from "../../utils/path"
|
||||
|
|
@ -62,7 +62,7 @@ export class CustomModesManager {
|
|||
try {
|
||||
const content = await fs.readFile(filePath, "utf-8")
|
||||
const settings = JSON.parse(content)
|
||||
const result = CustomModesSettingsSchema.safeParse(settings)
|
||||
const result = customModesSettingsSchema.safeParse(settings)
|
||||
if (!result.success) {
|
||||
return []
|
||||
}
|
||||
|
|
@ -144,7 +144,8 @@ export class CustomModesManager {
|
|||
return
|
||||
}
|
||||
|
||||
const result = CustomModesSettingsSchema.safeParse(config)
|
||||
const result = customModesSettingsSchema.safeParse(config)
|
||||
|
||||
if (!result.success) {
|
||||
vscode.window.showErrorMessage(errorMessage)
|
||||
return
|
||||
|
|
|
|||
|
|
@ -1,82 +0,0 @@
|
|||
import { z } from "zod"
|
||||
import { ModeConfig } from "../../shared/modes"
|
||||
import { TOOL_GROUPS, ToolGroup } from "../../shared/tool-groups"
|
||||
|
||||
// Create a schema for valid tool groups using the keys of TOOL_GROUPS
|
||||
const ToolGroupSchema = z.enum(Object.keys(TOOL_GROUPS) as [ToolGroup, ...ToolGroup[]])
|
||||
|
||||
// Schema for group options with regex validation
|
||||
const GroupOptionsSchema = z.object({
|
||||
fileRegex: z
|
||||
.string()
|
||||
.optional()
|
||||
.refine(
|
||||
(pattern) => {
|
||||
if (!pattern) return true // Optional, so empty is valid
|
||||
try {
|
||||
new RegExp(pattern)
|
||||
return true
|
||||
} catch {
|
||||
return false
|
||||
}
|
||||
},
|
||||
{ message: "Invalid regular expression pattern" },
|
||||
),
|
||||
description: z.string().optional(),
|
||||
})
|
||||
|
||||
// Schema for a group entry - either a tool group string or a tuple of [group, options]
|
||||
const GroupEntrySchema = z.union([ToolGroupSchema, z.tuple([ToolGroupSchema, GroupOptionsSchema])])
|
||||
|
||||
// Schema for array of groups
|
||||
const GroupsArraySchema = z.array(GroupEntrySchema).refine(
|
||||
(groups) => {
|
||||
const seen = new Set()
|
||||
return groups.every((group) => {
|
||||
// For tuples, check the group name (first element)
|
||||
const groupName = Array.isArray(group) ? group[0] : group
|
||||
if (seen.has(groupName)) return false
|
||||
seen.add(groupName)
|
||||
return true
|
||||
})
|
||||
},
|
||||
{ message: "Duplicate groups are not allowed" },
|
||||
)
|
||||
|
||||
// Schema for mode configuration
|
||||
export const CustomModeSchema = z.object({
|
||||
slug: z.string().regex(/^[a-zA-Z0-9-]+$/, "Slug must contain only letters numbers and dashes"),
|
||||
name: z.string().min(1, "Name is required"),
|
||||
roleDefinition: z.string().min(1, "Role definition is required"),
|
||||
customInstructions: z.string().optional(),
|
||||
groups: GroupsArraySchema,
|
||||
}) satisfies z.ZodType<ModeConfig>
|
||||
|
||||
// Schema for the entire custom modes settings file
|
||||
export const CustomModesSettingsSchema = z.object({
|
||||
customModes: z.array(CustomModeSchema).refine(
|
||||
(modes) => {
|
||||
const slugs = new Set()
|
||||
return modes.every((mode) => {
|
||||
if (slugs.has(mode.slug)) {
|
||||
return false
|
||||
}
|
||||
slugs.add(mode.slug)
|
||||
return true
|
||||
})
|
||||
},
|
||||
{
|
||||
message: "Duplicate mode slugs are not allowed",
|
||||
},
|
||||
),
|
||||
})
|
||||
|
||||
export type CustomModesSettings = z.infer<typeof CustomModesSettingsSchema>
|
||||
|
||||
/**
|
||||
* Validates a custom mode configuration against the schema
|
||||
* @throws {z.ZodError} if validation fails
|
||||
*/
|
||||
export function validateCustomMode(mode: unknown): asserts mode is ModeConfig {
|
||||
CustomModeSchema.parse(mode)
|
||||
}
|
||||
|
|
@ -1,9 +1,8 @@
|
|||
import { ExtensionContext } from "vscode"
|
||||
import { z } from "zod"
|
||||
|
||||
import { providerSettingsSchema } from "../../shared/globalState"
|
||||
import { providerSettingsSchema, ApiConfigMeta } from "../../schemas"
|
||||
import { Mode } from "../../shared/modes"
|
||||
import { ApiConfigMeta } from "../../shared/ExtensionMessage"
|
||||
|
||||
const providerSettingsWithIdSchema = providerSettingsSchema.extend({ id: z.string().optional() })
|
||||
|
||||
|
|
|
|||
|
|
@ -1,12 +1,9 @@
|
|||
// npx jest src/core/config/__tests__/ContextProxy.test.ts
|
||||
|
||||
import fs from "fs/promises"
|
||||
|
||||
import * as vscode from "vscode"
|
||||
import { ContextProxy } from "../ContextProxy"
|
||||
|
||||
import { logger } from "../../../utils/logging"
|
||||
import { GLOBAL_STATE_KEYS, SECRET_STATE_KEYS } from "../../../shared/globalState"
|
||||
import { GLOBAL_STATE_KEYS, SECRET_STATE_KEYS } from "../../../schemas"
|
||||
|
||||
jest.mock("vscode", () => ({
|
||||
Uri: {
|
||||
|
|
|
|||
|
|
@ -1,4 +1,6 @@
|
|||
import { CustomModesSettingsSchema } from "../CustomModesSchema"
|
||||
// npx jest src/core/config/__tests__/CustomModesSettings.test.ts
|
||||
|
||||
import { customModesSettingsSchema } from "../../../schemas"
|
||||
import { ModeConfig } from "../../../shared/modes"
|
||||
import { ZodError } from "zod"
|
||||
|
||||
|
|
@ -17,7 +19,7 @@ describe("CustomModesSettings", () => {
|
|||
}
|
||||
|
||||
expect(() => {
|
||||
CustomModesSettingsSchema.parse(validSettings)
|
||||
customModesSettingsSchema.parse(validSettings)
|
||||
}).not.toThrow()
|
||||
})
|
||||
|
||||
|
|
@ -27,7 +29,7 @@ describe("CustomModesSettings", () => {
|
|||
}
|
||||
|
||||
expect(() => {
|
||||
CustomModesSettingsSchema.parse(validSettings)
|
||||
customModesSettingsSchema.parse(validSettings)
|
||||
}).not.toThrow()
|
||||
})
|
||||
|
||||
|
|
@ -44,7 +46,7 @@ describe("CustomModesSettings", () => {
|
|||
}
|
||||
|
||||
expect(() => {
|
||||
CustomModesSettingsSchema.parse(validSettings)
|
||||
customModesSettingsSchema.parse(validSettings)
|
||||
}).not.toThrow()
|
||||
})
|
||||
|
||||
|
|
@ -52,7 +54,7 @@ describe("CustomModesSettings", () => {
|
|||
const invalidSettings = {} as any
|
||||
|
||||
expect(() => {
|
||||
CustomModesSettingsSchema.parse(invalidSettings)
|
||||
customModesSettingsSchema.parse(invalidSettings)
|
||||
}).toThrow(ZodError)
|
||||
})
|
||||
|
||||
|
|
@ -68,10 +70,10 @@ describe("CustomModesSettings", () => {
|
|||
}
|
||||
|
||||
expect(() => {
|
||||
CustomModesSettingsSchema.parse(invalidSettings)
|
||||
customModesSettingsSchema.parse(invalidSettings)
|
||||
}).toThrow(ZodError)
|
||||
expect(() => {
|
||||
CustomModesSettingsSchema.parse(invalidSettings)
|
||||
customModesSettingsSchema.parse(invalidSettings)
|
||||
}).toThrow("Slug must contain only letters numbers and dashes")
|
||||
})
|
||||
|
||||
|
|
@ -81,17 +83,17 @@ describe("CustomModesSettings", () => {
|
|||
}
|
||||
|
||||
expect(() => {
|
||||
CustomModesSettingsSchema.parse(invalidSettings)
|
||||
customModesSettingsSchema.parse(invalidSettings)
|
||||
}).toThrow(ZodError)
|
||||
})
|
||||
|
||||
test("rejects null or undefined", () => {
|
||||
expect(() => {
|
||||
CustomModesSettingsSchema.parse(null)
|
||||
customModesSettingsSchema.parse(null)
|
||||
}).toThrow(ZodError)
|
||||
|
||||
expect(() => {
|
||||
CustomModesSettingsSchema.parse(undefined)
|
||||
customModesSettingsSchema.parse(undefined)
|
||||
}).toThrow(ZodError)
|
||||
})
|
||||
|
||||
|
|
@ -104,7 +106,7 @@ describe("CustomModesSettings", () => {
|
|||
}
|
||||
|
||||
expect(() => {
|
||||
CustomModesSettingsSchema.parse(duplicateSettings)
|
||||
customModesSettingsSchema.parse(duplicateSettings)
|
||||
}).toThrow("Duplicate mode slugs are not allowed")
|
||||
})
|
||||
|
||||
|
|
@ -119,7 +121,7 @@ describe("CustomModesSettings", () => {
|
|||
}
|
||||
|
||||
expect(() => {
|
||||
CustomModesSettingsSchema.parse(invalidSettings)
|
||||
customModesSettingsSchema.parse(invalidSettings)
|
||||
}).toThrow(ZodError)
|
||||
})
|
||||
|
||||
|
|
@ -134,7 +136,7 @@ describe("CustomModesSettings", () => {
|
|||
}
|
||||
|
||||
expect(() => {
|
||||
CustomModesSettingsSchema.parse(validSettings)
|
||||
customModesSettingsSchema.parse(validSettings)
|
||||
}).not.toThrow()
|
||||
})
|
||||
})
|
||||
|
|
|
|||
|
|
@ -1,81 +0,0 @@
|
|||
import { CustomModeSchema } from "../CustomModesSchema"
|
||||
import { ModeConfig } from "../../../shared/modes"
|
||||
|
||||
describe("GroupConfigSchema", () => {
|
||||
const validBaseMode = {
|
||||
slug: "123e4567-e89b-12d3-a456-426614174000",
|
||||
name: "Test Mode",
|
||||
roleDefinition: "Test role definition",
|
||||
}
|
||||
|
||||
describe("group format validation", () => {
|
||||
test("accepts single group", () => {
|
||||
const mode = {
|
||||
...validBaseMode,
|
||||
groups: ["read"] as const,
|
||||
} satisfies ModeConfig
|
||||
|
||||
expect(() => CustomModeSchema.parse(mode)).not.toThrow()
|
||||
})
|
||||
|
||||
test("accepts multiple groups", () => {
|
||||
const mode = {
|
||||
...validBaseMode,
|
||||
groups: ["read", "edit", "browser"] as const,
|
||||
} satisfies ModeConfig
|
||||
|
||||
expect(() => CustomModeSchema.parse(mode)).not.toThrow()
|
||||
})
|
||||
|
||||
test("accepts all available groups", () => {
|
||||
const mode = {
|
||||
...validBaseMode,
|
||||
groups: ["read", "edit", "browser", "command", "mcp"] as const,
|
||||
} satisfies ModeConfig
|
||||
|
||||
expect(() => CustomModeSchema.parse(mode)).not.toThrow()
|
||||
})
|
||||
|
||||
test("rejects non-array group format", () => {
|
||||
const mode = {
|
||||
...validBaseMode,
|
||||
groups: "not-an-array" as any,
|
||||
}
|
||||
|
||||
expect(() => CustomModeSchema.parse(mode)).toThrow()
|
||||
})
|
||||
|
||||
test("rejects invalid group names", () => {
|
||||
const mode = {
|
||||
...validBaseMode,
|
||||
groups: ["invalid_group"] as any,
|
||||
}
|
||||
|
||||
expect(() => CustomModeSchema.parse(mode)).toThrow()
|
||||
})
|
||||
|
||||
test("rejects duplicate groups", () => {
|
||||
const mode = {
|
||||
...validBaseMode,
|
||||
groups: ["read", "read"] as any,
|
||||
}
|
||||
|
||||
expect(() => CustomModeSchema.parse(mode)).toThrow("Duplicate groups are not allowed")
|
||||
})
|
||||
|
||||
test("rejects null or undefined groups", () => {
|
||||
const modeWithNull = {
|
||||
...validBaseMode,
|
||||
groups: null as any,
|
||||
}
|
||||
|
||||
const modeWithUndefined = {
|
||||
...validBaseMode,
|
||||
groups: undefined as any,
|
||||
}
|
||||
|
||||
expect(() => CustomModeSchema.parse(modeWithNull)).toThrow()
|
||||
expect(() => CustomModeSchema.parse(modeWithUndefined)).toThrow()
|
||||
})
|
||||
})
|
||||
})
|
||||
|
|
@ -1,7 +1,14 @@
|
|||
// npx jest src/core/config/__tests__/ModeConfig.test.ts
|
||||
|
||||
import { ZodError } from "zod"
|
||||
import { CustomModeSchema, validateCustomMode } from "../CustomModesSchema"
|
||||
|
||||
import { modeConfigSchema } from "../../../schemas"
|
||||
import { ModeConfig } from "../../../shared/modes"
|
||||
|
||||
function validateCustomMode(mode: unknown): asserts mode is ModeConfig {
|
||||
modeConfigSchema.parse(mode)
|
||||
}
|
||||
|
||||
describe("CustomModeSchema", () => {
|
||||
describe("validateCustomMode", () => {
|
||||
test("accepts valid mode configuration", () => {
|
||||
|
|
@ -129,8 +136,8 @@ describe("CustomModeSchema", () => {
|
|||
],
|
||||
}
|
||||
|
||||
expect(() => CustomModeSchema.parse(modeWithJustRegex)).not.toThrow()
|
||||
expect(() => CustomModeSchema.parse(modeWithDescription)).not.toThrow()
|
||||
expect(() => modeConfigSchema.parse(modeWithJustRegex)).not.toThrow()
|
||||
expect(() => modeConfigSchema.parse(modeWithDescription)).not.toThrow()
|
||||
})
|
||||
|
||||
it("validates file regex patterns", () => {
|
||||
|
|
@ -144,7 +151,7 @@ describe("CustomModeSchema", () => {
|
|||
roleDefinition: "Test",
|
||||
groups: ["read", ["edit", { fileRegex: pattern }]],
|
||||
}
|
||||
expect(() => CustomModeSchema.parse(mode)).not.toThrow()
|
||||
expect(() => modeConfigSchema.parse(mode)).not.toThrow()
|
||||
})
|
||||
|
||||
invalidPatterns.forEach((pattern) => {
|
||||
|
|
@ -154,7 +161,7 @@ describe("CustomModeSchema", () => {
|
|||
roleDefinition: "Test",
|
||||
groups: ["read", ["edit", { fileRegex: pattern }]],
|
||||
}
|
||||
expect(() => CustomModeSchema.parse(mode)).toThrow()
|
||||
expect(() => modeConfigSchema.parse(mode)).toThrow()
|
||||
})
|
||||
})
|
||||
|
||||
|
|
@ -166,7 +173,84 @@ describe("CustomModeSchema", () => {
|
|||
groups: ["read", "read", ["edit", { fileRegex: "\\.md$" }], ["edit", { fileRegex: "\\.txt$" }]],
|
||||
}
|
||||
|
||||
expect(() => CustomModeSchema.parse(modeWithDuplicates)).toThrow(/Duplicate groups/)
|
||||
expect(() => modeConfigSchema.parse(modeWithDuplicates)).toThrow(/Duplicate groups/)
|
||||
})
|
||||
})
|
||||
|
||||
const validBaseMode = {
|
||||
slug: "123e4567-e89b-12d3-a456-426614174000",
|
||||
name: "Test Mode",
|
||||
roleDefinition: "Test role definition",
|
||||
}
|
||||
|
||||
describe("group format validation", () => {
|
||||
test("accepts single group", () => {
|
||||
const mode = {
|
||||
...validBaseMode,
|
||||
groups: ["read"] as const,
|
||||
} satisfies ModeConfig
|
||||
|
||||
expect(() => modeConfigSchema.parse(mode)).not.toThrow()
|
||||
})
|
||||
|
||||
test("accepts multiple groups", () => {
|
||||
const mode = {
|
||||
...validBaseMode,
|
||||
groups: ["read", "edit", "browser"] as const,
|
||||
} satisfies ModeConfig
|
||||
|
||||
expect(() => modeConfigSchema.parse(mode)).not.toThrow()
|
||||
})
|
||||
|
||||
test("accepts all available groups", () => {
|
||||
const mode = {
|
||||
...validBaseMode,
|
||||
groups: ["read", "edit", "browser", "command", "mcp"] as const,
|
||||
} satisfies ModeConfig
|
||||
|
||||
expect(() => modeConfigSchema.parse(mode)).not.toThrow()
|
||||
})
|
||||
|
||||
test("rejects non-array group format", () => {
|
||||
const mode = {
|
||||
...validBaseMode,
|
||||
groups: "not-an-array" as any,
|
||||
}
|
||||
|
||||
expect(() => modeConfigSchema.parse(mode)).toThrow()
|
||||
})
|
||||
|
||||
test("rejects invalid group names", () => {
|
||||
const mode = {
|
||||
...validBaseMode,
|
||||
groups: ["invalid_group"] as any,
|
||||
}
|
||||
|
||||
expect(() => modeConfigSchema.parse(mode)).toThrow()
|
||||
})
|
||||
|
||||
test("rejects duplicate groups", () => {
|
||||
const mode = {
|
||||
...validBaseMode,
|
||||
groups: ["read", "read"] as any,
|
||||
}
|
||||
|
||||
expect(() => modeConfigSchema.parse(mode)).toThrow("Duplicate groups are not allowed")
|
||||
})
|
||||
|
||||
test("rejects null or undefined groups", () => {
|
||||
const modeWithNull = {
|
||||
...validBaseMode,
|
||||
groups: null as any,
|
||||
}
|
||||
|
||||
const modeWithUndefined = {
|
||||
...validBaseMode,
|
||||
groups: undefined as any,
|
||||
}
|
||||
|
||||
expect(() => modeConfigSchema.parse(modeWithNull)).toThrow()
|
||||
expect(() => modeConfigSchema.parse(modeWithUndefined)).toThrow()
|
||||
})
|
||||
})
|
||||
})
|
||||
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
import { ExtensionContext } from "vscode"
|
||||
|
||||
import { ProviderSettings } from "../../../exports/roo-code"
|
||||
import { ProviderSettings } from "../../../schemas"
|
||||
import { ProviderSettingsManager, ProviderProfiles } from "../ProviderSettingsManager"
|
||||
|
||||
// Mock VSCode ExtensionContext
|
||||
|
|
|
|||
|
|
@ -6,10 +6,10 @@ import os from "os"
|
|||
|
||||
import * as vscode from "vscode"
|
||||
|
||||
import { ProviderName } from "../../../schemas"
|
||||
import { importSettings, exportSettings } from "../importExport"
|
||||
import { ProviderSettingsManager } from "../ProviderSettingsManager"
|
||||
import { ContextProxy } from "../ContextProxy"
|
||||
import { ProviderName } from "../../../exports/roo-code"
|
||||
|
||||
// Mock VSCode modules
|
||||
jest.mock("vscode", () => ({
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ import fs from "fs/promises"
|
|||
import * as vscode from "vscode"
|
||||
import { z } from "zod"
|
||||
|
||||
import { globalSettingsSchema } from "../../shared/globalState"
|
||||
import { globalSettingsSchema } from "../../schemas"
|
||||
import { ProviderSettingsManager, providerProfilesSchema } from "./ProviderSettingsManager"
|
||||
import { ContextProxy } from "./ContextProxy"
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { Mode, isToolAllowedForMode, getModeConfig, ModeConfig, FileRestrictionError } from "../shared/modes"
|
||||
import { Mode, isToolAllowedForMode, ModeConfig } from "../shared/modes"
|
||||
import { ToolName } from "../shared/tool-groups"
|
||||
|
||||
export { isToolAllowedForMode }
|
||||
|
|
|
|||
|
|
@ -1,16 +1,12 @@
|
|||
import * as vscode from "vscode"
|
||||
|
||||
import { SYSTEM_PROMPT } from "../system"
|
||||
import { McpHub } from "../../../services/mcp/McpHub"
|
||||
import { McpServer } from "../../../shared/mcp"
|
||||
import { ClineProvider } from "../../../core/webview/ClineProvider"
|
||||
import { SearchReplaceDiffStrategy } from "../../../core/diff/strategies/search-replace"
|
||||
import * as vscode from "vscode"
|
||||
import fs from "fs/promises"
|
||||
import os from "os"
|
||||
import { defaultModeSlug, modes, Mode, isToolAllowedForMode } from "../../../shared/modes"
|
||||
// Import path utils to get access to toPosix string extension
|
||||
import "../../../utils/path"
|
||||
import { defaultModeSlug, modes, Mode, ModeConfig } from "../../../shared/modes"
|
||||
import "../../../utils/path" // Import path utils to get access to toPosix string extension.
|
||||
import { addCustomInstructions } from "../sections/custom-instructions"
|
||||
import * as modesSection from "../sections/modes"
|
||||
import { EXPERIMENT_IDS } from "../../../shared/experiments"
|
||||
|
||||
// Mock the sections
|
||||
|
|
@ -386,7 +382,8 @@ describe("SYSTEM_PROMPT", () => {
|
|||
|
||||
it("should include custom mode role definition at top and instructions at bottom", async () => {
|
||||
const modeCustomInstructions = "Custom mode instructions"
|
||||
const customModes = [
|
||||
|
||||
const customModes: ModeConfig[] = [
|
||||
{
|
||||
slug: "custom-mode",
|
||||
name: "Custom Mode",
|
||||
|
|
|
|||
|
|
@ -1,8 +1,7 @@
|
|||
import fs from "fs/promises"
|
||||
import path from "path"
|
||||
|
||||
import { LANGUAGES } from "../../../shared/language"
|
||||
import { isLanguage } from "../../../shared/globalState"
|
||||
import { LANGUAGES, isLanguage } from "../../../shared/language"
|
||||
|
||||
async function safeReadFile(filePath: string): Promise<string> {
|
||||
try {
|
||||
|
|
|
|||
|
|
@ -15,9 +15,8 @@ import {
|
|||
Language,
|
||||
ProviderSettings,
|
||||
RooCodeSettings,
|
||||
GlobalStateKey,
|
||||
SecretStateKey,
|
||||
} from "../../exports/roo-code"
|
||||
ApiConfigMeta,
|
||||
} from "../../schemas"
|
||||
import { changeLanguage, t } from "../../i18n"
|
||||
import { setPanel } from "../../activate/registerCommands"
|
||||
import {
|
||||
|
|
@ -35,7 +34,7 @@ import { findLast } from "../../shared/array"
|
|||
import { supportPrompt } from "../../shared/support-prompt"
|
||||
import { GlobalFileNames } from "../../shared/globalFileNames"
|
||||
import { HistoryItem } from "../../shared/HistoryItem"
|
||||
import { ApiConfigMeta, ExtensionMessage } from "../../shared/ExtensionMessage"
|
||||
import { ExtensionMessage } from "../../shared/ExtensionMessage"
|
||||
import { checkoutDiffPayloadSchema, checkoutRestorePayloadSchema, WebviewMessage } from "../../shared/WebviewMessage"
|
||||
import { Mode, PromptComponent, defaultModeSlug, getModeBySlug, getGroupName } from "../../shared/modes"
|
||||
import { checkExistKey } from "../../shared/checkExistApiConfig"
|
||||
|
|
@ -2802,27 +2801,29 @@ export class ClineProvider extends EventEmitter<ClineProviderEvents> implements
|
|||
return history
|
||||
}
|
||||
|
||||
// global
|
||||
// ContextProxy
|
||||
|
||||
public async updateGlobalState<K extends GlobalStateKey>(key: K, value: GlobalState[K]) {
|
||||
// @deprecated - Use `ContextProxy#setValue` instead.
|
||||
private async updateGlobalState<K extends keyof GlobalState>(key: K, value: GlobalState[K]) {
|
||||
await this.contextProxy.setValue(key, value)
|
||||
}
|
||||
|
||||
public getGlobalState<K extends GlobalStateKey>(key: K) {
|
||||
// @deprecated - Use `ContextProxy#getValue` instead.
|
||||
private getGlobalState<K extends keyof GlobalState>(key: K) {
|
||||
return this.contextProxy.getValue(key)
|
||||
}
|
||||
|
||||
// secrets
|
||||
|
||||
public async storeSecret(key: SecretStateKey, value?: string) {
|
||||
public async setValue<K extends keyof RooCodeSettings>(key: K, value: RooCodeSettings[K]) {
|
||||
await this.contextProxy.setValue(key, value)
|
||||
}
|
||||
|
||||
private getSecret(key: SecretStateKey) {
|
||||
public getValue<K extends keyof RooCodeSettings>(key: K) {
|
||||
return this.contextProxy.getValue(key)
|
||||
}
|
||||
|
||||
// global + secret
|
||||
public getValues() {
|
||||
return this.contextProxy.getValues()
|
||||
}
|
||||
|
||||
public async setValues(values: RooCodeSettings) {
|
||||
await this.contextProxy.setValues(values)
|
||||
|
|
|
|||
|
|
@ -13,46 +13,6 @@ import { experimentDefault } from "../../../shared/experiments"
|
|||
// Mock setup must come before imports
|
||||
jest.mock("../../prompts/sections/custom-instructions")
|
||||
|
||||
// Mock ContextProxy
|
||||
jest.mock("../../config/ContextProxy", () => {
|
||||
return {
|
||||
ContextProxy: jest.fn().mockImplementation((context) => ({
|
||||
originalContext: context,
|
||||
isInitialized: true,
|
||||
initialize: jest.fn(),
|
||||
extensionUri: context.extensionUri,
|
||||
extensionPath: context.extensionPath,
|
||||
globalStorageUri: context.globalStorageUri,
|
||||
logUri: context.logUri,
|
||||
extension: context.extension,
|
||||
extensionMode: context.extensionMode,
|
||||
getGlobalState: jest
|
||||
.fn()
|
||||
.mockImplementation((key, defaultValue) => context.globalState.get(key, defaultValue)),
|
||||
updateGlobalState: jest.fn().mockImplementation((key, value) => context.globalState.update(key, value)),
|
||||
getSecret: jest.fn().mockImplementation((key) => context.secrets.get(key)),
|
||||
storeSecret: jest
|
||||
.fn()
|
||||
.mockImplementation((key, value) =>
|
||||
value ? context.secrets.store(key, value) : context.secrets.delete(key),
|
||||
),
|
||||
saveChanges: jest.fn().mockResolvedValue(undefined),
|
||||
dispose: jest.fn().mockResolvedValue(undefined),
|
||||
hasPendingChanges: jest.fn().mockReturnValue(false),
|
||||
setValue: jest.fn().mockImplementation((key, value) => {
|
||||
if (key.startsWith("apiKey") || key.startsWith("openAiApiKey")) {
|
||||
return context.secrets.store(key, value)
|
||||
}
|
||||
return context.globalState.update(key, value)
|
||||
}),
|
||||
setValues: jest.fn().mockImplementation((values) => {
|
||||
const promises = Object.entries(values).map(([key, value]) => context.globalState.update(key, value))
|
||||
return Promise.all(promises)
|
||||
}),
|
||||
})),
|
||||
}
|
||||
})
|
||||
|
||||
// Mock dependencies
|
||||
jest.mock("vscode")
|
||||
jest.mock("delay")
|
||||
|
|
@ -84,6 +44,7 @@ jest.mock("../../../services/browser/browserDiscovery", () => ({
|
|||
return "http://localhost:9222"
|
||||
}),
|
||||
}))
|
||||
|
||||
jest.mock(
|
||||
"@modelcontextprotocol/sdk/types.js",
|
||||
() => ({
|
||||
|
|
@ -111,6 +72,7 @@ jest.mock(
|
|||
|
||||
// Initialize mocks
|
||||
const mockAddCustomInstructions = jest.fn().mockResolvedValue("Combined instructions")
|
||||
|
||||
;(jest.requireMock("../../prompts/sections/custom-instructions") as any).addCustomInstructions =
|
||||
mockAddCustomInstructions
|
||||
|
||||
|
|
@ -205,6 +167,7 @@ jest.mock("../../../utils/sound", () => ({
|
|||
// Mock tts utility
|
||||
jest.mock("../../../utils/tts", () => ({
|
||||
setTtsEnabled: jest.fn(),
|
||||
setTtsSpeed: jest.fn(),
|
||||
}))
|
||||
|
||||
// Mock ESM modules
|
||||
|
|
@ -294,41 +257,34 @@ describe("ClineProvider", () => {
|
|||
let mockOutputChannel: vscode.OutputChannel
|
||||
let mockWebviewView: vscode.WebviewView
|
||||
let mockPostMessage: jest.Mock
|
||||
let mockContextProxy: {
|
||||
updateGlobalState: jest.Mock
|
||||
getGlobalState: jest.Mock
|
||||
setValue: jest.Mock
|
||||
setValues: jest.Mock
|
||||
storeSecret: jest.Mock
|
||||
dispose: jest.Mock
|
||||
}
|
||||
let updateGlobalStateSpy: jest.SpyInstance<ClineProvider["contextProxy"]["updateGlobalState"]>
|
||||
|
||||
beforeEach(() => {
|
||||
// Reset mocks
|
||||
jest.clearAllMocks()
|
||||
|
||||
// Mock context
|
||||
const globalState: Record<string, string | undefined> = {
|
||||
mode: "architect",
|
||||
currentApiConfigName: "current-config",
|
||||
}
|
||||
|
||||
const secrets: Record<string, string | undefined> = {}
|
||||
|
||||
mockContext = {
|
||||
extensionPath: "/test/path",
|
||||
extensionUri: {} as vscode.Uri,
|
||||
globalState: {
|
||||
get: jest.fn().mockImplementation((key: string) => {
|
||||
switch (key) {
|
||||
case "mode":
|
||||
return "architect"
|
||||
case "currentApiConfigName":
|
||||
return "new-config"
|
||||
default:
|
||||
return undefined
|
||||
}
|
||||
}),
|
||||
update: jest.fn(),
|
||||
keys: jest.fn().mockReturnValue([]),
|
||||
get: jest.fn().mockImplementation((key: string) => globalState[key]),
|
||||
update: jest
|
||||
.fn()
|
||||
.mockImplementation((key: string, value: string | undefined) => (globalState[key] = value)),
|
||||
keys: jest.fn().mockImplementation(() => Object.keys(globalState)),
|
||||
},
|
||||
secrets: {
|
||||
get: jest.fn(),
|
||||
store: jest.fn(),
|
||||
delete: jest.fn(),
|
||||
get: jest.fn().mockImplementation((key: string) => secrets[key]),
|
||||
store: jest.fn().mockImplementation((key: string, value: string | undefined) => (secrets[key] = value)),
|
||||
delete: jest.fn().mockImplementation((key: string) => delete secrets[key]),
|
||||
},
|
||||
subscriptions: [],
|
||||
extension: {
|
||||
|
|
@ -342,7 +298,7 @@ describe("ClineProvider", () => {
|
|||
// Mock CustomModesManager
|
||||
const mockCustomModesManager = {
|
||||
updateCustomMode: jest.fn().mockResolvedValue(undefined),
|
||||
getCustomModes: jest.fn().mockResolvedValue({ customModes: [] }),
|
||||
getCustomModes: jest.fn().mockResolvedValue([]),
|
||||
dispose: jest.fn(),
|
||||
}
|
||||
|
||||
|
|
@ -374,8 +330,9 @@ describe("ClineProvider", () => {
|
|||
} as unknown as vscode.WebviewView
|
||||
|
||||
provider = new ClineProvider(mockContext, mockOutputChannel)
|
||||
|
||||
// @ts-ignore - Access private property for testing
|
||||
mockContextProxy = provider.contextProxy
|
||||
updateGlobalStateSpy = jest.spyOn(provider.contextProxy, "setValue")
|
||||
|
||||
// @ts-ignore - Accessing private property for testing.
|
||||
provider.customModesManager = mockCustomModesManager
|
||||
|
|
@ -417,10 +374,10 @@ describe("ClineProvider", () => {
|
|||
expect(mockWebviewView.webview.html).toContain("<!DOCTYPE html>")
|
||||
|
||||
// Verify Content Security Policy contains the necessary PostHog domains
|
||||
expect(mockWebviewView.webview.html).toContain("connect-src https://us.i.posthog.com")
|
||||
expect(mockWebviewView.webview.html).toContain("https://us-assets.i.posthog.com")
|
||||
expect(mockWebviewView.webview.html).toContain(
|
||||
"connect-src https://openrouter.ai https://us.i.posthog.com https://us-assets.i.posthog.com;",
|
||||
)
|
||||
expect(mockWebviewView.webview.html).toContain("script-src 'nonce-")
|
||||
expect(mockWebviewView.webview.html).toContain("https://us-assets.i.posthog.com")
|
||||
})
|
||||
|
||||
test("postMessageToWebview sends message to webview", async () => {
|
||||
|
|
@ -552,10 +509,10 @@ describe("ClineProvider", () => {
|
|||
|
||||
test("language is set to VSCode language", async () => {
|
||||
// Mock VSCode language as Spanish
|
||||
;(vscode.env as any).language = "es-ES"
|
||||
;(vscode.env as any).language = "pt-BR"
|
||||
|
||||
const state = await provider.getState()
|
||||
expect(state.language).toBe("es-ES")
|
||||
expect(state.language).toBe("pt-BR")
|
||||
})
|
||||
|
||||
test("diffEnabled defaults to true when not set", async () => {
|
||||
|
|
@ -569,12 +526,9 @@ describe("ClineProvider", () => {
|
|||
|
||||
test("writeDelayMs defaults to 1000ms", async () => {
|
||||
// Mock globalState.get to return undefined for writeDelayMs
|
||||
;(mockContext.globalState.get as jest.Mock).mockImplementation((key: string) => {
|
||||
if (key === "writeDelayMs") {
|
||||
return undefined
|
||||
}
|
||||
return null
|
||||
})
|
||||
;(mockContext.globalState.get as jest.Mock).mockImplementation((key: string) =>
|
||||
key === "writeDelayMs" ? undefined : null,
|
||||
)
|
||||
|
||||
const state = await provider.getState()
|
||||
expect(state.writeDelayMs).toBe(1000)
|
||||
|
|
@ -586,7 +540,7 @@ describe("ClineProvider", () => {
|
|||
|
||||
await messageHandler({ type: "writeDelayMs", value: 2000 })
|
||||
|
||||
expect(mockContextProxy.updateGlobalState).toHaveBeenCalledWith("writeDelayMs", 2000)
|
||||
expect(updateGlobalStateSpy).toHaveBeenCalledWith("writeDelayMs", 2000)
|
||||
expect(mockContext.globalState.update).toHaveBeenCalledWith("writeDelayMs", 2000)
|
||||
expect(mockPostMessage).toHaveBeenCalled()
|
||||
})
|
||||
|
|
@ -600,7 +554,7 @@ describe("ClineProvider", () => {
|
|||
// Simulate setting sound to enabled
|
||||
await messageHandler({ type: "soundEnabled", bool: true })
|
||||
expect(setSoundEnabled).toHaveBeenCalledWith(true)
|
||||
expect(mockContextProxy.updateGlobalState).toHaveBeenCalledWith("soundEnabled", true)
|
||||
expect(updateGlobalStateSpy).toHaveBeenCalledWith("soundEnabled", true)
|
||||
expect(mockContext.globalState.update).toHaveBeenCalledWith("soundEnabled", true)
|
||||
expect(mockPostMessage).toHaveBeenCalled()
|
||||
|
||||
|
|
@ -676,13 +630,7 @@ describe("ClineProvider", () => {
|
|||
setModeConfig: jest.fn(),
|
||||
} as any
|
||||
|
||||
// Mock current config name
|
||||
;(mockContext.globalState.get as jest.Mock).mockImplementation((key: string) => {
|
||||
if (key === "currentApiConfigName") {
|
||||
return "current-config"
|
||||
}
|
||||
return undefined
|
||||
})
|
||||
provider.setValue("currentApiConfigName", "current-config")
|
||||
|
||||
// Switch to architect mode
|
||||
await messageHandler({ type: "mode", text: "architect" })
|
||||
|
|
@ -763,21 +711,20 @@ describe("ClineProvider", () => {
|
|||
await provider.resolveWebviewView(mockWebviewView)
|
||||
const messageHandler = (mockWebviewView.webview.onDidReceiveMessage as jest.Mock).mock.calls[0][0]
|
||||
|
||||
// Default value should be true
|
||||
expect((await provider.getState()).showRooIgnoredFiles).toBe(true)
|
||||
|
||||
// Test showRooIgnoredFiles with true
|
||||
await messageHandler({ type: "showRooIgnoredFiles", bool: true })
|
||||
expect(mockContext.globalState.update).toHaveBeenCalledWith("showRooIgnoredFiles", true)
|
||||
expect(mockPostMessage).toHaveBeenCalled()
|
||||
expect((await provider.getState()).showRooIgnoredFiles).toBe(true)
|
||||
|
||||
// Test showRooIgnoredFiles with false
|
||||
jest.clearAllMocks() // Clear all mocks including mockContext.globalState.update
|
||||
await messageHandler({ type: "showRooIgnoredFiles", bool: false })
|
||||
expect(mockContext.globalState.update).toHaveBeenCalledWith("showRooIgnoredFiles", false)
|
||||
expect(mockPostMessage).toHaveBeenCalled()
|
||||
|
||||
// Verify state includes showRooIgnoredFiles
|
||||
const state = await provider.getState()
|
||||
expect(state).toHaveProperty("showRooIgnoredFiles")
|
||||
expect(state.showRooIgnoredFiles).toBe(true) // Default value should be true
|
||||
expect((await provider.getState()).showRooIgnoredFiles).toBe(false)
|
||||
})
|
||||
|
||||
test("handles request delay settings messages", async () => {
|
||||
|
|
@ -786,7 +733,7 @@ describe("ClineProvider", () => {
|
|||
|
||||
// Test alwaysApproveResubmit
|
||||
await messageHandler({ type: "alwaysApproveResubmit", bool: true })
|
||||
expect(mockContextProxy.updateGlobalState).toHaveBeenCalledWith("alwaysApproveResubmit", true)
|
||||
expect(updateGlobalStateSpy).toHaveBeenCalledWith("alwaysApproveResubmit", true)
|
||||
expect(mockContext.globalState.update).toHaveBeenCalledWith("alwaysApproveResubmit", true)
|
||||
expect(mockPostMessage).toHaveBeenCalled()
|
||||
|
||||
|
|
@ -802,15 +749,17 @@ describe("ClineProvider", () => {
|
|||
|
||||
// Mock existing prompts
|
||||
const existingPrompts = {
|
||||
code: "existing code prompt",
|
||||
architect: "existing architect prompt",
|
||||
code: {
|
||||
roleDefinition: "existing code role",
|
||||
customInstructions: "existing code prompt",
|
||||
},
|
||||
architect: {
|
||||
roleDefinition: "existing architect role",
|
||||
customInstructions: "existing architect prompt",
|
||||
},
|
||||
}
|
||||
;(mockContext.globalState.get as jest.Mock).mockImplementation((key: string) => {
|
||||
if (key === "customModePrompts") {
|
||||
return existingPrompts
|
||||
}
|
||||
return undefined
|
||||
})
|
||||
|
||||
provider.setValue("customModePrompts", existingPrompts)
|
||||
|
||||
// Test updating a prompt
|
||||
await messageHandler({
|
||||
|
|
@ -858,12 +807,12 @@ describe("ClineProvider", () => {
|
|||
|
||||
await messageHandler({ type: "maxWorkspaceFiles", value: 300 })
|
||||
|
||||
expect(mockContextProxy.updateGlobalState).toHaveBeenCalledWith("maxWorkspaceFiles", 300)
|
||||
expect(updateGlobalStateSpy).toHaveBeenCalledWith("maxWorkspaceFiles", 300)
|
||||
expect(mockContext.globalState.update).toHaveBeenCalledWith("maxWorkspaceFiles", 300)
|
||||
expect(mockPostMessage).toHaveBeenCalled()
|
||||
})
|
||||
|
||||
test.only("uses mode-specific custom instructions in Cline initialization", async () => {
|
||||
test("uses mode-specific custom instructions in Cline initialization", async () => {
|
||||
// Setup mock state
|
||||
const modeCustomInstructions = "Code mode instructions"
|
||||
const mockApiConfig = {
|
||||
|
|
@ -1000,7 +949,7 @@ describe("ClineProvider", () => {
|
|||
|
||||
test('handles "Just this message" deletion correctly', async () => {
|
||||
// Mock user selecting "Just this message"
|
||||
;(vscode.window.showInformationMessage as jest.Mock).mockResolvedValue("Just this message")
|
||||
;(vscode.window.showInformationMessage as jest.Mock).mockResolvedValue("confirmation.just_this_message")
|
||||
|
||||
// Setup mock messages
|
||||
const mockMessages = [
|
||||
|
|
@ -1049,7 +998,7 @@ describe("ClineProvider", () => {
|
|||
|
||||
test('handles "This and all subsequent messages" deletion correctly', async () => {
|
||||
// Mock user selecting "This and all subsequent messages"
|
||||
;(vscode.window.showInformationMessage as jest.Mock).mockResolvedValue("This and all subsequent messages")
|
||||
;(vscode.window.showInformationMessage as jest.Mock).mockResolvedValue("confirmation.this_and_subsequent")
|
||||
|
||||
// Setup mock messages
|
||||
const mockMessages = [
|
||||
|
|
@ -1199,7 +1148,7 @@ describe("ClineProvider", () => {
|
|||
const messageHandler = (mockWebviewView.webview.onDidReceiveMessage as jest.Mock).mock.calls[0][0]
|
||||
await messageHandler({ type: "getSystemPrompt", mode: "code" })
|
||||
|
||||
expect(vscode.window.showErrorMessage).toHaveBeenCalledWith("Failed to get system prompt")
|
||||
expect(vscode.window.showErrorMessage).toHaveBeenCalledWith("errors.get_system_prompt")
|
||||
})
|
||||
|
||||
test("uses code mode custom instructions", async () => {
|
||||
|
|
@ -1230,16 +1179,14 @@ describe("ClineProvider", () => {
|
|||
})
|
||||
|
||||
test("passes diffStrategy and diffEnabled to SYSTEM_PROMPT when previewing", async () => {
|
||||
// Setup Cline instance with mocked api.getModel()
|
||||
const { Cline } = require("../../Cline")
|
||||
const mockCline = new Cline()
|
||||
mockCline.api = {
|
||||
// Mock buildApiHandler to return an API handler with supportsComputerUse: true
|
||||
const { buildApiHandler } = require("../../../api")
|
||||
;(buildApiHandler as jest.Mock).mockImplementation(() => ({
|
||||
getModel: jest.fn().mockReturnValue({
|
||||
id: "claude-3-sonnet",
|
||||
info: { supportsComputerUse: true },
|
||||
}),
|
||||
}
|
||||
await provider.addClineToStack(mockCline)
|
||||
}))
|
||||
|
||||
// Mock getState to return experimentalDiffStrategy, diffEnabled and fuzzyMatchThreshold
|
||||
jest.spyOn(provider, "getState").mockResolvedValue({
|
||||
|
|
@ -1338,7 +1285,7 @@ describe("ClineProvider", () => {
|
|||
expect(callArgs[4]).toHaveProperty("getToolDescription") // diffStrategy
|
||||
expect(callArgs[5]).toBe("900x600") // browserViewportSize
|
||||
expect(callArgs[6]).toBe("code") // mode
|
||||
expect(callArgs[10]).toBe(false) // diffEnabled should be false
|
||||
expect(callArgs[10]).toBe(false) // diffEnabled should be true
|
||||
})
|
||||
|
||||
test("uses correct mode-specific instructions when mode is specified", async () => {
|
||||
|
|
@ -1677,16 +1624,14 @@ describe("ClineProvider", () => {
|
|||
// Mock CustomModesManager methods
|
||||
;(provider as any).customModesManager = {
|
||||
updateCustomMode: jest.fn().mockResolvedValue(undefined),
|
||||
getCustomModes: jest.fn().mockResolvedValue({
|
||||
customModes: [
|
||||
{
|
||||
slug: "test-mode",
|
||||
name: "Test Mode",
|
||||
roleDefinition: "Updated role definition",
|
||||
groups: ["read"] as const,
|
||||
},
|
||||
],
|
||||
}),
|
||||
getCustomModes: jest.fn().mockResolvedValue([
|
||||
{
|
||||
slug: "test-mode",
|
||||
name: "Test Mode",
|
||||
roleDefinition: "Updated role definition",
|
||||
groups: ["read"] as const,
|
||||
},
|
||||
]),
|
||||
dispose: jest.fn(),
|
||||
} as any
|
||||
|
||||
|
|
@ -1711,14 +1656,9 @@ describe("ClineProvider", () => {
|
|||
)
|
||||
|
||||
// Verify state was updated
|
||||
expect(mockContext.globalState.update).toHaveBeenCalledWith("customModes", {
|
||||
customModes: [
|
||||
expect.objectContaining({
|
||||
slug: "test-mode",
|
||||
roleDefinition: "Updated role definition",
|
||||
}),
|
||||
],
|
||||
})
|
||||
expect(mockContext.globalState.update).toHaveBeenCalledWith("customModes", [
|
||||
{ groups: ["read"], name: "Test Mode", roleDefinition: "Updated role definition", slug: "test-mode" },
|
||||
])
|
||||
|
||||
// Verify state was posted to webview
|
||||
// Verify state was posted to webview with correct format
|
||||
|
|
@ -1726,14 +1666,12 @@ describe("ClineProvider", () => {
|
|||
expect.objectContaining({
|
||||
type: "state",
|
||||
state: expect.objectContaining({
|
||||
customModes: {
|
||||
customModes: [
|
||||
expect.objectContaining({
|
||||
slug: "test-mode",
|
||||
roleDefinition: "Updated role definition",
|
||||
}),
|
||||
],
|
||||
},
|
||||
customModes: [
|
||||
expect.objectContaining({
|
||||
slug: "test-mode",
|
||||
roleDefinition: "Updated role definition",
|
||||
}),
|
||||
],
|
||||
}),
|
||||
}),
|
||||
)
|
||||
|
|
@ -1742,7 +1680,7 @@ describe("ClineProvider", () => {
|
|||
|
||||
describe("upsertApiConfiguration", () => {
|
||||
test("handles error in upsertApiConfiguration gracefully", async () => {
|
||||
provider.resolveWebviewView(mockWebviewView)
|
||||
await provider.resolveWebviewView(mockWebviewView)
|
||||
const messageHandler = (mockWebviewView.webview.onDidReceiveMessage as jest.Mock).mock.calls[0][0]
|
||||
|
||||
;(provider as any).providerSettingsManager = {
|
||||
|
|
@ -1772,14 +1710,15 @@ describe("ClineProvider", () => {
|
|||
expect(mockOutputChannel.appendLine).toHaveBeenCalledWith(
|
||||
expect.stringContaining("Error create new api configuration"),
|
||||
)
|
||||
expect(vscode.window.showErrorMessage).toHaveBeenCalledWith("Failed to create api configuration")
|
||||
expect(vscode.window.showErrorMessage).toHaveBeenCalledWith("errors.create_api_config")
|
||||
})
|
||||
|
||||
test("handles successful upsertApiConfiguration", async () => {
|
||||
provider.resolveWebviewView(mockWebviewView)
|
||||
await provider.resolveWebviewView(mockWebviewView)
|
||||
const messageHandler = (mockWebviewView.webview.onDidReceiveMessage as jest.Mock).mock.calls[0][0]
|
||||
|
||||
;(provider as any).providerSettingsManager = {
|
||||
setModeConfig: jest.fn(),
|
||||
saveConfig: jest.fn().mockResolvedValue(undefined),
|
||||
listConfig: jest
|
||||
.fn()
|
||||
|
|
@ -1812,15 +1751,17 @@ describe("ClineProvider", () => {
|
|||
})
|
||||
|
||||
test("handles buildApiHandler error in updateApiConfiguration", async () => {
|
||||
provider.resolveWebviewView(mockWebviewView)
|
||||
await provider.resolveWebviewView(mockWebviewView)
|
||||
const messageHandler = (mockWebviewView.webview.onDidReceiveMessage as jest.Mock).mock.calls[0][0]
|
||||
|
||||
// Mock buildApiHandler to throw an error
|
||||
const { buildApiHandler } = require("../../../api")
|
||||
|
||||
;(buildApiHandler as jest.Mock).mockImplementationOnce(() => {
|
||||
throw new Error("API handler error")
|
||||
})
|
||||
;(provider as any).providerSettingsManager = {
|
||||
setModeConfig: jest.fn(),
|
||||
saveConfig: jest.fn().mockResolvedValue(undefined),
|
||||
listConfig: jest
|
||||
.fn()
|
||||
|
|
@ -1848,7 +1789,7 @@ describe("ClineProvider", () => {
|
|||
expect(mockOutputChannel.appendLine).toHaveBeenCalledWith(
|
||||
expect.stringContaining("Error create new api configuration"),
|
||||
)
|
||||
expect(vscode.window.showErrorMessage).toHaveBeenCalledWith("Failed to create api configuration")
|
||||
expect(vscode.window.showErrorMessage).toHaveBeenCalledWith("errors.create_api_config")
|
||||
|
||||
// Verify state was still updated
|
||||
expect(mockContext.globalState.update).toHaveBeenCalledWith("listApiConfigMeta", [
|
||||
|
|
@ -1858,10 +1799,11 @@ describe("ClineProvider", () => {
|
|||
})
|
||||
|
||||
test("handles successful saveApiConfiguration", async () => {
|
||||
provider.resolveWebviewView(mockWebviewView)
|
||||
await provider.resolveWebviewView(mockWebviewView)
|
||||
const messageHandler = (mockWebviewView.webview.onDidReceiveMessage as jest.Mock).mock.calls[0][0]
|
||||
|
||||
;(provider as any).providerSettingsManager = {
|
||||
setModeConfig: jest.fn(),
|
||||
saveConfig: jest.fn().mockResolvedValue(undefined),
|
||||
listConfig: jest
|
||||
.fn()
|
||||
|
|
@ -1887,7 +1829,7 @@ describe("ClineProvider", () => {
|
|||
expect(mockContext.globalState.update).toHaveBeenCalledWith("listApiConfigMeta", [
|
||||
{ name: "test-config", id: "test-id", apiProvider: "anthropic" },
|
||||
])
|
||||
expect(mockContextProxy.updateGlobalState).toHaveBeenCalledWith("listApiConfigMeta", [
|
||||
expect(updateGlobalStateSpy).toHaveBeenCalledWith("listApiConfigMeta", [
|
||||
{ name: "test-config", id: "test-id", apiProvider: "anthropic" },
|
||||
])
|
||||
})
|
||||
|
|
@ -2154,15 +2096,13 @@ describe("Project MCP Settings", () => {
|
|||
;(vscode.workspace as any).workspaceFolders = []
|
||||
|
||||
// Trigger openProjectMcpSettings
|
||||
await messageHandler({
|
||||
type: "openProjectMcpSettings",
|
||||
})
|
||||
await messageHandler({ type: "openProjectMcpSettings" })
|
||||
|
||||
// Verify error message was shown
|
||||
expect(vscode.window.showErrorMessage).toHaveBeenCalledWith("Please open a project folder first")
|
||||
expect(vscode.window.showErrorMessage).toHaveBeenCalledWith("no_workspace")
|
||||
})
|
||||
|
||||
test("handles openProjectMcpSettings file creation error", async () => {
|
||||
test.skip("handles openProjectMcpSettings file creation error", async () => {
|
||||
await provider.resolveWebviewView(mockWebviewView)
|
||||
const messageHandler = (mockWebviewView.webview.onDidReceiveMessage as jest.Mock).mock.calls[0][0]
|
||||
|
||||
|
|
@ -2185,7 +2125,7 @@ describe("Project MCP Settings", () => {
|
|||
})
|
||||
})
|
||||
|
||||
describe("ContextProxy integration", () => {
|
||||
describe.skip("ContextProxy integration", () => {
|
||||
let provider: ClineProvider
|
||||
let mockContext: vscode.ExtensionContext
|
||||
let mockOutputChannel: vscode.OutputChannel
|
||||
|
|
@ -2219,19 +2159,19 @@ describe("ContextProxy integration", () => {
|
|||
})
|
||||
|
||||
test("updateGlobalState uses contextProxy", async () => {
|
||||
await provider.updateGlobalState("currentApiConfigName", "testValue")
|
||||
await provider.setValue("currentApiConfigName", "testValue")
|
||||
expect(mockContextProxy.updateGlobalState).toHaveBeenCalledWith("currentApiConfigName", "testValue")
|
||||
})
|
||||
|
||||
test("getGlobalState uses contextProxy", async () => {
|
||||
mockContextProxy.getGlobalState.mockResolvedValueOnce("testValue")
|
||||
const result = await provider.getGlobalState("currentApiConfigName")
|
||||
const result = await provider.getValue("currentApiConfigName")
|
||||
expect(mockContextProxy.getGlobalState).toHaveBeenCalledWith("currentApiConfigName")
|
||||
expect(result).toBe("testValue")
|
||||
})
|
||||
|
||||
test("storeSecret uses contextProxy", async () => {
|
||||
await provider.storeSecret("apiKey", "test-secret")
|
||||
await provider.setValue("apiKey", "test-secret")
|
||||
expect(mockContextProxy.storeSecret).toHaveBeenCalledWith("apiKey", "test-secret")
|
||||
})
|
||||
|
||||
|
|
|
|||
|
|
@ -104,10 +104,22 @@ export class API extends EventEmitter<RooCodeEvents> implements RooCodeAPI {
|
|||
await this.provider.postMessageToWebview({ type: "invoke", invoke: "secondaryButtonClick" })
|
||||
}
|
||||
|
||||
public getConfiguration() {
|
||||
return this.provider.getValues()
|
||||
}
|
||||
|
||||
public getConfigurationValue<K extends keyof RooCodeSettings>(key: K) {
|
||||
return this.provider.getValue(key)
|
||||
}
|
||||
|
||||
public async setConfiguration(values: RooCodeSettings) {
|
||||
await this.provider.setValues(values)
|
||||
}
|
||||
|
||||
public async setConfigurationValue<K extends keyof RooCodeSettings>(key: K, value: RooCodeSettings[K]) {
|
||||
await this.provider.setValue(key, value)
|
||||
}
|
||||
|
||||
public isReady() {
|
||||
return this.provider.viewLaunched
|
||||
}
|
||||
|
|
|
|||
114
src/exports/interface.ts
Normal file
114
src/exports/interface.ts
Normal file
|
|
@ -0,0 +1,114 @@
|
|||
import { EventEmitter } from "events"
|
||||
|
||||
import type { ProviderSettings, GlobalSettings, ClineMessage, TokenUsage } from "./types"
|
||||
|
||||
type RooCodeSettings = GlobalSettings & ProviderSettings
|
||||
|
||||
export type { RooCodeSettings, ProviderSettings, GlobalSettings, ClineMessage, TokenUsage }
|
||||
|
||||
export interface RooCodeEvents {
|
||||
message: [{ taskId: string; action: "created" | "updated"; message: ClineMessage }]
|
||||
taskCreated: [taskId: string]
|
||||
taskStarted: [taskId: string]
|
||||
taskPaused: [taskId: string]
|
||||
taskUnpaused: [taskId: string]
|
||||
taskAskResponded: [taskId: string]
|
||||
taskAborted: [taskId: string]
|
||||
taskSpawned: [taskId: string, childTaskId: string]
|
||||
taskCompleted: [taskId: string, usage: TokenUsage]
|
||||
taskTokenUsageUpdated: [taskId: string, usage: TokenUsage]
|
||||
}
|
||||
|
||||
export interface RooCodeAPI extends EventEmitter<RooCodeEvents> {
|
||||
/**
|
||||
* Starts a new task with an optional initial message and images.
|
||||
* @param task Optional initial task message.
|
||||
* @param images Optional array of image data URIs (e.g., "data:image/webp;base64,...").
|
||||
* @returns The ID of the new task.
|
||||
*/
|
||||
startNewTask(task?: string, images?: string[]): Promise<string>
|
||||
|
||||
/**
|
||||
* Returns the current task stack.
|
||||
* @returns An array of task IDs.
|
||||
*/
|
||||
getCurrentTaskStack(): string[]
|
||||
|
||||
/**
|
||||
* Clears the current task.
|
||||
*/
|
||||
clearCurrentTask(lastMessage?: string): Promise<void>
|
||||
|
||||
/**
|
||||
* Cancels the current task.
|
||||
*/
|
||||
cancelCurrentTask(): Promise<void>
|
||||
|
||||
/**
|
||||
* Sends a message to the current task.
|
||||
* @param message Optional message to send.
|
||||
* @param images Optional array of image data URIs (e.g., "data:image/webp;base64,...").
|
||||
*/
|
||||
sendMessage(message?: string, images?: string[]): Promise<void>
|
||||
|
||||
/**
|
||||
* Simulates pressing the primary button in the chat interface.
|
||||
*/
|
||||
pressPrimaryButton(): Promise<void>
|
||||
|
||||
/**
|
||||
* Simulates pressing the secondary button in the chat interface.
|
||||
*/
|
||||
pressSecondaryButton(): Promise<void>
|
||||
|
||||
/**
|
||||
* Returns the current configuration.
|
||||
* @returns The current configuration.
|
||||
*/
|
||||
getConfiguration(): RooCodeSettings
|
||||
|
||||
/**
|
||||
* Returns the value of a configuration key.
|
||||
* @param key The key of the configuration value to return.
|
||||
* @returns The value of the configuration key.
|
||||
*/
|
||||
getConfigurationValue<K extends keyof RooCodeSettings>(key: K): RooCodeSettings[K]
|
||||
|
||||
/**
|
||||
* Sets the configuration for the current task.
|
||||
* @param values An object containing key-value pairs to set.
|
||||
*/
|
||||
setConfiguration(values: RooCodeSettings): Promise<void>
|
||||
|
||||
/**
|
||||
* Sets the value of a configuration key.
|
||||
* @param key The key of the configuration value to set.
|
||||
* @param value The value to set.
|
||||
*/
|
||||
setConfigurationValue<K extends keyof RooCodeSettings>(key: K, value: RooCodeSettings[K]): Promise<void>
|
||||
|
||||
/**
|
||||
* Returns true if the API is ready to use.
|
||||
*/
|
||||
isReady(): boolean
|
||||
|
||||
/**
|
||||
* Returns the messages for a given task.
|
||||
* @param taskId The ID of the task.
|
||||
* @returns An array of ClineMessage objects.
|
||||
*/
|
||||
getMessages(taskId: string): ClineMessage[]
|
||||
|
||||
/**
|
||||
* Returns the token usage for a given task.
|
||||
* @param taskId The ID of the task.
|
||||
* @returns A TokenUsage object.
|
||||
*/
|
||||
getTokenUsage(taskId: string): TokenUsage
|
||||
|
||||
/**
|
||||
* Logs a message to the output channel.
|
||||
* @param message The message to log.
|
||||
*/
|
||||
log(message: string): void
|
||||
}
|
||||
911
src/exports/roo-code.d.ts
vendored
911
src/exports/roo-code.d.ts
vendored
|
|
@ -1,18 +1,412 @@
|
|||
import * as vscode from "vscode"
|
||||
|
||||
import { EventEmitter } from "events"
|
||||
|
||||
export interface TokenUsage {
|
||||
type ProviderSettings = {
|
||||
apiProvider?:
|
||||
| (
|
||||
| "anthropic"
|
||||
| "glama"
|
||||
| "openrouter"
|
||||
| "bedrock"
|
||||
| "vertex"
|
||||
| "openai"
|
||||
| "ollama"
|
||||
| "vscode-lm"
|
||||
| "lmstudio"
|
||||
| "gemini"
|
||||
| "openai-native"
|
||||
| "mistral"
|
||||
| "deepseek"
|
||||
| "unbound"
|
||||
| "requesty"
|
||||
| "human-relay"
|
||||
| "fake-ai"
|
||||
)
|
||||
| undefined
|
||||
apiModelId?: string | undefined
|
||||
apiKey?: string | undefined
|
||||
anthropicBaseUrl?: string | undefined
|
||||
glamaModelId?: string | undefined
|
||||
glamaModelInfo?:
|
||||
| {
|
||||
maxTokens?: number | undefined
|
||||
contextWindow: number
|
||||
supportsImages?: boolean | undefined
|
||||
supportsComputerUse?: boolean | undefined
|
||||
supportsPromptCache: boolean
|
||||
inputPrice?: number | undefined
|
||||
outputPrice?: number | undefined
|
||||
cacheWritesPrice?: number | undefined
|
||||
cacheReadsPrice?: number | undefined
|
||||
description?: string | undefined
|
||||
reasoningEffort?: ("low" | "medium" | "high") | undefined
|
||||
thinking?: boolean | undefined
|
||||
}
|
||||
| undefined
|
||||
glamaApiKey?: string | undefined
|
||||
openRouterApiKey?: string | undefined
|
||||
openRouterModelId?: string | undefined
|
||||
openRouterModelInfo?:
|
||||
| {
|
||||
maxTokens?: number | undefined
|
||||
contextWindow: number
|
||||
supportsImages?: boolean | undefined
|
||||
supportsComputerUse?: boolean | undefined
|
||||
supportsPromptCache: boolean
|
||||
inputPrice?: number | undefined
|
||||
outputPrice?: number | undefined
|
||||
cacheWritesPrice?: number | undefined
|
||||
cacheReadsPrice?: number | undefined
|
||||
description?: string | undefined
|
||||
reasoningEffort?: ("low" | "medium" | "high") | undefined
|
||||
thinking?: boolean | undefined
|
||||
}
|
||||
| undefined
|
||||
openRouterBaseUrl?: string | undefined
|
||||
openRouterSpecificProvider?: string | undefined
|
||||
openRouterUseMiddleOutTransform?: boolean | undefined
|
||||
awsAccessKey?: string | undefined
|
||||
awsSecretKey?: string | undefined
|
||||
awsSessionToken?: string | undefined
|
||||
awsRegion?: string | undefined
|
||||
awsUseCrossRegionInference?: boolean | undefined
|
||||
awsUsePromptCache?: boolean | undefined
|
||||
awspromptCacheId?: string | undefined
|
||||
awsProfile?: string | undefined
|
||||
awsUseProfile?: boolean | undefined
|
||||
awsCustomArn?: string | undefined
|
||||
vertexKeyFile?: string | undefined
|
||||
vertexJsonCredentials?: string | undefined
|
||||
vertexProjectId?: string | undefined
|
||||
vertexRegion?: string | undefined
|
||||
openAiBaseUrl?: string | undefined
|
||||
openAiApiKey?: string | undefined
|
||||
openAiR1FormatEnabled?: boolean | undefined
|
||||
openAiModelId?: string | undefined
|
||||
openAiCustomModelInfo?:
|
||||
| {
|
||||
maxTokens?: number | undefined
|
||||
contextWindow: number
|
||||
supportsImages?: boolean | undefined
|
||||
supportsComputerUse?: boolean | undefined
|
||||
supportsPromptCache: boolean
|
||||
inputPrice?: number | undefined
|
||||
outputPrice?: number | undefined
|
||||
cacheWritesPrice?: number | undefined
|
||||
cacheReadsPrice?: number | undefined
|
||||
description?: string | undefined
|
||||
reasoningEffort?: ("low" | "medium" | "high") | undefined
|
||||
thinking?: boolean | undefined
|
||||
}
|
||||
| undefined
|
||||
openAiUseAzure?: boolean | undefined
|
||||
azureApiVersion?: string | undefined
|
||||
openAiStreamingEnabled?: boolean | undefined
|
||||
ollamaModelId?: string | undefined
|
||||
ollamaBaseUrl?: string | undefined
|
||||
vsCodeLmModelSelector?:
|
||||
| {
|
||||
vendor?: string | undefined
|
||||
family?: string | undefined
|
||||
version?: string | undefined
|
||||
id?: string | undefined
|
||||
}
|
||||
| undefined
|
||||
lmStudioModelId?: string | undefined
|
||||
lmStudioBaseUrl?: string | undefined
|
||||
lmStudioDraftModelId?: string | undefined
|
||||
lmStudioSpeculativeDecodingEnabled?: boolean | undefined
|
||||
geminiApiKey?: string | undefined
|
||||
googleGeminiBaseUrl?: string | undefined
|
||||
openAiNativeApiKey?: string | undefined
|
||||
mistralApiKey?: string | undefined
|
||||
mistralCodestralUrl?: string | undefined
|
||||
deepSeekBaseUrl?: string | undefined
|
||||
deepSeekApiKey?: string | undefined
|
||||
unboundApiKey?: string | undefined
|
||||
unboundModelId?: string | undefined
|
||||
unboundModelInfo?:
|
||||
| {
|
||||
maxTokens?: number | undefined
|
||||
contextWindow: number
|
||||
supportsImages?: boolean | undefined
|
||||
supportsComputerUse?: boolean | undefined
|
||||
supportsPromptCache: boolean
|
||||
inputPrice?: number | undefined
|
||||
outputPrice?: number | undefined
|
||||
cacheWritesPrice?: number | undefined
|
||||
cacheReadsPrice?: number | undefined
|
||||
description?: string | undefined
|
||||
reasoningEffort?: ("low" | "medium" | "high") | undefined
|
||||
thinking?: boolean | undefined
|
||||
}
|
||||
| undefined
|
||||
requestyApiKey?: string | undefined
|
||||
requestyModelId?: string | undefined
|
||||
requestyModelInfo?:
|
||||
| {
|
||||
maxTokens?: number | undefined
|
||||
contextWindow: number
|
||||
supportsImages?: boolean | undefined
|
||||
supportsComputerUse?: boolean | undefined
|
||||
supportsPromptCache: boolean
|
||||
inputPrice?: number | undefined
|
||||
outputPrice?: number | undefined
|
||||
cacheWritesPrice?: number | undefined
|
||||
cacheReadsPrice?: number | undefined
|
||||
description?: string | undefined
|
||||
reasoningEffort?: ("low" | "medium" | "high") | undefined
|
||||
thinking?: boolean | undefined
|
||||
}
|
||||
| undefined
|
||||
modelTemperature?: (number | null) | undefined
|
||||
modelMaxTokens?: number | undefined
|
||||
modelMaxThinkingTokens?: number | undefined
|
||||
includeMaxTokens?: boolean | undefined
|
||||
fakeAi?: unknown | undefined
|
||||
}
|
||||
|
||||
type GlobalSettings = {
|
||||
currentApiConfigName?: string | undefined
|
||||
listApiConfigMeta?:
|
||||
| {
|
||||
id: string
|
||||
name: string
|
||||
apiProvider?:
|
||||
| (
|
||||
| "anthropic"
|
||||
| "glama"
|
||||
| "openrouter"
|
||||
| "bedrock"
|
||||
| "vertex"
|
||||
| "openai"
|
||||
| "ollama"
|
||||
| "vscode-lm"
|
||||
| "lmstudio"
|
||||
| "gemini"
|
||||
| "openai-native"
|
||||
| "mistral"
|
||||
| "deepseek"
|
||||
| "unbound"
|
||||
| "requesty"
|
||||
| "human-relay"
|
||||
| "fake-ai"
|
||||
)
|
||||
| undefined
|
||||
}[]
|
||||
| undefined
|
||||
pinnedApiConfigs?:
|
||||
| {
|
||||
[x: string]: boolean
|
||||
}
|
||||
| undefined
|
||||
lastShownAnnouncementId?: string | undefined
|
||||
customInstructions?: string | undefined
|
||||
taskHistory?:
|
||||
| {
|
||||
id: string
|
||||
number: number
|
||||
ts: number
|
||||
task: string
|
||||
tokensIn: number
|
||||
tokensOut: number
|
||||
cacheWrites?: number | undefined
|
||||
cacheReads?: number | undefined
|
||||
totalCost: number
|
||||
size?: number | undefined
|
||||
}[]
|
||||
| undefined
|
||||
autoApprovalEnabled?: boolean | undefined
|
||||
alwaysAllowReadOnly?: boolean | undefined
|
||||
alwaysAllowReadOnlyOutsideWorkspace?: boolean | undefined
|
||||
alwaysAllowWrite?: boolean | undefined
|
||||
alwaysAllowWriteOutsideWorkspace?: boolean | undefined
|
||||
writeDelayMs?: number | undefined
|
||||
alwaysAllowBrowser?: boolean | undefined
|
||||
alwaysApproveResubmit?: boolean | undefined
|
||||
requestDelaySeconds?: number | undefined
|
||||
alwaysAllowMcp?: boolean | undefined
|
||||
alwaysAllowModeSwitch?: boolean | undefined
|
||||
alwaysAllowSubtasks?: boolean | undefined
|
||||
alwaysAllowExecute?: boolean | undefined
|
||||
allowedCommands?: string[] | undefined
|
||||
browserToolEnabled?: boolean | undefined
|
||||
browserViewportSize?: string | undefined
|
||||
screenshotQuality?: number | undefined
|
||||
remoteBrowserEnabled?: boolean | undefined
|
||||
remoteBrowserHost?: string | undefined
|
||||
enableCheckpoints?: boolean | undefined
|
||||
checkpointStorage?: ("task" | "workspace") | undefined
|
||||
ttsEnabled?: boolean | undefined
|
||||
ttsSpeed?: number | undefined
|
||||
soundEnabled?: boolean | undefined
|
||||
soundVolume?: number | undefined
|
||||
maxOpenTabsContext?: number | undefined
|
||||
maxWorkspaceFiles?: number | undefined
|
||||
showRooIgnoredFiles?: boolean | undefined
|
||||
maxReadFileLine?: number | undefined
|
||||
terminalOutputLineLimit?: number | undefined
|
||||
terminalShellIntegrationTimeout?: number | undefined
|
||||
rateLimitSeconds?: number | undefined
|
||||
diffEnabled?: boolean | undefined
|
||||
fuzzyMatchThreshold?: number | undefined
|
||||
experiments?:
|
||||
| {
|
||||
experimentalDiffStrategy: boolean
|
||||
search_and_replace: boolean
|
||||
insert_content: boolean
|
||||
powerSteering: boolean
|
||||
multi_search_and_replace: boolean
|
||||
}
|
||||
| undefined
|
||||
language?:
|
||||
| (
|
||||
| "ca"
|
||||
| "de"
|
||||
| "en"
|
||||
| "es"
|
||||
| "fr"
|
||||
| "hi"
|
||||
| "it"
|
||||
| "ja"
|
||||
| "ko"
|
||||
| "pl"
|
||||
| "pt-BR"
|
||||
| "tr"
|
||||
| "vi"
|
||||
| "zh-CN"
|
||||
| "zh-TW"
|
||||
)
|
||||
| undefined
|
||||
telemetrySetting?: ("unset" | "enabled" | "disabled") | undefined
|
||||
mcpEnabled?: boolean | undefined
|
||||
enableMcpServerCreation?: boolean | undefined
|
||||
mode?: string | undefined
|
||||
modeApiConfigs?:
|
||||
| {
|
||||
[x: string]: string
|
||||
}
|
||||
| undefined
|
||||
customModes?:
|
||||
| {
|
||||
slug: string
|
||||
name: string
|
||||
roleDefinition: string
|
||||
customInstructions?: string | undefined
|
||||
groups: (
|
||||
| ("read" | "edit" | "browser" | "command" | "mcp" | "modes")
|
||||
| [
|
||||
"read" | "edit" | "browser" | "command" | "mcp" | "modes",
|
||||
{
|
||||
fileRegex?: string | undefined
|
||||
description?: string | undefined
|
||||
},
|
||||
]
|
||||
)[]
|
||||
source?: ("global" | "project") | undefined
|
||||
}[]
|
||||
| undefined
|
||||
customModePrompts?:
|
||||
| {
|
||||
[x: string]:
|
||||
| {
|
||||
roleDefinition?: string | undefined
|
||||
customInstructions?: string | undefined
|
||||
}
|
||||
| undefined
|
||||
}
|
||||
| undefined
|
||||
customSupportPrompts?:
|
||||
| {
|
||||
[x: string]: string | undefined
|
||||
}
|
||||
| undefined
|
||||
enhancementApiConfigId?: string | undefined
|
||||
}
|
||||
|
||||
type ClineMessage = {
|
||||
ts: number
|
||||
type: "ask" | "say"
|
||||
ask?:
|
||||
| (
|
||||
| "followup"
|
||||
| "command"
|
||||
| "command_output"
|
||||
| "completion_result"
|
||||
| "tool"
|
||||
| "api_req_failed"
|
||||
| "resume_task"
|
||||
| "resume_completed_task"
|
||||
| "mistake_limit_reached"
|
||||
| "browser_action_launch"
|
||||
| "use_mcp_server"
|
||||
| "finishTask"
|
||||
)
|
||||
| undefined
|
||||
say?:
|
||||
| (
|
||||
| "task"
|
||||
| "error"
|
||||
| "api_req_started"
|
||||
| "api_req_finished"
|
||||
| "api_req_retried"
|
||||
| "api_req_retry_delayed"
|
||||
| "api_req_deleted"
|
||||
| "text"
|
||||
| "reasoning"
|
||||
| "completion_result"
|
||||
| "user_feedback"
|
||||
| "user_feedback_diff"
|
||||
| "command_output"
|
||||
| "tool"
|
||||
| "shell_integration_warning"
|
||||
| "browser_action"
|
||||
| "browser_action_result"
|
||||
| "command"
|
||||
| "mcp_server_request_started"
|
||||
| "mcp_server_response"
|
||||
| "new_task_started"
|
||||
| "new_task"
|
||||
| "checkpoint_saved"
|
||||
| "rooignore_error"
|
||||
)
|
||||
| undefined
|
||||
text?: string | undefined
|
||||
images?: string[] | undefined
|
||||
partial?: boolean | undefined
|
||||
reasoning?: string | undefined
|
||||
conversationHistoryIndex?: number | undefined
|
||||
checkpoint?:
|
||||
| {
|
||||
[x: string]: unknown
|
||||
}
|
||||
| undefined
|
||||
progressStatus?:
|
||||
| {
|
||||
icon?: string | undefined
|
||||
text?: string | undefined
|
||||
}
|
||||
| undefined
|
||||
}
|
||||
|
||||
type TokenUsage = {
|
||||
totalTokensIn: number
|
||||
totalTokensOut: number
|
||||
totalCacheWrites?: number
|
||||
totalCacheReads?: number
|
||||
totalCacheWrites?: number | undefined
|
||||
totalCacheReads?: number | undefined
|
||||
totalCost: number
|
||||
contextTokens: number
|
||||
}
|
||||
|
||||
export interface RooCodeEvents {
|
||||
message: [{ taskId: string; action: "created" | "updated"; message: ClineMessage }]
|
||||
type RooCodeSettings = GlobalSettings & ProviderSettings
|
||||
|
||||
interface RooCodeEvents {
|
||||
message: [
|
||||
{
|
||||
taskId: string
|
||||
action: "created" | "updated"
|
||||
message: ClineMessage
|
||||
},
|
||||
]
|
||||
taskCreated: [taskId: string]
|
||||
taskStarted: [taskId: string]
|
||||
taskPaused: [taskId: string]
|
||||
|
|
@ -23,8 +417,7 @@ export interface RooCodeEvents {
|
|||
taskCompleted: [taskId: string, usage: TokenUsage]
|
||||
taskTokenUsageUpdated: [taskId: string, usage: TokenUsage]
|
||||
}
|
||||
|
||||
export interface RooCodeAPI extends EventEmitter<RooCodeEvents> {
|
||||
interface RooCodeAPI extends EventEmitter<RooCodeEvents> {
|
||||
/**
|
||||
* Starts a new task with an optional initial message and images.
|
||||
* @param task Optional initial task message.
|
||||
|
|
@ -32,65 +425,71 @@ export interface RooCodeAPI extends EventEmitter<RooCodeEvents> {
|
|||
* @returns The ID of the new task.
|
||||
*/
|
||||
startNewTask(task?: string, images?: string[]): Promise<string>
|
||||
|
||||
/**
|
||||
* Returns the current task stack.
|
||||
* @returns An array of task IDs.
|
||||
*/
|
||||
getCurrentTaskStack(): string[]
|
||||
|
||||
/**
|
||||
* Clears the current task.
|
||||
*/
|
||||
clearCurrentTask(lastMessage?: string): Promise<void>
|
||||
|
||||
/**
|
||||
* Cancels the current task.
|
||||
*/
|
||||
cancelCurrentTask(): Promise<void>
|
||||
|
||||
/**
|
||||
* Sends a message to the current task.
|
||||
* @param message Optional message to send.
|
||||
* @param images Optional array of image data URIs (e.g., "data:image/webp;base64,...").
|
||||
*/
|
||||
sendMessage(message?: string, images?: string[]): Promise<void>
|
||||
|
||||
/**
|
||||
* Simulates pressing the primary button in the chat interface.
|
||||
*/
|
||||
pressPrimaryButton(): Promise<void>
|
||||
|
||||
/**
|
||||
* Simulates pressing the secondary button in the chat interface.
|
||||
*/
|
||||
pressSecondaryButton(): Promise<void>
|
||||
|
||||
/**
|
||||
* Returns the current configuration.
|
||||
* @returns The current configuration.
|
||||
*/
|
||||
getConfiguration(): RooCodeSettings
|
||||
/**
|
||||
* Returns the value of a configuration key.
|
||||
* @param key The key of the configuration value to return.
|
||||
* @returns The value of the configuration key.
|
||||
*/
|
||||
getConfigurationValue<K extends keyof RooCodeSettings>(key: K): RooCodeSettings[K]
|
||||
/**
|
||||
* Sets the configuration for the current task.
|
||||
* @param values An object containing key-value pairs to set.
|
||||
*/
|
||||
setConfiguration(values: Partial<ConfigurationValues>): Promise<void>
|
||||
|
||||
setConfiguration(values: RooCodeSettings): Promise<void>
|
||||
/**
|
||||
* Sets the value of a configuration key.
|
||||
* @param key The key of the configuration value to set.
|
||||
* @param value The value to set.
|
||||
*/
|
||||
setConfigurationValue<K extends keyof RooCodeSettings>(key: K, value: RooCodeSettings[K]): Promise<void>
|
||||
/**
|
||||
* Returns true if the API is ready to use.
|
||||
*/
|
||||
isReady(): boolean
|
||||
|
||||
/**
|
||||
* Returns the messages for a given task.
|
||||
* @param taskId The ID of the task.
|
||||
* @returns An array of ClineMessage objects.
|
||||
*/
|
||||
getMessages(taskId: string): ClineMessage[]
|
||||
|
||||
/**
|
||||
* Returns the token usage for a given task.
|
||||
* @param taskId The ID of the task.
|
||||
* @returns A TokenUsage object.
|
||||
*/
|
||||
getTokenUsage(taskId: string): TokenUsage
|
||||
|
||||
/**
|
||||
* Logs a message to the output channel.
|
||||
* @param message The message to log.
|
||||
|
|
@ -98,472 +497,4 @@ export interface RooCodeAPI extends EventEmitter<RooCodeEvents> {
|
|||
log(message: string): void
|
||||
}
|
||||
|
||||
export type ClineAsk =
|
||||
| "followup"
|
||||
| "command"
|
||||
| "command_output"
|
||||
| "completion_result"
|
||||
| "tool"
|
||||
| "api_req_failed"
|
||||
| "resume_task"
|
||||
| "resume_completed_task"
|
||||
| "mistake_limit_reached"
|
||||
| "browser_action_launch"
|
||||
| "use_mcp_server"
|
||||
| "finishTask"
|
||||
|
||||
export type ClineSay =
|
||||
| "task"
|
||||
| "error"
|
||||
| "api_req_started"
|
||||
| "api_req_finished"
|
||||
| "api_req_retried"
|
||||
| "api_req_retry_delayed"
|
||||
| "api_req_deleted"
|
||||
| "text"
|
||||
| "reasoning"
|
||||
| "completion_result"
|
||||
| "user_feedback"
|
||||
| "user_feedback_diff"
|
||||
| "command_output"
|
||||
| "tool"
|
||||
| "shell_integration_warning"
|
||||
| "browser_action"
|
||||
| "browser_action_result"
|
||||
| "command"
|
||||
| "mcp_server_request_started"
|
||||
| "mcp_server_response"
|
||||
| "new_task_started"
|
||||
| "new_task"
|
||||
| "checkpoint_saved"
|
||||
| "rooignore_error"
|
||||
|
||||
export interface ClineMessage {
|
||||
ts: number
|
||||
type: "ask" | "say"
|
||||
ask?: ClineAsk
|
||||
say?: ClineSay
|
||||
text?: string
|
||||
images?: string[]
|
||||
partial?: boolean
|
||||
reasoning?: string
|
||||
conversationHistoryIndex?: number
|
||||
checkpoint?: Record<string, unknown>
|
||||
progressStatus?: ToolProgressStatus
|
||||
}
|
||||
|
||||
export interface ModelInfo {
|
||||
maxTokens?: number
|
||||
contextWindow: number
|
||||
supportsImages?: boolean
|
||||
supportsComputerUse?: boolean
|
||||
supportsPromptCache: boolean // This value is hardcoded for now.
|
||||
inputPrice?: number
|
||||
outputPrice?: number
|
||||
cacheWritesPrice?: number
|
||||
cacheReadsPrice?: number
|
||||
description?: string
|
||||
reasoningEffort?: "low" | "medium" | "high"
|
||||
thinking?: boolean
|
||||
}
|
||||
|
||||
export interface ApiConfigMeta {
|
||||
id: string
|
||||
name: string
|
||||
apiProvider?: ProviderName
|
||||
}
|
||||
|
||||
export type HistoryItem = {
|
||||
id: string
|
||||
number: number
|
||||
ts: number
|
||||
task: string
|
||||
tokensIn: number
|
||||
tokensOut: number
|
||||
cacheWrites?: number
|
||||
cacheReads?: number
|
||||
totalCost: number
|
||||
size?: number
|
||||
}
|
||||
|
||||
export type ExperimentId =
|
||||
| "experimentalDiffStrategy"
|
||||
| "search_and_replace"
|
||||
| "insert_content"
|
||||
| "powerSteering"
|
||||
| "multi_search_and_replace"
|
||||
|
||||
export type CheckpointStorage = "task" | "workspace"
|
||||
|
||||
export type GroupOptions = {
|
||||
fileRegex?: string // Regular expression pattern.
|
||||
description?: string // Human-readable description of the pattern.
|
||||
}
|
||||
|
||||
export type ToolGroup = "read" | "edit" | "browser" | "command" | "mcp" | "modes"
|
||||
|
||||
export type GroupEntry = ToolGroup | readonly [ToolGroup, GroupOptions]
|
||||
|
||||
export type ModeConfig = {
|
||||
slug: string
|
||||
name: string
|
||||
roleDefinition: string
|
||||
customInstructions?: string
|
||||
groups: readonly GroupEntry[] // Now supports both simple strings and tuples with options
|
||||
source?: "global" | "project" // Where this mode was loaded from
|
||||
}
|
||||
|
||||
export type PromptComponent = {
|
||||
roleDefinition?: string
|
||||
customInstructions?: string
|
||||
}
|
||||
|
||||
export type CustomModePrompts = {
|
||||
[key: string]: PromptComponent | undefined
|
||||
}
|
||||
|
||||
export type CustomSupportPrompts = {
|
||||
[key: string]: string | undefined
|
||||
}
|
||||
|
||||
export type TelemetrySetting = "unset" | "enabled" | "disabled"
|
||||
|
||||
export type Language =
|
||||
| "ca"
|
||||
| "de"
|
||||
| "en"
|
||||
| "es"
|
||||
| "fr"
|
||||
| "hi"
|
||||
| "it"
|
||||
| "ja"
|
||||
| "ko"
|
||||
| "pl"
|
||||
| "pt-BR"
|
||||
| "tr"
|
||||
| "vi"
|
||||
| "zh-CN"
|
||||
| "zh-TW"
|
||||
|
||||
/**
|
||||
* GlobalSettings
|
||||
*
|
||||
* These are settings that apply globally.
|
||||
* They are all stored in the global state.
|
||||
*/
|
||||
|
||||
export interface GlobalSettings {
|
||||
currentApiConfigName?: string
|
||||
listApiConfigMeta?: ApiConfigMeta[]
|
||||
pinnedApiConfigs?: Record<string, boolean>
|
||||
|
||||
lastShownAnnouncementId?: string
|
||||
customInstructions?: string
|
||||
taskHistory?: HistoryItem[]
|
||||
|
||||
autoApprovalEnabled?: boolean
|
||||
alwaysAllowReadOnly?: boolean
|
||||
alwaysAllowReadOnlyOutsideWorkspace?: boolean
|
||||
alwaysAllowWrite?: boolean
|
||||
alwaysAllowWriteOutsideWorkspace?: boolean
|
||||
writeDelayMs?: number
|
||||
alwaysAllowBrowser?: boolean
|
||||
alwaysApproveResubmit?: boolean
|
||||
requestDelaySeconds?: number
|
||||
alwaysAllowMcp?: boolean
|
||||
alwaysAllowModeSwitch?: boolean
|
||||
alwaysAllowSubtasks?: boolean
|
||||
alwaysAllowExecute?: boolean
|
||||
allowedCommands?: string[]
|
||||
|
||||
browserToolEnabled?: boolean
|
||||
browserViewportSize?: string
|
||||
screenshotQuality?: number
|
||||
remoteBrowserEnabled?: boolean
|
||||
remoteBrowserHost?: string
|
||||
|
||||
enableCheckpoints?: boolean
|
||||
checkpointStorage?: CheckpointStorage
|
||||
|
||||
ttsEnabled?: boolean
|
||||
ttsSpeed?: number
|
||||
soundEnabled?: boolean
|
||||
soundVolume?: number
|
||||
|
||||
maxOpenTabsContext?: number
|
||||
maxWorkspaceFiles?: number
|
||||
showRooIgnoredFiles?: boolean
|
||||
maxReadFileLine?: number
|
||||
|
||||
terminalOutputLineLimit?: number
|
||||
terminalShellIntegrationTimeout?: number
|
||||
|
||||
rateLimitSeconds?: number
|
||||
diffEnabled?: boolean
|
||||
fuzzyMatchThreshold?: number
|
||||
experiments?: Record<ExperimentId, boolean> // Map of experiment IDs to their enabled state.
|
||||
|
||||
language?: Language
|
||||
|
||||
telemetrySetting?: TelemetrySetting
|
||||
|
||||
mcpEnabled?: boolean
|
||||
enableMcpServerCreation?: boolean
|
||||
|
||||
mode?: string
|
||||
modeApiConfigs?: Record<string, string>
|
||||
customModes?: ModeConfig[]
|
||||
customModePrompts?: CustomModePrompts
|
||||
customSupportPrompts?: CustomSupportPrompts
|
||||
enhancementApiConfigId?: string
|
||||
}
|
||||
|
||||
export type GlobalSettingsKey = keyof GlobalSettings
|
||||
|
||||
/**
|
||||
* ProviderSettings
|
||||
*
|
||||
* These are settings that apply on a per-provider basis.
|
||||
* Non-sensitive values are stored in the global state.
|
||||
* Sensitive values are stored in VSCode secrets.
|
||||
*/
|
||||
|
||||
/**
|
||||
* DiscriminatedProviderSettings
|
||||
*
|
||||
* NOTE: This is actually how our provider settings should be typed, but it
|
||||
* will take a little elbow grease to move to this shape. For now we're just
|
||||
* using it to generate the `ProviderName`.
|
||||
*/
|
||||
|
||||
export type DiscriminatedProviderSettings =
|
||||
| {
|
||||
apiProvider: "anthropic"
|
||||
apiKey?: string
|
||||
anthropicBaseUrl?: string
|
||||
apiModelId?: string
|
||||
}
|
||||
| {
|
||||
apiProvider: "glama"
|
||||
glamaApiKey?: string
|
||||
glamaModelId?: string
|
||||
}
|
||||
| {
|
||||
apiProvider: "openrouter"
|
||||
openRouterApiKey?: string
|
||||
openRouterModelId?: string
|
||||
openRouterBaseUrl?: string
|
||||
openRouterSpecificProvider?: string
|
||||
openRouterUseMiddleOutTransform?: boolean
|
||||
}
|
||||
| {
|
||||
apiProvider: "bedrock"
|
||||
awsAccessKey?: string
|
||||
awsSecretKey?: string
|
||||
awsSessionToken?: string
|
||||
awsRegion?: string
|
||||
awsUseCrossRegionInference?: boolean
|
||||
awsUsePromptCache?: boolean
|
||||
awspromptCacheId?: string
|
||||
awsProfile?: string
|
||||
awsUseProfile?: boolean
|
||||
awsCustomArn?: string
|
||||
}
|
||||
| {
|
||||
apiProvider: "vertex"
|
||||
vertexKeyFile?: string
|
||||
vertexJsonCredentials?: string
|
||||
vertexProjectId?: string
|
||||
vertexRegion?: string
|
||||
}
|
||||
| {
|
||||
apiProvider: "openai"
|
||||
openAiApiKey?: string
|
||||
openAiBaseUrl?: string
|
||||
openAiR1FormatEnabled?: boolean
|
||||
openAiModelId?: string
|
||||
openAiUseAzure?: boolean
|
||||
azureApiVersion?: string
|
||||
openAiStreamingEnabled?: boolean
|
||||
}
|
||||
| {
|
||||
apiProvider: "ollama"
|
||||
ollamaModelId?: string
|
||||
ollamaBaseUrl?: string
|
||||
}
|
||||
| {
|
||||
apiProvider: "vscode-lm"
|
||||
vsCodeLmModelSelector?: vscode.LanguageModelChatSelector
|
||||
}
|
||||
| {
|
||||
apiProvider: "lmstudio"
|
||||
lmStudioModelId?: string
|
||||
lmStudioBaseUrl?: string
|
||||
lmStudioDraftModelId?: string
|
||||
lmStudioSpeculativeDecodingEnabled?: boolean
|
||||
}
|
||||
| {
|
||||
apiProvider: "gemini"
|
||||
googleGeminiBaseUrl?: string
|
||||
}
|
||||
| {
|
||||
apiProvider: "openai-native"
|
||||
openAiNativeApiKey?: string
|
||||
}
|
||||
| {
|
||||
apiProvider: "mistral"
|
||||
mistralApiKey?: string
|
||||
mistralCodestralUrl?: string
|
||||
}
|
||||
| {
|
||||
apiProvider: "deepseek"
|
||||
deepSeekApiKey?: string
|
||||
deepSeekBaseUrl?: string
|
||||
}
|
||||
| {
|
||||
apiProvider: "unbound"
|
||||
unboundApiKey?: string
|
||||
unboundModelId?: string
|
||||
}
|
||||
| {
|
||||
apiProvider: "requesty"
|
||||
requestyApiKey?: string
|
||||
requestyModelId?: string
|
||||
}
|
||||
| {
|
||||
apiProvider: "human-relay"
|
||||
}
|
||||
| {
|
||||
apiProvider: "fake-ai"
|
||||
fakeAi?: unknown
|
||||
}
|
||||
|
||||
export type ProviderName = DiscriminatedProviderSettings["apiProvider"]
|
||||
|
||||
export interface ProviderSettings {
|
||||
apiProvider?: ProviderName
|
||||
apiModelId?: string
|
||||
// Anthropic
|
||||
apiKey?: string // secret
|
||||
anthropicBaseUrl?: string
|
||||
// Glama
|
||||
glamaApiKey?: string // secret
|
||||
glamaModelId?: string
|
||||
glamaModelInfo?: ModelInfo
|
||||
// OpenRouter
|
||||
openRouterApiKey?: string // secret
|
||||
openRouterModelId?: string
|
||||
openRouterModelInfo?: ModelInfo
|
||||
openRouterBaseUrl?: string
|
||||
openRouterSpecificProvider?: string
|
||||
openRouterUseMiddleOutTransform?: boolean
|
||||
// AWS Bedrock
|
||||
awsAccessKey?: string // secret
|
||||
awsSecretKey?: string // secret
|
||||
awsSessionToken?: string // secret
|
||||
awsRegion?: string
|
||||
awsUseCrossRegionInference?: boolean
|
||||
awsUsePromptCache?: boolean
|
||||
awspromptCacheId?: string
|
||||
awsProfile?: string
|
||||
awsUseProfile?: boolean
|
||||
awsCustomArn?: string
|
||||
// Google Vertex
|
||||
vertexKeyFile?: string
|
||||
vertexJsonCredentials?: string
|
||||
vertexProjectId?: string
|
||||
vertexRegion?: string
|
||||
// OpenAI
|
||||
openAiApiKey?: string // secret
|
||||
openAiBaseUrl?: string
|
||||
openAiR1FormatEnabled?: boolean
|
||||
openAiModelId?: string
|
||||
openAiCustomModelInfo?: ModelInfo
|
||||
openAiUseAzure?: boolean
|
||||
azureApiVersion?: string
|
||||
openAiStreamingEnabled?: boolean
|
||||
// Ollama
|
||||
ollamaModelId?: string
|
||||
ollamaBaseUrl?: string
|
||||
// VS Code LM
|
||||
vsCodeLmModelSelector?: vscode.LanguageModelChatSelector
|
||||
// LM Studio
|
||||
lmStudioModelId?: string
|
||||
lmStudioBaseUrl?: string
|
||||
lmStudioDraftModelId?: string
|
||||
lmStudioSpeculativeDecodingEnabled?: boolean
|
||||
// Gemini
|
||||
geminiApiKey?: string // secret
|
||||
googleGeminiBaseUrl?: string
|
||||
// OpenAI Native
|
||||
openAiNativeApiKey?: string // secret
|
||||
// Mistral
|
||||
mistralApiKey?: string // secret
|
||||
mistralCodestralUrl?: string // New option for Codestral URL.
|
||||
// DeepSeek
|
||||
deepSeekApiKey?: string // secret
|
||||
deepSeekBaseUrl?: string
|
||||
// Unbound
|
||||
unboundApiKey?: string // secret
|
||||
unboundModelId?: string
|
||||
unboundModelInfo?: ModelInfo
|
||||
// Requesty
|
||||
requestyApiKey?: string
|
||||
requestyModelId?: string
|
||||
requestyModelInfo?: ModelInfo
|
||||
// Claude 3.7 Sonnet Thinking
|
||||
modelTemperature?: number | null
|
||||
modelMaxTokens?: number
|
||||
modelMaxThinkingTokens?: number
|
||||
// Generic (For now though, OpenAI, DeekSeek, Mistral, and Requesty make reference to it.)
|
||||
includeMaxTokens?: boolean
|
||||
// Fake AI
|
||||
fakeAi?: unknown
|
||||
}
|
||||
|
||||
export type ProviderSettingsKey = keyof ProviderSettings
|
||||
|
||||
/**
|
||||
* RooCodeSettings
|
||||
*
|
||||
* All settings, irrespective of scope and storage.
|
||||
*/
|
||||
|
||||
export type RooCodeSettings = GlobalSettings & ProviderSettings
|
||||
|
||||
export type RooCodeSettingsKey = keyof RooCodeSettings
|
||||
|
||||
/**
|
||||
* SecretState
|
||||
*
|
||||
* All settings that are stored in VSCode secrets.
|
||||
*/
|
||||
|
||||
export type SecretState = Pick<
|
||||
RooCodeSettings,
|
||||
| "apiKey"
|
||||
| "glamaApiKey"
|
||||
| "openRouterApiKey"
|
||||
| "awsAccessKey"
|
||||
| "awsSecretKey"
|
||||
| "awsSessionToken"
|
||||
| "openAiApiKey"
|
||||
| "geminiApiKey"
|
||||
| "openAiNativeApiKey"
|
||||
| "deepSeekApiKey"
|
||||
| "mistralApiKey"
|
||||
| "unboundApiKey"
|
||||
| "requestyApiKey"
|
||||
>
|
||||
|
||||
export type SecretStateKey = keyof SecretState
|
||||
|
||||
/**
|
||||
* GlobalState
|
||||
*
|
||||
* All settings that are stored in the global state.
|
||||
*/
|
||||
|
||||
export type GlobalState = Omit<RooCodeSettings, SecretStateKey>
|
||||
|
||||
export type GlobalStateKey = keyof GlobalState
|
||||
export type { ClineMessage, GlobalSettings, ProviderSettings, RooCodeAPI, RooCodeEvents, RooCodeSettings, TokenUsage }
|
||||
|
|
|
|||
407
src/exports/types.ts
Normal file
407
src/exports/types.ts
Normal file
|
|
@ -0,0 +1,407 @@
|
|||
// This file is automatically generated by running `npm run generate-types`
|
||||
// Do not edit it directly.
|
||||
|
||||
type ProviderSettings = {
|
||||
apiProvider?:
|
||||
| (
|
||||
| "anthropic"
|
||||
| "glama"
|
||||
| "openrouter"
|
||||
| "bedrock"
|
||||
| "vertex"
|
||||
| "openai"
|
||||
| "ollama"
|
||||
| "vscode-lm"
|
||||
| "lmstudio"
|
||||
| "gemini"
|
||||
| "openai-native"
|
||||
| "mistral"
|
||||
| "deepseek"
|
||||
| "unbound"
|
||||
| "requesty"
|
||||
| "human-relay"
|
||||
| "fake-ai"
|
||||
)
|
||||
| undefined
|
||||
apiModelId?: string | undefined
|
||||
apiKey?: string | undefined
|
||||
anthropicBaseUrl?: string | undefined
|
||||
glamaModelId?: string | undefined
|
||||
glamaModelInfo?:
|
||||
| {
|
||||
maxTokens?: number | undefined
|
||||
contextWindow: number
|
||||
supportsImages?: boolean | undefined
|
||||
supportsComputerUse?: boolean | undefined
|
||||
supportsPromptCache: boolean
|
||||
inputPrice?: number | undefined
|
||||
outputPrice?: number | undefined
|
||||
cacheWritesPrice?: number | undefined
|
||||
cacheReadsPrice?: number | undefined
|
||||
description?: string | undefined
|
||||
reasoningEffort?: ("low" | "medium" | "high") | undefined
|
||||
thinking?: boolean | undefined
|
||||
}
|
||||
| undefined
|
||||
glamaApiKey?: string | undefined
|
||||
openRouterApiKey?: string | undefined
|
||||
openRouterModelId?: string | undefined
|
||||
openRouterModelInfo?:
|
||||
| {
|
||||
maxTokens?: number | undefined
|
||||
contextWindow: number
|
||||
supportsImages?: boolean | undefined
|
||||
supportsComputerUse?: boolean | undefined
|
||||
supportsPromptCache: boolean
|
||||
inputPrice?: number | undefined
|
||||
outputPrice?: number | undefined
|
||||
cacheWritesPrice?: number | undefined
|
||||
cacheReadsPrice?: number | undefined
|
||||
description?: string | undefined
|
||||
reasoningEffort?: ("low" | "medium" | "high") | undefined
|
||||
thinking?: boolean | undefined
|
||||
}
|
||||
| undefined
|
||||
openRouterBaseUrl?: string | undefined
|
||||
openRouterSpecificProvider?: string | undefined
|
||||
openRouterUseMiddleOutTransform?: boolean | undefined
|
||||
awsAccessKey?: string | undefined
|
||||
awsSecretKey?: string | undefined
|
||||
awsSessionToken?: string | undefined
|
||||
awsRegion?: string | undefined
|
||||
awsUseCrossRegionInference?: boolean | undefined
|
||||
awsUsePromptCache?: boolean | undefined
|
||||
awspromptCacheId?: string | undefined
|
||||
awsProfile?: string | undefined
|
||||
awsUseProfile?: boolean | undefined
|
||||
awsCustomArn?: string | undefined
|
||||
vertexKeyFile?: string | undefined
|
||||
vertexJsonCredentials?: string | undefined
|
||||
vertexProjectId?: string | undefined
|
||||
vertexRegion?: string | undefined
|
||||
openAiBaseUrl?: string | undefined
|
||||
openAiApiKey?: string | undefined
|
||||
openAiR1FormatEnabled?: boolean | undefined
|
||||
openAiModelId?: string | undefined
|
||||
openAiCustomModelInfo?:
|
||||
| {
|
||||
maxTokens?: number | undefined
|
||||
contextWindow: number
|
||||
supportsImages?: boolean | undefined
|
||||
supportsComputerUse?: boolean | undefined
|
||||
supportsPromptCache: boolean
|
||||
inputPrice?: number | undefined
|
||||
outputPrice?: number | undefined
|
||||
cacheWritesPrice?: number | undefined
|
||||
cacheReadsPrice?: number | undefined
|
||||
description?: string | undefined
|
||||
reasoningEffort?: ("low" | "medium" | "high") | undefined
|
||||
thinking?: boolean | undefined
|
||||
}
|
||||
| undefined
|
||||
openAiUseAzure?: boolean | undefined
|
||||
azureApiVersion?: string | undefined
|
||||
openAiStreamingEnabled?: boolean | undefined
|
||||
ollamaModelId?: string | undefined
|
||||
ollamaBaseUrl?: string | undefined
|
||||
vsCodeLmModelSelector?:
|
||||
| {
|
||||
vendor?: string | undefined
|
||||
family?: string | undefined
|
||||
version?: string | undefined
|
||||
id?: string | undefined
|
||||
}
|
||||
| undefined
|
||||
lmStudioModelId?: string | undefined
|
||||
lmStudioBaseUrl?: string | undefined
|
||||
lmStudioDraftModelId?: string | undefined
|
||||
lmStudioSpeculativeDecodingEnabled?: boolean | undefined
|
||||
geminiApiKey?: string | undefined
|
||||
googleGeminiBaseUrl?: string | undefined
|
||||
openAiNativeApiKey?: string | undefined
|
||||
mistralApiKey?: string | undefined
|
||||
mistralCodestralUrl?: string | undefined
|
||||
deepSeekBaseUrl?: string | undefined
|
||||
deepSeekApiKey?: string | undefined
|
||||
unboundApiKey?: string | undefined
|
||||
unboundModelId?: string | undefined
|
||||
unboundModelInfo?:
|
||||
| {
|
||||
maxTokens?: number | undefined
|
||||
contextWindow: number
|
||||
supportsImages?: boolean | undefined
|
||||
supportsComputerUse?: boolean | undefined
|
||||
supportsPromptCache: boolean
|
||||
inputPrice?: number | undefined
|
||||
outputPrice?: number | undefined
|
||||
cacheWritesPrice?: number | undefined
|
||||
cacheReadsPrice?: number | undefined
|
||||
description?: string | undefined
|
||||
reasoningEffort?: ("low" | "medium" | "high") | undefined
|
||||
thinking?: boolean | undefined
|
||||
}
|
||||
| undefined
|
||||
requestyApiKey?: string | undefined
|
||||
requestyModelId?: string | undefined
|
||||
requestyModelInfo?:
|
||||
| {
|
||||
maxTokens?: number | undefined
|
||||
contextWindow: number
|
||||
supportsImages?: boolean | undefined
|
||||
supportsComputerUse?: boolean | undefined
|
||||
supportsPromptCache: boolean
|
||||
inputPrice?: number | undefined
|
||||
outputPrice?: number | undefined
|
||||
cacheWritesPrice?: number | undefined
|
||||
cacheReadsPrice?: number | undefined
|
||||
description?: string | undefined
|
||||
reasoningEffort?: ("low" | "medium" | "high") | undefined
|
||||
thinking?: boolean | undefined
|
||||
}
|
||||
| undefined
|
||||
modelTemperature?: (number | null) | undefined
|
||||
modelMaxTokens?: number | undefined
|
||||
modelMaxThinkingTokens?: number | undefined
|
||||
includeMaxTokens?: boolean | undefined
|
||||
fakeAi?: unknown | undefined
|
||||
}
|
||||
|
||||
export type { ProviderSettings }
|
||||
|
||||
type GlobalSettings = {
|
||||
currentApiConfigName?: string | undefined
|
||||
listApiConfigMeta?:
|
||||
| {
|
||||
id: string
|
||||
name: string
|
||||
apiProvider?:
|
||||
| (
|
||||
| "anthropic"
|
||||
| "glama"
|
||||
| "openrouter"
|
||||
| "bedrock"
|
||||
| "vertex"
|
||||
| "openai"
|
||||
| "ollama"
|
||||
| "vscode-lm"
|
||||
| "lmstudio"
|
||||
| "gemini"
|
||||
| "openai-native"
|
||||
| "mistral"
|
||||
| "deepseek"
|
||||
| "unbound"
|
||||
| "requesty"
|
||||
| "human-relay"
|
||||
| "fake-ai"
|
||||
)
|
||||
| undefined
|
||||
}[]
|
||||
| undefined
|
||||
pinnedApiConfigs?:
|
||||
| {
|
||||
[x: string]: boolean
|
||||
}
|
||||
| undefined
|
||||
lastShownAnnouncementId?: string | undefined
|
||||
customInstructions?: string | undefined
|
||||
taskHistory?:
|
||||
| {
|
||||
id: string
|
||||
number: number
|
||||
ts: number
|
||||
task: string
|
||||
tokensIn: number
|
||||
tokensOut: number
|
||||
cacheWrites?: number | undefined
|
||||
cacheReads?: number | undefined
|
||||
totalCost: number
|
||||
size?: number | undefined
|
||||
}[]
|
||||
| undefined
|
||||
autoApprovalEnabled?: boolean | undefined
|
||||
alwaysAllowReadOnly?: boolean | undefined
|
||||
alwaysAllowReadOnlyOutsideWorkspace?: boolean | undefined
|
||||
alwaysAllowWrite?: boolean | undefined
|
||||
alwaysAllowWriteOutsideWorkspace?: boolean | undefined
|
||||
writeDelayMs?: number | undefined
|
||||
alwaysAllowBrowser?: boolean | undefined
|
||||
alwaysApproveResubmit?: boolean | undefined
|
||||
requestDelaySeconds?: number | undefined
|
||||
alwaysAllowMcp?: boolean | undefined
|
||||
alwaysAllowModeSwitch?: boolean | undefined
|
||||
alwaysAllowSubtasks?: boolean | undefined
|
||||
alwaysAllowExecute?: boolean | undefined
|
||||
allowedCommands?: string[] | undefined
|
||||
browserToolEnabled?: boolean | undefined
|
||||
browserViewportSize?: string | undefined
|
||||
screenshotQuality?: number | undefined
|
||||
remoteBrowserEnabled?: boolean | undefined
|
||||
remoteBrowserHost?: string | undefined
|
||||
enableCheckpoints?: boolean | undefined
|
||||
checkpointStorage?: ("task" | "workspace") | undefined
|
||||
ttsEnabled?: boolean | undefined
|
||||
ttsSpeed?: number | undefined
|
||||
soundEnabled?: boolean | undefined
|
||||
soundVolume?: number | undefined
|
||||
maxOpenTabsContext?: number | undefined
|
||||
maxWorkspaceFiles?: number | undefined
|
||||
showRooIgnoredFiles?: boolean | undefined
|
||||
maxReadFileLine?: number | undefined
|
||||
terminalOutputLineLimit?: number | undefined
|
||||
terminalShellIntegrationTimeout?: number | undefined
|
||||
rateLimitSeconds?: number | undefined
|
||||
diffEnabled?: boolean | undefined
|
||||
fuzzyMatchThreshold?: number | undefined
|
||||
experiments?:
|
||||
| {
|
||||
experimentalDiffStrategy: boolean
|
||||
search_and_replace: boolean
|
||||
insert_content: boolean
|
||||
powerSteering: boolean
|
||||
multi_search_and_replace: boolean
|
||||
}
|
||||
| undefined
|
||||
language?:
|
||||
| (
|
||||
| "ca"
|
||||
| "de"
|
||||
| "en"
|
||||
| "es"
|
||||
| "fr"
|
||||
| "hi"
|
||||
| "it"
|
||||
| "ja"
|
||||
| "ko"
|
||||
| "pl"
|
||||
| "pt-BR"
|
||||
| "tr"
|
||||
| "vi"
|
||||
| "zh-CN"
|
||||
| "zh-TW"
|
||||
)
|
||||
| undefined
|
||||
telemetrySetting?: ("unset" | "enabled" | "disabled") | undefined
|
||||
mcpEnabled?: boolean | undefined
|
||||
enableMcpServerCreation?: boolean | undefined
|
||||
mode?: string | undefined
|
||||
modeApiConfigs?:
|
||||
| {
|
||||
[x: string]: string
|
||||
}
|
||||
| undefined
|
||||
customModes?:
|
||||
| {
|
||||
slug: string
|
||||
name: string
|
||||
roleDefinition: string
|
||||
customInstructions?: string | undefined
|
||||
groups: (
|
||||
| ("read" | "edit" | "browser" | "command" | "mcp" | "modes")
|
||||
| [
|
||||
"read" | "edit" | "browser" | "command" | "mcp" | "modes",
|
||||
{
|
||||
fileRegex?: string | undefined
|
||||
description?: string | undefined
|
||||
},
|
||||
]
|
||||
)[]
|
||||
source?: ("global" | "project") | undefined
|
||||
}[]
|
||||
| undefined
|
||||
customModePrompts?:
|
||||
| {
|
||||
[x: string]:
|
||||
| {
|
||||
roleDefinition?: string | undefined
|
||||
customInstructions?: string | undefined
|
||||
}
|
||||
| undefined
|
||||
}
|
||||
| undefined
|
||||
customSupportPrompts?:
|
||||
| {
|
||||
[x: string]: string | undefined
|
||||
}
|
||||
| undefined
|
||||
enhancementApiConfigId?: string | undefined
|
||||
}
|
||||
|
||||
export type { GlobalSettings }
|
||||
|
||||
type ClineMessage = {
|
||||
ts: number
|
||||
type: "ask" | "say"
|
||||
ask?:
|
||||
| (
|
||||
| "followup"
|
||||
| "command"
|
||||
| "command_output"
|
||||
| "completion_result"
|
||||
| "tool"
|
||||
| "api_req_failed"
|
||||
| "resume_task"
|
||||
| "resume_completed_task"
|
||||
| "mistake_limit_reached"
|
||||
| "browser_action_launch"
|
||||
| "use_mcp_server"
|
||||
| "finishTask"
|
||||
)
|
||||
| undefined
|
||||
say?:
|
||||
| (
|
||||
| "task"
|
||||
| "error"
|
||||
| "api_req_started"
|
||||
| "api_req_finished"
|
||||
| "api_req_retried"
|
||||
| "api_req_retry_delayed"
|
||||
| "api_req_deleted"
|
||||
| "text"
|
||||
| "reasoning"
|
||||
| "completion_result"
|
||||
| "user_feedback"
|
||||
| "user_feedback_diff"
|
||||
| "command_output"
|
||||
| "tool"
|
||||
| "shell_integration_warning"
|
||||
| "browser_action"
|
||||
| "browser_action_result"
|
||||
| "command"
|
||||
| "mcp_server_request_started"
|
||||
| "mcp_server_response"
|
||||
| "new_task_started"
|
||||
| "new_task"
|
||||
| "checkpoint_saved"
|
||||
| "rooignore_error"
|
||||
)
|
||||
| undefined
|
||||
text?: string | undefined
|
||||
images?: string[] | undefined
|
||||
partial?: boolean | undefined
|
||||
reasoning?: string | undefined
|
||||
conversationHistoryIndex?: number | undefined
|
||||
checkpoint?:
|
||||
| {
|
||||
[x: string]: unknown
|
||||
}
|
||||
| undefined
|
||||
progressStatus?:
|
||||
| {
|
||||
icon?: string | undefined
|
||||
text?: string | undefined
|
||||
}
|
||||
| undefined
|
||||
}
|
||||
|
||||
export type { ClineMessage }
|
||||
|
||||
type TokenUsage = {
|
||||
totalTokensIn: number
|
||||
totalTokensOut: number
|
||||
totalCacheWrites?: number | undefined
|
||||
totalCacheReads?: number | undefined
|
||||
totalCost: number
|
||||
contextTokens: number
|
||||
}
|
||||
|
||||
export type { TokenUsage }
|
||||
17
src/schemas/__tests__/index.test.ts
Normal file
17
src/schemas/__tests__/index.test.ts
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
// npx jest src/schemas/__tests__/index.test.ts
|
||||
|
||||
import { GLOBAL_STATE_KEYS } from "../index"
|
||||
|
||||
describe("GLOBAL_STATE_KEYS", () => {
|
||||
it("should contain provider settings keys", () => {
|
||||
expect(GLOBAL_STATE_KEYS).toContain("autoApprovalEnabled")
|
||||
})
|
||||
|
||||
it("should contain provider settings keys", () => {
|
||||
expect(GLOBAL_STATE_KEYS).toContain("anthropicBaseUrl")
|
||||
})
|
||||
|
||||
it("should not contain secret state keys", () => {
|
||||
expect(GLOBAL_STATE_KEYS).not.toContain("openRouterApiKey")
|
||||
})
|
||||
})
|
||||
804
src/schemas/index.ts
Normal file
804
src/schemas/index.ts
Normal file
|
|
@ -0,0 +1,804 @@
|
|||
// Updates to this file will automatically propgate to src/exports/types.ts
|
||||
// via a pre-commit hook. If you want to update the types before committing you
|
||||
// can run `npm run generate-types`.
|
||||
|
||||
import { z } from "zod"
|
||||
|
||||
import { Equals, Keys, AssertEqual } from "../utils/type-fu"
|
||||
|
||||
/**
|
||||
* ProviderName
|
||||
*/
|
||||
|
||||
export const providerNames = [
|
||||
"anthropic",
|
||||
"glama",
|
||||
"openrouter",
|
||||
"bedrock",
|
||||
"vertex",
|
||||
"openai",
|
||||
"ollama",
|
||||
"vscode-lm",
|
||||
"lmstudio",
|
||||
"gemini",
|
||||
"openai-native",
|
||||
"mistral",
|
||||
"deepseek",
|
||||
"unbound",
|
||||
"requesty",
|
||||
"human-relay",
|
||||
"fake-ai",
|
||||
] as const
|
||||
|
||||
export const providerNamesSchema = z.enum(providerNames)
|
||||
|
||||
export type ProviderName = z.infer<typeof providerNamesSchema>
|
||||
|
||||
/**
|
||||
* ToolGroup
|
||||
*/
|
||||
|
||||
export const toolGroups = ["read", "edit", "browser", "command", "mcp", "modes"] as const
|
||||
|
||||
export const toolGroupsSchema = z.enum(toolGroups)
|
||||
|
||||
export type ToolGroup = z.infer<typeof toolGroupsSchema>
|
||||
|
||||
/**
|
||||
* CheckpointStorage
|
||||
*/
|
||||
|
||||
export const checkpointStorages = ["task", "workspace"] as const
|
||||
|
||||
export const checkpointStoragesSchema = z.enum(checkpointStorages)
|
||||
|
||||
export type CheckpointStorage = z.infer<typeof checkpointStoragesSchema>
|
||||
|
||||
export const isCheckpointStorage = (value: string): value is CheckpointStorage =>
|
||||
checkpointStorages.includes(value as CheckpointStorage)
|
||||
|
||||
/**
|
||||
* Language
|
||||
*/
|
||||
|
||||
export const languages = [
|
||||
"ca",
|
||||
"de",
|
||||
"en",
|
||||
"es",
|
||||
"fr",
|
||||
"hi",
|
||||
"it",
|
||||
"ja",
|
||||
"ko",
|
||||
"pl",
|
||||
"pt-BR",
|
||||
"tr",
|
||||
"vi",
|
||||
"zh-CN",
|
||||
"zh-TW",
|
||||
] as const
|
||||
|
||||
export const languagesSchema = z.enum(languages)
|
||||
|
||||
export type Language = z.infer<typeof languagesSchema>
|
||||
|
||||
export const isLanguage = (value: string): value is Language => languages.includes(value as Language)
|
||||
|
||||
/**
|
||||
* TelemetrySetting
|
||||
*/
|
||||
|
||||
export const telemetrySettings = ["unset", "enabled", "disabled"] as const
|
||||
|
||||
export const telemetrySettingsSchema = z.enum(telemetrySettings)
|
||||
|
||||
export type TelemetrySetting = z.infer<typeof telemetrySettingsSchema>
|
||||
|
||||
/**
|
||||
* ModelInfo
|
||||
*/
|
||||
|
||||
export const modelInfoSchema = z.object({
|
||||
maxTokens: z.number().optional(),
|
||||
contextWindow: z.number(),
|
||||
supportsImages: z.boolean().optional(),
|
||||
supportsComputerUse: z.boolean().optional(),
|
||||
supportsPromptCache: z.boolean(),
|
||||
inputPrice: z.number().optional(),
|
||||
outputPrice: z.number().optional(),
|
||||
cacheWritesPrice: z.number().optional(),
|
||||
cacheReadsPrice: z.number().optional(),
|
||||
description: z.string().optional(),
|
||||
reasoningEffort: z.enum(["low", "medium", "high"]).optional(),
|
||||
thinking: z.boolean().optional(),
|
||||
})
|
||||
|
||||
export type ModelInfo = z.infer<typeof modelInfoSchema>
|
||||
|
||||
/**
|
||||
* ApiConfigMeta
|
||||
*/
|
||||
|
||||
export const apiConfigMetaSchema = z.object({
|
||||
id: z.string(),
|
||||
name: z.string(),
|
||||
apiProvider: providerNamesSchema.optional(),
|
||||
})
|
||||
|
||||
export type ApiConfigMeta = z.infer<typeof apiConfigMetaSchema>
|
||||
|
||||
/**
|
||||
* HistoryItem
|
||||
*/
|
||||
|
||||
export const historyItemSchema = z.object({
|
||||
id: z.string(),
|
||||
number: z.number(),
|
||||
ts: z.number(),
|
||||
task: z.string(),
|
||||
tokensIn: z.number(),
|
||||
tokensOut: z.number(),
|
||||
cacheWrites: z.number().optional(),
|
||||
cacheReads: z.number().optional(),
|
||||
totalCost: z.number(),
|
||||
size: z.number().optional(),
|
||||
})
|
||||
|
||||
export type HistoryItem = z.infer<typeof historyItemSchema>
|
||||
|
||||
/**
|
||||
* GroupOptions
|
||||
*/
|
||||
|
||||
export const groupOptionsSchema = z.object({
|
||||
fileRegex: z
|
||||
.string()
|
||||
.optional()
|
||||
.refine(
|
||||
(pattern) => {
|
||||
if (!pattern) {
|
||||
return true // Optional, so empty is valid.
|
||||
}
|
||||
|
||||
try {
|
||||
new RegExp(pattern)
|
||||
return true
|
||||
} catch {
|
||||
return false
|
||||
}
|
||||
},
|
||||
{ message: "Invalid regular expression pattern" },
|
||||
),
|
||||
description: z.string().optional(),
|
||||
})
|
||||
|
||||
export type GroupOptions = z.infer<typeof groupOptionsSchema>
|
||||
|
||||
/**
|
||||
* GroupEntry
|
||||
*/
|
||||
|
||||
export const groupEntrySchema = z.union([toolGroupsSchema, z.tuple([toolGroupsSchema, groupOptionsSchema])])
|
||||
|
||||
export type GroupEntry = z.infer<typeof groupEntrySchema>
|
||||
|
||||
/**
|
||||
* ModeConfig
|
||||
*/
|
||||
|
||||
const groupEntryArraySchema = z.array(groupEntrySchema).refine(
|
||||
(groups) => {
|
||||
const seen = new Set()
|
||||
|
||||
return groups.every((group) => {
|
||||
// For tuples, check the group name (first element).
|
||||
const groupName = Array.isArray(group) ? group[0] : group
|
||||
|
||||
if (seen.has(groupName)) {
|
||||
return false
|
||||
}
|
||||
|
||||
seen.add(groupName)
|
||||
return true
|
||||
})
|
||||
},
|
||||
{ message: "Duplicate groups are not allowed" },
|
||||
)
|
||||
|
||||
export const modeConfigSchema = z.object({
|
||||
slug: z.string().regex(/^[a-zA-Z0-9-]+$/, "Slug must contain only letters numbers and dashes"),
|
||||
name: z.string().min(1, "Name is required"),
|
||||
roleDefinition: z.string().min(1, "Role definition is required"),
|
||||
customInstructions: z.string().optional(),
|
||||
groups: groupEntryArraySchema,
|
||||
source: z.enum(["global", "project"]).optional(),
|
||||
})
|
||||
|
||||
export type ModeConfig = z.infer<typeof modeConfigSchema>
|
||||
|
||||
/**
|
||||
* CustomModesSettings
|
||||
*/
|
||||
|
||||
export const customModesSettingsSchema = z.object({
|
||||
customModes: z.array(modeConfigSchema).refine(
|
||||
(modes) => {
|
||||
const slugs = new Set()
|
||||
|
||||
return modes.every((mode) => {
|
||||
if (slugs.has(mode.slug)) {
|
||||
return false
|
||||
}
|
||||
|
||||
slugs.add(mode.slug)
|
||||
return true
|
||||
})
|
||||
},
|
||||
{
|
||||
message: "Duplicate mode slugs are not allowed",
|
||||
},
|
||||
),
|
||||
})
|
||||
|
||||
export type CustomModesSettings = z.infer<typeof customModesSettingsSchema>
|
||||
|
||||
/**
|
||||
* PromptComponent
|
||||
*/
|
||||
|
||||
export const promptComponentSchema = z.object({
|
||||
roleDefinition: z.string().optional(),
|
||||
customInstructions: z.string().optional(),
|
||||
})
|
||||
|
||||
export type PromptComponent = z.infer<typeof promptComponentSchema>
|
||||
|
||||
/**
|
||||
* CustomModePrompts
|
||||
*/
|
||||
|
||||
export const customModePromptsSchema = z.record(z.string(), promptComponentSchema.optional())
|
||||
|
||||
export type CustomModePrompts = z.infer<typeof customModePromptsSchema>
|
||||
|
||||
/**
|
||||
* CustomSupportPrompts
|
||||
*/
|
||||
|
||||
export const customSupportPromptsSchema = z.record(z.string(), z.string().optional())
|
||||
|
||||
export type CustomSupportPrompts = z.infer<typeof customSupportPromptsSchema>
|
||||
|
||||
/**
|
||||
* ExperimentId
|
||||
*/
|
||||
|
||||
export const experimentIds = [
|
||||
"experimentalDiffStrategy",
|
||||
"search_and_replace",
|
||||
"insert_content",
|
||||
"powerSteering",
|
||||
"multi_search_and_replace",
|
||||
] as const
|
||||
|
||||
export const experimentIdsSchema = z.enum(experimentIds)
|
||||
|
||||
export type ExperimentId = z.infer<typeof experimentIdsSchema>
|
||||
|
||||
/**
|
||||
* Experiments
|
||||
*/
|
||||
|
||||
const experimentsSchema = z.object({
|
||||
experimentalDiffStrategy: z.boolean(),
|
||||
search_and_replace: z.boolean(),
|
||||
insert_content: z.boolean(),
|
||||
powerSteering: z.boolean(),
|
||||
multi_search_and_replace: z.boolean(),
|
||||
})
|
||||
|
||||
export type Experiments = z.infer<typeof experimentsSchema>
|
||||
|
||||
type _AssertExperiments = AssertEqual<Equals<ExperimentId, Keys<Experiments>>>
|
||||
|
||||
/**
|
||||
* ProviderSettings
|
||||
*/
|
||||
|
||||
export const providerSettingsSchema = z.object({
|
||||
apiProvider: providerNamesSchema.optional(),
|
||||
// Anthropic
|
||||
apiModelId: z.string().optional(),
|
||||
apiKey: z.string().optional(),
|
||||
anthropicBaseUrl: z.string().optional(),
|
||||
// Glama
|
||||
glamaModelId: z.string().optional(),
|
||||
glamaModelInfo: modelInfoSchema.optional(),
|
||||
glamaApiKey: z.string().optional(),
|
||||
// OpenRouter
|
||||
openRouterApiKey: z.string().optional(),
|
||||
openRouterModelId: z.string().optional(),
|
||||
openRouterModelInfo: modelInfoSchema.optional(),
|
||||
openRouterBaseUrl: z.string().optional(),
|
||||
openRouterSpecificProvider: z.string().optional(),
|
||||
openRouterUseMiddleOutTransform: z.boolean().optional(),
|
||||
// AWS Bedrock
|
||||
awsAccessKey: z.string().optional(),
|
||||
awsSecretKey: z.string().optional(),
|
||||
awsSessionToken: z.string().optional(),
|
||||
awsRegion: z.string().optional(),
|
||||
awsUseCrossRegionInference: z.boolean().optional(),
|
||||
awsUsePromptCache: z.boolean().optional(),
|
||||
awspromptCacheId: z.string().optional(),
|
||||
awsProfile: z.string().optional(),
|
||||
awsUseProfile: z.boolean().optional(),
|
||||
awsCustomArn: z.string().optional(),
|
||||
// Google Vertex
|
||||
vertexKeyFile: z.string().optional(),
|
||||
vertexJsonCredentials: z.string().optional(),
|
||||
vertexProjectId: z.string().optional(),
|
||||
vertexRegion: z.string().optional(),
|
||||
// OpenAI
|
||||
openAiBaseUrl: z.string().optional(),
|
||||
openAiApiKey: z.string().optional(),
|
||||
openAiR1FormatEnabled: z.boolean().optional(),
|
||||
openAiModelId: z.string().optional(),
|
||||
openAiCustomModelInfo: modelInfoSchema.optional(),
|
||||
openAiUseAzure: z.boolean().optional(),
|
||||
azureApiVersion: z.string().optional(),
|
||||
openAiStreamingEnabled: z.boolean().optional(),
|
||||
// Ollama
|
||||
ollamaModelId: z.string().optional(),
|
||||
ollamaBaseUrl: z.string().optional(),
|
||||
// VS Code LM
|
||||
vsCodeLmModelSelector: z
|
||||
.object({
|
||||
vendor: z.string().optional(),
|
||||
family: z.string().optional(),
|
||||
version: z.string().optional(),
|
||||
id: z.string().optional(),
|
||||
})
|
||||
.optional(),
|
||||
// LM Studio
|
||||
lmStudioModelId: z.string().optional(),
|
||||
lmStudioBaseUrl: z.string().optional(),
|
||||
lmStudioDraftModelId: z.string().optional(),
|
||||
lmStudioSpeculativeDecodingEnabled: z.boolean().optional(),
|
||||
// Gemini
|
||||
geminiApiKey: z.string().optional(),
|
||||
googleGeminiBaseUrl: z.string().optional(),
|
||||
// OpenAI Native
|
||||
openAiNativeApiKey: z.string().optional(),
|
||||
// Mistral
|
||||
mistralApiKey: z.string().optional(),
|
||||
mistralCodestralUrl: z.string().optional(),
|
||||
// DeepSeek
|
||||
deepSeekBaseUrl: z.string().optional(),
|
||||
deepSeekApiKey: z.string().optional(),
|
||||
// Unbound
|
||||
unboundApiKey: z.string().optional(),
|
||||
unboundModelId: z.string().optional(),
|
||||
unboundModelInfo: modelInfoSchema.optional(),
|
||||
// Requesty
|
||||
requestyApiKey: z.string().optional(),
|
||||
requestyModelId: z.string().optional(),
|
||||
requestyModelInfo: modelInfoSchema.optional(),
|
||||
// Claude 3.7 Sonnet Thinking
|
||||
modelTemperature: z.number().nullish(),
|
||||
modelMaxTokens: z.number().optional(),
|
||||
modelMaxThinkingTokens: z.number().optional(),
|
||||
// Generic
|
||||
includeMaxTokens: z.boolean().optional(),
|
||||
// Fake AI
|
||||
fakeAi: z.unknown().optional(),
|
||||
})
|
||||
|
||||
export type ProviderSettings = z.infer<typeof providerSettingsSchema>
|
||||
|
||||
type ProviderSettingsRecord = Record<Keys<ProviderSettings>, undefined>
|
||||
|
||||
const providerSettingsRecord: ProviderSettingsRecord = {
|
||||
apiProvider: undefined,
|
||||
// Anthropic
|
||||
apiModelId: undefined,
|
||||
apiKey: undefined,
|
||||
anthropicBaseUrl: undefined,
|
||||
// Glama
|
||||
glamaModelId: undefined,
|
||||
glamaModelInfo: undefined,
|
||||
glamaApiKey: undefined,
|
||||
// OpenRouter
|
||||
openRouterApiKey: undefined,
|
||||
openRouterModelId: undefined,
|
||||
openRouterModelInfo: undefined,
|
||||
openRouterBaseUrl: undefined,
|
||||
openRouterSpecificProvider: undefined,
|
||||
openRouterUseMiddleOutTransform: undefined,
|
||||
// AWS Bedrock
|
||||
awsAccessKey: undefined,
|
||||
awsSecretKey: undefined,
|
||||
awsSessionToken: undefined,
|
||||
awsRegion: undefined,
|
||||
awsUseCrossRegionInference: undefined,
|
||||
awsUsePromptCache: undefined,
|
||||
awspromptCacheId: undefined,
|
||||
awsProfile: undefined,
|
||||
awsUseProfile: undefined,
|
||||
awsCustomArn: undefined,
|
||||
// Google Vertex
|
||||
vertexKeyFile: undefined,
|
||||
vertexJsonCredentials: undefined,
|
||||
vertexProjectId: undefined,
|
||||
vertexRegion: undefined,
|
||||
// OpenAI
|
||||
openAiBaseUrl: undefined,
|
||||
openAiApiKey: undefined,
|
||||
openAiR1FormatEnabled: undefined,
|
||||
openAiModelId: undefined,
|
||||
openAiCustomModelInfo: undefined,
|
||||
openAiUseAzure: undefined,
|
||||
azureApiVersion: undefined,
|
||||
openAiStreamingEnabled: undefined,
|
||||
// Ollama
|
||||
ollamaModelId: undefined,
|
||||
ollamaBaseUrl: undefined,
|
||||
// VS Code LM
|
||||
vsCodeLmModelSelector: undefined,
|
||||
lmStudioModelId: undefined,
|
||||
lmStudioBaseUrl: undefined,
|
||||
lmStudioDraftModelId: undefined,
|
||||
lmStudioSpeculativeDecodingEnabled: undefined,
|
||||
// Gemini
|
||||
geminiApiKey: undefined,
|
||||
googleGeminiBaseUrl: undefined,
|
||||
// OpenAI Native
|
||||
openAiNativeApiKey: undefined,
|
||||
// Mistral
|
||||
mistralApiKey: undefined,
|
||||
mistralCodestralUrl: undefined,
|
||||
// DeepSeek
|
||||
deepSeekBaseUrl: undefined,
|
||||
deepSeekApiKey: undefined,
|
||||
// Unbound
|
||||
unboundApiKey: undefined,
|
||||
unboundModelId: undefined,
|
||||
unboundModelInfo: undefined,
|
||||
// Requesty
|
||||
requestyApiKey: undefined,
|
||||
requestyModelId: undefined,
|
||||
requestyModelInfo: undefined,
|
||||
// Claude 3.7 Sonnet Thinking
|
||||
modelTemperature: undefined,
|
||||
modelMaxTokens: undefined,
|
||||
modelMaxThinkingTokens: undefined,
|
||||
// Generic
|
||||
includeMaxTokens: undefined,
|
||||
// Fake AI
|
||||
fakeAi: undefined,
|
||||
}
|
||||
|
||||
export const PROVIDER_SETTINGS_KEYS = Object.keys(providerSettingsRecord) as Keys<ProviderSettings>[]
|
||||
|
||||
/**
|
||||
* GlobalSettings
|
||||
*/
|
||||
|
||||
export const globalSettingsSchema = z.object({
|
||||
currentApiConfigName: z.string().optional(),
|
||||
listApiConfigMeta: z.array(apiConfigMetaSchema).optional(),
|
||||
pinnedApiConfigs: z.record(z.string(), z.boolean()).optional(),
|
||||
|
||||
lastShownAnnouncementId: z.string().optional(),
|
||||
customInstructions: z.string().optional(),
|
||||
taskHistory: z.array(historyItemSchema).optional(),
|
||||
|
||||
autoApprovalEnabled: z.boolean().optional(),
|
||||
alwaysAllowReadOnly: z.boolean().optional(),
|
||||
alwaysAllowReadOnlyOutsideWorkspace: z.boolean().optional(),
|
||||
alwaysAllowWrite: z.boolean().optional(),
|
||||
alwaysAllowWriteOutsideWorkspace: z.boolean().optional(),
|
||||
writeDelayMs: z.number().optional(),
|
||||
alwaysAllowBrowser: z.boolean().optional(),
|
||||
alwaysApproveResubmit: z.boolean().optional(),
|
||||
requestDelaySeconds: z.number().optional(),
|
||||
alwaysAllowMcp: z.boolean().optional(),
|
||||
alwaysAllowModeSwitch: z.boolean().optional(),
|
||||
alwaysAllowSubtasks: z.boolean().optional(),
|
||||
alwaysAllowExecute: z.boolean().optional(),
|
||||
allowedCommands: z.array(z.string()).optional(),
|
||||
|
||||
browserToolEnabled: z.boolean().optional(),
|
||||
browserViewportSize: z.string().optional(),
|
||||
screenshotQuality: z.number().optional(),
|
||||
remoteBrowserEnabled: z.boolean().optional(),
|
||||
remoteBrowserHost: z.string().optional(),
|
||||
|
||||
enableCheckpoints: z.boolean().optional(),
|
||||
checkpointStorage: checkpointStoragesSchema.optional(),
|
||||
|
||||
ttsEnabled: z.boolean().optional(),
|
||||
ttsSpeed: z.number().optional(),
|
||||
soundEnabled: z.boolean().optional(),
|
||||
soundVolume: z.number().optional(),
|
||||
|
||||
maxOpenTabsContext: z.number().optional(),
|
||||
maxWorkspaceFiles: z.number().optional(),
|
||||
showRooIgnoredFiles: z.boolean().optional(),
|
||||
maxReadFileLine: z.number().optional(),
|
||||
|
||||
terminalOutputLineLimit: z.number().optional(),
|
||||
terminalShellIntegrationTimeout: z.number().optional(),
|
||||
|
||||
rateLimitSeconds: z.number().optional(),
|
||||
diffEnabled: z.boolean().optional(),
|
||||
fuzzyMatchThreshold: z.number().optional(),
|
||||
experiments: experimentsSchema.optional(),
|
||||
|
||||
language: languagesSchema.optional(),
|
||||
|
||||
telemetrySetting: telemetrySettingsSchema.optional(),
|
||||
|
||||
mcpEnabled: z.boolean().optional(),
|
||||
enableMcpServerCreation: z.boolean().optional(),
|
||||
|
||||
mode: z.string().optional(),
|
||||
modeApiConfigs: z.record(z.string(), z.string()).optional(),
|
||||
customModes: z.array(modeConfigSchema).optional(),
|
||||
customModePrompts: customModePromptsSchema.optional(),
|
||||
customSupportPrompts: customSupportPromptsSchema.optional(),
|
||||
enhancementApiConfigId: z.string().optional(),
|
||||
})
|
||||
|
||||
export type GlobalSettings = z.infer<typeof globalSettingsSchema>
|
||||
|
||||
type GlobalSettingsRecord = Record<Keys<GlobalSettings>, undefined>
|
||||
|
||||
const globalSettingsRecord: GlobalSettingsRecord = {
|
||||
currentApiConfigName: undefined,
|
||||
listApiConfigMeta: undefined,
|
||||
pinnedApiConfigs: undefined,
|
||||
|
||||
lastShownAnnouncementId: undefined,
|
||||
customInstructions: undefined,
|
||||
taskHistory: undefined,
|
||||
|
||||
autoApprovalEnabled: undefined,
|
||||
alwaysAllowReadOnly: undefined,
|
||||
alwaysAllowReadOnlyOutsideWorkspace: undefined,
|
||||
alwaysAllowWrite: undefined,
|
||||
alwaysAllowWriteOutsideWorkspace: undefined,
|
||||
writeDelayMs: undefined,
|
||||
alwaysAllowBrowser: undefined,
|
||||
alwaysApproveResubmit: undefined,
|
||||
requestDelaySeconds: undefined,
|
||||
alwaysAllowMcp: undefined,
|
||||
alwaysAllowModeSwitch: undefined,
|
||||
alwaysAllowSubtasks: undefined,
|
||||
alwaysAllowExecute: undefined,
|
||||
allowedCommands: undefined,
|
||||
|
||||
browserToolEnabled: undefined,
|
||||
browserViewportSize: undefined,
|
||||
screenshotQuality: undefined,
|
||||
remoteBrowserEnabled: undefined,
|
||||
remoteBrowserHost: undefined,
|
||||
|
||||
enableCheckpoints: undefined,
|
||||
checkpointStorage: undefined,
|
||||
|
||||
ttsEnabled: undefined,
|
||||
ttsSpeed: undefined,
|
||||
soundEnabled: undefined,
|
||||
soundVolume: undefined,
|
||||
|
||||
maxOpenTabsContext: undefined,
|
||||
maxWorkspaceFiles: undefined,
|
||||
showRooIgnoredFiles: undefined,
|
||||
maxReadFileLine: undefined,
|
||||
|
||||
terminalOutputLineLimit: undefined,
|
||||
terminalShellIntegrationTimeout: undefined,
|
||||
|
||||
rateLimitSeconds: undefined,
|
||||
diffEnabled: undefined,
|
||||
fuzzyMatchThreshold: undefined,
|
||||
experiments: undefined,
|
||||
|
||||
language: undefined,
|
||||
|
||||
telemetrySetting: undefined,
|
||||
|
||||
mcpEnabled: undefined,
|
||||
enableMcpServerCreation: undefined,
|
||||
|
||||
mode: undefined,
|
||||
modeApiConfigs: undefined,
|
||||
customModes: undefined,
|
||||
customModePrompts: undefined,
|
||||
customSupportPrompts: undefined,
|
||||
enhancementApiConfigId: undefined,
|
||||
}
|
||||
|
||||
export const GLOBAL_SETTINGS_KEYS = Object.keys(globalSettingsRecord) as Keys<GlobalSettings>[]
|
||||
|
||||
/**
|
||||
* RooCodeSettings
|
||||
*/
|
||||
|
||||
export type RooCodeSettings = GlobalSettings & ProviderSettings
|
||||
|
||||
/**
|
||||
* SecretState
|
||||
*/
|
||||
|
||||
export type SecretState = Pick<
|
||||
ProviderSettings,
|
||||
| "apiKey"
|
||||
| "glamaApiKey"
|
||||
| "openRouterApiKey"
|
||||
| "awsAccessKey"
|
||||
| "awsSecretKey"
|
||||
| "awsSessionToken"
|
||||
| "openAiApiKey"
|
||||
| "geminiApiKey"
|
||||
| "openAiNativeApiKey"
|
||||
| "deepSeekApiKey"
|
||||
| "mistralApiKey"
|
||||
| "unboundApiKey"
|
||||
| "requestyApiKey"
|
||||
>
|
||||
|
||||
type SecretStateRecord = Record<Keys<SecretState>, undefined>
|
||||
|
||||
const secretStateRecord: SecretStateRecord = {
|
||||
apiKey: undefined,
|
||||
glamaApiKey: undefined,
|
||||
openRouterApiKey: undefined,
|
||||
awsAccessKey: undefined,
|
||||
awsSecretKey: undefined,
|
||||
awsSessionToken: undefined,
|
||||
openAiApiKey: undefined,
|
||||
geminiApiKey: undefined,
|
||||
openAiNativeApiKey: undefined,
|
||||
deepSeekApiKey: undefined,
|
||||
mistralApiKey: undefined,
|
||||
unboundApiKey: undefined,
|
||||
requestyApiKey: undefined,
|
||||
}
|
||||
|
||||
export const SECRET_STATE_KEYS = Object.keys(secretStateRecord) as Keys<SecretState>[]
|
||||
|
||||
export const isSecretStateKey = (key: string): key is Keys<SecretState> =>
|
||||
SECRET_STATE_KEYS.includes(key as Keys<SecretState>)
|
||||
|
||||
/**
|
||||
* GlobalState
|
||||
*/
|
||||
|
||||
export type GlobalState = Omit<RooCodeSettings, Keys<SecretState>>
|
||||
|
||||
export const GLOBAL_STATE_KEYS = [...GLOBAL_SETTINGS_KEYS, ...PROVIDER_SETTINGS_KEYS].filter(
|
||||
(key: Keys<RooCodeSettings>) => !SECRET_STATE_KEYS.includes(key as Keys<SecretState>),
|
||||
) as Keys<GlobalState>[]
|
||||
|
||||
export const isGlobalStateKey = (key: string): key is Keys<GlobalState> =>
|
||||
GLOBAL_STATE_KEYS.includes(key as Keys<GlobalState>)
|
||||
|
||||
/**
|
||||
* ClineAsk
|
||||
*/
|
||||
|
||||
export const clineAsks = [
|
||||
"followup",
|
||||
"command",
|
||||
"command_output",
|
||||
"completion_result",
|
||||
"tool",
|
||||
"api_req_failed",
|
||||
"resume_task",
|
||||
"resume_completed_task",
|
||||
"mistake_limit_reached",
|
||||
"browser_action_launch",
|
||||
"use_mcp_server",
|
||||
"finishTask",
|
||||
] as const
|
||||
|
||||
export const clineAskSchema = z.enum(clineAsks)
|
||||
|
||||
export type ClineAsk = z.infer<typeof clineAskSchema>
|
||||
|
||||
// ClineSay
|
||||
|
||||
export const clineSays = [
|
||||
"task",
|
||||
"error",
|
||||
"api_req_started",
|
||||
"api_req_finished",
|
||||
"api_req_retried",
|
||||
"api_req_retry_delayed",
|
||||
"api_req_deleted",
|
||||
"text",
|
||||
"reasoning",
|
||||
"completion_result",
|
||||
"user_feedback",
|
||||
"user_feedback_diff",
|
||||
"command_output",
|
||||
"tool",
|
||||
"shell_integration_warning",
|
||||
"browser_action",
|
||||
"browser_action_result",
|
||||
"command",
|
||||
"mcp_server_request_started",
|
||||
"mcp_server_response",
|
||||
"new_task_started",
|
||||
"new_task",
|
||||
"checkpoint_saved",
|
||||
"rooignore_error",
|
||||
] as const
|
||||
|
||||
export const clineSaySchema = z.enum(clineSays)
|
||||
|
||||
export type ClineSay = z.infer<typeof clineSaySchema>
|
||||
|
||||
/**
|
||||
* ToolProgressStatus
|
||||
*/
|
||||
|
||||
export const toolProgressStatusSchema = z.object({
|
||||
icon: z.string().optional(),
|
||||
text: z.string().optional(),
|
||||
})
|
||||
|
||||
export type ToolProgressStatus = z.infer<typeof toolProgressStatusSchema>
|
||||
|
||||
/**
|
||||
* ClineMessage
|
||||
*/
|
||||
|
||||
export const clineMessageSchema = z.object({
|
||||
ts: z.number(),
|
||||
type: z.union([z.literal("ask"), z.literal("say")]),
|
||||
ask: clineAskSchema.optional(),
|
||||
say: clineSaySchema.optional(),
|
||||
text: z.string().optional(),
|
||||
images: z.array(z.string()).optional(),
|
||||
partial: z.boolean().optional(),
|
||||
reasoning: z.string().optional(),
|
||||
conversationHistoryIndex: z.number().optional(),
|
||||
checkpoint: z.record(z.string(), z.unknown()).optional(),
|
||||
progressStatus: toolProgressStatusSchema.optional(),
|
||||
})
|
||||
|
||||
export type ClineMessage = z.infer<typeof clineMessageSchema>
|
||||
|
||||
/**
|
||||
* TokenUsage
|
||||
*/
|
||||
|
||||
export const tokenUsageSchema = z.object({
|
||||
totalTokensIn: z.number(),
|
||||
totalTokensOut: z.number(),
|
||||
totalCacheWrites: z.number().optional(),
|
||||
totalCacheReads: z.number().optional(),
|
||||
totalCost: z.number(),
|
||||
contextTokens: z.number(),
|
||||
})
|
||||
|
||||
export type TokenUsage = z.infer<typeof tokenUsageSchema>
|
||||
|
||||
/**
|
||||
* TypeDefinition
|
||||
*/
|
||||
|
||||
type TypeDefinition = {
|
||||
schema: z.ZodTypeAny
|
||||
identifier: string
|
||||
}
|
||||
|
||||
export const typeDefinitions: TypeDefinition[] = [
|
||||
{ schema: providerSettingsSchema, identifier: "ProviderSettings" },
|
||||
{ schema: globalSettingsSchema, identifier: "GlobalSettings" },
|
||||
{ schema: clineMessageSchema, identifier: "ClineMessage" },
|
||||
{ schema: tokenUsageSchema, identifier: "TokenUsage" },
|
||||
]
|
||||
|
|
@ -1,14 +1,23 @@
|
|||
import { ApiConfiguration, ModelInfo } from "./api"
|
||||
import { HistoryItem } from "./HistoryItem"
|
||||
import {
|
||||
ModelInfo,
|
||||
GlobalSettings,
|
||||
ApiConfigMeta,
|
||||
ProviderSettings as ApiConfiguration,
|
||||
HistoryItem,
|
||||
ModeConfig,
|
||||
CheckpointStorage,
|
||||
TelemetrySetting,
|
||||
ExperimentId,
|
||||
ClineAsk,
|
||||
ClineSay,
|
||||
ToolProgressStatus,
|
||||
ClineMessage,
|
||||
} from "../schemas"
|
||||
import { McpServer } from "./mcp"
|
||||
import { GitCommit } from "../utils/git"
|
||||
import { Mode, ModeConfig } from "./modes"
|
||||
import { ExperimentId } from "./experiments"
|
||||
import { CheckpointStorage } from "./checkpoints"
|
||||
import { TelemetrySetting } from "./TelemetrySetting"
|
||||
import type { GlobalSettings, ApiConfigMeta, ClineMessage, ClineAsk, ClineSay } from "../exports/roo-code"
|
||||
import { Mode } from "./modes"
|
||||
|
||||
export type { ApiConfigMeta }
|
||||
export type { ApiConfigMeta, ToolProgressStatus }
|
||||
|
||||
export interface LanguageModelChatSelector {
|
||||
vendor?: string
|
||||
|
|
@ -261,8 +270,3 @@ export interface ClineApiReqInfo {
|
|||
}
|
||||
|
||||
export type ClineApiReqCancelReason = "streaming_failed" | "user_cancelled"
|
||||
|
||||
export type ToolProgressStatus = {
|
||||
icon?: string
|
||||
text?: string
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
import type { HistoryItem } from "../exports/roo-code"
|
||||
import type { HistoryItem } from "../schemas"
|
||||
|
||||
export type { HistoryItem }
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { ModelInfo, ProviderName, ProviderSettings } from "../exports/roo-code"
|
||||
import { ModelInfo, ProviderName, ProviderSettings } from "../schemas"
|
||||
|
||||
export type { ModelInfo, ProviderName as ApiProvider }
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,4 @@
|
|||
import { ProviderSettings } from "../exports/roo-code"
|
||||
import { SECRET_STATE_KEYS } from "./globalState"
|
||||
import { SECRET_STATE_KEYS, ProviderSettings } from "../schemas"
|
||||
|
||||
export function checkExistKey(config: ProviderSettings | undefined) {
|
||||
if (!config) {
|
||||
|
|
|
|||
|
|
@ -1,7 +1,3 @@
|
|||
import { CheckpointStorage } from "../exports/roo-code"
|
||||
import { CheckpointStorage, isCheckpointStorage } from "../schemas"
|
||||
|
||||
export type { CheckpointStorage }
|
||||
|
||||
export const isCheckpointStorage = (value: string): value is CheckpointStorage => {
|
||||
return value === "task" || value === "workspace"
|
||||
}
|
||||
export { type CheckpointStorage, isCheckpointStorage }
|
||||
|
|
|
|||
|
|
@ -1,5 +1,4 @@
|
|||
import { ExperimentId } from "../exports/roo-code"
|
||||
|
||||
import { ExperimentId } from "../schemas"
|
||||
import { AssertEqual, Equals, Keys, Values } from "../utils/type-fu"
|
||||
|
||||
export type { ExperimentId }
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { TokenUsage } from "../exports/roo-code"
|
||||
import { TokenUsage } from "../schemas"
|
||||
|
||||
import { ClineMessage } from "./ExtensionMessage"
|
||||
|
||||
|
|
|
|||
|
|
@ -1,695 +0,0 @@
|
|||
import { z } from "zod"
|
||||
|
||||
import type {
|
||||
ProviderName,
|
||||
CheckpointStorage,
|
||||
ToolGroup,
|
||||
Language,
|
||||
TelemetrySetting,
|
||||
ProviderSettingsKey,
|
||||
SecretStateKey,
|
||||
GlobalStateKey,
|
||||
ModelInfo,
|
||||
ApiConfigMeta,
|
||||
HistoryItem,
|
||||
GroupEntry,
|
||||
ModeConfig,
|
||||
ExperimentId,
|
||||
ProviderSettings,
|
||||
GlobalSettings,
|
||||
} from "../exports/roo-code"
|
||||
|
||||
import { Keys, AssertEqual, Equals } from "../utils/type-fu"
|
||||
|
||||
/**
|
||||
* ProviderName
|
||||
*/
|
||||
|
||||
const providerNames: Record<ProviderName, true> = {
|
||||
anthropic: true,
|
||||
glama: true,
|
||||
openrouter: true,
|
||||
bedrock: true,
|
||||
vertex: true,
|
||||
openai: true,
|
||||
ollama: true,
|
||||
lmstudio: true,
|
||||
gemini: true,
|
||||
"openai-native": true,
|
||||
deepseek: true,
|
||||
"vscode-lm": true,
|
||||
mistral: true,
|
||||
unbound: true,
|
||||
requesty: true,
|
||||
"human-relay": true,
|
||||
"fake-ai": true,
|
||||
}
|
||||
|
||||
const PROVIDER_NAMES = Object.keys(providerNames) as ProviderName[]
|
||||
|
||||
const providerNamesEnum: [ProviderName, ...ProviderName[]] = [
|
||||
PROVIDER_NAMES[0],
|
||||
...PROVIDER_NAMES.slice(1).map((p) => p),
|
||||
]
|
||||
|
||||
/**
|
||||
* CheckpointStorage
|
||||
*/
|
||||
|
||||
const checkpointStorages: Record<CheckpointStorage, true> = {
|
||||
task: true,
|
||||
workspace: true,
|
||||
}
|
||||
|
||||
const CHECKPOINT_STORAGES = Object.keys(checkpointStorages) as CheckpointStorage[]
|
||||
|
||||
const checkpointStoragesEnum: [CheckpointStorage, ...CheckpointStorage[]] = [
|
||||
CHECKPOINT_STORAGES[0],
|
||||
...CHECKPOINT_STORAGES.slice(1).map((p) => p),
|
||||
]
|
||||
|
||||
/**
|
||||
* ToolGroup
|
||||
*/
|
||||
|
||||
const toolGroups: Record<ToolGroup, true> = {
|
||||
read: true,
|
||||
edit: true,
|
||||
browser: true,
|
||||
command: true,
|
||||
mcp: true,
|
||||
modes: true,
|
||||
}
|
||||
|
||||
const TOOL_GROUPS = Object.keys(toolGroups) as ToolGroup[]
|
||||
|
||||
const toolGroupsEnum: [ToolGroup, ...ToolGroup[]] = [TOOL_GROUPS[0], ...TOOL_GROUPS.slice(1).map((p) => p)]
|
||||
|
||||
/**
|
||||
* Language
|
||||
*/
|
||||
|
||||
const languages: Record<Language, true> = {
|
||||
ca: true,
|
||||
de: true,
|
||||
en: true,
|
||||
es: true,
|
||||
fr: true,
|
||||
hi: true,
|
||||
it: true,
|
||||
ja: true,
|
||||
ko: true,
|
||||
pl: true,
|
||||
"pt-BR": true,
|
||||
tr: true,
|
||||
vi: true,
|
||||
"zh-CN": true,
|
||||
"zh-TW": true,
|
||||
}
|
||||
|
||||
const LANGUAGES = Object.keys(languages) as Language[]
|
||||
|
||||
const languagesEnum: [Language, ...Language[]] = [LANGUAGES[0], ...LANGUAGES.slice(1).map((p) => p)]
|
||||
|
||||
export const isLanguage = (key: string): key is Language => LANGUAGES.includes(key as Language)
|
||||
|
||||
/**
|
||||
* TelemetrySetting
|
||||
*/
|
||||
|
||||
const telemetrySettings: Record<TelemetrySetting, true> = {
|
||||
unset: true,
|
||||
enabled: true,
|
||||
disabled: true,
|
||||
}
|
||||
|
||||
const TELEMETRY_SETTINGS = Object.keys(telemetrySettings) as TelemetrySetting[]
|
||||
|
||||
const telemetrySettingsEnum: [TelemetrySetting, ...TelemetrySetting[]] = [
|
||||
TELEMETRY_SETTINGS[0],
|
||||
...TELEMETRY_SETTINGS.slice(1).map((p) => p),
|
||||
]
|
||||
|
||||
/**
|
||||
* ProviderSettingsKey
|
||||
*/
|
||||
|
||||
const providerSettingsKeys: Record<ProviderSettingsKey, true> = {
|
||||
apiProvider: true,
|
||||
apiModelId: true,
|
||||
// Anthropic
|
||||
apiKey: true,
|
||||
anthropicBaseUrl: true,
|
||||
// Glama
|
||||
glamaApiKey: true,
|
||||
glamaModelId: true,
|
||||
glamaModelInfo: true,
|
||||
// OpenRouter
|
||||
openRouterApiKey: true,
|
||||
openRouterModelId: true,
|
||||
openRouterModelInfo: true,
|
||||
openRouterBaseUrl: true,
|
||||
openRouterSpecificProvider: true,
|
||||
openRouterUseMiddleOutTransform: true,
|
||||
// AWS Bedrock
|
||||
awsAccessKey: true,
|
||||
awsSecretKey: true,
|
||||
awsSessionToken: true,
|
||||
awsRegion: true,
|
||||
awsUseCrossRegionInference: true,
|
||||
awsUsePromptCache: true,
|
||||
awspromptCacheId: true,
|
||||
awsProfile: true,
|
||||
awsUseProfile: true,
|
||||
awsCustomArn: true,
|
||||
// Google Vertex
|
||||
vertexKeyFile: true,
|
||||
vertexJsonCredentials: true,
|
||||
vertexProjectId: true,
|
||||
vertexRegion: true,
|
||||
// OpenAI
|
||||
openAiApiKey: true,
|
||||
openAiBaseUrl: true,
|
||||
openAiR1FormatEnabled: true,
|
||||
openAiModelId: true,
|
||||
openAiCustomModelInfo: true,
|
||||
openAiUseAzure: true,
|
||||
azureApiVersion: true,
|
||||
openAiStreamingEnabled: true,
|
||||
// Ollama
|
||||
ollamaModelId: true,
|
||||
ollamaBaseUrl: true,
|
||||
// VS Code LM
|
||||
vsCodeLmModelSelector: true,
|
||||
// LM Studio
|
||||
lmStudioModelId: true,
|
||||
lmStudioBaseUrl: true,
|
||||
lmStudioDraftModelId: true,
|
||||
lmStudioSpeculativeDecodingEnabled: true,
|
||||
// Gemini
|
||||
geminiApiKey: true,
|
||||
googleGeminiBaseUrl: true,
|
||||
// OpenAI Native
|
||||
openAiNativeApiKey: true,
|
||||
// Mistral
|
||||
mistralApiKey: true,
|
||||
mistralCodestralUrl: true,
|
||||
// DeepSeek
|
||||
deepSeekApiKey: true,
|
||||
deepSeekBaseUrl: true,
|
||||
includeMaxTokens: true,
|
||||
// Unbound
|
||||
unboundApiKey: true,
|
||||
unboundModelId: true,
|
||||
unboundModelInfo: true,
|
||||
// Requesty
|
||||
requestyApiKey: true,
|
||||
requestyModelId: true,
|
||||
requestyModelInfo: true,
|
||||
// Claude 3.7 Sonnet Thinking
|
||||
modelTemperature: true,
|
||||
modelMaxTokens: true,
|
||||
modelMaxThinkingTokens: true,
|
||||
// Fake AI
|
||||
fakeAi: true,
|
||||
}
|
||||
|
||||
export const PROVIDER_SETTINGS_KEYS = Object.keys(providerSettingsKeys) as ProviderSettingsKey[]
|
||||
|
||||
/**
|
||||
* SecretStateKey
|
||||
*/
|
||||
|
||||
const secretStateKeys: Record<SecretStateKey, true> = {
|
||||
apiKey: true,
|
||||
glamaApiKey: true,
|
||||
openRouterApiKey: true,
|
||||
awsAccessKey: true,
|
||||
awsSecretKey: true,
|
||||
awsSessionToken: true,
|
||||
openAiApiKey: true,
|
||||
geminiApiKey: true,
|
||||
openAiNativeApiKey: true,
|
||||
deepSeekApiKey: true,
|
||||
mistralApiKey: true,
|
||||
unboundApiKey: true,
|
||||
requestyApiKey: true,
|
||||
}
|
||||
|
||||
export const SECRET_STATE_KEYS = Object.keys(secretStateKeys) as SecretStateKey[]
|
||||
|
||||
export const isSecretStateKey = (key: string): key is SecretStateKey =>
|
||||
SECRET_STATE_KEYS.includes(key as SecretStateKey)
|
||||
|
||||
/**
|
||||
* GlobalStateKey
|
||||
*/
|
||||
|
||||
const globalStateKeys: Record<GlobalStateKey, true> = {
|
||||
apiProvider: true,
|
||||
apiModelId: true,
|
||||
// Anthropic
|
||||
// apiKey: true,
|
||||
anthropicBaseUrl: true,
|
||||
// Glama
|
||||
// glamaApiKey: true,
|
||||
glamaModelId: true,
|
||||
glamaModelInfo: true,
|
||||
// OpenRouter
|
||||
// openRouterApiKey: true,
|
||||
openRouterModelId: true,
|
||||
openRouterModelInfo: true,
|
||||
openRouterBaseUrl: true,
|
||||
openRouterSpecificProvider: true,
|
||||
openRouterUseMiddleOutTransform: true,
|
||||
// AWS Bedrock
|
||||
// awsAccessKey: true,
|
||||
// awsSecretKey: true,
|
||||
// awsSessionToken: true,
|
||||
awsRegion: true,
|
||||
awsUseCrossRegionInference: true,
|
||||
awsUsePromptCache: true,
|
||||
awspromptCacheId: true,
|
||||
awsProfile: true,
|
||||
awsUseProfile: true,
|
||||
awsCustomArn: true,
|
||||
// Google Vertex
|
||||
vertexKeyFile: true,
|
||||
vertexJsonCredentials: true,
|
||||
vertexProjectId: true,
|
||||
vertexRegion: true,
|
||||
// OpenAI
|
||||
// openAiApiKey: true,
|
||||
openAiBaseUrl: true,
|
||||
openAiR1FormatEnabled: true,
|
||||
openAiModelId: true,
|
||||
openAiCustomModelInfo: true,
|
||||
openAiUseAzure: true,
|
||||
azureApiVersion: true,
|
||||
openAiStreamingEnabled: true,
|
||||
// Ollama
|
||||
ollamaModelId: true,
|
||||
ollamaBaseUrl: true,
|
||||
// VS Code LM
|
||||
vsCodeLmModelSelector: true,
|
||||
// LM Studio
|
||||
lmStudioModelId: true,
|
||||
lmStudioBaseUrl: true,
|
||||
lmStudioDraftModelId: true,
|
||||
lmStudioSpeculativeDecodingEnabled: true,
|
||||
// Gemini
|
||||
// geminiApiKey: true,
|
||||
googleGeminiBaseUrl: true,
|
||||
// OpenAI Native
|
||||
// openAiNativeApiKey: true,
|
||||
// Mistral
|
||||
// mistralApiKey: true,
|
||||
mistralCodestralUrl: true,
|
||||
// DeepSeek
|
||||
// deepSeekApiKey: true,
|
||||
deepSeekBaseUrl: true,
|
||||
includeMaxTokens: true,
|
||||
// Unbound
|
||||
// unboundApiKey: true,
|
||||
unboundModelId: true,
|
||||
unboundModelInfo: true,
|
||||
// Requesty
|
||||
// requestyApiKey: true,
|
||||
requestyModelId: true,
|
||||
requestyModelInfo: true,
|
||||
// Claude 3.7 Sonnet Thinking
|
||||
modelTemperature: true,
|
||||
modelMaxTokens: true,
|
||||
modelMaxThinkingTokens: true,
|
||||
// Fake AI
|
||||
fakeAi: true,
|
||||
|
||||
currentApiConfigName: true,
|
||||
listApiConfigMeta: true,
|
||||
pinnedApiConfigs: true,
|
||||
|
||||
lastShownAnnouncementId: true,
|
||||
customInstructions: true,
|
||||
taskHistory: true,
|
||||
|
||||
autoApprovalEnabled: true,
|
||||
alwaysAllowReadOnly: true,
|
||||
alwaysAllowReadOnlyOutsideWorkspace: true,
|
||||
alwaysAllowWrite: true,
|
||||
alwaysAllowWriteOutsideWorkspace: true,
|
||||
writeDelayMs: true,
|
||||
alwaysAllowBrowser: true,
|
||||
alwaysApproveResubmit: true,
|
||||
requestDelaySeconds: true,
|
||||
alwaysAllowMcp: true,
|
||||
alwaysAllowModeSwitch: true,
|
||||
alwaysAllowSubtasks: true,
|
||||
alwaysAllowExecute: true,
|
||||
allowedCommands: true,
|
||||
|
||||
browserToolEnabled: true,
|
||||
browserViewportSize: true,
|
||||
screenshotQuality: true,
|
||||
remoteBrowserEnabled: true,
|
||||
remoteBrowserHost: true,
|
||||
|
||||
enableCheckpoints: true,
|
||||
checkpointStorage: true,
|
||||
|
||||
ttsEnabled: true,
|
||||
ttsSpeed: true,
|
||||
soundEnabled: true,
|
||||
soundVolume: true,
|
||||
|
||||
maxOpenTabsContext: true,
|
||||
maxWorkspaceFiles: true,
|
||||
showRooIgnoredFiles: true,
|
||||
maxReadFileLine: true,
|
||||
|
||||
terminalOutputLineLimit: true,
|
||||
terminalShellIntegrationTimeout: true,
|
||||
|
||||
rateLimitSeconds: true,
|
||||
diffEnabled: true,
|
||||
fuzzyMatchThreshold: true,
|
||||
experiments: true,
|
||||
|
||||
language: true,
|
||||
|
||||
telemetrySetting: true,
|
||||
|
||||
mcpEnabled: true,
|
||||
enableMcpServerCreation: true,
|
||||
|
||||
mode: true,
|
||||
modeApiConfigs: true,
|
||||
customModes: true,
|
||||
customModePrompts: true,
|
||||
customSupportPrompts: true,
|
||||
enhancementApiConfigId: true,
|
||||
}
|
||||
|
||||
export const GLOBAL_STATE_KEYS = Object.keys(globalStateKeys) as GlobalStateKey[]
|
||||
|
||||
/**
|
||||
* PassThroughStateKey
|
||||
*
|
||||
* TODO: Why is this necessary?
|
||||
*/
|
||||
|
||||
const PASS_THROUGH_STATE_KEYS = ["taskHistory"] as const
|
||||
|
||||
type PassThroughStateKey = (typeof PASS_THROUGH_STATE_KEYS)[number]
|
||||
|
||||
export const isPassThroughStateKey = (key: string): key is PassThroughStateKey =>
|
||||
PASS_THROUGH_STATE_KEYS.includes(key as PassThroughStateKey)
|
||||
|
||||
/**
|
||||
* Schemas
|
||||
*/
|
||||
|
||||
/**
|
||||
* ModelInfo
|
||||
*/
|
||||
|
||||
const modelInfoSchema = z.object({
|
||||
maxTokens: z.number().optional(),
|
||||
contextWindow: z.number(),
|
||||
supportsImages: z.boolean().optional(),
|
||||
supportsComputerUse: z.boolean().optional(),
|
||||
supportsPromptCache: z.boolean(),
|
||||
inputPrice: z.number().optional(),
|
||||
outputPrice: z.number().optional(),
|
||||
cacheWritesPrice: z.number().optional(),
|
||||
cacheReadsPrice: z.number().optional(),
|
||||
description: z.string().optional(),
|
||||
reasoningEffort: z.enum(["low", "medium", "high"]).optional(),
|
||||
thinking: z.boolean().optional(),
|
||||
})
|
||||
|
||||
// Throws a type error if the inferred type of the modelInfoSchema is not equal
|
||||
// to ModelInfo.
|
||||
type _AssertModelInfo = AssertEqual<Equals<ModelInfo, z.infer<typeof modelInfoSchema>>>
|
||||
|
||||
/**
|
||||
* ApiConfigMeta
|
||||
*/
|
||||
|
||||
const apiConfigMetaSchema = z.object({
|
||||
id: z.string(),
|
||||
name: z.string(),
|
||||
apiProvider: z.enum(providerNamesEnum).optional(),
|
||||
})
|
||||
|
||||
type _AssertApiConfigMeta = AssertEqual<Equals<ApiConfigMeta, z.infer<typeof apiConfigMetaSchema>>>
|
||||
|
||||
/**
|
||||
* HistoryItem
|
||||
*/
|
||||
|
||||
const historyItemSchema = z.object({
|
||||
id: z.string(),
|
||||
number: z.number(),
|
||||
ts: z.number(),
|
||||
task: z.string(),
|
||||
tokensIn: z.number(),
|
||||
tokensOut: z.number(),
|
||||
cacheWrites: z.number().optional(),
|
||||
cacheReads: z.number().optional(),
|
||||
totalCost: z.number(),
|
||||
size: z.number().optional(),
|
||||
})
|
||||
|
||||
type _AssertHistoryItem = AssertEqual<Equals<HistoryItem, z.infer<typeof historyItemSchema>>>
|
||||
|
||||
/**
|
||||
* GroupEntry
|
||||
*/
|
||||
|
||||
const groupEntrySchema = z.union([
|
||||
z.enum(toolGroupsEnum),
|
||||
z
|
||||
.tuple([
|
||||
z.enum(toolGroupsEnum),
|
||||
z.object({
|
||||
fileRegex: z.string().optional(),
|
||||
description: z.string().optional(),
|
||||
}),
|
||||
])
|
||||
.readonly(),
|
||||
])
|
||||
|
||||
type _AssertGroupEntry = AssertEqual<Equals<GroupEntry, z.infer<typeof groupEntrySchema>>>
|
||||
|
||||
/**
|
||||
* ModeConfig
|
||||
*/
|
||||
|
||||
const modeConfigSchema = z.object({
|
||||
slug: z.string(),
|
||||
name: z.string(),
|
||||
roleDefinition: z.string(),
|
||||
customInstructions: z.string().optional(),
|
||||
groups: z.array(groupEntrySchema).readonly(),
|
||||
source: z.enum(["global", "project"]).optional(),
|
||||
})
|
||||
|
||||
type _AssertModeConfig = AssertEqual<Equals<ModeConfig, z.infer<typeof modeConfigSchema>>>
|
||||
|
||||
/**
|
||||
* ExperimentId
|
||||
*/
|
||||
|
||||
const experimentsSchema = z.object({
|
||||
experimentalDiffStrategy: z.boolean(),
|
||||
search_and_replace: z.boolean(),
|
||||
insert_content: z.boolean(),
|
||||
powerSteering: z.boolean(),
|
||||
multi_search_and_replace: z.boolean(),
|
||||
})
|
||||
|
||||
// Throws a type error if the inferred type of the experimentsSchema is not
|
||||
// equal to ExperimentId.
|
||||
type _AssertExperiments = AssertEqual<Equals<ExperimentId, Keys<z.infer<typeof experimentsSchema>>>>
|
||||
|
||||
/**
|
||||
* GlobalSettings
|
||||
*/
|
||||
|
||||
export const globalSettingsSchema = z.object({
|
||||
currentApiConfigName: z.string().optional(),
|
||||
listApiConfigMeta: z.array(apiConfigMetaSchema).optional(),
|
||||
pinnedApiConfigs: z.record(z.string(), z.boolean()).optional(),
|
||||
|
||||
lastShownAnnouncementId: z.string().optional(),
|
||||
customInstructions: z.string().optional(),
|
||||
taskHistory: z.array(historyItemSchema).optional(),
|
||||
|
||||
autoApprovalEnabled: z.boolean().optional(),
|
||||
alwaysAllowReadOnly: z.boolean().optional(),
|
||||
alwaysAllowReadOnlyOutsideWorkspace: z.boolean().optional(),
|
||||
alwaysAllowWrite: z.boolean().optional(),
|
||||
alwaysAllowWriteOutsideWorkspace: z.boolean().optional(),
|
||||
writeDelayMs: z.number().optional(),
|
||||
alwaysAllowBrowser: z.boolean().optional(),
|
||||
alwaysApproveResubmit: z.boolean().optional(),
|
||||
requestDelaySeconds: z.number().optional(),
|
||||
alwaysAllowMcp: z.boolean().optional(),
|
||||
alwaysAllowModeSwitch: z.boolean().optional(),
|
||||
alwaysAllowSubtasks: z.boolean().optional(),
|
||||
alwaysAllowExecute: z.boolean().optional(),
|
||||
allowedCommands: z.array(z.string()).optional(),
|
||||
|
||||
browserToolEnabled: z.boolean().optional(),
|
||||
browserViewportSize: z.string().optional(),
|
||||
screenshotQuality: z.number().optional(),
|
||||
remoteBrowserEnabled: z.boolean().optional(),
|
||||
remoteBrowserHost: z.string().optional(),
|
||||
|
||||
enableCheckpoints: z.boolean().optional(),
|
||||
checkpointStorage: z.enum(checkpointStoragesEnum).optional(),
|
||||
|
||||
ttsEnabled: z.boolean().optional(),
|
||||
ttsSpeed: z.number().optional(),
|
||||
soundEnabled: z.boolean().optional(),
|
||||
soundVolume: z.number().optional(),
|
||||
|
||||
maxOpenTabsContext: z.number().optional(),
|
||||
maxWorkspaceFiles: z.number().optional(),
|
||||
showRooIgnoredFiles: z.boolean().optional(),
|
||||
maxReadFileLine: z.number().optional(),
|
||||
|
||||
terminalOutputLineLimit: z.number().optional(),
|
||||
terminalShellIntegrationTimeout: z.number().optional(),
|
||||
|
||||
rateLimitSeconds: z.number().optional(),
|
||||
diffEnabled: z.boolean().optional(),
|
||||
fuzzyMatchThreshold: z.number().optional(),
|
||||
experiments: experimentsSchema.optional(),
|
||||
|
||||
language: z.enum(languagesEnum).optional(),
|
||||
|
||||
telemetrySetting: z.enum(telemetrySettingsEnum).optional(),
|
||||
|
||||
mcpEnabled: z.boolean().optional(),
|
||||
enableMcpServerCreation: z.boolean().optional(),
|
||||
|
||||
mode: z.string().optional(),
|
||||
modeApiConfigs: z.record(z.string(), z.string()).optional(),
|
||||
customModes: z.array(modeConfigSchema).optional(),
|
||||
customModePrompts: z
|
||||
.record(
|
||||
z.string(),
|
||||
z
|
||||
.object({
|
||||
roleDefinition: z.string().optional(),
|
||||
customInstructions: z.string().optional(),
|
||||
})
|
||||
.optional(),
|
||||
)
|
||||
.optional(),
|
||||
customSupportPrompts: z.record(z.string(), z.string().optional()).optional(),
|
||||
enhancementApiConfigId: z.string().optional(),
|
||||
})
|
||||
|
||||
// Throws a type error if the inferred type of the globalSettingsSchema is not
|
||||
// equal to GlobalSettings.
|
||||
type _AssertGlobalSettings = AssertEqual<Equals<GlobalSettings, z.infer<typeof globalSettingsSchema>>>
|
||||
|
||||
/**
|
||||
* ProviderSettings
|
||||
*/
|
||||
|
||||
export const providerSettingsSchema = z.object({
|
||||
apiProvider: z.enum(providerNamesEnum).optional(),
|
||||
// Anthropic
|
||||
apiModelId: z.string().optional(),
|
||||
apiKey: z.string().optional(),
|
||||
anthropicBaseUrl: z.string().optional(),
|
||||
// Glama
|
||||
glamaModelId: z.string().optional(),
|
||||
glamaModelInfo: modelInfoSchema.optional(),
|
||||
glamaApiKey: z.string().optional(),
|
||||
// OpenRouter
|
||||
openRouterApiKey: z.string().optional(),
|
||||
openRouterModelId: z.string().optional(),
|
||||
openRouterModelInfo: modelInfoSchema.optional(),
|
||||
openRouterBaseUrl: z.string().optional(),
|
||||
openRouterSpecificProvider: z.string().optional(),
|
||||
// AWS Bedrock
|
||||
awsAccessKey: z.string().optional(),
|
||||
awsSecretKey: z.string().optional(),
|
||||
awsSessionToken: z.string().optional(),
|
||||
awsRegion: z.string().optional(),
|
||||
awsUseCrossRegionInference: z.boolean().optional(),
|
||||
awsUsePromptCache: z.boolean().optional(),
|
||||
awspromptCacheId: z.string().optional(),
|
||||
awsProfile: z.string().optional(),
|
||||
awsUseProfile: z.boolean().optional(),
|
||||
awsCustomArn: z.string().optional(),
|
||||
// Google Vertex
|
||||
vertexKeyFile: z.string().optional(),
|
||||
vertexJsonCredentials: z.string().optional(),
|
||||
vertexProjectId: z.string().optional(),
|
||||
vertexRegion: z.string().optional(),
|
||||
// OpenAI
|
||||
openAiBaseUrl: z.string().optional(),
|
||||
openAiApiKey: z.string().optional(),
|
||||
openAiR1FormatEnabled: z.boolean().optional(),
|
||||
openAiModelId: z.string().optional(),
|
||||
openAiCustomModelInfo: modelInfoSchema.optional(),
|
||||
openAiUseAzure: z.boolean().optional(),
|
||||
// Ollama
|
||||
ollamaModelId: z.string().optional(),
|
||||
ollamaBaseUrl: z.string().optional(),
|
||||
// VS Code LM
|
||||
vsCodeLmModelSelector: z
|
||||
.object({
|
||||
vendor: z.string().optional(),
|
||||
family: z.string().optional(),
|
||||
version: z.string().optional(),
|
||||
id: z.string().optional(),
|
||||
})
|
||||
.optional(),
|
||||
// LM Studio
|
||||
lmStudioModelId: z.string().optional(),
|
||||
lmStudioBaseUrl: z.string().optional(),
|
||||
lmStudioDraftModelId: z.string().optional(),
|
||||
lmStudioSpeculativeDecodingEnabled: z.boolean().optional(),
|
||||
// Gemini
|
||||
geminiApiKey: z.string().optional(),
|
||||
googleGeminiBaseUrl: z.string().optional(),
|
||||
// OpenAI Native
|
||||
openAiNativeApiKey: z.string().optional(),
|
||||
// Mistral
|
||||
mistralApiKey: z.string().optional(),
|
||||
mistralCodestralUrl: z.string().optional(),
|
||||
// Azure
|
||||
azureApiVersion: z.string().optional(),
|
||||
// OpenRouter
|
||||
openRouterUseMiddleOutTransform: z.boolean().optional(),
|
||||
openAiStreamingEnabled: z.boolean().optional(),
|
||||
// DeepSeek
|
||||
deepSeekBaseUrl: z.string().optional(),
|
||||
deepSeekApiKey: z.string().optional(),
|
||||
// Unbound
|
||||
unboundApiKey: z.string().optional(),
|
||||
unboundModelId: z.string().optional(),
|
||||
unboundModelInfo: modelInfoSchema.optional(),
|
||||
// Requesty
|
||||
requestyApiKey: z.string().optional(),
|
||||
requestyModelId: z.string().optional(),
|
||||
requestyModelInfo: modelInfoSchema.optional(),
|
||||
// Claude 3.7 Sonnet Thinking
|
||||
modelTemperature: z.number().nullish(),
|
||||
modelMaxTokens: z.number().optional(),
|
||||
modelMaxThinkingTokens: z.number().optional(),
|
||||
// Generic
|
||||
includeMaxTokens: z.boolean().optional(),
|
||||
// Fake AI
|
||||
fakeAi: z.unknown().optional(),
|
||||
})
|
||||
|
||||
// Throws a type error if the inferred type of the providerSettingsSchema is not
|
||||
// equal to ProviderSettings.
|
||||
type _AssertProviderSettings = AssertEqual<Equals<ProviderSettings, z.infer<typeof providerSettingsSchema>>>
|
||||
|
|
@ -1,7 +1,6 @@
|
|||
import { type Language } from "../exports/roo-code"
|
||||
import { isLanguage } from "./globalState"
|
||||
import { type Language, isLanguage } from "../schemas"
|
||||
|
||||
export type { Language }
|
||||
export { type Language, isLanguage }
|
||||
|
||||
/**
|
||||
* Language name mapping from ISO codes to full language names
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import * as vscode from "vscode"
|
||||
|
||||
import { GroupOptions, GroupEntry, ModeConfig, PromptComponent, CustomModePrompts } from "../exports/roo-code"
|
||||
import { GroupOptions, GroupEntry, ModeConfig, PromptComponent, CustomModePrompts } from "../schemas"
|
||||
import { TOOL_GROUPS, ToolGroup, ALWAYS_AVAILABLE_TOOLS } from "./tool-groups"
|
||||
import { addCustomInstructions } from "../core/prompts/sections/custom-instructions"
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import type { ToolGroup } from "../exports/roo-code"
|
||||
import type { ToolGroup } from "../schemas"
|
||||
|
||||
// Define tool group configuration
|
||||
export type ToolGroupConfig = {
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ import {
|
|||
ModeConfig,
|
||||
GroupEntry,
|
||||
} from "../../../../src/shared/modes"
|
||||
import { CustomModeSchema } from "../../../../src/core/config/CustomModesSchema"
|
||||
import { modeConfigSchema } from "../../../../src/schemas"
|
||||
import { supportPrompt, SupportPromptType } from "../../../../src/shared/support-prompt"
|
||||
|
||||
import { TOOL_GROUPS, ToolGroup } from "../../../../src/shared/tool-groups"
|
||||
|
|
@ -223,7 +223,8 @@ const PromptsView = ({ onDone }: PromptsViewProps) => {
|
|||
}
|
||||
|
||||
// Validate the mode against the schema
|
||||
const result = CustomModeSchema.safeParse(newMode)
|
||||
const result = modeConfigSchema.safeParse(newMode)
|
||||
|
||||
if (!result.success) {
|
||||
// Map Zod errors to specific fields
|
||||
result.error.errors.forEach((error) => {
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue