diff --git a/.changeset/agent-manager-terminal-rendering.md b/.changeset/agent-manager-terminal-rendering.md
deleted file mode 100644
index c375a577662..00000000000
--- a/.changeset/agent-manager-terminal-rendering.md
+++ /dev/null
@@ -1,5 +0,0 @@
----
-"kilo-code": minor
----
-
-Improved command output rendering in Agent Manager with new CommandExecutionBlock component that displays terminal output with status indicators, collapsible output sections, and proper escape sequence handling.
diff --git a/.changeset/am-base-branch-selection.md b/.changeset/am-base-branch-selection.md
deleted file mode 100644
index 5d60e448d4f..00000000000
--- a/.changeset/am-base-branch-selection.md
+++ /dev/null
@@ -1,5 +0,0 @@
----
-"kilo-code": minor
----
-
-Add branch picker to Agent Manager for selecting base branch in worktree mode
diff --git a/.changeset/clean-dogs-teach.md b/.changeset/clean-dogs-teach.md
deleted file mode 100644
index 57c599f4594..00000000000
--- a/.changeset/clean-dogs-teach.md
+++ /dev/null
@@ -1,5 +0,0 @@
----
-"kilo-code": patch
----
-
-Add a tooltip explaining why speech-to-text may be unavailable
diff --git a/.changeset/few-ducks-smoke.md b/.changeset/few-ducks-smoke.md
deleted file mode 100644
index c240a509538..00000000000
--- a/.changeset/few-ducks-smoke.md
+++ /dev/null
@@ -1,5 +0,0 @@
----
-"kilo-code": patch
----
-
-Added a snooze for autocomplete in the settings
diff --git a/.changeset/green-cobras-dress.md b/.changeset/green-cobras-dress.md
deleted file mode 100644
index 83017648a53..00000000000
--- a/.changeset/green-cobras-dress.md
+++ /dev/null
@@ -1,5 +0,0 @@
----
-"kilo-code": patch
----
-
-Fix text.startsWith is not a function crash
diff --git a/.changeset/little-ravens-rest.md b/.changeset/little-ravens-rest.md
deleted file mode 100644
index d56f2dded97..00000000000
--- a/.changeset/little-ravens-rest.md
+++ /dev/null
@@ -1,5 +0,0 @@
----
-"kilo-code": patch
----
-
-Fix image generation handler not using Kilo Gateway properly
diff --git a/.changeset/neat-ghosts-argue.md b/.changeset/neat-ghosts-argue.md
deleted file mode 100644
index adf36b41c51..00000000000
--- a/.changeset/neat-ghosts-argue.md
+++ /dev/null
@@ -1,5 +0,0 @@
----
-"kilo-code": patch
----
-
-Prevent autocomplete from showing suggestions duplicating the previous or next line
diff --git a/.changeset/silly-lamps-create.md b/.changeset/silly-lamps-create.md
deleted file mode 100644
index 897d1154b5a..00000000000
--- a/.changeset/silly-lamps-create.md
+++ /dev/null
@@ -1,5 +0,0 @@
----
-"kilo-code": patch
----
-
-Fix duplicate tool processing in OpenAI-compatible provider
diff --git a/.changeset/tall-rockets-brake.md b/.changeset/tall-rockets-brake.md
deleted file mode 100644
index bb010658435..00000000000
--- a/.changeset/tall-rockets-brake.md
+++ /dev/null
@@ -1,5 +0,0 @@
----
-"kilo-code": patch
----
-
-Normalize line endings in search and replace tool
diff --git a/.changeset/wacky-lions-kiss.md b/.changeset/wacky-lions-kiss.md
new file mode 100644
index 00000000000..be58208d287
--- /dev/null
+++ b/.changeset/wacky-lions-kiss.md
@@ -0,0 +1,5 @@
+---
+"kilo-code": patch
+---
+
+Change the default value of auto-approval for reading outside workspace to false
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 8c2cee44ea8..640da968382 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,33 @@
# kilo-code
+## 4.139.0
+
+### Minor Changes
+
+- [#4481](https://github.com/Kilo-Org/kilocode/pull/4481) [`61c951c`](https://github.com/Kilo-Org/kilocode/commit/61c951c0ad11d60b07406338b6053cc5d1f01cac) Thanks [@marius-kilocode](https://github.com/marius-kilocode)! - Improved command output rendering in Agent Manager with new CommandExecutionBlock component that displays terminal output with status indicators, collapsible output sections, and proper escape sequence handling.
+
+- [#4483](https://github.com/Kilo-Org/kilocode/pull/4483) [`fd639ab`](https://github.com/Kilo-Org/kilocode/commit/fd639ab78aa4ab62ea2d120bd2844d1160b20067) Thanks [@marius-kilocode](https://github.com/marius-kilocode)! - Add branch picker to Agent Manager for selecting base branch in worktree mode
+
+- [#4539](https://github.com/Kilo-Org/kilocode/pull/4539) [`62a0241`](https://github.com/Kilo-Org/kilocode/commit/62a02418cafa23a733f92a9e14ba904552acdcc4) Thanks [@brianc](https://github.com/brianc)! - Improve managed indexer error handling & backoff.
+
+### Patch Changes
+
+- [#4512](https://github.com/Kilo-Org/kilocode/pull/4512) [`f979b56`](https://github.com/Kilo-Org/kilocode/commit/f979b56b6a631eeeb671caaca276316b63b5fb82) Thanks [@hassoncs](https://github.com/hassoncs)! - Add a tooltip explaining why speech-to-text may be unavailable
+
+- [#4424](https://github.com/Kilo-Org/kilocode/pull/4424) [`cd0cd88`](https://github.com/Kilo-Org/kilocode/commit/cd0cd8833f0e892cc2f1c96bb24ede6254cf12c9) Thanks [@markijbema](https://github.com/markijbema)! - Added a snooze for autocomplete in the settings
+
+- [#4519](https://github.com/Kilo-Org/kilocode/pull/4519) [`a9fd203`](https://github.com/Kilo-Org/kilocode/commit/a9fd2038ecb60fd799d164bcf1b2e4393302d15a) Thanks [@chrarnoldus](https://github.com/chrarnoldus)! - Fix text.startsWith is not a function crash
+
+- [#4536](https://github.com/Kilo-Org/kilocode/pull/4536) [`51f4774`](https://github.com/Kilo-Org/kilocode/commit/51f4774adcb90778826e00e9a50c45bb7bf11bc8) Thanks [@kevinvandijk](https://github.com/kevinvandijk)! - Fix image generation handler not using Kilo Gateway properly
+
+- [#4491](https://github.com/Kilo-Org/kilocode/pull/4491) [`823b86f`](https://github.com/Kilo-Org/kilocode/commit/823b86f196868f12efc60e5acb9b385d014bc644) Thanks [@markijbema](https://github.com/markijbema)! - Prevent autocomplete from showing suggestions duplicating the previous or next line
+
+- [#4531](https://github.com/Kilo-Org/kilocode/pull/4531) [`9413d73`](https://github.com/Kilo-Org/kilocode/commit/9413d730814d88ac67c88e6eec9a66c2c701613e) Thanks [@chrarnoldus](https://github.com/chrarnoldus)! - Fix duplicate tool processing in OpenAI-compatible provider
+
+- [#4533](https://github.com/Kilo-Org/kilocode/pull/4533) [`20b2c29`](https://github.com/Kilo-Org/kilocode/commit/20b2c29140f401ac65d437e35c52b48329e5f52d) Thanks [@mcowger](https://github.com/mcowger)! - Add gemini-3-flash-preview model configuration to vertex models
+
+- [#4520](https://github.com/Kilo-Org/kilocode/pull/4520) [`8342fc4`](https://github.com/Kilo-Org/kilocode/commit/8342fc4fbdc2a83601c706e734ef3377ef114f98) Thanks [@chrarnoldus](https://github.com/chrarnoldus)! - Normalize line endings in search and replace tool
+
## 4.138.0
### Minor Changes
diff --git a/apps/kilocode-docs/docs/features/auto-approving-actions.md b/apps/kilocode-docs/docs/features/auto-approving-actions.md
index e3d2db8a2db..28726acb3f6 100644
--- a/apps/kilocode-docs/docs/features/auto-approving-actions.md
+++ b/apps/kilocode-docs/docs/features/auto-approving-actions.md
@@ -80,6 +80,22 @@ _Complete settings panel view_
**Risk level:** Medium
While this setting only allows reading files (not modifying them), it could potentially expose sensitive data. Still recommended as a starting point for most users, but be mindful of what files Kilo Code can access.
+
+#### Read Outside Workspace
+
+**Setting:** "Allow reading files outside the workspace"
+
+**Description:** "When enabled, Kilo Code can read files outside the current workspace directory without asking for approval."
+
+**Risk level:** Medium-High
+
+This setting extends read permissions beyond your project folder. Consider the security implications:
+
+- Kilo Code could access sensitive files in your home directory
+- Configuration files, SSH keys, or credentials could be read
+- Only enable if you trust the AI and need it to access external files
+
+**Recommendation:** Keep disabled unless you specifically need Kilo Code to read files outside your project.
:::
### Write Operations
@@ -102,25 +118,52 @@ This setting allows Kilo Code to modify your files without confirmation. The del
- Lower values: Use only when speed is critical and you're in a controlled environment
- Zero: No delay for diagnostics (not recommended for critical code)
-#### Write Delay & Problems Pane Integration
+#### Write Outside Workspace
-
+**Setting:** "Allow writing files outside the workspace"
-_VSCode Problems pane that Kilo Code checks during the write delay_
+**Description:** "When enabled, Kilo Code can create or modify files outside the current workspace directory without asking for approval."
-When you enable auto-approval for writing files, the delay timer works with VSCode's Problems pane:
+**Risk level:** Very High
-1. Kilo Code makes a change to your file
-2. VSCode's diagnostic tools analyze the change
-3. The Problems pane updates with any errors or warnings
-4. Kilo Code notices these issues before continuing
+Use with caution and in controlled environments. It allows Kilo Code to:
-This works like a human developer pausing to check for errors after changing code. You can adjust the delay time based on:
+- Modify your shell configuration files
+- Change system configurations
+- Write to any location your user has access to
-- Project complexity
-- Language server speed
-- How important error detection is for your workflow
- :::
+**Recommendation:** Keep disabled unless absolutely necessary. Even experienced users should avoid this setting.
+
+#### Write to Protected Files
+
+**Setting:** "Allow writing to protected files"
+
+**Description:** "When enabled, Kilo Code can overwrite or modify files that are normally protected by the `.kilocodeignore` file."
+
+**Risk level:** Very High
+
+Protected files are intentionally shielded from modification. Enable only if you understand the consequences.
+
+### Delete Operations
+
+:::danger Delete Operations
+
+**Setting:** "Always approve delete operations"
+
+**Description:** "Automatically delete files and directories without requiring approval"
+
+**Risk level:** Very High
+
+This setting allows Kilo Code to permanently remove files without confirmation.
+
+**Safeguards:**
+
+- Kilo Code still respects `.kilocodeignore` rules
+- Protected files cannot be deleted
+- The delete tool shows what will be removed before execution
+
+**Recommendation:** Enable only in isolated environments or when working with temporary/generated files. Always ensure you have backups, checkpoints, or version control.
+:::
### Browser Actions
@@ -153,11 +196,11 @@ Consider the security implications of allowing automated browser access.
**Description:** "Automatically retry failed API requests when server returns an error response"
-**Delay slider:** "Delay before retrying the request" (Default: 5s)
-
**Risk level:** Low
-This setting automatically retries API calls when they fail. The delay controls how long Kilo Code waits before trying again:
+This setting automatically retries API calls when they fail.
+
+The delay controls how long Kilo Code waits before trying again:
- Longer delays are gentler on API rate limits
- Shorter delays give faster recovery from transient errors
@@ -214,23 +257,34 @@ Enables Kilo Code to create and complete subtasks automatically. This relates to
**Description:** "Automatically execute allowed terminal commands without requiring approval"
-**Command management:** "Command prefixes that can be auto-executed when 'Always approve execute operations' is enabled. Add \* to allow all commands (use with caution)."
-
**Risk level:** High
-This setting allows terminal command execution with controls. While risky, the whitelist feature limits what commands can run. Important security features:
+This setting allows terminal command execution with controls. While risky, the allowlist and denylist features limit what commands can run.
-- Whitelist specific command prefixes (recommended)
+- Allowlist specific command prefixes (recommended)
- Never use \* wildcard in production or with sensitive data
- Consider security implications of each allowed command
+- Consider including potentially dangerous common commands in the deny list
- Always verify commands that interact with external systems
+#### Allowed Commands
+
+**Setting:** "Command prefixes that can be auto-executed"
+
+Add command prefixes (e.g., `git`, `npm`, `ls`) that Kilo Code can run without asking. Use `*` to allow all commands (use with caution).
+
**Interface elements:**
- Text field to enter command prefixes (e.g., 'git')
- "Add" button to add new prefixes
- Clickable command buttons with X to remove them
- :::
+
+#### Denied Commands
+
+**Setting:** "Command prefixes that are always blocked"
+
+Commands in this list will never run, even if `*` is in the allowed list. Use this to create exceptions for potentially dangerous commands.
+:::
### Follow-Up Questions
@@ -240,9 +294,9 @@ This setting allows terminal command execution with controls. While risky, the w
**Description:** Automatically selects the first AI-suggested answer for a follow-up question after a configurable timeout. This speeds up your workflow by letting Kilo Code proceed without manual intervention.
-**Visual countdown:** When enabled, a countdown timer appears on the first suggestion button, showing the remaining time before auto-selection. The timer is displayed as a circular progress indicator that depletes as time passes.
+**Visual countdown:** When enabled, a countdown timer appears on the first suggestion button in the chat interface, showing the remaining time before auto-selection. The timer displays seconds remaining (e.g., "3s") and counts down in real-time.
-**Timeout slider:** Use the slider to set the wait time from 1 to 300 seconds (Default: 60s).
+**Timeout slider:** Use the slider to set the wait time (Range: 1-300 seconds, Default: 60s).
**Override options:** You can cancel the auto-selection at any time by:
@@ -277,13 +331,6 @@ This setting allows Kilo Code to automatically update task progress and todo lis
- Updating task status (pending, in progress, completed)
- Reorganizing task priorities
-**Benefits:**
-
-- Maintains real-time task progress visibility
-- Reduces interruptions during multi-step workflows
-- Keeps project status accurately reflected
-- Helps track complex task dependencies
-
**Use cases:**
- Long-running development sessions
@@ -294,7 +341,7 @@ This setting allows Kilo Code to automatically update task progress and todo lis
This is particularly useful when combined with the Subtasks permission, as it allows Kilo Code to maintain a complete picture of project progress without constant approval requests.
:::
-## YOLO mode
+## YOLO Mode
:::danger YOLO Mode (Risk: Maximum)
diff --git a/apps/kilocode-docs/static/img/auto-approving-actions/auto-approving-actions-1.png b/apps/kilocode-docs/static/img/auto-approving-actions/auto-approving-actions-1.png
index a10c0fce509..679265c5c5a 100644
Binary files a/apps/kilocode-docs/static/img/auto-approving-actions/auto-approving-actions-1.png and b/apps/kilocode-docs/static/img/auto-approving-actions/auto-approving-actions-1.png differ
diff --git a/apps/kilocode-docs/static/img/auto-approving-actions/auto-approving-actions-10.png b/apps/kilocode-docs/static/img/auto-approving-actions/auto-approving-actions-10.png
deleted file mode 100644
index 75d8b2834e1..00000000000
Binary files a/apps/kilocode-docs/static/img/auto-approving-actions/auto-approving-actions-10.png and /dev/null differ
diff --git a/apps/kilocode-docs/static/img/auto-approving-actions/auto-approving-actions-11.png b/apps/kilocode-docs/static/img/auto-approving-actions/auto-approving-actions-11.png
deleted file mode 100644
index 0b4e3ad7315..00000000000
Binary files a/apps/kilocode-docs/static/img/auto-approving-actions/auto-approving-actions-11.png and /dev/null differ
diff --git a/apps/kilocode-docs/static/img/auto-approving-actions/auto-approving-actions-12.png b/apps/kilocode-docs/static/img/auto-approving-actions/auto-approving-actions-12.png
deleted file mode 100644
index b00c8b442cb..00000000000
Binary files a/apps/kilocode-docs/static/img/auto-approving-actions/auto-approving-actions-12.png and /dev/null differ
diff --git a/apps/kilocode-docs/static/img/auto-approving-actions/auto-approving-actions-13.png b/apps/kilocode-docs/static/img/auto-approving-actions/auto-approving-actions-13.png
index d7afb227ad2..6998442dbc5 100644
Binary files a/apps/kilocode-docs/static/img/auto-approving-actions/auto-approving-actions-13.png and b/apps/kilocode-docs/static/img/auto-approving-actions/auto-approving-actions-13.png differ
diff --git a/apps/kilocode-docs/static/img/auto-approving-actions/auto-approving-actions-4.png b/apps/kilocode-docs/static/img/auto-approving-actions/auto-approving-actions-4.png
index 795e6d4ec85..aca74d783f9 100644
Binary files a/apps/kilocode-docs/static/img/auto-approving-actions/auto-approving-actions-4.png and b/apps/kilocode-docs/static/img/auto-approving-actions/auto-approving-actions-4.png differ
diff --git a/apps/kilocode-docs/static/img/auto-approving-actions/auto-approving-actions-5.png b/apps/kilocode-docs/static/img/auto-approving-actions/auto-approving-actions-5.png
deleted file mode 100644
index 67a78e503ed..00000000000
Binary files a/apps/kilocode-docs/static/img/auto-approving-actions/auto-approving-actions-5.png and /dev/null differ
diff --git a/apps/kilocode-docs/static/img/auto-approving-actions/auto-approving-actions-6.png b/apps/kilocode-docs/static/img/auto-approving-actions/auto-approving-actions-6.png
deleted file mode 100644
index 428b3963671..00000000000
Binary files a/apps/kilocode-docs/static/img/auto-approving-actions/auto-approving-actions-6.png and /dev/null differ
diff --git a/apps/kilocode-docs/static/img/auto-approving-actions/auto-approving-actions-7.png b/apps/kilocode-docs/static/img/auto-approving-actions/auto-approving-actions-7.png
deleted file mode 100644
index b99d3a12181..00000000000
Binary files a/apps/kilocode-docs/static/img/auto-approving-actions/auto-approving-actions-7.png and /dev/null differ
diff --git a/apps/kilocode-docs/static/img/auto-approving-actions/auto-approving-actions-8.png b/apps/kilocode-docs/static/img/auto-approving-actions/auto-approving-actions-8.png
deleted file mode 100644
index d495e474ce9..00000000000
Binary files a/apps/kilocode-docs/static/img/auto-approving-actions/auto-approving-actions-8.png and /dev/null differ
diff --git a/apps/kilocode-docs/static/img/auto-approving-actions/auto-approving-actions-9.png b/apps/kilocode-docs/static/img/auto-approving-actions/auto-approving-actions-9.png
index f5650cff7a2..238cbd30c77 100644
Binary files a/apps/kilocode-docs/static/img/auto-approving-actions/auto-approving-actions-9.png and b/apps/kilocode-docs/static/img/auto-approving-actions/auto-approving-actions-9.png differ
diff --git a/packages/types/src/providers/vertex.ts b/packages/types/src/providers/vertex.ts
index 53b67418cf0..db7c90edafe 100644
--- a/packages/types/src/providers/vertex.ts
+++ b/packages/types/src/providers/vertex.ts
@@ -6,6 +6,19 @@ export type VertexModelId = keyof typeof vertexModels
export const vertexDefaultModelId: VertexModelId = "claude-sonnet-4-5@20250929"
export const vertexModels = {
+ "gemini-3-flash-preview": {
+ maxTokens: 65_536,
+ contextWindow: 1_048_576,
+ supportsImages: true,
+ supportsNativeTools: true,
+ supportsPromptCache: true,
+ supportsReasoningEffort: ["minimal", "low", "medium", "high"],
+ reasoningEffort: "medium",
+ supportsTemperature: true,
+ defaultTemperature: 1,
+ inputPrice: 0.5,
+ outputPrice: 3.0,
+ },
"gemini-3-pro-preview": {
maxTokens: 65_536,
contextWindow: 1_048_576,
diff --git a/src/core/webview/ClineProvider.ts b/src/core/webview/ClineProvider.ts
index 52436dccdb1..d4008f7001c 100644
--- a/src/core/webview/ClineProvider.ts
+++ b/src/core/webview/ClineProvider.ts
@@ -2264,7 +2264,7 @@ ${prompt}
apiConfiguration,
customInstructions,
alwaysAllowReadOnly: alwaysAllowReadOnly ?? true,
- alwaysAllowReadOnlyOutsideWorkspace: alwaysAllowReadOnlyOutsideWorkspace ?? true,
+ alwaysAllowReadOnlyOutsideWorkspace: alwaysAllowReadOnlyOutsideWorkspace ?? false,
alwaysAllowWrite: alwaysAllowWrite ?? true,
alwaysAllowWriteOutsideWorkspace: alwaysAllowWriteOutsideWorkspace ?? false,
alwaysAllowWriteProtected: alwaysAllowWriteProtected ?? false,
@@ -2559,7 +2559,7 @@ ${prompt}
customInstructions: stateValues.customInstructions,
apiModelId: stateValues.apiModelId,
alwaysAllowReadOnly: stateValues.alwaysAllowReadOnly ?? true,
- alwaysAllowReadOnlyOutsideWorkspace: stateValues.alwaysAllowReadOnlyOutsideWorkspace ?? true,
+ alwaysAllowReadOnlyOutsideWorkspace: stateValues.alwaysAllowReadOnlyOutsideWorkspace ?? false,
alwaysAllowWrite: stateValues.alwaysAllowWrite ?? true,
alwaysAllowWriteOutsideWorkspace: stateValues.alwaysAllowWriteOutsideWorkspace ?? false,
alwaysAllowWriteProtected: stateValues.alwaysAllowWriteProtected ?? false,
diff --git a/src/package.json b/src/package.json
index efe6a90785b..6594de000cc 100644
--- a/src/package.json
+++ b/src/package.json
@@ -3,7 +3,7 @@
"displayName": "%extension.displayName%",
"description": "%extension.description%",
"publisher": "kilocode",
- "version": "4.138.0",
+ "version": "4.139.0",
"icon": "assets/icons/logo-outline-black.png",
"galleryBanner": {
"color": "#FFFFFF",
diff --git a/src/services/code-index/managed/ManagedIndexer.ts b/src/services/code-index/managed/ManagedIndexer.ts
index 54f5900bf13..d38c414b8f3 100644
--- a/src/services/code-index/managed/ManagedIndexer.ts
+++ b/src/services/code-index/managed/ManagedIndexer.ts
@@ -63,6 +63,55 @@ interface ManagedIndexerWorkspaceFolderState {
ignoreController: RooIgnoreController | null
}
+function logGitEvent(event: GitWatcherEvent) {
+ // Handle different event types
+ switch (event.type) {
+ case "branch-changed": {
+ console.info(`[ManagedIndexer] Branch changed from ${event.previousBranch} to ${event.newBranch}`)
+ break
+ }
+
+ case "commit": {
+ console.info(`[ManagedIndexer] Commit detected from ${event.previousCommit} to ${event.newCommit}`)
+ break
+ }
+
+ case "start": {
+ console.info(
+ `[ManagedIndexer] Watcher started on branch ${event.branch} ${event.isBaseBranch ? `(base)` : `(feature)`} - doing initial indexing`,
+ )
+ break
+ }
+ }
+}
+
+/**
+ * Serialize workspace folder state to a plain object for communication
+ * @param state The workspace folder state to serialize
+ * @returns A serializable object representation of the state
+ */
+function serializeWorkspaceFolderState(state: ManagedIndexerWorkspaceFolderState) {
+ return {
+ workspaceFolderPath: state.workspaceFolder.uri.fsPath,
+ workspaceFolderName: state.workspaceFolder.name,
+ gitBranch: state.gitBranch,
+ projectId: state.projectId,
+ repositoryUrl: state.repositoryUrl,
+ isIndexing: state.isIndexing,
+ hasManifest: !!state.manifest,
+ manifestFileCount: state.manifest ? Object.keys(state.manifest.files).length : 0,
+ hasWatcher: !!state.watcher,
+ error: state.error
+ ? {
+ type: state.error.type,
+ message: state.error.message,
+ timestamp: state.error.timestamp,
+ context: state.error.context,
+ }
+ : undefined,
+ }
+}
+
export class ManagedIndexer implements vscode.Disposable {
private static prevInstance: ManagedIndexer | null = null
private disabledViaConfig: boolean = false
@@ -154,40 +203,23 @@ export class ManagedIndexer implements vscode.Disposable {
}
/**
- * Get a complete serializable snapshot of the managed indexer state
- * for communication to the webview
+ * Send the complete managed indexer state to the webview
*/
- private getManagedIndexerStateSnapshot() {
- return {
+ sendStateToWebview(stateOverride?: ManagedIndexerWorkspaceFolderState, fileCount?: number) {
+ const state = {
isEnabled: this.isEnabled(),
isActive: this.isActive,
- workspaceFolders: this.workspaceFolderState.map((state) => ({
- workspaceFolderPath: state.workspaceFolder.uri.fsPath,
- workspaceFolderName: state.workspaceFolder.name,
- gitBranch: state.gitBranch,
- projectId: state.projectId,
- repositoryUrl: state.repositoryUrl,
- isIndexing: state.isIndexing,
- hasManifest: !!state.manifest,
- manifestFileCount: state.manifest ? Object.keys(state.manifest.files).length : 0,
- hasWatcher: !!state.watcher,
- error: state.error
- ? {
- type: state.error.type,
- message: state.error.message,
- timestamp: state.error.timestamp,
- context: state.error.context,
- }
- : undefined,
- })),
+ workspaceFolders: this.workspaceFolderState.map(serializeWorkspaceFolderState),
+ }
+ if (stateOverride && fileCount) {
+ const index = this.workspaceFolderState.indexOf(stateOverride)
+ if (index > -1) {
+ const folderState = state.workspaceFolders[index]
+ if (folderState) {
+ folderState.manifestFileCount = fileCount
+ }
+ }
}
- }
-
- /**
- * Send the complete managed indexer state to the webview
- */
- sendStateToWebview() {
- const state = this.getManagedIndexerStateSnapshot()
const provider = ClineProvider.getVisibleInstance()
if (provider) {
provider.postMessageToWebview({
@@ -415,11 +447,6 @@ export class ManagedIndexer implements vscode.Disposable {
return state.manifestFetchPromise
}
- // If manifest is already cached for this branch, return it
- if (state.manifest && state.gitBranch === branch && !force) {
- return state.manifest
- }
-
// Update branch BEFORE starting fetch so concurrent calls know we're fetching for this branch
state.gitBranch = branch
@@ -517,43 +544,10 @@ export class ManagedIndexer implements vscode.Disposable {
const controller = new AbortController()
state.currentAbortController = controller
- try {
- // Handle different event types
- switch (event.type) {
- case "branch-changed": {
- console.info(`[ManagedIndexer] Branch changed from ${event.previousBranch} to ${event.newBranch}`)
-
- try {
- // Fetch manifest for the new branch (will reuse if already fetching)
- await this.getManifest(state, event.newBranch)
- } catch (error) {
- // Error already logged and stored in getManifest
- console.warn(`[ManagedIndexer] Continuing despite manifest fetch error`)
- }
-
- // Process files from the async iterable
- await this.processFiles(state, event, controller.signal)
- break
- }
-
- case "commit": {
- console.info(`[ManagedIndexer] Commit detected from ${event.previousCommit} to ${event.newCommit}`)
-
- // Process files from the async iterable
- await this.processFiles(state, event, controller.signal)
- break
- }
+ logGitEvent(event)
- case "start": {
- console.info(
- `[ManagedIndexer] Watcher started on branch ${event.branch} ${event.isBaseBranch ? `(base)` : `(feature)`} - doing initial indexing`,
- )
-
- // Process files from the async iterable
- await this.processFiles(state, event, controller.signal)
- break
- }
- }
+ try {
+ await this.processFiles(state, event, controller.signal)
} catch (error) {
// Check if this was an abort
if (error instanceof Error && (error.name === "AbortError" || error.message === "AbortError")) {
@@ -597,6 +591,8 @@ export class ManagedIndexer implements vscode.Disposable {
// Start with all files from manifest - we'll remove entries as we encounter them in git
const manifestFilesToCheck = new Set(Object.values(manifest.files))
const filesToDelete: string[] = []
+ let upsertCount = manifestFilesToCheck.size
+ let errorCount = 0
await pMap(
event.files,
@@ -610,6 +606,10 @@ export class ManagedIndexer implements vscode.Disposable {
throw new Error("ManagedIndexing is not enabled")
}
+ if (!this.isActive) {
+ return
+ }
+
const { filePath } = file
if (file.type === "file-deleted") {
@@ -636,90 +636,97 @@ export class ManagedIndexer implements vscode.Disposable {
return
}
- {
- // Check if operation was aborted before processing
- if (signal.aborted) {
- throw new Error("AbortError")
+ // Check if operation was aborted before processing
+ if (signal.aborted) {
+ throw new Error("AbortError")
+ }
+
+ try {
+ // Ensure we have the necessary configuration
+ // check again inside loop as this can change mid-flight
+ if (!this.config?.kilocodeToken || !state.projectId) {
+ return
+ }
+ const projectId = state.projectId
+
+ const absoluteFilePath = path.isAbsolute(filePath)
+ ? filePath
+ : path.join(event.watcher.config.cwd, filePath)
+
+ // if file is larger than 1 megabyte, skip it
+ const stats = await fs.stat(absoluteFilePath)
+ if (stats.size > 1 * 1024 * 1024) {
+ return
+ }
+
+ const fileBuffer = await fs.readFile(absoluteFilePath)
+ const relativeFilePath = path.relative(event.watcher.config.cwd, absoluteFilePath)
+
+ // Check RooIgnoreController
+ const ignore = state.ignoreController
+ if (ignore && !ignore.validateAccess(relativeFilePath)) {
+ return
}
- try {
- // Ensure we have the necessary configuration
- // check again inside loop as this can change mid-flight
- if (!this.config?.kilocodeToken || !state.projectId) {
- return
- }
- const projectId = state.projectId
-
- const absoluteFilePath = path.isAbsolute(filePath)
- ? filePath
- : path.join(event.watcher.config.cwd, filePath)
-
- // if file is larger than 1 megabyte, skip it
- const stats = await fs.stat(absoluteFilePath)
- if (stats.size > 1 * 1024 * 1024) {
- return
- }
-
- const fileBuffer = await fs.readFile(absoluteFilePath)
- const relativeFilePath = path.relative(event.watcher.config.cwd, absoluteFilePath)
-
- // Check RooIgnoreController
- const ignore = state.ignoreController
- if (ignore && !ignore.validateAccess(relativeFilePath)) {
- return
- }
-
- // Check hardcoded ignore list
- if (shouldIgnoreFile(relativeFilePath)) {
- return
- }
-
- // Call the upsertFile API with abort signal
- await upsertFile(
- {
- fileBuffer,
- fileHash,
- filePath: relativeFilePath,
- gitBranch: event.branch,
- isBaseBranch: event.isBaseBranch,
- organizationId: this.config.kilocodeOrganizationId,
- projectId,
- kilocodeToken: this.config.kilocodeToken,
- },
- signal,
- )
-
- // Clear any previous file-upsert errors on success
- if (state.error?.type === "file-upsert") {
- state.error = undefined
- this.sendStateToWebview()
- }
- } catch (error) {
- // Don't log abort errors as failures
- if (error instanceof Error && error.message === "AbortError") {
- throw error
- }
-
- const errorMessage = error instanceof Error ? error.message : String(error)
- console.error(`[ManagedIndexer] Failed to upsert file ${filePath}: ${errorMessage}`)
-
- // Store the error in state
- state.error = {
- type: "file-upsert",
- message: `Failed to upsert file: ${errorMessage}`,
- timestamp: new Date().toISOString(),
- context: {
- filePath,
- branch: event.branch,
- operation: "file-upsert",
- },
- details: error instanceof Error ? error.stack : undefined,
- }
+ // Check hardcoded ignore list
+ if (shouldIgnoreFile(relativeFilePath)) {
+ return
+ }
+
+ // Call the upsertFile API with abort signal
+ await upsertFile(
+ {
+ fileBuffer,
+ fileHash,
+ filePath: relativeFilePath,
+ gitBranch: event.branch,
+ isBaseBranch: event.isBaseBranch,
+ organizationId: this.config.kilocodeOrganizationId,
+ projectId,
+ kilocodeToken: this.config.kilocodeToken,
+ },
+ signal,
+ )
+
+ upsertCount++
+ this.sendStateToWebview(state, upsertCount)
+
+ // Clear any previous file-upsert errors on success
+ if (state.error?.type === "file-upsert") {
+ state.error = undefined
this.sendStateToWebview()
}
+ } catch (error) {
+ // Don't log abort errors as failures
+ if (error instanceof Error && error.message === "AbortError") {
+ throw error
+ }
+
+ errorCount++
+ // if we have 3 indexing errors, something is wrong....stop trying
+ if (errorCount > 2) {
+ this.dispose()
+ }
+
+ const errorMessage = error instanceof Error ? error.message : String(error)
+ console.error(`[ManagedIndexer] Failed to upsert file ${filePath}: ${errorMessage}`)
+
+ // Store the error in state
+ state.error = {
+ type: "file-upsert",
+ message: `Failed to upsert file: ${errorMessage}`,
+ timestamp: new Date().toISOString(),
+ context: {
+ filePath,
+ branch: event.branch,
+ operation: "file-upsert",
+ },
+ details: error instanceof Error ? error.stack : undefined,
+ }
+ this.sendStateToWebview()
}
},
- { concurrency: 5 },
+ { concurrency: 2 },
)
// Any files remaining in manifestFilesToCheck were not encountered in git
@@ -784,31 +791,6 @@ export class ManagedIndexer implements vscode.Disposable {
this.sendStateToWebview()
}
- /**
- * Get a serializable representation of the current workspace folder state
- * for debugging and introspection purposes
- */
- getWorkspaceFolderStateSnapshot() {
- return this.workspaceFolderState.map((state) => ({
- workspaceFolderPath: state.workspaceFolder.uri.fsPath,
- workspaceFolderName: state.workspaceFolder.name,
- gitBranch: state.gitBranch,
- projectId: state.projectId,
- isIndexing: state.isIndexing,
- hasManifest: !!state.manifest,
- manifestFileCount: state.manifest ? Object.keys(state.manifest.files).length : 0,
- hasWatcher: !!state.watcher,
- error: state.error
- ? {
- type: state.error.type,
- message: state.error.message,
- timestamp: state.error.timestamp,
- context: state.error.context,
- }
- : undefined,
- }))
- }
-
public async search(query: string, directoryPrefix?: string): Promise {
const { kilocodeOrganizationId, kilocodeToken } = this.config ?? {}
@@ -852,102 +834,4 @@ export class ManagedIndexer implements vscode.Disposable {
}))
.sort((a, b) => b.score - a.score)
}
-
- /**
- * Manually trigger a scan for a specific workspace folder
- * This is useful for forcing a rescan from the UI
- *
- * @param workspaceFolderPath The path of the workspace folder to scan
- * @throws Error if the workspace folder is not found or not properly initialized
- */
- async startScanForWorkspaceFolder(workspaceFolderPath: string): Promise {
- console.log("[ManagedIndexer] Manual scan requested for workspace folder", { workspaceFolderPath })
-
- if (!this.isActive) {
- throw new Error("ManagedIndexer is not active")
- }
-
- // Find the workspace folder state
- const state = this.workspaceFolderState.find((s) => s.workspaceFolder.uri.fsPath === workspaceFolderPath)
-
- if (!state) {
- throw new Error(`Workspace folder not found: ${workspaceFolderPath}`)
- }
-
- if (!state.watcher) {
- throw new Error(`Watcher not initialized for workspace folder: ${workspaceFolderPath}`)
- }
-
- if (!state.projectId || !state.gitBranch) {
- throw new Error(`Workspace folder not fully initialized: ${workspaceFolderPath}`)
- }
-
- // Cancel any previous indexing operation
- if (state.currentAbortController) {
- console.info("[ManagedIndexer] Aborting previous indexing operation for manual scan")
- state.currentAbortController.abort()
- }
-
- // Create new AbortController for this operation
- const controller = new AbortController()
- state.currentAbortController = controller
-
- try {
- console.info(
- `[ManagedIndexer] Starting manual scan for ${workspaceFolderPath} on branch ${state.gitBranch}`,
- )
-
- // Determine if this is the base branch
- const defaultBranch = await state.watcher?.getDefaultBranch()
- const isBaseBranch = state.gitBranch.toLowerCase() === defaultBranch.toLowerCase()
-
- // Create a synthetic event to trigger file processing using GitWatcher's getFiles method
- const syntheticEvent: GitWatcherEvent = {
- type: "commit",
- previousCommit: "",
- newCommit: await getCurrentCommitSha(state.workspaceFolder.uri.fsPath),
- branch: state.gitBranch,
- isBaseBranch,
- watcher: state.watcher,
- files: state.watcher.getFiles(state.gitBranch, isBaseBranch),
- }
-
- // Refresh the manifest before scanning
- try {
- await this.getManifest(state, state.gitBranch)
- } catch (error) {
- console.warn(`[ManagedIndexer] Failed to refresh manifest, continuing with cached version`)
- }
-
- // Process files using the existing logic
- await this.processFiles(state, syntheticEvent, controller.signal)
-
- console.info(`[ManagedIndexer] Manual scan completed for ${workspaceFolderPath}`)
- } catch (error) {
- // Check if this was an abort
- if (error instanceof Error && (error.name === "AbortError" || error.message === "AbortError")) {
- console.info("[ManagedIndexer] Manual scan was aborted")
- return
- }
-
- const errorMessage = error instanceof Error ? error.message : String(error)
- console.error(`[ManagedIndexer] Manual scan failed for ${workspaceFolderPath}: ${errorMessage}`)
-
- state.error = {
- type: "scan",
- message: `Manual scan failed: ${errorMessage}`,
- timestamp: new Date().toISOString(),
- context: {
- operation: "manual-scan",
- branch: state.gitBranch,
- },
- details: error instanceof Error ? error.stack : undefined,
- }
-
- // Send state update after error
- this.sendStateToWebview()
-
- throw error
- }
- }
}
diff --git a/src/services/code-index/managed/api-client.ts b/src/services/code-index/managed/api-client.ts
index 1c755b69487..5da76a379d2 100644
--- a/src/services/code-index/managed/api-client.ts
+++ b/src/services/code-index/managed/api-client.ts
@@ -21,6 +21,7 @@ export async function isEnabled(kilocodeToken: string, organizationId: string |
const response = await fetchWithRetries({
url,
method: "GET",
+ retries: 2,
headers: {
Authorization: `Bearer ${kilocodeToken}`,
"Content-Type": "application/json",
@@ -60,6 +61,7 @@ export async function searchCode(
const response = await fetchWithRetries({
url: `${baseUrl}/api/code-indexing/search`,
method: "POST",
+ retries: 2,
headers: {
Authorization: `Bearer ${kilocodeToken}`,
"Content-Type": "application/json",
@@ -144,6 +146,7 @@ export async function upsertFile(params: UpsertFileParams, signal?: AbortSignal)
const response = await fetchWithRetries({
url: `${baseUrl}/api/code-indexing/upsert-by-file`,
method: "PUT",
+ retries: 2,
headers: {
Authorization: `Bearer ${kilocodeToken}`,
},
@@ -197,6 +200,7 @@ export async function getServerManifest(
const response = await fetchWithRetries({
url: `${baseUrl}/api/code-indexing/manifest?${params.toString()}`,
method: "GET",
+ retries: 2,
headers: {
Authorization: `Bearer ${kilocodeToken}`,
"Content-Type": "application/json",
@@ -266,6 +270,7 @@ export async function deleteFiles(params: DeleteFilesParams, signal?: AbortSigna
const response = await fetchWithRetries({
url: `${baseUrl}/api/code-indexing/delete`,
method: "POST",
+ retries: 2,
headers: {
Authorization: `Bearer ${kilocodeToken}`,
"Content-Type": "application/json",