Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -66,3 +66,6 @@ qdrant_storage/
# allow multiple local clones with different workspaces with different colors
# to make it easier to work on features in parallel
*.code-workspace

# Manual release guide
MANUAL_RELEASE.md
Empty file added CHANGELOG.md
Empty file.
2 changes: 1 addition & 1 deletion src/api/providers/__tests__/constants.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,6 @@ describe("DEFAULT_HEADERS", () => {
it("should have exactly 4 headers", () => {
const headerKeys = Object.keys(DEFAULT_HEADERS)
expect(headerKeys).toHaveLength(4)
expect(headerKeys).toEqual(["HTTP-Referer", "X-Title", "X-KiloCode-Version", "User-Agent"])
expect(headerKeys).toEqual(["HTTP-Referer", "X-Title", "X-AxonCode-Version", "User-Agent"])
})
})
2 changes: 1 addition & 1 deletion src/api/providers/__tests__/openai.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@ describe("OpenAiHandler", () => {
defaultHeaders: {
"HTTP-Referer": "https://matterai.so",
"X-Title": "Axon Code",
"X-KiloCode-Version": Package.version,
"X-AxonCode-Version": Package.version,
"User-Agent": `Kilo-Code/${Package.version}`,
},
timeout: expect.any(Number),
Expand Down
2 changes: 1 addition & 1 deletion src/api/providers/__tests__/openrouter.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ describe("OpenRouterHandler", () => {
defaultHeaders: {
"HTTP-Referer": "https://matterai.so",
"X-Title": "Axon Code",
"X-KiloCode-Version": Package.version,
"X-AxonCode-Version": Package.version,
"User-Agent": `Kilo-Code/${Package.version}`,
},
})
Expand Down
4 changes: 2 additions & 2 deletions src/api/providers/__tests__/requesty.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ describe("RequestyHandler", () => {
defaultHeaders: {
"HTTP-Referer": "https://matterai.so",
"X-Title": "Axon Code",
"X-KiloCode-Version": Package.version,
"X-AxonCode-Version": Package.version,
"User-Agent": `Kilo-Code/${Package.version}`,
},
})
Expand All @@ -76,7 +76,7 @@ describe("RequestyHandler", () => {
defaultHeaders: {
"HTTP-Referer": "https://matterai.so",
"X-Title": "Axon Code",
"X-KiloCode-Version": Package.version,
"X-AxonCode-Version": Package.version,
"User-Agent": `Kilo-Code/${Package.version}`,
},
})
Expand Down
1 change: 1 addition & 0 deletions src/api/providers/fetchers/openrouter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,7 @@ export async function getOpenRouterModelEndpoints(
const baseURL = "https://api.matterai.so/v1/web"

try {
console.log("getOpenRouterModelEndpoints 1", baseURL, modelId)
const response = await axios.get<OpenRouterModelEndpointsResponse>(`${baseURL}/models/${modelId}`, {
timeout: 120000, // 60 seconds timeout
})
Expand Down
3 changes: 1 addition & 2 deletions src/api/providers/openrouter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -146,8 +146,8 @@ export class OpenRouterHandler extends BaseProvider implements SingleCompletionH
const baseURL = this.options.openRouterBaseUrl || "https://api.matterai.so/v1/web"
const apiKey = this.options.openRouterApiKey ?? "not-provided"

// this.client = new OpenAI({ baseURL, apiKey, defaultHeaders: DEFAULT_HEADERS })
this.client = new OpenAI({ baseURL, apiKey, defaultHeaders: DEFAULT_HEADERS })
Comment on lines 147 to 150
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔴 Security

Issue: The API key defaults to "not-provided" when openRouterApiKey is undefined, which could lead to runtime failures or expose development configuration in production.

Fix: Add proper validation to ensure the API key is provided before client initialization, or throw a clear error if missing.

Impact: Prevents runtime errors and ensures proper configuration validation

Suggested change
const apiKey = this.options.openRouterApiKey ?? "not-provided"
// this.client = new OpenAI({ baseURL, apiKey, defaultHeaders: DEFAULT_HEADERS })
this.client = new OpenAI({ baseURL, apiKey, defaultHeaders: DEFAULT_HEADERS })
if (!this.options.openRouterApiKey) {
throw new Error('OpenRouter API key is required but not provided');
}
const apiKey = this.options.openRouterApiKey;
this.client = new OpenAI({ baseURL, apiKey, defaultHeaders: DEFAULT_HEADERS })

Fix in Cursor

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

/matter remember this is by design

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Got it — Updated memory for OpenRouter API Key Validation Design Decision 📖

// this.client = new OpenAI({ baseURL: "http://localhost:4064/v1/web", apiKey, defaultHeaders: DEFAULT_HEADERS })
}

// kilocode_change start
Expand Down Expand Up @@ -306,7 +306,6 @@ export class OpenRouterHandler extends BaseProvider implements SingleCompletionH

newText = newText.replace(/<\/?think>/g, "")
newText = newText.replace(/<think>/g, "")
newText = newText.trim()

yield {
type: "reasoning",
Expand Down
9 changes: 8 additions & 1 deletion src/core/assistant-message/AssistantMessageParser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -161,7 +161,14 @@ export class AssistantMessageParser {

try {
if (accumulatedCall.function!.arguments.trim()) {
parsedArgs = JSON.parse(accumulatedCall.function!.arguments)
// Fix common JSON formatting issues before parsing
let fixedArgs = accumulatedCall.function!.arguments

// Fix unquoted string values in JSON (e.g., file_pattern:*.js -> file_pattern:"*.js")
// This regex looks for property names followed by colon and unquoted values that contain word characters, dots, asterisks, etc.
fixedArgs = fixedArgs.replace(/("([^"]+)"\s*:\s*)([a-zA-Z0-9_.*\/\\-]+)(?=\s*[,\]}])/g, '$1"$3"')

parsedArgs = JSON.parse(fixedArgs)

// Fix any double-encoded parameters
parsedArgs = parseDoubleEncodedParams(parsedArgs)
Expand Down
137 changes: 137 additions & 0 deletions src/core/prompts/system.ts
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,143 @@ The tool accepts these parameters:

- \`command\` (required): The CLI command to execute. Must be valid for the user's operating system.
- \`cwd\` (optional): The working directory to execute the command in. If not provided, the current working directory is used. Ensure this is always an absolute path, starting with \`/\`. If you are running the command in the root directly, skip this parameter. The command executor is defaulted to run in the root directory. You already have the Current Workspace Directory in <environment_details>.

## search_files

The \`search_files\` tool allows you to search for patterns across files in a directory using regex.

### Parameters

1. **path** (string, required): Directory to search recursively, relative to workspace
2. **regex** (string, required): Rust-compatible regular expression pattern
3. **file_pattern** (string or null, required): Glob pattern to filter files OR null

### CRITICAL: file_pattern Must Be a String or null

**The \`file_pattern\` parameter MUST ALWAYS be:**
- A properly quoted string: \`"*.js"\`, \`"*.tsx"\`, \`"**/*.json"\`
- OR explicitly \`null\` if you want to search all files

**NEVER provide an unquoted value like \`*.js\` - this will cause a JSON parsing error.**

### Correct Examples
\`\`\`json
// Search for "import" in all TypeScript files
{
"path": "src",
"regex": "import.*from",
"file_pattern": "*.ts"
}

// Search for "TODO" in all files (no filter)
{
"path": "src",
"regex": "TODO:",
"file_pattern": null
}

// Search in JSX/TSX files only
{
"path": "src/components",
"regex": "useState",
"file_pattern": "*.{jsx,tsx}"
}

// Search in nested directories
{
"path": ".",
"regex": "API_KEY",
"file_pattern": "**/*.env*"
}
\`\`\`

### ❌ INCORRECT Examples
\`\`\`json
// WRONG - Unquoted file_pattern (will cause JSON error)
{
"path": "src",
"regex": "import",
"file_pattern": *.js
}

// WRONG - Missing file_pattern entirely
{
"path": "src",
"regex": "import"
}

// WRONG - Empty string instead of null
{
"path": "src",
"regex": "import",
"file_pattern": ""
}
\`\`\`

### Regex Pattern Tips

- Use Rust regex syntax (similar to PCRE)
- Escape special characters: \`\.\`, \`\(\`, \`\[\`, etc.
- Common patterns:
- \`"word"\` - literal match
- \`"\\bword\\b"\` - word boundary match
- \`"function\\s+\\w+"\` - function declarations
- \`"import.*from\\s+['\"].*['\"]"\` - import statements

### File Pattern Glob Syntax

When using a string value for \`file_pattern\`:
- \`"*.js"\` - All .js files in directory
- \`"*.{js,ts}"\` - All .js and .ts files
- \`"**/*.json"\` - All .json files recursively
- \`"test_*.py"\` - Files starting with test_
- \`"src/**/*.tsx"\` - All .tsx files under src/

**When in doubt, use \`null\` to search all files.**

### Common Use Cases
\`\`\`json
// Find all TODO comments
{
"path": "src",
"regex": "TODO:|FIXME:",
"file_pattern": null
}

// Find specific function calls
{
"path": "src",
"regex": "localStorage\\.(get|set)Item",
"file_pattern": "*.{js,jsx,ts,tsx}"
}

// Find imports of a specific module
{
"path": ".",
"regex": "from ['\"]react['\"]",
"file_pattern": "**/*.tsx"
}

// Find environment variable usage
{
"path": "src",
"regex": "process\\.env\\.",
"file_pattern": "*.js"
}
\`\`\`

### Parameter Validation Checklist

Before submitting, verify:
- ✅ \`path\` is a string (directory path)
- ✅ \`regex\` is a string (valid Rust regex)
- ✅ \`file_pattern\` is EITHER a quoted string OR null
- ✅ All three parameters are present
- ✅ No unquoted glob patterns like \`*.js\`

### Remember

**Always quote the file_pattern value or use null. Never use bare/unquoted glob patterns.**
`

async function generatePrompt(
Expand Down
2 changes: 1 addition & 1 deletion src/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
"displayName": "%extension.displayName%",
"description": "%extension.description%",
"publisher": "matterai",
"version": "4.115.0",
"version": "4.116.0",
"icon": "assets/icons/matterai-ic.png",
"galleryBanner": {
"color": "#FFFFFF",
Expand Down
11 changes: 9 additions & 2 deletions src/services/code-index/config-manager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,12 @@ export class CodeIndexConfigManager {
private geminiOptions?: { apiKey: string }
private mistralOptions?: { apiKey: string }
private vercelAiGatewayOptions?: { apiKey: string }
private openRouterApiKey?: string
private qdrantUrl?: string = "http://localhost:6333"
private qdrantApiKey?: string
private searchMinScore?: number
private searchMaxResults?: number
private matterAiOptions?: { apiKey: string }

constructor(private readonly contextProxy: ContextProxy) {
// Initialize with current configuration to avoid false restart triggers
Expand Down Expand Up @@ -65,6 +67,8 @@ export class CodeIndexConfigManager {

const openAiKey = this.contextProxy?.getSecret("codeIndexOpenAiKey") ?? ""
const qdrantApiKey = this.contextProxy?.getSecret("codeIndexQdrantApiKey") ?? ""
// Get openRouterApiKey from main API configuration, not as separate secret
const openRouterApiKey = this.contextProxy?.getSecret("openRouterApiKey") ?? ""
// Fix: Read OpenAI Compatible settings from the correct location within codebaseIndexConfig
const openAiCompatibleBaseUrl = codebaseIndexConfig.codebaseIndexOpenAiCompatibleBaseUrl ?? ""
const openAiCompatibleApiKey = this.contextProxy?.getSecret("codebaseIndexOpenAiCompatibleApiKey") ?? ""
Expand All @@ -76,6 +80,7 @@ export class CodeIndexConfigManager {
this.codebaseIndexEnabled = codebaseIndexEnabled ?? true
this.qdrantUrl = codebaseIndexQdrantUrl
this.qdrantApiKey = qdrantApiKey ?? ""
this.openRouterApiKey = openRouterApiKey
this.searchMinScore = codebaseIndexSearchMinScore
this.searchMaxResults = codebaseIndexSearchMaxResults

Expand Down Expand Up @@ -395,10 +400,12 @@ export class CodeIndexConfigManager {
geminiOptions: this.geminiOptions,
mistralOptions: this.mistralOptions,
vercelAiGatewayOptions: this.vercelAiGatewayOptions,
openRouterApiKey: this.openRouterApiKey,
qdrantUrl: this.qdrantUrl,
qdrantApiKey: this.qdrantApiKey,
searchMinScore: this.currentSearchMinScore,
searchMaxResults: this.currentSearchMaxResults,
searchMinScore: this.searchMinScore,
searchMaxResults: this.searchMaxResults,
matterAiOptions: this.matterAiOptions,
}
}

Expand Down
88 changes: 88 additions & 0 deletions src/services/code-index/embedders/matterai.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
import { OpenAICompatibleEmbedder } from "./openai-compatible"
import { IEmbedder, EmbeddingResponse, EmbedderInfo } from "../interfaces/embedder"
import { GEMINI_MAX_ITEM_TOKENS, MAX_ITEM_TOKENS } from "../constants"
import { t } from "../../../i18n"
import { TelemetryEventName } from "@roo-code/types"
import { TelemetryService } from "@roo-code/telemetry"

/**
* MatterAI embedder implementation that wraps the OpenAI Compatible embedder
* with configuration for MatterAI's embedding API.
*
* Supported models:
* - matterai-embedding-large (dimension: 3072)
*/
export class MatterAiEmbedder implements IEmbedder {
private readonly openAICompatibleEmbedder: OpenAICompatibleEmbedder
private static readonly MATTERAI_BASE_URL = "https://api.matterai.so/v1/embed"
private static readonly DEFAULT_MODEL = "matterai-embedding-large"
private readonly modelId: string

/**
* Creates a new MatterAI embedder
* @param modelId The model ID to use (defaults to matterai-embedding-large)
*/
constructor(apiKey: string, modelId?: string) {
// Use provided model or default
this.modelId = modelId || MatterAiEmbedder.DEFAULT_MODEL

// Create an OpenAI Compatible embedder with MatterAI's configuration
this.openAICompatibleEmbedder = new OpenAICompatibleEmbedder(
MatterAiEmbedder.MATTERAI_BASE_URL,
apiKey,
this.modelId,
2048,
)
}

/**
* Creates embeddings for the given texts using Gemini's embedding API
* @param texts Array of text strings to embed
* @param model Optional model identifier (uses constructor model if not provided)
* @returns Promise resolving to embedding response
*/
async createEmbeddings(texts: string[], model?: string): Promise<EmbeddingResponse> {
try {
// Use the provided model or fall back to the instance's model
const modelToUse = model || this.modelId
return await this.openAICompatibleEmbedder.createEmbeddings(texts, modelToUse)
} catch (error) {
TelemetryService.instance.captureEvent(TelemetryEventName.CODE_INDEX_ERROR, {
error: error instanceof Error ? error.message : String(error),
stack: error instanceof Error ? error.stack : undefined,
location: "MatterAiEmbedder:createEmbeddings",
})
console.error("MatterAI embedder error in createEmbeddings:", error) // kilocode_change
throw error
}
}

/**
* Validates the MatterAI embedder configuration by delegating to the underlying OpenAI-compatible embedder
* @returns Promise resolving to validation result with success status and optional error message
*/
async validateConfiguration(): Promise<{ valid: boolean; error?: string }> {
try {
// Delegate validation to the OpenAI-compatible embedder
// The error messages will be specific to MatterAI since we're using MatterAI's base URL
return await this.openAICompatibleEmbedder.validateConfiguration()
} catch (error) {
TelemetryService.instance.captureEvent(TelemetryEventName.CODE_INDEX_ERROR, {
error: error instanceof Error ? error.message : String(error),
stack: error instanceof Error ? error.stack : undefined,
location: "MatterAiEmbedder:validateConfiguration",
})
console.error("MatterAI embedder error in validateConfiguration:", error) // kilocode_change
throw error
}
}

/**
* Returns information about this embedder
*/
get embedderInfo(): EmbedderInfo {
return {
name: "matterai",
}
}
}
2 changes: 2 additions & 0 deletions src/services/code-index/interfaces/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,12 @@ export interface CodeIndexConfig {
geminiOptions?: { apiKey: string }
mistralOptions?: { apiKey: string }
vercelAiGatewayOptions?: { apiKey: string }
openRouterApiKey?: string // Add OpenRouter API key field
qdrantUrl?: string
qdrantApiKey?: string
searchMinScore?: number
searchMaxResults?: number
matterAiOptions?: { apiKey: string }
}

/**
Expand Down
Loading
Loading