Procházet zdrojové kódy

Merge branch 'main' into add-canceling-indexing

Kevin van Dijk před 4 měsíci
rodič
revize
d53cf20292
100 změnil soubory, kde provedl 4677 přidání a 849 odebrání
  1. 0 5
      .changeset/angry-boxes-turn.md
  2. 0 5
      .changeset/brave-beers-clean.md
  3. 0 5
      .changeset/late-eggs-enter.md
  4. 0 5
      .changeset/public-cloths-shine.md
  5. 27 20
      .github/workflows/marketplace-publish.yml
  6. 0 3
      .github/workflows/storybook-playwright-snapshot.yml
  7. 70 0
      .github/workflows/update-contributors.yml
  8. 4 0
      .gitignore
  9. 109 0
      CHANGELOG.md
  10. 57 1
      README.md
  11. 29 3
      apps/kilocode-docs/.kilocode/rules/memory-bank/tech.md
  12. 4 0
      apps/kilocode-docs/docs/advanced-usage/custom-rules.md
  13. 4 0
      apps/kilocode-docs/docs/advanced-usage/memory-bank.md
  14. 94 0
      apps/kilocode-docs/docs/basic-usage/autocomplete.md
  15. 1 1
      apps/kilocode-docs/docs/basic-usage/model-selection-guide.md
  16. 151 0
      apps/kilocode-docs/docs/features/auto-launch-configuration.md
  17. 4 0
      apps/kilocode-docs/docs/features/codebase-indexing.md
  18. 4 0
      apps/kilocode-docs/docs/getting-started/your-first-task.md
  19. 114 0
      apps/kilocode-docs/docs/jetbrains-troubleshooting.md
  20. 11 0
      apps/kilocode-docs/docs/providers/kilocode.md
  21. 76 0
      apps/kilocode-docs/docs/providers/vercel-ai-gateway.md
  22. 111 0
      apps/kilocode-docs/docs/teams/analytics.md
  23. 119 0
      apps/kilocode-docs/docs/teams/billing.md
  24. 143 0
      apps/kilocode-docs/docs/teams/dashboard.md
  25. 92 0
      apps/kilocode-docs/docs/teams/getting-started.md
  26. 375 0
      apps/kilocode-docs/docs/teams/migration.md
  27. 181 0
      apps/kilocode-docs/docs/teams/roles-permissions.md
  28. 164 0
      apps/kilocode-docs/docs/teams/team-management.md
  29. 60 61
      apps/kilocode-docs/i18n/zh-CN/docusaurus-theme-classic/footer.json
  30. 15 0
      apps/kilocode-docs/sidebars.ts
  31. 16 16
      apps/kilocode-docs/src/constants.ts
  32. binární
      apps/kilocode-docs/static/img/jetbrains/android-studio-jcef-enable.gif
  33. binární
      apps/kilocode-docs/static/img/kilo-provider/connected-accounts.png
  34. binární
      apps/kilocode-docs/static/img/teams/create-team.png
  35. binární
      apps/kilocode-docs/static/img/teams/dashboard.png
  36. binární
      apps/kilocode-docs/static/img/teams/invite-member.png
  37. binární
      apps/kilocode-docs/static/img/teams/subscribe.png
  38. binární
      apps/kilocode-docs/static/img/teams/usage-details.png
  39. 10 0
      apps/kilocode-docs/turbo.json
  40. 2 0
      apps/storybook/.storybook/main.ts
  41. 111 0
      apps/storybook/src/components/CodeHighlighterExample.tsx
  42. 8 0
      apps/storybook/src/mocks/jsdom.ts
  43. 59 0
      apps/storybook/src/mocks/vscode.ts
  44. 87 0
      apps/storybook/stories/SvgCodeHighlighter.stories.tsx
  45. 1 1
      apps/web-roo-code/next.config.ts
  46. 24 101
      apps/web-roo-code/src/app/evals/evals.tsx
  47. 336 0
      apps/web-roo-code/src/app/evals/plot.tsx
  48. 9 0
      apps/web-roo-code/src/app/evals/types.ts
  49. 15 26
      apps/web-roo-code/src/components/chromes/nav-bar.tsx
  50. 1 0
      apps/web-roo-code/src/lib/constants.ts
  51. 7 1
      apps/web-roo-code/src/lib/format-currency.ts
  52. 5 1
      apps/web-roo-code/src/lib/format-duration.ts
  53. 5 1
      apps/web-roo-code/src/lib/format-tokens.ts
  54. 1 1
      apps/web-roo-code/src/lib/hooks/use-open-router-models.ts
  55. 0 1
      jetbrains/plugin/.gitignore
  56. 12 6
      jetbrains/plugin/genPlatform.gradle
  57. 14 7
      jetbrains/plugin/src/main/kotlin/ai/kilocode/jetbrains/actors/MainThreadConsoleShape.kt
  58. 31 12
      jetbrains/plugin/src/main/kotlin/ai/kilocode/jetbrains/core/ExtensionSocketServer.kt
  59. 35 9
      jetbrains/plugin/src/main/kotlin/ai/kilocode/jetbrains/core/ExtensionUnixDomainSocketServer.kt
  60. 30 7
      jetbrains/plugin/src/main/kotlin/ai/kilocode/jetbrains/ipc/NodeSocket.kt
  61. 1 1
      jetbrains/plugin/src/main/kotlin/ai/kilocode/jetbrains/ipc/proxy/RPCProtocol.kt
  62. 75 34
      jetbrains/plugin/src/main/kotlin/ai/kilocode/jetbrains/plugin/SystemObjectProvider.kt
  63. 113 27
      jetbrains/plugin/src/main/kotlin/ai/kilocode/jetbrains/plugin/WecoderPlugin.kt
  64. 31 9
      jetbrains/plugin/src/main/kotlin/ai/kilocode/jetbrains/terminal/WeCoderTerminalCustomizer.kt
  65. 2 2
      jetbrains/plugin/src/main/kotlin/ai/kilocode/jetbrains/ui/RooToolWindowFactory.kt
  66. 74 0
      jetbrains/plugin/src/main/kotlin/ai/kilocode/jetbrains/util/LogLevelConfig.kt
  67. 156 94
      jetbrains/plugin/src/main/kotlin/ai/kilocode/jetbrains/webview/WebViewManager.kt
  68. 5 0
      packages/cloud/src/CloudService.ts
  69. 15 0
      packages/cloud/src/CloudSettingsService.ts
  70. 7 0
      packages/cloud/src/StaticSettingsService.ts
  71. 2 2
      packages/cloud/src/TelemetryClient.ts
  72. 8 0
      packages/cloud/src/__tests__/CloudService.test.ts
  73. 189 0
      packages/cloud/src/__tests__/CloudSettingsService.test.ts
  74. 23 0
      packages/cloud/src/__tests__/StaticSettingsService.test.ts
  75. 22 64
      packages/cloud/src/__tests__/TelemetryClient.test.ts
  76. 19 4
      packages/cloud/src/bridge/BridgeOrchestrator.ts
  77. 2 0
      packages/evals/package.json
  78. 6 0
      packages/evals/src/db/migrations/0002_bouncy_blazing_skull.sql
  79. 453 0
      packages/evals/src/db/migrations/meta/0002_snapshot.json
  80. 7 0
      packages/evals/src/db/migrations/meta/_journal.json
  81. 6 0
      packages/evals/src/db/schema.ts
  82. 4 3
      packages/telemetry/src/TelemetryService.ts
  83. 1 1
      packages/types/npm/package.metadata.json
  84. 9 0
      packages/types/src/cloud.ts
  85. 1 4
      packages/types/src/global-settings.ts
  86. 1 0
      packages/types/src/kilocode.ts
  87. 6 1
      packages/types/src/provider-settings.ts
  88. 105 21
      packages/types/src/providers/chutes.ts
  89. 8 8
      packages/types/src/providers/deepseek.ts
  90. 14 0
      packages/types/src/providers/openai.ts
  91. 20 0
      packages/types/src/providers/sambanova.ts
  92. 55 0
      packages/types/src/providers/vertex.ts
  93. 50 0
      packages/types/src/providers/xai.ts
  94. 14 0
      packages/types/src/providers/zai.ts
  95. 7 2
      packages/types/src/telemetry.ts
  96. 158 273
      pnpm-lock.yaml
  97. binární
      releases/3.28.0-release.png
  98. binární
      releases/3.28.1-release.png
  99. binární
      releases/3.28.2-release.png
  100. 170 0
      scripts/update-contributors.js

+ 0 - 5
.changeset/angry-boxes-turn.md

@@ -1,5 +0,0 @@
----
-"kilo-code": patch
----
-
-Improved the behavior of the Virtual Quota Fallback provider when there are no limits configured.

+ 0 - 5
.changeset/brave-beers-clean.md

@@ -1,5 +0,0 @@
----
-"kilo-code": patch
----
-
-Change default mode on first start from architect to code and tweak mode selector menu to show all default modes

+ 0 - 5
.changeset/late-eggs-enter.md

@@ -1,5 +0,0 @@
----
-"kilo-code": minor
----
-
-Jetbrains - Improve Light Theme

+ 0 - 5
.changeset/public-cloths-shine.md

@@ -1,5 +0,0 @@
----
-"kilo-code": patch
----
-
-Add support for Qwen3-Next-80B-A3B-Instruct and Qwen3-Next-80B-A3B-Thinking to Chutes provider

+ 27 - 20
.github/workflows/marketplace-publish.yml

@@ -109,12 +109,17 @@ jobs:
 
     publish-jetbrains:
         runs-on: ubuntu-latest
+        if: >
+            ( github.event_name == 'pull_request' &&
+            github.event.pull_request.base.ref == 'main' &&
+            contains(github.event.pull_request.title, 'Changeset version bump') ) ||
+            github.event_name == 'workflow_dispatch'
         steps:
             - name: Checkout code
               uses: actions/checkout@v4
               with:
-                submodules: recursive
-                lfs: true
+                  submodules: recursive
+                  lfs: true
             - name: Install pnpm
               uses: pnpm/action-setup@v4
               with:
@@ -126,17 +131,17 @@ jobs:
                   cache: "pnpm"
             - uses: actions/setup-java@v4
               with:
-                  distribution: 'jetbrains'
-                  java-version: '17'
+                  distribution: "jetbrains"
+                  java-version: "17"
             - name: Install system dependencies
               run: |
-                sudo apt-get update
-                sudo apt-get install -y \
-                  libx11-dev \
-                  libxkbfile-dev \
-                  pkg-config \
-                  build-essential \
-                  python3
+                  sudo apt-get update
+                  sudo apt-get install -y \
+                    libx11-dev \
+                    libxkbfile-dev \
+                    pkg-config \
+                    build-essential \
+                    python3
             - name: Turbo cache setup
               uses: actions/cache@v4
               with:
@@ -146,6 +151,8 @@ jobs:
                       ${{ runner.os }}-turbo-
             - name: Install dependencies
               run: pnpm install
+            - name: Create .env file
+              run: echo "KILOCODE_POSTHOG_API_KEY=${{ secrets.POSTHOG_API_KEY }}" >> .env
             - name: Build
               run: pnpm run jetbrains:bundle
               shell: bash
@@ -158,12 +165,12 @@ jobs:
                   name: ${{ env.BUNDLE_NAME }}
                   path: jetbrains/plugin/build/distributions/${{ env.BUNDLE_NAME }}
             - name: JetBrains Marketplace Publisher
-              run: | 
-                curl \
-                -X POST \
-                -H "Authorization: Bearer ${{ secrets.JETBRAINS_MARKETPLACE_TOKEN }}" \
-                -F "file=@jetbrains/plugin/build/distributions/${{ env.BUNDLE_NAME }}" \
-                -F "pluginId=28350" \
-                -F "channel=stable" \
-                -F "isHidden=false" \
-                https://plugins.jetbrains.com/plugin/uploadPlugin
+              run: |
+                  curl \
+                  -X POST \
+                  -H "Authorization: Bearer ${{ secrets.JETBRAINS_MARKETPLACE_TOKEN }}" \
+                  -F "file=@jetbrains/plugin/build/distributions/${{ env.BUNDLE_NAME }}" \
+                  -F "pluginId=28350" \
+                  -F "channel=stable" \
+                  -F "isHidden=false" \
+                  https://plugins.jetbrains.com/plugin/uploadPlugin

+ 0 - 3
.github/workflows/storybook-playwright-snapshot.yml

@@ -4,9 +4,6 @@ on:
     workflow_dispatch:
     push:
         branches: [main]
-    pull_request:
-        types: [opened, reopened, ready_for_review, synchronize]
-        branches: [main]
 
 # Cancel in-progress jobs when new workflow is triggered
 concurrency:

+ 70 - 0
.github/workflows/update-contributors.yml

@@ -0,0 +1,70 @@
+name: Update Contributors
+
+on:
+    schedule:
+        - cron: "0 0 * * *" # Run daily at midnight UTC
+    workflow_dispatch: # Allow manual trigger
+
+jobs:
+    update-contributors:
+        runs-on: ubuntu-latest
+        steps:
+            - name: Checkout repository
+              uses: actions/checkout@v4
+              with:
+                  token: ${{ secrets.GITHUB_TOKEN }}
+
+            - name: Setup pnpm
+              uses: pnpm/action-setup@v2
+              with:
+                  version: latest
+
+            - name: Setup Node.js
+              uses: actions/setup-node@v4
+              with:
+                  node-version: 22
+                  cache: "pnpm"
+
+            - name: Install dependencies
+              run: pnpm install --frozen-lockfile
+
+            - name: Update contributors
+              run: pnpm run update-contributors
+
+            - name: Check for changes
+              id: check
+              run: |
+                  git diff --quiet README.md || echo "changes=true" >> $GITHUB_OUTPUT
+
+            - name: Create Pull Request
+              if: steps.check.outputs.changes == 'true'
+              env:
+                  GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+              run: |
+                  # Configure git
+                  git config user.name "github-actions[bot]"
+                  git config user.email "github-actions[bot]@users.noreply.github.com"
+
+                  # Create a new branch
+                  BRANCH_NAME="update-contributors-${{ github.run_number }}"
+                  git checkout -b "$BRANCH_NAME"
+
+                  # Commit the changes
+                  git add README.md
+                  git commit -m "chore: update contributors list"
+
+                  # Push the branch
+                  git push -u origin "$BRANCH_NAME"
+
+                  # Create the PR using GitHub CLI
+                  gh pr create \
+                    --title "chore: update contributors list" \
+                    --body "## Automated Contributors Update
+
+                  This PR automatically updates the contributors list based on recent activity.
+
+                  Generated by the daily contributors update workflow." \
+                    --label "documentation" \
+                    --label "automated" \
+                    --base main \
+                    --head "$BRANCH_NAME"

+ 4 - 0
.gitignore

@@ -62,3 +62,7 @@ deps/vscode/*
 
 # Qdrant
 qdrant_storage/
+
+# allow multiple local clones with different workspaces with different colors
+# to make it easier to work on features in parallel
+*.code-workspace

+ 109 - 0
CHANGELOG.md

@@ -1,5 +1,114 @@
 # kilo-code
 
+## [v4.97.2]
+
+- [#2655](https://github.com/Kilo-Org/kilocode/pull/2655) [`3f83727`](https://github.com/Kilo-Org/kilocode/commit/3f8372708344171f4b379b90ad04693e1f67be39) Thanks [@PierreAncey](https://github.com/PierreAncey)! - Add Grok 4 Fast model to xAI provider
+
+- [#2648](https://github.com/Kilo-Org/kilocode/pull/2648) [`6f3f9fb`](https://github.com/Kilo-Org/kilocode/commit/6f3f9fba397ad34430c98a6db7ef535fe32622e8) Thanks [@catrielmuller](https://github.com/catrielmuller)! - Fix error logging behavior in JetBrains plugin by updating console bridge log levels
+
+- [#2617](https://github.com/Kilo-Org/kilocode/pull/2617) [`a94bf01`](https://github.com/Kilo-Org/kilocode/commit/a94bf01f7df542ffd372bbb0d385b39941187b0d) Thanks [@RSO](https://github.com/RSO)! - JetBrains: Fix terminal not having complete path
+
+## [v4.97.1]
+
+- [#2625](https://github.com/Kilo-Org/kilocode/pull/2625) [`3409665`](https://github.com/Kilo-Org/kilocode/commit/340966544bda3a069f9cf2478658bf58f5e2cf3c) Thanks [@kevinvandijk](https://github.com/kevinvandijk)! - Add fix for Gemini CLI not being able to refresh access token anymore
+
+- [#2536](https://github.com/Kilo-Org/kilocode/pull/2536) [`1a01114`](https://github.com/Kilo-Org/kilocode/commit/1a011145572333d053b8999c3f38bf718bbedf66) Thanks [@mcowger](https://github.com/mcowger)! - Only validate embedders when they match the currently configured provider
+
+- [#2491](https://github.com/Kilo-Org/kilocode/pull/2491) [`06afc76`](https://github.com/Kilo-Org/kilocode/commit/06afc769d29740083027a1caa6195edcfbbb94e2) Thanks [@Thireus](https://github.com/Thireus)! - Increase OpenAI Compatible timeout
+
+## [v4.97.0]
+
+- [#2505](https://github.com/Kilo-Org/kilocode/pull/2505) [`a59e7f5`](https://github.com/Kilo-Org/kilocode/commit/a59e7f565478c7405e62c59448bf7667e4b26c8f) Thanks [@markijbema](https://github.com/markijbema)! - Added option to the Display tab of the settings to disable autocomplete gutter animation
+
+- [#2602](https://github.com/Kilo-Org/kilocode/pull/2602) [`0807e5f`](https://github.com/Kilo-Org/kilocode/commit/0807e5ffdfcef1f90e6469a964d47ec177cca706) Thanks [@kevinvandijk](https://github.com/kevinvandijk)! - Add GPT-5-Codex to OpenAI provider (thanks Roo / @daniel-lxs)
+
+### Patch Changes
+
+- [#2583](https://github.com/Kilo-Org/kilocode/pull/2583) [`0c13d2d`](https://github.com/Kilo-Org/kilocode/commit/0c13d2db8391f194150001a2fc1e247573a95db2) Thanks [@chrarnoldus](https://github.com/chrarnoldus)! - The rate limiter no longer generates timeouts longer than the configured limit.
+
+- [#2596](https://github.com/Kilo-Org/kilocode/pull/2596) [`38f4547`](https://github.com/Kilo-Org/kilocode/commit/38f45478d4183f375e8a717a3564d3ac91fd6daa) Thanks [@chrarnoldus](https://github.com/chrarnoldus)! - Reasoning can now be disabled for DeepSeek V3.1 models when using Kilo Code or OpenRouter providers by setting Reasoning Effort to minimal
+
+- [#2586](https://github.com/Kilo-Org/kilocode/pull/2586) [`0b4025d`](https://github.com/Kilo-Org/kilocode/commit/0b4025df4c44d86a0aba20d19d5b32f2eaa214c6) Thanks [@b3nw](https://github.com/b3nw)! - New Chutes AI models added and pricing updated
+
+- [#2603](https://github.com/Kilo-Org/kilocode/pull/2603) [`b5325a8`](https://github.com/Kilo-Org/kilocode/commit/b5325a82abe94e195b580ac27cd0a8bf7f8577a7) Thanks [@chrarnoldus](https://github.com/chrarnoldus)! - Reasoning can now be disabled for Grok 4 Fast on OpenRouter by setting Reasoning Effort to minimal. Note that Grok 4 Fast does not expose its reasoning tokens.
+
+- [#2570](https://github.com/Kilo-Org/kilocode/pull/2570) [`18963de`](https://github.com/Kilo-Org/kilocode/commit/18963de4dce86be883c03ceeb418e820bd2c0635) Thanks [@snova-jorgep](https://github.com/snova-jorgep)! - Update available SambaNova models
+
+## [v4.96.2]
+
+- [#2521](https://github.com/Kilo-Org/kilocode/pull/2521) [`9304511`](https://github.com/Kilo-Org/kilocode/commit/9304511cb001114886f026744c3492f6a6a839f2) Thanks [@mcowger](https://github.com/mcowger)! - Update loop error message to refer to model instead of Kilo Code as the cause.
+
+- [#2532](https://github.com/Kilo-Org/kilocode/pull/2532) [`8103ad4`](https://github.com/Kilo-Org/kilocode/commit/8103ad4b59135888861b06c2cff7fc35ba965607) Thanks [@chrarnoldus](https://github.com/chrarnoldus)! - The description of the read_file tool was tweaked to make it more likely a vision-capable model will use it for image reading.
+
+- [#2558](https://github.com/Kilo-Org/kilocode/pull/2558) [`3044c43`](https://github.com/Kilo-Org/kilocode/commit/3044c43479b7d64599af536d3df90251b850ea24) Thanks [@ivanarifin](https://github.com/ivanarifin)! - Fix env path resolution for custom gemini cli oauth path
+
+## [v4.96.1]
+
+- [#2452](https://github.com/Kilo-Org/kilocode/pull/2452) [`d4cfbe9`](https://github.com/Kilo-Org/kilocode/commit/d4cfbe98a7ca4e2ce389fe221875f6158688ff69) Thanks [@catrielmuller](https://github.com/catrielmuller)! - Jetbrains - Fix reload extension when switch project
+
+## [v4.96.0]
+
+- [#2504](https://github.com/Kilo-Org/kilocode/pull/2504) [`4927414`](https://github.com/Kilo-Org/kilocode/commit/4927414d0737312796a0c5ae9b0e5a9d7629fbbc) Thanks [@chrarnoldus](https://github.com/chrarnoldus)! - Include changes from Roo Code v3.28.0-v3.28.2:
+
+    - Improve auto-approve UI with smaller and more subtle design (thanks @brunobergher!)
+    - Fix: Message queue re-queue loop in Task.ask() causing performance issues (#7861 by @hannesrudolph, PR by @daniel-lxs)
+    - Fix: Restrict @-mention parsing to line-start or whitespace boundaries to prevent false triggers (#7875 by @hannesrudolph, PR by @app/roomote)
+    - Fix: Make nested git repository warning persistent with path info for better visibility (#7884 by @hannesrudolph, PR by @app/roomote)
+    - Fix: Include API key in Ollama /api/tags requests for authenticated instances (#7902 by @ItsOnlyBinary, PR by @app/roomote)
+    - Fix: Preserve original first message context during conversation condensing (thanks @daniel-lxs!)
+    - Make Posthog telemetry the default (thanks @mrubens!)
+    - Bust cache in generated image preview (thanks @mrubens!)
+    - Fix: Center active mode in selector dropdown on open (#7882 by @hannesrudolph, PR by @app/roomote)
+    - Fix: Preserve first message during conversation condensing (thanks @daniel-lxs!)
+    - feat: Add click-to-edit, ESC-to-cancel, and fix padding consistency for chat messages (#7788 by @hannesrudolph, PR by @app/roomote)
+    - feat: Make reasoning more visible (thanks @app/roomote!)
+    - fix: Fix Groq context window display (thanks @mrubens!)
+    - fix: Add GIT_EDITOR env var to merge-resolver mode for non-interactive rebase (thanks @daniel-lxs!)
+    - fix: Resolve chat message edit/delete duplication issues (thanks @daniel-lxs!)
+    - fix: Reduce CodeBlock button z-index to prevent overlap with popovers (#7703 by @A0nameless0man, PR by @daniel-lxs)
+    - fix: Revert PR #7188 - Restore temperature parameter to fix TabbyApi/ExLlamaV2 crashes (#7581 by @drknyt, PR by @daniel-lxs)
+    - fix: Make ollama models info transport work like lmstudio (#7674 by @ItsOnlyBinary, PR by @ItsOnlyBinary)
+    - fix: Update DeepSeek pricing to new unified rates effective Sept 5, 2025 (#7685 by @NaccOll, PR by @app/roomote)
+    - feat: Update Vertex AI models and regions (#7725 by @ssweens, PR by @ssweens)
+
+### Patch Changes
+
+- [#2484](https://github.com/Kilo-Org/kilocode/pull/2484) [`f57fa9c`](https://github.com/Kilo-Org/kilocode/commit/f57fa9c58baca627a84003f0da133286212dba92) Thanks [@hassoncs](https://github.com/hassoncs)! - Fix the autocomplete status bar appearing when autocomplete is not enabled
+
+- [#2260](https://github.com/Kilo-Org/kilocode/pull/2260) [`9d4b078`](https://github.com/Kilo-Org/kilocode/commit/9d4b078c867c5b160af7a3f4629adfb016f9c2d9) Thanks [@anhhct](https://github.com/anhhct)! - The follow_up parameter of the ask_followup_question tool is now optional
+
+- [#2458](https://github.com/Kilo-Org/kilocode/pull/2458) [`6a79d3b`](https://github.com/Kilo-Org/kilocode/commit/6a79d3b640f8c7e3f24e54bcf17ce63127fbce57) Thanks [@NaccOll](https://github.com/NaccOll)! - Fix Highlight is on the wrong places when referencing context
+
+## [v4.95.0]
+
+- [#2437](https://github.com/Kilo-Org/kilocode/pull/2437) [`5591bcb`](https://github.com/Kilo-Org/kilocode/commit/5591bcbb68d2e8e5af49baf45b8614982ab71e2f) Thanks [@hassoncs](https://github.com/hassoncs)! - You can now auto-start a task in a given profile/mode by creating a `.kilocode/launchConfig.json` before starting VS Code.
+
+    See the docs for more information!
+
+- [#2394](https://github.com/Kilo-Org/kilocode/pull/2394) [`94ce7ca`](https://github.com/Kilo-Org/kilocode/commit/94ce7ca174c4569d8e31fe11d075f04631fc42f4) Thanks [@chrarnoldus](https://github.com/chrarnoldus)! - The Task History tab is now paginated. This should help with reducing memory consumption.
+
+- [#2417](https://github.com/Kilo-Org/kilocode/pull/2417) [`0d4a18f`](https://github.com/Kilo-Org/kilocode/commit/0d4a18fd0ff5a1948405405644ff30b9cbfa3e43) Thanks [@hassoncs](https://github.com/hassoncs)! - Inline assist / autocomplete suggestions now support colorized code highlighting
+
+### Patch Changes
+
+- [#2421](https://github.com/Kilo-Org/kilocode/pull/2421) [`825f7df`](https://github.com/Kilo-Org/kilocode/commit/825f7df5da5a6bbdbfe26739cd5adfc2836fb7a1) Thanks [@chrarnoldus](https://github.com/chrarnoldus)! - Improved proxy support in cases where previously the Kilo Code and OpenRouter model lists would remain empty
+
+## [v4.94.0]
+
+- [#2361](https://github.com/Kilo-Org/kilocode/pull/2361) [`9b553d3`](https://github.com/Kilo-Org/kilocode/commit/9b553d32940736fec49dde8de75faba1e0890471) Thanks [@catrielmuller](https://github.com/catrielmuller)! - Jetbrains - Improve Light Theme
+
+- [#2407](https://github.com/Kilo-Org/kilocode/pull/2407) [`aacf662`](https://github.com/Kilo-Org/kilocode/commit/aacf662030e25c64fbc8800bcf514832949f74ec) Thanks [@kevinvandijk](https://github.com/kevinvandijk)! - Re-add codeblock menu bar for easy copying and syntax highlighting toggling
+
+### Patch Changes
+
+- [#2423](https://github.com/Kilo-Org/kilocode/pull/2423) [`ed12b48`](https://github.com/Kilo-Org/kilocode/commit/ed12b4897bc65df822fa994c13bf325c12055842) Thanks [@mcowger](https://github.com/mcowger)! - Improved the behavior of the Virtual Quota Fallback provider when there are no limits configured.
+
+- [#2412](https://github.com/Kilo-Org/kilocode/pull/2412) [`e7fc4b4`](https://github.com/Kilo-Org/kilocode/commit/e7fc4b473b105ce8a6d92df17f1893f724c158a1) Thanks [@kevinvandijk](https://github.com/kevinvandijk)! - Change default mode on first start from architect to code and tweak mode selector menu to show all default modes
+
+- [#2402](https://github.com/Kilo-Org/kilocode/pull/2402) [`cb44445`](https://github.com/Kilo-Org/kilocode/commit/cb44445574a43179968656ade28bfce666973f9d) Thanks [@chrarnoldus](https://github.com/chrarnoldus)! - The Z.ai provider now supports their coding plan (subscription)
+
+- [#2408](https://github.com/Kilo-Org/kilocode/pull/2408) [`53b387c`](https://github.com/Kilo-Org/kilocode/commit/53b387ce388dbd0c51547934c308d305128f9e5a) Thanks [@kevinvandijk](https://github.com/kevinvandijk)! - Add support for Qwen3-Next-80B-A3B-Instruct and Qwen3-Next-80B-A3B-Thinking to Chutes provider
+
 ## [v4.93.2]
 
 - [#2401](https://github.com/Kilo-Org/kilocode/pull/2401) [`4c0c434`](https://github.com/Kilo-Org/kilocode/commit/4c0c434fce4bd8ce9c31a396c98e21b62cb300c1) Thanks [@chrarnoldus](https://github.com/chrarnoldus)! - Commit Message Generation and Enhance Prompt now support billing through Kilo for Teams

+ 57 - 1
README.md

@@ -1,6 +1,6 @@
 <p align="center">
   <a href="https://marketplace.visualstudio.com/items?itemName=kilocode.Kilo-Code"><img src="https://img.shields.io/visual-studio-marketplace/v/kilocode.Kilo-Code.svg?label=VS%20Code%20Marketplace" alt="VS Code Marketplace"></a>
-  <a href="https://x.com/kilo_code"><img src="https://img.shields.io/twitter/follow/kilo_code?style=flat&logo=x&color=555" alt="X (Twitter)"></a>
+  <a href="https://x.com/kilocode"><img src="https://img.shields.io/twitter/follow/kilocode?style=flat&logo=x&color=555" alt="X (Twitter)"></a>
   <a href="https://blog.kilocode.ai"><img src="https://img.shields.io/badge/Blog-555?style=flat&logo=substack&logoColor=white" alt="Substack Blog"></a>
   <a href="https://kilocode.ai/discord"><img src="https://img.shields.io/discord/1349288496988160052?style=flat&logo=discord&logoColor=white" alt="Discord"></a>
   <a href="https://www.reddit.com/r/kilocode/"><img src="https://img.shields.io/reddit/subreddit-subscribers/kilocode?style=flat&logo=reddit&logoColor=white" alt="Reddit"></a>
@@ -59,3 +59,59 @@ Kilo Code is a direct fork from Roo Code, and also includes the following featur
 ## Extension Development
 
 For details on building and developing the extension, see [DEVELOPMENT.md](/DEVELOPMENT.md)
+
+## Contributors
+
+Thanks to all the contributors who help make Kilo Code better!
+
+<table>
+  <tr>
+    <td align="center">
+      <a href="https://github.com/mcowger">
+        <img src="https://avatars.githubusercontent.com/u/1929548?size=100" width="100" height="100" alt="mcowger" style="border-radius: 50%;" />
+      </a>
+    </td>    <td align="center">
+      <a href="https://github.com/bhaktatejas922">
+        <img src="https://avatars.githubusercontent.com/u/26863466?size=100" width="100" height="100" alt="bhaktatejas922" style="border-radius: 50%;" />
+      </a>
+    </td>    <td align="center">
+      <a href="https://github.com/NyxJae">
+        <img src="https://avatars.githubusercontent.com/u/52313587?size=100" width="100" height="100" alt="NyxJae" style="border-radius: 50%;" />
+      </a>
+    </td>    <td align="center">
+      <a href="https://github.com/Aikiboy123">
+        <img src="https://avatars.githubusercontent.com/u/161741275?size=100" width="100" height="100" alt="Aikiboy123" style="border-radius: 50%;" />
+      </a>
+    </td>    <td align="center">
+      <a href="https://github.com/cobra91">
+        <img src="https://avatars.githubusercontent.com/u/1060585?size=100" width="100" height="100" alt="cobra91" style="border-radius: 50%;" />
+      </a>
+    </td>
+  </tr>
+  <tr>
+    <td align="center">
+      <a href="https://github.com/ivanarifin">
+        <img src="https://avatars.githubusercontent.com/u/111653938?size=100" width="100" height="100" alt="ivanarifin" style="border-radius: 50%;" />
+      </a>
+    </td>    <td align="center">
+      <a href="https://github.com/PeterDaveHello">
+        <img src="https://avatars.githubusercontent.com/u/3691490?size=100" width="100" height="100" alt="PeterDaveHello" style="border-radius: 50%;" />
+      </a>
+    </td>    <td align="center">
+      <a href="https://github.com/possible055">
+        <img src="https://avatars.githubusercontent.com/u/38576169?size=100" width="100" height="100" alt="possible055" style="border-radius: 50%;" />
+      </a>
+    </td>    <td align="center">
+      <a href="https://github.com/seuros">
+        <img src="https://avatars.githubusercontent.com/u/2394703?size=100" width="100" height="100" alt="seuros" style="border-radius: 50%;" />
+      </a>
+    </td>    <td align="center">
+      <!-- added this line to test github action -->
+      <a href="https://kilocode.ai/#contributors">
+        <b>more ...</b>
+      </a>
+    </td>
+  </tr>
+</table>
+
+<!-- END CONTRIBUTORS SECTION -->

+ 29 - 3
apps/kilocode-docs/.kilocode/rules/memory-bank/tech.md

@@ -3,6 +3,7 @@
 ## Core Framework
 
 ### Docusaurus 3.8.1
+
 - **Purpose**: Modern static site generator optimized for documentation
 - **Key Features**: React-based, MDX support, built-in search, theming
 - **Configuration**: [`docusaurus.config.ts`](docusaurus.config.ts:1)
@@ -10,6 +11,7 @@
 ## Runtime Environment
 
 ### Node.js
+
 - **Required Version**: Node.js 18.0 or higher
 - **Package Manager**: npm (with package-lock.json for dependency locking)
 - **Development Server**: Hot reload with live editing support
@@ -17,40 +19,47 @@
 ## Core Dependencies
 
 ### React Ecosystem
+
 - **React 19.0.0**: Core UI library
 - **React DOM 19.0.0**: DOM rendering
 - **@mdx-js/react 3.0.0**: MDX component integration
 - **clsx 2.0.0**: Conditional CSS class utility
 
 ### Docusaurus Plugins & Presets
+
 - **@docusaurus/preset-classic 3.8.1**: Standard Docusaurus configuration
 - **@docusaurus/plugin-client-redirects 3.8.1**: URL redirect management
 - **@easyops-cn/docusaurus-search-local 0.48.5**: Local search functionality
 
 ### Styling & UI Components
+
 - **@vscode/codicons 0.0.36**: VS Code icon integration
 - **prism-react-renderer 2.3.0**: Syntax highlighting for code blocks
 - **Custom CSS**: VS Code-inspired theme in [`src/css/custom.css`](src/css/custom.css:1)
 
 ### Analytics & Tracking
+
 - **posthog-docusaurus 2.0.4**: User behavior analytics and insights
 - **Configuration**: Environment-based with POSTHOG_API_KEY
 
 ## Development Dependencies
 
 ### TypeScript Support
+
 - **TypeScript 5.6.2**: Type checking and development tooling
 - **@docusaurus/types 3.8.1**: Docusaurus TypeScript definitions
 - **@docusaurus/module-type-aliases 3.8.1**: Module type aliases
 - **@docusaurus/tsconfig 3.8.1**: Shared TypeScript configuration
 
 ### Development Tools
+
 - **dotenv 16.4.7**: Environment variable management
 - **husky 9.1.7**: Git hooks for development workflow
 
 ## Build System
 
 ### Development Workflow
+
 ```bash
 npm start          # Local development server with hot reload
 npm run build      # Production build with optimization
@@ -59,12 +68,14 @@ npm run clear      # Clear Docusaurus cache
 ```
 
 Not that on Windows, it may be useful to use:
+
 ```bash
 npx docusaurus start
 npx docusaurus build
 ```
 
 ### Build Configuration
+
 - **Host**: 0.0.0.0 (accessible from network)
 - **Environment Variables**: Loaded via dotenv
 - **Output**: Static HTML/CSS/JS files for CDN deployment
@@ -72,39 +83,46 @@ npx docusaurus build
 ## Browser Support
 
 ### Production Targets
+
 - **Modern Browsers**: >0.5% usage, not dead, not Opera Mini
 - **Specific Exclusions**: Opera Mini (limited JavaScript support)
 
 ### Development Targets
+
 - **Chrome**: Last 3 versions
-- **Firefox**: Last 3 versions  
+- **Firefox**: Last 3 versions
 - **Safari**: Last 5 versions
 
 ## External Integrations
 
 ### GitHub Integration
+
 - **Repository**: https://github.com/Kilo-Org/docs
 - **Edit Links**: Direct links to GitHub for documentation editing
 - **Issue Reporting**: Integrated issue creation workflow
 
 ### Community Platforms
+
 - **Discord**: https://kilocode.ai/discord
 - **Reddit**: https://www.reddit.com/r/kilocode/
-- **Twitter**: https://x.com/Kilo_Code
+- **Twitter**: https://x.com/kilocode
 - **YouTube**: https://www.youtube.com/@Kilo-Code
 
 ### VS Code Marketplace
+
 - **Extension URL**: https://marketplace.visualstudio.com/items?itemName=kilocode.kilo-code
 - **Open VSX**: https://open-vsx.org/extension/kilocode/kilo-code
 
 ## Deployment Architecture
 
 ### Static Site Hosting
+
 - **Production URL**: https://kilocode.ai/docs
 - **Base Path**: /docs (configured in docusaurus.config.ts)
 - **CDN**: Static file distribution for global performance
 
 ### Content Delivery
+
 - **Static Assets**: Images, downloads, and media files
 - **Search Index**: Local search data bundled with site
 - **Sitemap**: Automatic generation for SEO
@@ -112,11 +130,13 @@ npx docusaurus build
 ## Development Constraints
 
 ### File Organization
+
 - **Documentation**: Markdown/MDX files in `/docs` directory
 - **Static Assets**: Organized by feature in `/static/img`
 - **Components**: Custom React components in `/src/components`
 
 ### Content Management
+
 - **Version Control**: Git-based workflow for all content
 - **Asset Optimization**: Manual image optimization required
 - **Link Validation**: Docusaurus validates internal links during build
@@ -124,11 +144,13 @@ npx docusaurus build
 ## Performance Considerations
 
 ### Build Optimization
+
 - **Code Splitting**: Automatic JavaScript bundle optimization
 - **Static Generation**: Pre-rendered HTML for fast initial load
 - **Asset Optimization**: CSS/JS minification in production builds
 
 ### Runtime Performance
+
 - **Client-Side Routing**: Fast navigation between pages
 - **Search Performance**: Local search index for instant results
 - **Image Loading**: Manual lazy loading implementation where needed
@@ -136,11 +158,13 @@ npx docusaurus build
 ## Security & Privacy
 
 ### Data Collection
+
 - **Analytics**: PostHog for usage tracking (configurable)
 - **Privacy Policy**: Links to both website and extension privacy policies
 - **User Control**: Analytics can be disabled via environment configuration
 
 ### Content Security
+
 - **Static Generation**: No server-side vulnerabilities
 - **External Links**: Proper target and rel attributes for security
 - **Asset Validation**: Build-time validation of all assets and links
@@ -148,11 +172,13 @@ npx docusaurus build
 ## Maintenance Requirements
 
 ### Regular Updates
+
 - **Dependencies**: Monthly security and feature updates
 - **Docusaurus**: Follow major version updates for new features
 - **Node.js**: Maintain compatibility with LTS versions
 
 ### Content Synchronization
+
 - **Extension Features**: Documentation must reflect current extension capabilities
 - **Provider APIs**: Keep provider setup guides current with API changes
-- **Community Links**: Verify external links remain active
+- **Community Links**: Verify external links remain active

+ 4 - 0
apps/kilocode-docs/docs/advanced-usage/custom-rules.md

@@ -1,5 +1,9 @@
 # Custom Rules
 
+<YouTubeEmbed
+  url="https://youtu.be/GF0vjB8NxYg"
+/>
+
 Custom rules provide a powerful way to define project-specific and global behaviors and constraints for the Kilo Code AI agent. With custom rules, you can ensure consistent formatting, restrict access to sensitive files, enforce coding standards, and customize the AI's behavior for your specific project needs or across all projects.
 
 ## Overview

+ 4 - 0
apps/kilocode-docs/docs/advanced-usage/memory-bank.md

@@ -2,6 +2,10 @@
 
 ## Overview
 
+<YouTubeEmbed
+  url="https://youtu.be/FwAYGslfB6Y"
+/>
+
 <figure style={{ float: 'right', width: '40%', maxWidth: '350px', margin: '0 0 10px 20px' }}>
   <img src="/docs/img/memory-bank/at-work.png" alt="Executing task with the memory bank" style={{ border: '1px solid grey', borderRadius: '5px', width: '100%' }} />
   <figcaption style={{ fontSize: '0.9rem', color: '#666', marginTop: '8px', textAlign: 'center' }}>

+ 94 - 0
apps/kilocode-docs/docs/basic-usage/autocomplete.md

@@ -0,0 +1,94 @@
+---
+title: Autocomplete
+sidebar_position: 4
+---
+
+# Autocomplete
+
+Kilo Code's autocomplete feature provides intelligent code suggestions and completions while you're typing, helping you write code faster and more efficiently. It offers both automatic and manual triggering options.
+
+## How Autocomplete Works
+
+Autocomplete analyzes your code context and provides:
+
+- **Inline completions** as you type
+- **Quick fixes** for common code patterns
+- **Contextual suggestions** based on your surrounding code
+- **Multi-line completions** for complex code structures
+
+The feature uses your selected AI provider to generate intelligent suggestions that match your coding style and project context.
+
+## Triggering Options
+
+### Pause to Complete
+
+When enabled, Kilo Code automatically triggers autocomplete when you pause typing. This provides a seamless coding experience where suggestions appear naturally as you work.
+
+- **Auto Trigger Delay**: Configure the delay (in seconds) before autocomplete triggers after you stop typing
+- Default is 3 seconds, but this can be adjusted up or down
+- Shorter delays mean quicker suggestions but may be more resource-intensive
+
+### Quick Task (Cmd+I)
+
+Need to make a quick change? The Quick Task feature allows you to:
+
+1. Select code in your editor (or place your cursor where you want changes)
+2. Press `Cmd+I` (Mac) or `Ctrl+I` (Windows/Linux)
+3. Describe your goal in plain English
+4. Receive a code suggestion without going to the chat
+
+**Examples:**
+
+- "create a React component with these props"
+- "add error handling to this function"
+- "convert this to TypeScript"
+- "optimize this loop for performance"
+
+You can customize the keyboard shortcut in VS Code's keyboard shortcuts settings.
+
+### Manual Autocomplete (Cmd+L)
+
+For more control over when suggestions appear:
+
+1. Position your cursor where you need assistance
+2. Press `Cmd+L` (Mac) or `Ctrl+L` (Windows/Linux)
+3. Kilo Code analyzes the surrounding context
+4. Receive immediate improvements or completions
+
+This is ideal for:
+
+- Quick fixes
+- Code completions
+- Refactoring suggestions
+- Keeping you in the flow without interruptions
+
+You can customize this keyboard shortcut as well in your VS Code settings.
+
+## Advanced Settings
+
+### Provider Configuration
+
+By default, autocomplete uses your main Kilo Code/OpenRouter/Mistral credentials with the Codestral model.
+This model is recommended as it strikes an optimal balance between performance speed and capability.
+However, you can:
+
+- **Use custom provider**: Toggle this option to use a different AI provider specifically for autocomplete
+- **Select different models**: Choose models optimized for speed vs. quality based on your needs
+
+## Best Practices
+
+1. **Balance speed and quality**: Faster models provide quicker suggestions but may be less accurate
+2. **Adjust trigger delay**: Find the sweet spot between responsiveness and avoiding too many API calls
+3. **Use Quick Task for complex changes**: It's designed for more substantial code modifications
+4. **Use Manual Autocomplete for precision**: When you need suggestions at specific moments
+5. **Configure providers wisely**: Consider using faster, cheaper models for autocomplete while keeping more powerful models for chat
+
+## Tips
+
+- Autocomplete works best with clear, well-structured code
+- Comments above functions help autocomplete understand intent
+- Variable and function names matter - descriptive names lead to better suggestions
+
+## Related Features
+
+- [Code Actions](../features/code-actions) - Context menu options for common coding tasks

+ 1 - 1
apps/kilocode-docs/docs/basic-usage/model-selection-guide.md

@@ -14,7 +14,7 @@ The AI model landscape evolves rapidly, so this guide focuses on what's deliveri
 | -------------------- | -------------- | ------------------ | ---------- | ------------- | ------------- | -------------- | ------------------------------------------- |
 | **GPT-5**            | 400K tokens    | 74.9%              | 96.3%      | 68.2%         | $1.25         | $10            | Latest capabilities, multi-modal coding     |
 | **Claude Sonnet 4**  | 1M tokens      | 72.7%              | 94.8%      | 65.9%         | $3-6          | $15-22.50      | Enterprise code generation, complex systems |
-| **Grok Code Fast 1** | 256K tokens    | 70.8%              | 92.1%      | 63.4%         | $0.75         | $3.50          | Rapid development, cost-performance balance |
+| **Grok Code Fast 1** | 256K tokens    | 70.8%              | 92.1%      | 63.4%         | $0.20         | $1.50          | Rapid development, cost-performance balance |
 | **Qwen3 Coder**      | 256K tokens    | 68.4%              | 91.7%      | 61.8%         | $0.20         | $0.80          | Pure coding tasks, rapid prototyping        |
 | **Gemini 2.5 Pro**   | 1M+ tokens     | 67.2%              | 89.9%      | 59.3%         | TBD           | TBD            | Massive codebases, architectural planning   |
 

+ 151 - 0
apps/kilocode-docs/docs/features/auto-launch-configuration.md

@@ -0,0 +1,151 @@
+# Auto-Launch Configuration
+
+Auto-Launch Configuration allows you to automatically start a Kilo Code task when opening a workspace, with support for specific profiles and modes. This was originally developed as an internal test feature, but we decided to expose it to users in case anyone finds it useful!
+
+:::info
+Auto-Launch Configuration is particularly useful for testing the same prompt against multiple models or project directories.
+:::
+
+## How It Works
+
+When you open a workspace in VS Code, Kilo Code automatically checks for a launch configuration JSON file. If found, it:
+
+- Switches to the specified provider profile (if provided)
+- Changes to the specified mode (if provided)
+- Launches a task with your predefined prompt
+
+This happens seamlessly in the background, requiring no manual intervention.
+
+## Creating a Launch Configuration
+
+### Basic Setup
+
+1. Create a `.kilocode` directory in your workspace root (if it doesn't exist)
+2. Create a `launchConfig.json` file inside the `.kilocode` directory
+3. Configure your launch settings using the JSON format below
+
+### Configuration Format
+
+```json
+{
+	"prompt": "Your task description here",
+	"profile": "Profile Name (optional)",
+	"mode": "mode-name (optional)"
+}
+```
+
+#### Required Fields
+
+- **`prompt`** (string): The task message that will be sent to the AI when the workspace opens
+
+#### Optional Fields
+
+- **`profile`** (string): Name of an existing [API Configuration Profile](/features/api-configuration-profiles) to use for this task. Must exactly match a profile name from your settings.
+
+- **`mode`** (string): The Kilo Code mode to use for this task. Available modes:
+    - `"code"` - General-purpose coding tasks
+    - `"architect"` - Planning and technical design
+    - `"ask"` - Questions and explanations
+    - `"debug"` - Problem diagnosis and troubleshooting
+    - `"test"` - Testing-focused workflows
+    - Custom mode slugs (if you have [custom modes](/features/custom-modes))
+
+## Example Configurations
+
+### Basic Task Launch
+
+```json
+{
+	"prompt": "Review this codebase and suggest improvements for performance and maintainability"
+}
+```
+
+### Profile-Specific Task
+
+```json
+{
+	"prompt": "Create comprehensive unit tests for all components in the src/ directory",
+	"profile": "GPT-4 Turbo"
+}
+```
+
+### Architecture Planning with Claude
+
+```json
+{
+	"prompt": "Design a scalable microservices architecture for this e-commerce platform with focus on security and performance",
+	"profile": "🎻 Sonnet 4",
+	"mode": "architect"
+}
+```
+
+### Model Comparison Setup
+
+```json
+{
+	"prompt": "Optimize this algorithm for better time complexity and explain your approach",
+	"profile": "🧠 Qwen",
+	"mode": "code"
+}
+```
+
+## Use Cases
+
+### Development Workflows
+
+- **Project Templates**: Include launch configurations in project templates to immediately start with appropriate AI assistance
+- **Code Reviews**: Automatically trigger code review tasks when opening pull request branches
+- **Documentation**: Launch documentation generation tasks for new projects
+
+### Testing and Comparison
+
+- **Model Testing**: Create different configurations to test how various AI models handle the same prompt
+- **A/B Testing**: Compare approaches by switching between different profiles and modes
+- **Benchmarking**: Systematically test AI performance across different scenarios
+
+### Team Collaboration
+
+- **Consistent Setup**: Ensure all team members use the same AI configuration for specific projects
+- **Onboarding**: Help new team members start with optimal AI settings automatically
+- **Standards**: Enforce coding standards by launching with specific profiles and modes
+
+## File Location
+
+The configuration file must be located at:
+
+```
+your-workspace/
+└── .kilocode/
+    └── launchConfig.json
+```
+
+This file should be at the root of your workspace (the same level as your main project files).
+
+## Behavior and Timing
+
+- Auto-launch triggers approximately 500ms after Kilo Code extension activation
+- The sidebar automatically receives focus before the task launches
+- Profile switching happens before mode switching (if both are specified)
+- The task launches after all configuration changes are applied
+- If profile or mode switching fails, the task continues with current settings
+
+## Troubleshooting
+
+### Configuration Not Loading
+
+1. Verify file location: `.kilocode/launchConfig.json` in workspace root
+2. Check JSON syntax with a JSON validator
+3. Ensure `prompt` field is present and not empty
+4. Check VS Code Developer Console for error messages
+
+### Profile Not Switching
+
+1. Verify the profile name exactly matches one from your settings
+2. Profile names are case-sensitive and must match exactly (including emojis)
+3. Check that the profile exists in your [API Configuration Profiles](/features/api-configuration-profiles)
+
+### Mode Not Switching
+
+1. Verify the mode name is valid (code, architect, ask, debug, test)
+2. For custom modes, use the exact mode slug from your configuration
+3. Mode names are case-sensitive and should be lowercase

+ 4 - 0
apps/kilocode-docs/docs/features/codebase-indexing.md

@@ -2,6 +2,10 @@ import Codicon from '@site/src/components/Codicon';
 
 # Codebase Indexing
 
+<YouTubeEmbed
+  url="https://www.youtube.com/watch?v=dj59Vi83oDw"
+/>
+
 Codebase Indexing enables semantic code search across your entire project using AI embeddings. Instead of searching for exact text matches, it understands the _meaning_ of your queries, helping Kilo Code find relevant code even when you don't know specific function names or file locations.
 
 <img src="/docs/img/codebase-indexing/codebase-indexing.png" alt="Codebase Indexing Settings" width="800" />

+ 4 - 0
apps/kilocode-docs/docs/getting-started/your-first-task.md

@@ -4,6 +4,10 @@ sidebar_label: Your First Task
 
 # Starting Your First Task with Kilo Code
 
+<YouTubeEmbed
+  url="https://www.youtube.com/watch?v=pO7zRLQS-p0"
+/>
+
 This quick tour shows how Kilo Code handles a simple request from start to finish.
 
 After you [set up Kilo Code](/getting-started/setting-up), follow these steps:

+ 114 - 0
apps/kilocode-docs/docs/jetbrains-troubleshooting.md

@@ -0,0 +1,114 @@
+# JetBrains Plugin Troubleshooting
+
+This guide covers common issues when using Kilo Code in JetBrains IDEs (IntelliJ IDEA, Android Studio, WebStorm, PyCharm, etc.).
+
+## Known Missing Features
+
+The following features, available in the VS Code version of Kilo Code, are not currently implemented in the JetBrains version:
+
+- **Autocomplete/QuickTasks**
+- **Git Commit Message Generation** This feature is missing but will be added soon!
+
+We're actively working on bringing feature parity between the VS Code and JetBrains versions. Check our [GitHub repository](https://github.com/Kilo-Org/kilocode) for updates on development progress.
+
+## Node.js Requirements
+
+### Why Node.js is Required
+
+The JetBrains Kilo Extension requires Node.js to be installed on your system. Node.js is used to run the extension's backend services and handle communication between the IDE and Kilo Code's AI features.
+
+### Installing Node.js
+
+Visit the official Node.js website for installation instructions for your platform: [https://nodejs.org/en/download](https://nodejs.org/en/download)
+
+We recommend downloading the **LTS (Long Term Support)** version for stability.
+
+### Verifying Node.js Installation
+
+After installation, verify that Node.js is properly installed by opening a terminal and running:
+
+```bash
+node --version
+npm --version
+```
+
+Both commands should return version numbers.
+
+## JCEF (Java Chromium Embedded Framework) Issues
+
+### What is JCEF?
+
+JCEF (Java Chromium Embedded Framework) is required for Kilo Code's web-based interface to display properly in JetBrains IDEs. Most JetBrains IDEs include JCEF support by default, but some configurations may need manual activation.
+
+## Fixing JCEF Issues by IDE
+
+### Android Studio
+
+JCEF is available in Android Studio but may need to be enabled manually:
+
+1. **Open Settings/Preferences:**
+
+    - **Windows/Linux:** File → Settings
+    - **macOS:** Help → Find Action...
+
+2. **Navigate to Boot Java Runtime:**
+
+    - Choose Boot Java Runtime for the IDE...
+
+3. **Pick a new runtime**
+
+    - Pick one that has "with JCEF" in the name
+
+4. **Restart Android Studio:**
+
+    - Close and reopen Android Studio for the changes to take effect
+
+5. **Verify:**
+    - Open Kilo Code panel
+    - The JCEF warning should be gone, and the interface should load properly
+
+**Visual Guide:**
+
+<img src="/docs/img/jetbrains/android-studio-jcef-enable.gif" alt="Step-by-step guide showing how to enable JCEF in Android Studio" width="600" />
+
+_This animation shows the complete process of enabling JCEF in Android Studio._
+
+### IntelliJ IDEA
+
+JCEF should be enabled by default in IntelliJ IDEA. If you see JCEF warnings:
+
+1. **Update IntelliJ IDEA:**
+
+    - Ensure you're running the latest version
+    - Go to Help → Check for Updates
+
+2. **Verify JetBrains Runtime:**
+
+    - IntelliJ IDEA should use JetBrains Runtime (JBR) by default
+    - JBR includes JCEF support
+
+3. **Check Advanced Settings:**
+    - Go to File → Settings (Windows/Linux) or IntelliJ IDEA → Preferences (macOS)
+    - Navigate to Advanced Settings
+    - Look for any JCEF-related options and ensure they're enabled
+
+### Other JetBrains IDEs
+
+For WebStorm, PyCharm, PhpStorm, RubyMine, CLion, GoLand, DataGrip, and Rider:
+
+1. **Update to Latest Version:**
+
+    - Most JCEF issues are resolved in recent versions
+    - Use the built-in updater: Help → Check for Updates
+
+2. **Verify JetBrains Runtime:**
+
+    - These IDEs should use JetBrains Runtime by default
+    - JBR includes comprehensive JCEF support
+
+3. **Check Settings:**
+    - Go to File → Settings (Windows/Linux) or [IDE Name] → Preferences (macOS)
+    - Navigate to Advanced Settings
+    - Enable any JCEF-related options
+
+_For general Kilo Code support and documentation, visit [kilocode.ai/docs](https://kilocode.ai/docs)_

+ 11 - 0
apps/kilocode-docs/docs/providers/kilocode.md

@@ -49,6 +49,17 @@ Once you've completed the registration process, Kilo Code is automatically confi
 2. **No API Key Management:** Your authentication is handled seamlessly through the registration process
 3. **Model Selection:** Access to frontier models is provided automatically through your Kilo Code account
 
+## Connected Accounts
+
+With the Kilo Code provider, if you sign up with Google you can also connect other sign in accounts - like GitHub - by:
+
+1. Go to your profile
+2. Select [**Connected Accounts**](https://app.kilocode.ai/connected-accounts)
+3. Under "Link a New account" select the type of account to link
+4. Complete the OAuth authorization, and you'll see your connected accounts!
+
+<img src="/docs/img/kilo-provider/connected-accounts.png" alt="Connect account screen" width="600" />
+
 ## Tips and Notes
 
 - **Free Credits:** New users receive free credits to explore Kilo Code's capabilities

+ 76 - 0
apps/kilocode-docs/docs/providers/vercel-ai-gateway.md

@@ -0,0 +1,76 @@
+---
+description: Configure the Vercel AI Gateway in Kilo Code to robustly access 100+ language models from various providers through a centralized interface.
+keywords:
+    - kilo code
+    - vercel ai gateway
+    - ai provider
+    - language models
+    - api configuration
+    - model selection
+    - prompt caching
+    - usage tracking
+    - byok
+sidebar_label: Vercel AI Gateway
+---
+
+# Using Vercel AI Gateway With Kilo Code
+
+The AI Gateway provides a unified API to access hundreds of models through a single endpoint. It gives you the ability to set budgets, monitor usage, load-balance requests, and manage fallbacks.
+
+Useful links:
+
+- Team dashboard: https://vercel.com/d?to=%2F%5Bteam%5D%2F%7E%2Fai
+- Models catalog: https://vercel.com/ai-gateway/models
+- Docs: https://vercel.com/docs/ai-gateway
+
+---
+
+## Getting an API Key
+
+An API key is required for authentication.
+
+1.  **Sign Up/Sign In:** Go to the [Vercel Website](https://vercel.com/) and sign in.
+2.  **Get an API Key:** Go to the [API Key page](https://vercel.com/d?to=%2F%5Bteam%5D%2F%7E%2Fai%2Fapi-keys&title=AI+Gateway+API+Key) in the AI Gateway tab. Create a new key.
+3.  **Copy the Key:** Copy the API key.
+
+---
+
+## Supported Models
+
+The Vercel AI Gateway supports a large and growing number of models. Kilo Code automatically fetches the list of available models from the `https://ai-gateway.vercel.sh/v1/models` endpoint. Only language models are shown.
+
+The default model is `anthropic/claude-sonnet-4` if no model is selected.
+
+Refer to the [Vercel AI Gateway Models page](https://vercel.com/ai-gateway/models) for the complete and up-to-date list.
+
+### Model Capabilities
+
+- **Vision Support**: Many models support image inputs.
+- **Tool/Computer Use**: Select models support function calling and computer use.
+
+Check the model description in the dropdown for specific capabilities.
+
+---
+
+## Configuration in Kilo Code
+
+1.  **Open Kilo Code Settings:** Click the gear icon (<Codicon name="gear" />) in the Kilo Code panel.
+2.  **Select Provider:** Choose "Vercel AI Gateway" from the "API Provider" dropdown.
+3.  **Enter API Key:** Paste your Vercel AI Gateway API key into the "Vercel AI Gateway API Key" field.
+4.  **Select Model:** Choose your desired model from the "Model" dropdown.
+
+---
+
+## Prompt Caching
+
+Vercel AI Gateway supports automatic prompt caching for select models including Anthropic Claude and OpenAI GPT models. This reduces costs by caching frequently used prompts.
+
+---
+
+## Tips and Notes
+
+- **Model Selection:** The Vercel AI Gateway offers a wide range of models. Experiment to find the best one for your needs.
+- **Pricing:** The Vercel AI Gateway charges based on the underlying model's pricing, including costs for cached prompts. See the [Vercel AI Gateway Models page](https://vercel.com/ai-gateway/models) for details.
+- **Temperature:** The default temperature is `0.7` and is configurable per model.
+- **Bring Your Own Key (BYOK):** The Vercel AI Gateway has **no markup** if you decide to use your own key for the underlying service.
+- **More info:** Vercel does not add rate limits. Upstream providers may. New accounts receive $5 credits every 30 days until the first payment.

+ 111 - 0
apps/kilocode-docs/docs/teams/analytics.md

@@ -0,0 +1,111 @@
+---
+sidebar_label: Usage Analytics & Reporting
+---
+
+# Usage Analytics & Reporting
+
+Kilo for Teams provides detailed usage analytics to help you monitor and understand your team's AI usage patterns, costs, and activity through the Kilo Code API provider.
+
+<center>
+    <img src="/docs/img/teams/usage-details.png" alt="Team usage analytics dashboard" width="600" />
+</center>
+
+## Analytics Dashboard Overview
+
+Access your team's usage analytics through the **Usage Details** section in your dashboard. The analytics show comprehensive data about your team's usage of the Kilo Code API provider.
+
+:::info Usage Scope
+This usage overview includes all of your usage of the Kilo Code API provider. It does **NOT** include any usage made via the Kilo Code extension to other, non-Kilo Code providers. You can choose which API provider to use from the extension's main settings page.
+:::
+
+## Summary Metrics
+
+The dashboard displays five key metrics at the top:
+
+- **Total Spent** - Total cost for the selected time period
+- **Total Requests** - Number of API requests made
+- **Avg Cost per Request** - Average cost per individual request
+- **Total Tokens** - Total tokens processed (input + output)
+- **Active Users** - Number of team members who made requests
+
+## Time Period Filters
+
+Select from four time period options to view usage data:
+
+- **Past Week** - Last 7 days of usage
+- **Past Month** - Last 30 days of usage
+- **Past Year** - Last 365 days of usage
+- **All** - Complete usage history
+
+## Usage View Options
+
+### Only My Usage Toggle
+
+Use the **"Only my usage"** toggle to filter the data:
+
+- **Enabled** - Shows only your personal usage data
+- **Disabled** - Shows team-wide usage data for all members
+
+### Data Breakdown Views
+
+Choose between two data presentation formats:
+
+#### By Day View
+
+Shows usage aggregated by date with columns:
+
+- **DATE** - The specific date
+- **COST** - Total spending for that date
+- **REQUESTS** - Number of API requests made
+- **TOKENS** - Total tokens processed (hover to show input vs. output tokens)
+- **USERS** - Number of active users that date
+
+When viewing team data, you can click on any date row to expand and see individual user breakdowns for that day, showing each team member's usage, cost, requests, and tokens.
+
+#### By Model & Day View
+
+Shows detailed usage broken down by AI model and date with columns:
+
+- **DATE** - The specific date
+- **MODEL** - The AI model used (e.g., anthropic/claude-sonnet-4, openai/gpt-4)
+- **COST** - Cost for that model on that date
+- **REQUESTS** - Number of requests to that model
+- **TOKENS** - Tokens processed by that model (hover to show input vs. output tokens)
+- **USERS** - Number of users who used that model
+
+Click on any row to expand and see which specific team members used that model on that date, along with their individual usage statistics.
+
+## Understanding the Data
+
+### Model Information
+
+The analytics track usage across different AI models, showing the specific model identifiers such as:
+
+- `anthropic/claude-sonnet-4`
+- `openai/gpt-5`
+- `x-ai/grok-code-fast-1`
+- `mistralai/codestral-2508`
+
+### User Attribution
+
+When viewing team data, you can see:
+
+- Individual team member usage within expanded rows
+- Email addresses for user identification
+- Per-user cost, request, and token breakdowns
+
+### Cost Tracking
+
+All costs are displayed in USD with detailed precision, helping you:
+
+- Monitor spending patterns over time
+- Identify high-usage periods or models
+- Track individual team member contributions to costs
+
+## Next Steps
+
+- [Manage team billing settings](/teams/billing)
+- [Configure team roles and permissions](/teams/roles-permissions)
+- [Learn about team roles and permissions](/teams/roles-permissions)
+
+The usage analytics provide the insights needed to optimize your team's AI usage while maintaining visibility into costs and activity patterns.

+ 119 - 0
apps/kilocode-docs/docs/teams/billing.md

@@ -0,0 +1,119 @@
+---
+sidebar_label: Billing & Credits
+---
+
+# Billing & Credits
+
+Kilo for Teams uses a transparent, two-part billing system: a monthly subscription for seats plus pay-as-you-go AI credits with zero markup.
+
+## Understanding the Billing Model
+
+### Monthly Subscription
+
+- **$29 per user per month** for platform access
+- **Billed monthly** on your signup anniversary
+- **Includes** dashboard access, team management, and all platform features
+
+### AI Credits System
+
+- **Pay exactly what AI providers charge** - no markup
+- **Pre-purchase credits** to fund AI requests
+- **Real-time deduction** as your team uses AI models
+- **Transparent pricing** - see exact provider costs
+
+## How AI Credits Work
+
+### Credit Purchase Process
+
+1. **Navigate to Billing tab** in dashboard
+2. **Click "Buy Credits"**
+3. **Select credit amount** ($50, $100, $250, $500, $1000+)
+4. **Complete payment** using saved payment method
+5. **Credits available immediately** for team use
+
+### Credit Consumption
+
+- **Per-request billing** - pay only for what you use
+- **Model-specific rates** - different models have different costs
+- **Token-based pricing** - input and output tokens counted separately
+- **Real-time tracking** - see costs as requests happen
+
+## Subscription Management
+
+### Adding Seats
+
+1. **Go to Subscriptions tab**
+2. **Click "Add Seats"**
+3. **Enter number of additional seats**
+4. **Review pro-rated cost** for current billing cycle
+5. **Confirm changes**
+
+### Removing Seats
+
+1. **Navigate to Subscriptions tab**
+2. **Click "Remove Seats"**
+3. **Select seats to remove** (must remove team members first)
+4. **Confirm reduction**
+
+### Pro-Rating Calculations
+
+- **Adding seats mid-cycle:** Pay proportional amount for remaining days
+- **Removing seats mid-cycle:** Seat change will start at the next billing cycle
+- **Billing date remains the same** regardless of seat changes
+- **Next full cycle** reflects new seat count
+
+## Invoice Management
+
+### Monthly Invoices
+
+Each invoice includes:
+
+- **Subscription charges** for all seats
+- **AI credit purchases** during billing period
+- **Tax calculations** based on your location
+- **Payment method** used for charges
+- **Detailed line items** for transparency
+
+### Downloading Invoices
+
+1. **Access Billing tab**
+2. **Find desired invoice** in history
+3. **Click "Download PDF"**
+4. **Save for expense reporting**
+
+## Billing Troubleshooting
+
+### Failed Payments
+
+If a payment fails:
+
+1. **Check payment method** validity (expiration, limits)
+2. **Update payment information** if needed
+3. **Retry payment** manually in dashboard
+4. **Contact support** if issues persist
+
+### Billing Disputes
+
+For billing questions:
+
+1. **Review invoice details** in dashboard
+2. **Check usage analytics** for the billing period
+3. **Contact support** with specific questions
+4. **Provide invoice number** and dispute details
+
+### Service Suspension
+
+If payment fails repeatedly:
+
+- **3-day grace period** to resolve payment issues
+- **Service suspension** after grace period expires
+- **Data retention** for 30 days during suspension
+- **Immediate restoration** upon payment resolution
+
+## Next Steps
+
+- [Explore usage analytics](/teams/analytics)
+- [Learn about team roles and permissions](/teams/roles-permissions)
+- [Learn about team management](/teams/team-management)
+
+Transparent billing ensures you always know exactly what you're paying for, with no hidden fees or surprise charges.

+ 143 - 0
apps/kilocode-docs/docs/teams/dashboard.md

@@ -0,0 +1,143 @@
+---
+sidebar_label: Dashboard Overview
+---
+
+# Teams Dashboard
+
+The Kilo for Teams dashboard provides complete visibility into your team's AI usage, costs, and management.
+
+<center>
+<img src="/docs/img/teams/dashboard.png" alt="Invite your team members" width="700" />
+</center>
+
+## Dashboard Navigation
+
+The dashboard is organized into five main tabs, each serving specific management needs:
+
+- **Organization** - Team composition and quick actions
+- **Usage** - Real-time analytics and cost tracking
+- **Billing** - Financial management and invoicing
+- **Subscriptions** - Plan management and seat allocation
+
+## Organization Tab
+
+Your central hub for team management and organization overview.
+
+### Key Information Display
+
+- **Organization name** and creation date
+- **Current seat usage** (e.g., "8 of 10 seats used")
+- **Active members count** with role breakdown
+- **Data collection policy** status
+
+### Team Member List
+
+View all team members with:
+
+- Name and email address
+- Current role (Owner, Admin, Member)
+- Last activity timestamp
+
+### Quick Actions
+
+- **Buy Credits** - Direct link to credit purchase
+- **Invite Member** - Send team invitations
+- **Manage Seats** - Adjust subscription size
+- **Policy Settings** - Configure data collection preferences
+
+### Data Collection Controls
+
+Toggle organization-wide policies:
+
+- **Code training opt-out** - Prevent AI providers from using your code for training
+- **Usage analytics** - Control internal usage tracking
+
+## Usage Tab
+
+Real-time visibility into your team's AI consumption and costs.
+
+### Overview Metrics
+
+- **Total spend** (current billing period)
+- **Request count** (successful AI requests)
+- **Average cost per request**
+- **Token usage** (input/output breakdown)
+- **Active users** (users with activity in last 7 days)
+
+### Model Popularity
+
+Visual breakdown showing:
+
+- Most-used AI models by request count
+- Cost distribution across different models
+- Provider usage patterns
+- Model performance metrics
+
+### Time-Based Analytics
+
+Interactive graphs displaying:
+
+- **Daily usage trends** - Spot peak usage periods
+- **Weekly patterns** - Understand team workflows
+- **Monthly comparisons** - Track growth and optimization
+
+### User-Level Insights
+
+- Individual usage statistics (Owners and Admins only)
+- Top users by request volume
+- Usage distribution across team members
+
+## Billing Tab
+
+Complete financial management for your Kilo for Teams subscription.
+
+- **Available credits** remaining
+- **Downloadable invoices** for expense reporting
+- **Payment status** for each billing cycle
+- **Primary payment method** on file
+- **Payment history** with transaction details
+
+### Purchase History
+
+- **Credit purchases** with timestamps
+- **Subscription changes** (seat additions/removals)
+- **Refunds and adjustments** (if any)
+- **Promotional credits** applied
+
+## Subscriptions Tab
+
+Manage your Kilo for Teams plan and seat allocation.
+
+### Current Plan Details
+
+- **Plan type** (Kilo for Teams)
+- **Monthly cost** per seat ($29/user/month)
+- **Billing cycle** dates and next charge
+- **Plan benefits** and included features
+
+### Seat Management
+
+- **Current seat count** and utilization
+- **Available seats** for new team members
+- **Seat history** showing additions and removals
+- **Cost impact** of seat changes with pro-rating
+
+### Quick Actions
+
+- **Add seats** for team growth
+- **Remove unused seats** to optimize costs
+- **Change billing frequency** (if available)
+- **Cancel subscription** (with confirmation)
+
+### Billing Cycle Information
+
+- **Next billing date** and amount
+- **Pro-rating calculations** for mid-cycle changes
+- **Renewal settings** and automatic billing
+- **Cancellation policy** and effective dates
+
+## Next Steps
+
+- [Learn about team management](/teams/team-management)
+- [Understand billing and credits](/teams/billing)
+- [Explore usage analytics](/teams/analytics)

+ 92 - 0
apps/kilocode-docs/docs/teams/getting-started.md

@@ -0,0 +1,92 @@
+---
+sidebar_label: Getting Started
+---
+
+# Get Started with Kilo for Teams in 10 Minutes
+
+Kilo for Teams brings transparent AI coding to your entire engineering organization. No markup on AI costs, no vendor lock-in, complete usage visibility.
+
+**Launch Date:** September 10, 2025 | **Pricing:** $29/user/month
+
+## What You Get
+
+- **Zero markup** on AI provider costs - pay exactly what providers charge
+- **No rate limiting** or quality degradation during peak usage
+- **Centralized billing** - one invoice for your whole team
+- **Complete transparency** - see every request, cost, and usage pattern
+- **Team management** - roles, permissions, and usage controls
+- **Early adopter bonus** - $20 in free AI credits per seat
+
+## Before You Begin
+
+- Company email address (recommended for easier team management)
+- Approximate team size for initial seat planning
+- Credit card for billing setup
+- VS Code installed for team members
+
+## Quick Setup Guide
+
+### Step 1: Create Your Organization
+
+1. Visit [app.kilocode.com](https://app.kilocode.com)
+2. Sign up using your company email
+3. Click **Organizations** in the left sidebar and then **Create New Organization**
+
+<img src="/docs/img/teams/create-team.png" alt="Create new organization button" width="600" />
+
+### Step 2: Subscribe to Teams
+
+1. Enter your organization name
+2. Select your initial seat count
+3. Complete checkout process
+
+<img src="/docs/img/teams/subscribe.png" alt="Create your organization and subscribe" width="600" />
+
+💡 **Early Adopter Bonus:** Receive $20 in free AI credits per seat when you sign up before October 31, 2025.
+
+### Step 3: Invite Your Team
+
+1. Go to your **Organization**
+2. Click **Invite Member**
+3. Enter team member email
+4. Assign roles:
+    - **Owner** - Full administrative access
+    - **Admin** - Team management without billing
+    - **Member** - Standard usage access
+
+<img src="/docs/img/teams/invite-member.png" alt="Invite your team members" width="600" />
+
+### Step 4: Team Members Install Extension
+
+Team members receive invitation emails with these steps:
+
+1. Accept the team invitation
+2. Install Kilo Code from [VS Code Marketplace](vscode:extension/kilocode.kilo-code)
+3. Sign in with their invited email
+4. Start coding with AI assistance
+
+## What Happens Next
+
+- **Immediate access** to all supported AI models
+- **Real-time usage tracking** in your dashboard
+- **Transparent billing** - see exactly what each request costs
+- **Team analytics** - understand usage patterns and optimization opportunities
+
+<img src="/docs/img/teams/usage-details.png" alt="Team usage details page" width="600" />
+
+## First Steps for Your Team
+
+1. **Try basic tasks** - code generation, debugging, documentation
+2. **Explore different modes** - Code, Architect, Ask, Debug
+3. **Set personal preferences** - model selection, auto-approval settings
+4. **Review usage patterns** in the dashboard after first week
+
+## Getting Support
+
+You can find the dedicated Teams support methods directly on your Organaization's page.
+
+## Next Steps
+
+- [Learn about team roles and permissions](/teams/roles-permissions)
+- [Explore the dashboard features](/teams/dashboard)
+- [Set up team management policies](/teams/team-management)

+ 375 - 0
apps/kilocode-docs/docs/teams/migration.md

@@ -0,0 +1,375 @@
+---
+sidebar_label: Migrating from Other Tools
+---
+
+# Migrating from Other Tools
+
+Switch to Kilo for Teams from other AI coding tools and experience transparent pricing, no vendor lock-in, and superior team management capabilities.
+
+## Why Teams Switch to Kilo
+
+### Transparency vs. Opacity
+
+**Other AI coding vendors** hide their true costs behind opaque subscription models, leaving you wondering what you're actually paying for.
+
+**Kilo for Teams** shows you exactly what each AI request costs - no markup, no hidden fees, complete transparency.
+
+### No Rate Limiting
+
+**Other tools** slow you down with rate limits and model switching when you need AI most.
+
+**Kilo for Teams** never limits your usage - pay for what you use, use what you need.
+
+### True Team Management
+
+**Other solutions** offer basic user management with limited visibility.
+
+**Kilo for Teams** provides comprehensive team analytics, role-based permissions, and detailed usage insights.
+
+## Migrating from Cursor
+
+### What You're Leaving Behind
+
+- **Opaque pricing** - Never knowing true AI costs
+- **Rate limiting** during peak usage periods
+- **Limited team visibility** into usage patterns
+- **Vendor lock-in** with proprietary systems
+- **Hidden model switching** that degrades quality
+
+### What You Gain with Kilo for Teams
+
+- **Transparent AI costs** - See exactly what providers charge
+- **No rate limiting** - Use AI when you need it most
+- **Comprehensive analytics** - Understand team usage patterns
+- **Open source extension** - No vendor lock-in
+- **Consistent quality** - No hidden model downgrades
+
+### Migration Process
+
+**Step 1: Team Assessment**
+
+1. **Audit current Cursor usage** across your team
+2. **Identify active users** and their usage patterns
+3. **Calculate current costs** (if visible) vs. Kilo for Teams pricing
+4. **Plan migration timeline** to minimize disruption
+
+**Step 2: Kilo for Teams Setup**
+
+1. **Create organization** at [app.kilocode.com](https://app.kilocode.com)
+2. **Subscribe to Teams** with appropriate seat count
+3. **Configure team settings** and usage policies
+4. **Purchase initial AI credits** based on usage estimates
+
+**Step 3: Team Migration**
+
+1. **Invite team members** to Kilo for Teams
+2. **Install Kilo Code extension** alongside Cursor initially
+3. **Migrate projects gradually** starting with non-critical work
+4. **Train team** on Kilo Code features and workflows
+
+**Step 4: Full Transition**
+
+1. **Monitor usage patterns** in Kilo for Teams dashboard
+2. **Optimize settings** based on team feedback
+3. **Cancel Cursor subscriptions** once fully migrated
+4. **Uninstall Cursor** from team machines
+
+### Cursor Feature Mapping
+
+| Cursor Feature         | Kilo for Teams Equivalent             |
+| ---------------------- | ------------------------------------- |
+| AI Chat                | Chat interface with multiple modes    |
+| Code Generation        | Code mode with advanced tools         |
+| Code Editing           | Fast edits and surgical modifications |
+| Codebase Understanding | Codebase indexing and search          |
+| Team Management        | Comprehensive team dashboard          |
+| Usage Analytics        | Detailed usage and cost analytics     |
+
+## Migrating from GitHub Copilot
+
+### Limitations You're Escaping
+
+- **Limited model choice** - Stuck with GitHub's model selection
+- **Basic team features** - Minimal team management capabilities
+- **No cost visibility** - Hidden usage costs in subscription
+- **Microsoft ecosystem lock-in** - Tied to Microsoft services
+- **Limited customization** - Few options for team-specific needs
+
+### Kilo for Teams Advantages
+
+- **Multiple AI providers** - Choose from 18+ model providers
+- **Advanced team management** - Roles, permissions, and analytics
+- **Transparent pricing** - See exact costs for every request
+- **Provider flexibility** - Switch providers or use your own API keys
+- **Extensive customization** - Custom modes and team policies
+
+### Migration Strategy
+
+**Phase 1: Parallel Usage (Week 1-2)**
+
+1. **Keep GitHub Copilot** active during transition
+2. **Install Kilo Code** extension for team members
+3. **Start with simple tasks** in Kilo Code
+4. **Compare results** and team satisfaction
+
+**Phase 2: Gradual Transition (Week 3-4)**
+
+1. **Use Kilo Code** for new projects
+2. **Migrate existing projects** one at a time
+3. **Train team** on advanced features
+4. **Optimize usage patterns** based on analytics
+
+**Phase 3: Full Migration (Week 5+)**
+
+1. **Disable GitHub Copilot** for most team members
+2. **Cancel GitHub Copilot** subscriptions
+3. **Optimize Kilo for Teams** settings
+4. **Document new workflows** and best practices
+
+### GitHub Copilot Feature Comparison
+
+| GitHub Copilot   | Kilo for Teams                   | Advantage              |
+| ---------------- | -------------------------------- | ---------------------- |
+| Code suggestions | AI-powered code generation       | ✅ More model choices  |
+| Chat interface   | Multi-mode chat system           | ✅ Specialized modes   |
+| Team admin       | Comprehensive team management    | ✅ Advanced analytics  |
+| Usage insights   | Detailed usage and cost tracking | ✅ Transparent pricing |
+| Model selection  | 18+ AI providers and models      | ✅ No vendor lock-in   |
+
+## Migrating from Other AI Coding Tools
+
+### Common Migration Patterns
+
+**From Tabnine**
+
+- **Benefit:** More advanced AI models and team features
+- **Process:** Export settings, migrate team, configure advanced features
+- **Timeline:** 1-2 weeks for full transition
+
+**From CodeWhisperer**
+
+- **Benefit:** Escape AWS ecosystem lock-in, better team management
+- **Process:** Parallel usage, gradual migration, team training
+- **Timeline:** 2-3 weeks for enterprise teams
+
+**From Replit AI**
+
+- **Benefit:** Use in VS Code instead of web-based IDE
+- **Process:** Export projects, set up local development, team onboarding
+- **Timeline:** 3-4 weeks including development environment setup
+
+### Universal Migration Checklist
+
+**Pre-Migration Planning**
+
+- [ ] Audit current AI coding tool usage
+- [ ] Identify team members and their roles
+- [ ] Calculate current costs vs. Kilo for Teams pricing
+- [ ] Plan migration timeline and milestones
+- [ ] Prepare team communication and training
+
+**Migration Execution**
+
+- [ ] Set up Kilo for Teams organization
+- [ ] Configure team settings and policies
+- [ ] Invite team members and assign roles
+- [ ] Install Kilo Code extension across team
+- [ ] Start with pilot projects or non-critical work
+
+**Post-Migration Optimization**
+
+- [ ] Monitor usage patterns and costs
+- [ ] Optimize team settings based on analytics
+- [ ] Train team on advanced features
+- [ ] Cancel previous AI coding tool subscriptions
+- [ ] Document new workflows and best practices
+
+## Cost Comparison Analysis
+
+### Hidden Costs in Other Tools
+
+**Subscription Models Hide True Costs**
+
+- Monthly fees regardless of actual usage
+- No visibility into per-request costs
+- Rate limiting forces inefficient workflows
+- Model switching without notification
+
+**Kilo for Teams Transparent Pricing**
+
+- Pay exactly what AI providers charge
+- See cost of every request in real-time
+- No rate limiting or usage restrictions
+- Choose optimal models for each task
+
+### ROI Calculation Framework
+
+**Current Tool Analysis**
+
+1. **Monthly subscription costs** × team size
+2. **Hidden productivity losses** from rate limiting
+3. **Opportunity costs** from limited model access
+4. **Management overhead** from poor team visibility
+
+**Kilo for Teams Benefits**
+
+1. **Transparent AI costs** (typically 30-50% lower)
+2. **Productivity gains** from no rate limiting
+3. **Better outcomes** from optimal model selection
+4. **Reduced management time** with comprehensive analytics
+
+### Sample Cost Comparison
+
+**10-person team, moderate AI usage:**
+
+| Tool           | Monthly Cost    | Hidden Costs         | Total       |
+| -------------- | --------------- | -------------------- | ----------- |
+| Cursor Pro     | $200/month      | Rate limiting losses | ~$300/month |
+| GitHub Copilot | $190/month      | Limited model access | ~$250/month |
+| Kilo for Teams | $290 + AI costs | None                 | ~$200/month |
+
+_Actual savings vary based on usage patterns and team efficiency gains._
+
+## Team Training and Adoption
+
+### Training Program Structure
+
+**Week 1: Basics**
+
+- Kilo Code extension installation and setup
+- Basic chat interface and mode usage
+- Understanding transparent pricing model
+- Team dashboard overview
+
+**Week 2: Advanced Features**
+
+- Custom modes and specialized workflows
+- Advanced tools and automation
+- Team collaboration features
+- Usage optimization strategies
+
+**Week 3: Team Optimization**
+
+- Analytics review and insights
+- Cost optimization techniques
+- Workflow integration and best practices
+- Advanced team management features
+
+### Adoption Best Practices
+
+**Start Small**
+
+- Begin with volunteer early adopters
+- Use for non-critical projects initially
+- Gather feedback and iterate
+- Expand gradually across team
+
+**Provide Support**
+
+- Dedicated migration support channel
+- Regular check-ins with team members
+- Documentation and training resources
+- Quick resolution of issues and questions
+
+**Measure Success**
+
+- Track usage adoption rates
+- Monitor cost savings and efficiency gains
+- Collect team satisfaction feedback
+- Document success stories and best practices
+
+## Common Migration Challenges
+
+### Technical Challenges
+
+**Extension Conflicts**
+
+- **Issue:** Multiple AI coding extensions interfering
+- **Solution:** Disable old extensions during transition
+- **Prevention:** Staged migration with clear timelines
+
+**Workflow Disruption**
+
+- **Issue:** Team productivity dip during transition
+- **Solution:** Parallel usage period with gradual migration
+- **Prevention:** Comprehensive training and support
+
+**Settings Migration**
+
+- **Issue:** Lost customizations from previous tools
+- **Solution:** Document and recreate important settings
+- **Prevention:** Settings audit before migration
+
+### Organizational Challenges
+
+**Change Resistance**
+
+- **Issue:** Team members reluctant to switch tools
+- **Solution:** Demonstrate clear benefits and provide training
+- **Prevention:** Involve team in migration planning
+
+**Budget Approval**
+
+- **Issue:** Finance team concerns about new tool costs
+- **Solution:** Provide detailed cost comparison and ROI analysis
+- **Prevention:** Transparent pricing documentation
+
+**Timeline Pressure**
+
+- **Issue:** Pressure to migrate quickly without proper planning
+- **Solution:** Phased migration approach with clear milestones
+- **Prevention:** Realistic timeline planning with buffer time
+
+## Migration Support
+
+### Professional Migration Services
+
+- **Migration planning** and timeline development
+- **Team training** and onboarding support
+- **Custom integration** development
+- **Ongoing optimization** consulting
+
+### Self-Service Resources
+
+- **Migration guides** for specific tools
+- **Video tutorials** for common migration scenarios
+- **Community support** through Discord and forums
+- **Documentation** and best practices
+
+### Getting Migration Help
+
+- **Email:** [email protected]
+- **Discord:** Join our migration support channel
+- **Consultation:** Schedule free migration planning call
+- **Documentation:** Comprehensive migration guides
+
+## Success Stories
+
+### Mid-Size Software Company (25 developers)
+
+**Previous:** Cursor Pro subscriptions
+**Challenge:** High costs with limited visibility
+**Result:** 40% cost reduction with better team insights
+**Timeline:** 3-week migration with zero productivity loss
+
+### Enterprise Development Team (100+ developers)
+
+**Previous:** GitHub Copilot Enterprise
+**Challenge:** Limited model choice and team management
+**Result:** Improved code quality and team collaboration
+**Timeline:** 6-week phased migration across multiple teams
+
+### Startup Engineering Team (8 developers)
+
+**Previous:** Multiple individual AI tool subscriptions
+**Challenge:** Expense report chaos and no team coordination
+**Result:** Centralized billing and improved team efficiency
+**Timeline:** 1-week migration with immediate benefits
+
+## Next Steps
+
+- [Get started with your team](/teams/getting-started)
+- [Explore team management features](/teams/team-management)
+- [Understand billing and pricing](/teams/billing)
+
+Ready to make the switch? Contact our migration team at [email protected] to plan your transition to transparent AI coding.

+ 181 - 0
apps/kilocode-docs/docs/teams/roles-permissions.md

@@ -0,0 +1,181 @@
+---
+sidebar_label: Team Roles & Permissions
+---
+
+# Team Roles & Permissions
+
+Kilo for Teams uses a three-tier role system designed for clear responsibility separation and secure team management.
+
+## Role Overview
+
+Every team member has one of three roles that determine their access level and capabilities within your organization.
+
+### Owner
+
+**Full administrative control** - The person who created the organization or was promoted by another owner.
+
+**Key Responsibilities:**
+
+- Financial management and billing oversight
+- Strategic team planning and seat allocation
+- Ultimate security and compliance authority
+
+### Admin
+
+**Team management without financial access** - Trusted team leads who manage day-to-day operations.
+
+**Key Responsibilities:**
+
+- Team member onboarding and management
+- Usage monitoring and optimization
+- Policy enforcement and compliance
+
+### Member
+
+**Standard usage access** - Individual contributors who use Kilo Code for development work.
+
+**Key Responsibilities:**
+
+- Personal usage monitoring
+- Following team policies and guidelines
+- Productive AI-assisted development
+
+## Detailed Permissions Matrix
+
+| Capability                       | Owner | Admin          | Member |
+| -------------------------------- | ----- | -------------- | ------ |
+| **Financial Management**         |
+| Purchase AI credits              | ✅    | ❌             | ❌     |
+| View billing history             | ✅    | ✅ (read-only) | ❌     |
+| Manage payment methods           | ✅    | ❌             | ❌     |
+| Download invoices                | ✅    | ✅             | ❌     |
+| **Team Management**              |
+| Add/remove members               | ✅    | ✅             | ❌     |
+| Change member roles              | ✅    | ✅\*           | ❌     |
+| Manage seat count                | ✅    | ❌             | ❌     |
+| View team composition            | ✅    | ✅             | ✅     |
+| **Usage Controls**               |
+| Set daily usage limits           | ✅    | ✅             | ❌     |
+| View all usage statistics        | ✅    | ✅             | ❌     |
+| View personal usage              | ✅    | ✅             | ✅     |
+| **Security & Compliance**        |
+| Control data collection policies | ✅    | ✅             | ❌     |
+| Manage model access permissions  | ✅    | ✅             | ❌     |
+| Configure SSO settings           | ✅    | ❌             | ❌     |
+| **Development Access**           |
+| Use AI coding assistance         | ✅    | ✅             | ✅     |
+| Access all enabled models        | ✅    | ✅             | ✅     |
+| Personal settings management     | ✅    | ✅             | ✅     |
+
+\*Admins can change roles for other members and admins, but cannot promote/demote owners.
+
+## Role Assignment Best Practices
+
+### Choose Owners Carefully
+
+- Limit to 1-2 people maximum
+- Select individuals with financial authority
+- Ensure owners understand billing implications
+- Consider succession planning
+
+### Leverage Admin Role
+
+- Assign to team leads and senior developers
+- Perfect for those managing development workflows
+- Ideal for compliance officers or security leads
+- Use for people who need visibility without financial access
+
+### Member Role for Most Users
+
+- Default role for individual contributors
+- Appropriate for contractors and temporary team members
+- Suitable for junior developers learning the platform
+- Best for users who only need coding assistance
+
+## Changing Roles
+
+### Promoting Members
+
+1. Navigate to **Organization** tab
+2. Find the team member in the list
+3. Click the role dropdown next to their name
+4. Select the new role
+5. Confirm the change
+
+### Role Change Limitations
+
+- Only owners can promote other owners
+- Admins cannot change owner roles
+- Role changes take effect immediately
+- Members are notified of role changes via email
+
+## Security Considerations
+
+### Owner Security
+
+- Enable two-factor authentication
+- Use strong, unique passwords
+- Regularly review team access
+- Monitor billing for unusual activity
+
+### Admin Oversight
+
+- Admins should regularly audit team usage
+- Review and update usage limits quarterly
+- Monitor for policy violations
+- Ensure compliance with data policies
+
+### Member Guidelines
+
+- Members should report suspicious activity
+- Follow organization data policies
+- Use AI assistance responsibly
+- Report technical issues promptly
+
+## Common Role Scenarios
+
+### Small Team (2-5 people)
+
+- **1 Owner:** Founder or technical lead
+- **1 Admin:** Senior developer or team lead
+- **2-3 Members:** Individual contributors
+
+### Medium Team (6-20 people)
+
+- **1-2 Owners:** CTO and engineering manager
+- **2-3 Admins:** Team leads and senior developers
+- **15+ Members:** Individual contributors and junior developers
+
+### Large Team (20+ people)
+
+- **2 Owners:** CTO and VP of Engineering
+- **4-6 Admins:** Team leads, security officer, compliance manager
+- **20+ Members:** All other developers and contributors
+
+## Troubleshooting Role Issues
+
+### Can't Change Someone's Role
+
+- Verify you have sufficient permissions (Owner or Admin)
+- Check if you're trying to modify an Owner (only Owners can do this)
+- Ensure the person is still an active team member
+
+### Missing Permissions
+
+- Confirm your current role in the Organization tab
+- Contact an Owner or Admin if you need elevated access
+- Check if your role was recently changed
+
+### Billing Access Issues
+
+- Only Owners can manage billing and payments
+- Admins can view billing history but cannot make changes
+- Contact an Owner for billing-related requests
+
+## Next Steps
+
+- [Learn about dashboard features](/teams/dashboard)
+- [Set up team management policies](/teams/team-management)
+- [Configure billing and credits](/teams/billing)
+
+Understanding roles and permissions ensures your team operates securely and efficiently with clear accountability.

+ 164 - 0
apps/kilocode-docs/docs/teams/team-management.md

@@ -0,0 +1,164 @@
+---
+sidebar_label: Managing Your Team
+---
+
+# Managing Your Team
+
+Effective team management in Kilo for Teams ensures optimal AI usage, cost control, and smooth collaboration across your engineering organization.
+
+## Adding Team Members
+
+### Invitation Process
+
+1. **Navigate to Organization Tab** in your profile page and click on the team you want to manage
+2. **Click "Invite Member"** button
+3. **Enter the team member's email address**
+4. **Select initial role** (Member, Admin, or Owner)
+5. Click **Send Invitation**
+
+<img src="/docs/img/teams/invite-member.png" alt="Invite your team members" width="600" />
+
+<!-- ## Setting Usage Limits
+
+### Daily Spending Limits
+
+Control costs by setting daily limits per user:
+
+1. **Go to Organization tab**
+2. **Find team member** in the list
+3. **Click "Set Limit"** next to their name
+4. **Enter daily dollar amount** (e.g., $10.00)
+5. **Save changes**
+
+### How Limits Work
+
+- **$0 = Unlimited** - No spending restrictions
+- **Soft limits** - Users receive warnings at 80% of limit
+- **Hard limits** - AI requests blocked when limit reached
+- **Daily reset** - Limits reset at midnight UTC
+- **Rollover** - Unused limits don't carry over to next day
+
+### Recommended Limit Guidelines
+
+**Individual Contributors**
+
+- **Junior developers:** $5-10/day
+- **Senior developers:** $10-20/day
+- **Occasional users:** $2-5/day
+
+**Team Leads and Architects**
+
+- **Team leads:** $15-25/day
+- **Architects:** $20-30/day
+- **Heavy users:** $25-50/day
+
+**Special Cases**
+
+- **Demo/training days:** Temporarily increase limits
+- **Hackathons:** Remove limits for event duration
+- **New feature development:** Higher limits for exploration
+
+### Monitoring Usage Patterns
+
+Track team usage to optimize limits:
+
+- **Weekly usage reports** show actual consumption
+- **Peak usage days** help identify patterns
+- **Model preferences** affect cost per request
+- **Task complexity** influences daily needs -->
+
+## Managing Team Composition
+
+### Viewing Team Status
+
+The Organization tab shows:
+
+- **Active members** with last activity
+- **Pending invitations** awaiting acceptance
+- **Seat utilization** (used vs. available)
+- **Role distribution** across the team
+
+### Removing Team Members
+
+When team members leave:
+
+1. **Navigate to Organization tab**
+2. **Find the departing member**
+3. **Click "Remove" button**
+4. **Confirm removal**
+5. **Seat becomes available** immediately
+
+### Role Changes
+
+Promote or demote team members as needed:
+
+1. **Locate team member** in Organization tab
+2. **Click role dropdown** next to their name
+3. **Select new role** (Member, Admin, Owner)
+4. **Confirm change**
+5. **Member receives email notification**
+
+### Cost Optimization Strategies
+
+**Model Selection**
+
+- Train team on cost-effective model choices
+- Use cheaper models for simple tasks
+- Reserve expensive models for complex problems
+- Monitor model performance vs. cost
+
+**Usage Patterns**
+
+- Identify peak usage times
+- Batch similar requests when possible
+- Avoid redundant AI requests
+- Share solutions across team members
+
+**Limit Adjustments**
+
+- Regularly review and adjust individual limits
+- Increase limits for high-value activities
+- Decrease limits for occasional users
+- Set temporary limits for special projects
+
+## Troubleshooting Common Issues
+
+### Invitation Problems
+
+**Invitations Not Received**
+
+- Check spam/junk folders
+- Verify email address spelling
+- Resend invitation after 24 hours
+- Contact support if persistent issues
+
+**Can't Accept Invitation**
+
+- Ensure using correct email address
+- Clear browser cache and cookies
+- Try different browser or incognito mode
+- Check for corporate firewall restrictions
+
+### Access Issues
+
+**Member Can't Access Dashboard**
+
+- Verify they accepted the invitation
+- Check their role permissions
+- Ensure they're using invited email address
+- Confirm organization membership
+
+**Usage Limits Not Working**
+
+- Verify limits are set correctly
+- Check timezone differences (limits reset at UTC midnight)
+- Confirm user is within their role permissions
+- Review recent usage in dashboard
+
+## Next Steps
+
+- [Understand billing and credits](/teams/billing)
+- [Explore usage analytics](/teams/analytics)
+- [Learn about team roles and permissions](/teams/roles-permissions)
+
+Effective team management ensures your organization maximizes the benefits of AI-assisted development while maintaining cost control and security.

+ 60 - 61
apps/kilocode-docs/i18n/zh-CN/docusaurus-theme-classic/footer.json

@@ -1,63 +1,62 @@
 {
-  "link.title.Community": {
-    "message": "社区",
-    "description": "The title of the footer links column with title=Community in the footer"
-  },
-  "link.title.GitHub": {
-    "message": "GitHub",
-    "description": "The title of the footer links column with title=GitHub in the footer"
-  },
-  "link.title.Download": {
-    "message": "下载",
-    "description": "The title of the footer links column with title=Download in the footer"
-  },
-  "link.title.Company": {
-    "message": "公司",
-    "description": "The title of the footer links column with title=Company in the footer"
-  },
-  "link.item.label.Discord": {
-    "message": "Discord",
-    "description": "The label of footer link with label=Discord linking to https://kilocode.ai/discord"
-  },
-  "link.item.label.Reddit": {
-    "message": "Reddit",
-    "description": "The label of footer link with label=Reddit linking to https://www.reddit.com/r/kilocode/"
-  },
-  "link.item.label.Twitter": {
-    "message": "Twitter",
-    "description": "The label of footer link with label=Twitter linking to https://x.com/Kilo_Code"
-  },
-  "link.item.label.Issues": {
-    "message": "问题与反馈",
-    "description": "The label of footer link with label=Issues linking to https://github.com/Kilo-Org/kilocode/issues"
-  },
-  "link.item.label.Feature Requests": {
-    "message": "功能建议",
-    "description": "The label of footer link with label=Feature Requests linking to https://github.com/Kilo-Org/kilocode/discussions/categories/ideas"
-  },
-  "link.item.label.VS Code Marketplace": {
-    "message": "VS Code 扩展市场",
-    "description": "The label of footer link with label=VS Code Marketplace linking to https://marketplace.visualstudio.com/items?itemName=kilocode.kilo-code"
-  },
-  "link.item.label.Open VSX Registry": {
-    "message": "Open VSX Registry",
-    "description": "The label of footer link with label=Open VSX Registry linking to https://open-vsx.org/extension/kilocode/kilo-code"
-  },
-  "link.item.label.Contact": {
-    "message": "联系我们",
-    "description": "The label of footer link with label=Contact linking to mailto:[email protected]"
-  },
-  "link.item.label.Careers": {
-    "message": "官网",
-    "description": "The label of footer link with label=Careers linking to https://kilocode.ai"
-  },
-  "link.item.label.Website Privacy Policy": {
-    "message": "网站隐私政策",
-    "description": "The label of footer link with label=Website Privacy Policy linking to https://kilocode.ai/privacy"
-  },
-  "link.item.label.Extension Privacy Policy": {
-    "message": "扩展隐私政策",
-    "description": "The label of footer link with label=Extension Privacy Policy linking to https://github.com/Kilo-Org/kilocode/blob/main/PRIVACY.md"
-  }
+	"link.title.Community": {
+		"message": "社区",
+		"description": "The title of the footer links column with title=Community in the footer"
+	},
+	"link.title.GitHub": {
+		"message": "GitHub",
+		"description": "The title of the footer links column with title=GitHub in the footer"
+	},
+	"link.title.Download": {
+		"message": "下载",
+		"description": "The title of the footer links column with title=Download in the footer"
+	},
+	"link.title.Company": {
+		"message": "公司",
+		"description": "The title of the footer links column with title=Company in the footer"
+	},
+	"link.item.label.Discord": {
+		"message": "Discord",
+		"description": "The label of footer link with label=Discord linking to https://kilocode.ai/discord"
+	},
+	"link.item.label.Reddit": {
+		"message": "Reddit",
+		"description": "The label of footer link with label=Reddit linking to https://www.reddit.com/r/kilocode/"
+	},
+	"link.item.label.Twitter": {
+		"message": "Twitter",
+		"description": "The label of footer link with label=Twitter linking to https://x.com/kilocode"
+	},
+	"link.item.label.Issues": {
+		"message": "问题与反馈",
+		"description": "The label of footer link with label=Issues linking to https://github.com/Kilo-Org/kilocode/issues"
+	},
+	"link.item.label.Feature Requests": {
+		"message": "功能建议",
+		"description": "The label of footer link with label=Feature Requests linking to https://github.com/Kilo-Org/kilocode/discussions/categories/ideas"
+	},
+	"link.item.label.VS Code Marketplace": {
+		"message": "VS Code 扩展市场",
+		"description": "The label of footer link with label=VS Code Marketplace linking to https://marketplace.visualstudio.com/items?itemName=kilocode.kilo-code"
+	},
+	"link.item.label.Open VSX Registry": {
+		"message": "Open VSX Registry",
+		"description": "The label of footer link with label=Open VSX Registry linking to https://open-vsx.org/extension/kilocode/kilo-code"
+	},
+	"link.item.label.Contact": {
+		"message": "联系我们",
+		"description": "The label of footer link with label=Contact linking to mailto:[email protected]"
+	},
+	"link.item.label.Careers": {
+		"message": "官网",
+		"description": "The label of footer link with label=Careers linking to https://kilocode.ai"
+	},
+	"link.item.label.Website Privacy Policy": {
+		"message": "网站隐私政策",
+		"description": "The label of footer link with label=Website Privacy Policy linking to https://kilocode.ai/privacy"
+	},
+	"link.item.label.Extension Privacy Policy": {
+		"message": "扩展隐私政策",
+		"description": "The label of footer link with label=Extension Privacy Policy linking to https://github.com/Kilo-Org/kilocode/blob/main/PRIVACY.md"
+	}
 }
-

+ 15 - 0
apps/kilocode-docs/sidebars.ts

@@ -24,6 +24,7 @@ const sidebars: SidebarsConfig = {
 				"basic-usage/the-chat-interface",
 				"basic-usage/model-selection-guide",
 				"basic-usage/using-modes",
+				"basic-usage/autocomplete",
 				"basic-usage/context-mentions",
 				{
 					type: "category",
@@ -70,6 +71,7 @@ const sidebars: SidebarsConfig = {
 								"providers/requesty",
 								"providers/unbound",
 								"providers/v0",
+								"providers/vercel-ai-gateway",
 								"providers/virtual-quota-fallback",
 								"providers/vscode-lm",
 								"providers/xai",
@@ -92,6 +94,19 @@ const sidebars: SidebarsConfig = {
 				"tips-and-tricks",
 			],
 		},
+		{
+			type: "category",
+			label: "Kilo for Teams",
+			items: [
+				"teams/getting-started",
+				"teams/roles-permissions",
+				"teams/dashboard",
+				"teams/team-management",
+				"teams/billing",
+				"teams/analytics",
+				"teams/migration",
+			],
+		},
 		{
 			type: "category",
 			label: "Advanced Usage",

+ 16 - 16
apps/kilocode-docs/src/constants.ts

@@ -3,27 +3,27 @@
  */
 
 // GitHub repository information
-export const GITHUB_REPO_URL = 'https://github.com/Kilo-Org/docs';
-export const GITHUB_ISSUES_URL = `${GITHUB_REPO_URL}/issues`;
-export const GITHUB_NEW_ISSUE_URL = `${GITHUB_ISSUES_URL}/new`;
+export const GITHUB_REPO_URL = "https://github.com/Kilo-Org/docs"
+export const GITHUB_ISSUES_URL = `${GITHUB_REPO_URL}/issues`
+export const GITHUB_NEW_ISSUE_URL = `${GITHUB_ISSUES_URL}/new`
 
 // Community links
-export const DISCORD_URL = 'https://kilocode.ai/discord';
-export const REDDIT_URL = 'https://www.reddit.com/r/kilocode/';
-export const TWITTER_URL = 'https://x.com/Kilo_Code';
-export const YOUTUBE_URL= 'https://www.youtube.com/@Kilo-Code';
+export const DISCORD_URL = "https://kilocode.ai/discord"
+export const REDDIT_URL = "https://www.reddit.com/r/kilocode/"
+export const TWITTER_URL = "https://x.com/kilocode"
+export const YOUTUBE_URL = "https://www.youtube.com/@Kilo-Code"
 
 // GitHub links
-export const GITHUB_MAIN_REPO_URL = 'https://github.com/Kilo-Org/kilocode';
-export const GITHUB_ISSUES_MAIN_URL = `${GITHUB_MAIN_REPO_URL}/issues`;
-export const GITHUB_FEATURES_URL = `${GITHUB_MAIN_REPO_URL}/discussions/categories/ideas`;
+export const GITHUB_MAIN_REPO_URL = "https://github.com/Kilo-Org/kilocode"
+export const GITHUB_ISSUES_MAIN_URL = `${GITHUB_MAIN_REPO_URL}/issues`
+export const GITHUB_FEATURES_URL = `${GITHUB_MAIN_REPO_URL}/discussions/categories/ideas`
 
 // Download links
-export const VSCODE_MARKETPLACE_URL = 'https://marketplace.visualstudio.com/items?itemName=kilocode.kilo-code';
-export const OPEN_VSX_URL = 'https://open-vsx.org/extension/kilocode/kilo-code';
+export const VSCODE_MARKETPLACE_URL = "https://marketplace.visualstudio.com/items?itemName=kilocode.kilo-code"
+export const OPEN_VSX_URL = "https://open-vsx.org/extension/kilocode/kilo-code"
 
 // Company links
-export const CONTACT_EMAIL = 'mailto:[email protected]';
-export const CAREERS_URL = 'https://kilocode.ai';
-export const WEBSITE_PRIVACY_URL = 'https://kilocode.ai/privacy';
-export const EXTENSION_PRIVACY_URL = `${GITHUB_MAIN_REPO_URL}/blob/main/PRIVACY.md`;
+export const CONTACT_EMAIL = "mailto:[email protected]"
+export const CAREERS_URL = "https://kilocode.ai"
+export const WEBSITE_PRIVACY_URL = "https://kilocode.ai/privacy"
+export const EXTENSION_PRIVACY_URL = `${GITHUB_MAIN_REPO_URL}/blob/main/PRIVACY.md`

binární
apps/kilocode-docs/static/img/jetbrains/android-studio-jcef-enable.gif


binární
apps/kilocode-docs/static/img/kilo-provider/connected-accounts.png


binární
apps/kilocode-docs/static/img/teams/create-team.png


binární
apps/kilocode-docs/static/img/teams/dashboard.png


binární
apps/kilocode-docs/static/img/teams/invite-member.png


binární
apps/kilocode-docs/static/img/teams/subscribe.png


binární
apps/kilocode-docs/static/img/teams/usage-details.png


+ 10 - 0
apps/kilocode-docs/turbo.json

@@ -3,6 +3,16 @@
 	"extends": ["//"],
 	"tasks": {
 		"build": {
+			"inputs": [
+				"docs/**",
+				"blog-posts/**",
+				"src/**",
+				"static/**",
+				"i18n/**",
+				"docusaurus.config.ts",
+				"sidebars.ts",
+				"package.json"
+			],
 			"outputs": ["build/**"],
 			"env": ["POSTHOG_API_KEY", "FREE_TIER_AMOUNT"]
 		}

+ 2 - 0
apps/storybook/.storybook/main.ts

@@ -51,6 +51,8 @@ const config: StorybookConfig = {
 			// Add automatically generated aliases from tsconfig
 			...tsConfigAliases,
 			// Mock overrides for Storybook (these override the tsconfig paths)
+			jsdom: resolve(currentDirname, "../src/mocks/jsdom"),
+			vscode: resolve(currentDirname, "../src/mocks/vscode"),
 			"@src/utils/clipboard": resolve(currentDirname, "../src/mocks/utils"),
 			"@src/utils/highlighter": resolve(currentDirname, "../src/mocks/utils"),
 			"@src/i18n/TranslationContext": resolve(currentDirname, "../src/mocks/utils"),

+ 111 - 0
apps/storybook/src/components/CodeHighlighterExample.tsx

@@ -0,0 +1,111 @@
+import React, { useState, useEffect } from "react"
+import { calculateDiff } from "../../../../src/services/ghost/utils/CharacterDiff"
+import { generateHighlightedHtmlWithRanges } from "../../../../src/services/ghost/utils/CodeHighlighter"
+import { SvgRenderer } from "../../../../src/services/ghost/utils/SvgRenderer"
+export interface CharacterHighlightingProps {
+	originalText: string
+	newText: string
+	language: string
+	fontSize: number
+	width: number
+}
+
+export const SvgCodeHighlighterExample: React.FC<CharacterHighlightingProps> = ({
+	originalText: initialOriginalText,
+	newText: initialNewText,
+	language,
+	fontSize,
+	width,
+}) => {
+	const [originalText, setOriginalText] = useState(initialOriginalText)
+	const [newText, setNewText] = useState(initialNewText)
+	const [svg, setSvg] = useState<string>("")
+	const [loading, setLoading] = useState(true)
+
+	useEffect(() => {
+		let canceled = false
+		const generateSvg = async () => {
+			try {
+				setLoading(true)
+
+				// Calculate character differences
+				const ranges = calculateDiff(originalText, newText)
+
+				// Generate highlighted HTML
+				const { html, themeColors } = await generateHighlightedHtmlWithRanges(newText, language, ranges)
+				if (canceled) return
+
+				// Create SVG renderer
+				const renderer = new SvgRenderer(html, {
+					width,
+					height: fontSize * 2,
+					fontSize,
+					fontFamily: "Menlo, Monaco, 'Courier New', monospace",
+					letterSpacing: 0,
+					lineHeight: fontSize * 1.5,
+					themeColors,
+				})
+
+				// Render the SVG
+				const svgResult = renderer.render()
+				setSvg(svgResult)
+			} catch (error) {
+				const errorMessage = error instanceof Error ? error.message : String(error)
+				setSvg(
+					`<svg width="400" height="50"><text x="10" y="25" fill="red">Error: ${errorMessage}</text></svg>`,
+				)
+			} finally {
+				setLoading(false)
+			}
+		}
+
+		generateSvg()
+		return () => {
+			canceled = true
+		}
+	}, [originalText, newText, language, fontSize, width])
+
+	// Update internal state when props change
+	useEffect(() => {
+		setOriginalText(initialOriginalText)
+	}, [initialOriginalText])
+
+	useEffect(() => {
+		setNewText(initialNewText)
+	}, [initialNewText])
+
+	return (
+		<div className="p-4 text-vscode-foreground">
+			<h3 className="text-xl font-semibold mb-4">SVG Code Highlighter Demo</h3>
+
+			<div className="flex gap-5 mb-5">
+				<div className="flex-1">
+					<label className="block mb-1 font-bold text-sm py-1">Original Text</label>
+					<textarea
+						value={originalText}
+						onChange={(e) => setOriginalText(e.target.value)}
+						className="w-full h-30 bg-vscode-input-background text-vscode-input-foreground border border-vscode-input-border rounded p-2 font-mono text-sm resize-y focus:outline-none focus:ring-2 focus:ring-vscode-focusBorder"
+					/>
+				</div>
+				<div className="flex-1">
+					<label className="block mb-1 font-bold text-sm py-1">Updated Text</label>
+					<textarea
+						value={newText}
+						onChange={(e) => setNewText(e.target.value)}
+						className="w-full h-30 bg-vscode-input-background text-vscode-input-foreground border border-vscode-input-border rounded p-2 font-mono text-sm resize-y focus:ring-2 focus:ring-vscode-focusBorder"
+					/>
+				</div>
+			</div>
+
+			<div className="flex flex-col">
+				<strong className="text-sm py-1">SVG Preview</strong>
+				<div className="border border-vscode-input-border p-2.5 rounded">
+					{loading && (
+						<div className="text-vscode-descriptionForeground italic text-sm mt-1">Generating...</div>
+					)}
+					<div className="mt-2.5 min-h-[50px]" dangerouslySetInnerHTML={{ __html: svg }} />
+				</div>
+			</div>
+		</div>
+	)
+}

+ 8 - 0
apps/storybook/src/mocks/jsdom.ts

@@ -0,0 +1,8 @@
+// JSDOM is used in the extension for SVG code highlighting.
+// We render sample SVGs in Storybook for testing/ debugging purposes,
+// but use actual browser APIs instead of JSDOM there.
+// See ../../../../src/services/ghost/utils/htmlParser.ts
+
+export const JSDOM = null
+
+export default null

+ 59 - 0
apps/storybook/src/mocks/vscode.ts

@@ -0,0 +1,59 @@
+// Minimal VSCode API mock for browser environments (Storybook)
+
+const ColorThemeKind = {
+	Light: 1,
+	Dark: 2,
+	HighContrast: 3,
+	HighContrastLight: 4,
+} as const
+
+const window = {
+	get activeColorTheme() {
+		try {
+			// In browser/Storybook environment, detect theme from DOM
+			const themeContainer = document.querySelector("[data-theme]")
+			const currentTheme = themeContainer?.getAttribute("data-theme")
+
+			if (currentTheme === "light") {
+				return { kind: ColorThemeKind.Light }
+			}
+		} catch {
+			// Fallback on any DOM errors
+		}
+
+		// Default to dark theme
+		return { kind: ColorThemeKind.Dark }
+	},
+}
+
+const workspace = {
+	getConfiguration: () => ({
+		get: (key: string) => {
+			if (key === "workbench.colorTheme") {
+				try {
+					const themeContainer = document.querySelector("[data-theme]")
+					const currentTheme = themeContainer?.getAttribute("data-theme")
+
+					if (currentTheme === "light") {
+						return "Light+ (default light)"
+					}
+				} catch {
+					// Fallback on any DOM errors
+				}
+
+				return "Dark+ (default dark)"
+			}
+			return undefined
+		},
+	}),
+}
+
+// Export everything for both namespace and named imports
+export { ColorThemeKind, window, workspace }
+
+// Export everything as default for compatibility
+export default {
+	ColorThemeKind,
+	window,
+	workspace,
+}

+ 87 - 0
apps/storybook/stories/SvgCodeHighlighter.stories.tsx

@@ -0,0 +1,87 @@
+import type { Meta, StoryObj } from "@storybook/react-vite"
+import { SvgCodeHighlighterExample } from "../src/components/CodeHighlighterExample"
+
+const meta = {
+	title: "Autocomplete/SvgCodeHighlighter",
+	component: SvgCodeHighlighterExample,
+	argTypes: {
+		originalText: {
+			control: "text",
+			description: "Original line of code",
+		},
+		newText: {
+			control: "text",
+			description: "New line of code with changes",
+		},
+		language: {
+			control: { type: "select" },
+			options: ["typescript", "javascript", "python", "html", "css"],
+			description: "Programming language for syntax highlighting",
+		},
+		fontSize: {
+			control: { type: "range", min: 10, max: 24, step: 1 },
+			description: "Font size for the rendered text",
+		},
+		width: {
+			control: { type: "range", min: 200, max: 800, step: 50 },
+			description: "SVG width",
+		},
+	},
+} satisfies Meta<typeof SvgCodeHighlighterExample>
+
+export default meta
+type Story = StoryObj<typeof meta>
+
+export const BasicExample: Story = {
+	args: {
+		originalText: 'const hello = "hi"',
+		newText: 'const hello = "hi";',
+		language: "typescript",
+		fontSize: 14,
+		width: 400,
+	},
+}
+
+export const MultilineChange: Story = {
+	args: {
+		originalText: `function sum(a, b) {
+	return a + b;
+}`,
+		newText: `function multiply(x, y) {
+	return x * y;
+}`,
+		language: "typescript",
+		fontSize: 14,
+		width: 400,
+	},
+}
+
+export const WordChange: Story = {
+	args: {
+		originalText: 'const hello = "hi";',
+		newText: 'const greeting = "hi";',
+		language: "typescript",
+		fontSize: 14,
+		width: 300,
+	},
+}
+
+export const SuffixAddition: Story = {
+	args: {
+		originalText: "ABC",
+		newText: "ABCDEF",
+		language: "typescript",
+		fontSize: 14,
+		width: 200,
+	},
+}
+
+export const MiddleCharacterChange: Story = {
+	args: {
+		originalText: "abc",
+		newText: "acc",
+		language: "typescript",
+		fontSize: 14,
+		width: 200,
+	},
+}

+ 1 - 1
apps/web-roo-code/next.config.ts

@@ -21,7 +21,7 @@ const nextConfig: NextConfig = {
 				destination: "https://roocode.com/:path*",
 				permanent: true,
 			},
-			// Redirect cloud waitlist to Notion page
+			// Redirect cloud waitlist to Notion page (kept for extension compatibility)
 			{
 				source: "/cloud-waitlist",
 				destination: "https://roo-code.notion.site/238fd1401b0a8087b858e1ad431507cf?pvs=105",

+ 24 - 101
apps/web-roo-code/src/app/evals/evals.tsx

@@ -1,59 +1,33 @@
 "use client"
 
 import { useMemo } from "react"
-import { ScatterChart, Scatter, XAxis, YAxis, Label, Customized, Cross } from "recharts"
-
-import type { TaskMetrics, Run } from "@roo-code/evals"
 
 import { formatTokens, formatCurrency, formatDuration, formatScore } from "@/lib"
 import { useOpenRouterModels } from "@/lib/hooks"
-import {
-	ChartContainer,
-	ChartTooltip,
-	ChartTooltipContent,
-	ChartConfig,
-	ChartLegend,
-	ChartLegendContent,
-	Table,
-	TableBody,
-	TableCaption,
-	TableCell,
-	TableHead,
-	TableHeader,
-	TableRow,
-} from "@/components/ui"
+import { Table, TableBody, TableCaption, TableCell, TableHead, TableHeader, TableRow } from "@/components/ui"
+
+import type { EvalRun } from "./types"
+import { Plot } from "./plot"
 
-export function Evals({
-	runs,
-}: {
-	runs: (Run & {
-		label: string
-		score: number
-		languageScores?: Record<"go" | "java" | "javascript" | "python" | "rust", number>
-		taskMetrics: TaskMetrics
-		modelId?: string
-	})[]
-}) {
+export function Evals({ runs }: { runs: EvalRun[] }) {
 	const { data: openRouterModels } = useOpenRouterModels()
 
-	const tableData = useMemo(
+	const tableData: (EvalRun & { label: string; cost: number })[] = useMemo(
 		() =>
-			runs.map((run) => ({
-				...run,
-				label: run.description || run.model,
-				score: run.score,
-				cost: run.taskMetrics.cost,
-				model: openRouterModels?.[run.modelId ?? ""],
-				modelInfo: openRouterModels?.[run.modelId ?? ""]?.modelInfo,
-			})),
-		[runs, openRouterModels],
-	)
+			runs.map((run) => {
+				const openRouterModelInfo = openRouterModels?.[run.modelId ?? ""]?.modelInfo
 
-	const chartData = useMemo(() => tableData.filter(({ cost }) => cost < 100), [tableData])
-
-	const chartConfig = useMemo(
-		() => chartData.reduce((acc, run) => ({ ...acc, [run.label]: run }), {} as ChartConfig),
-		[chartData],
+				return {
+					...run,
+					label: run.name || run.description || run.model,
+					cost: run.taskMetrics.cost,
+					description: run.description ?? openRouterModelInfo?.description ?? null,
+					contextWindow: run.contextWindow ?? openRouterModelInfo?.contextWindow ?? null,
+					inputPrice: run.inputPrice ?? openRouterModelInfo?.inputPrice ?? null,
+					outputPrice: run.outputPrice ?? openRouterModelInfo?.outputPrice ?? null,
+				}
+			}),
+		[runs, openRouterModels],
 	)
 
 	return (
@@ -127,17 +101,15 @@ export function Evals({
 				<TableBody className="font-mono">
 					{tableData.map((run) => (
 						<TableRow key={run.id}>
-							<TableCell title={run.model?.description}>
+							<TableCell title={run.description ?? undefined}>
 								<div className="font-sans">{run.label}</div>
-								<div className="text-xs opacity-50">
-									{formatTokens(run.modelInfo?.contextWindow ?? 0)}
-								</div>
+								<div className="text-xs opacity-50">{formatTokens(run.contextWindow)}</div>
 							</TableCell>
 							<TableCell className="border-r">
 								<div className="flex flex-row gap-2">
-									<div>{formatCurrency(run.modelInfo?.inputPrice ?? 0)}</div>
+									<div>{formatCurrency(run.inputPrice)}</div>
 									<div className="opacity-25">/</div>
-									<div>{formatCurrency(run.modelInfo?.outputPrice ?? 0)}</div>
+									<div>{formatCurrency(run.outputPrice)}</div>
 								</div>
 							</TableCell>
 							<TableCell className="font-mono">{formatDuration(run.taskMetrics.duration)}</TableCell>
@@ -169,58 +141,9 @@ export function Evals({
 					))}
 				</TableBody>
 				<TableCaption>
-					<div className="pb-4 font-medium">Cost Versus Score</div>
-					<ChartContainer config={chartConfig} className="h-[500px] w-full">
-						<ScatterChart margin={{ top: 0, right: 0, bottom: 0, left: 20 }}>
-							<XAxis
-								type="number"
-								dataKey="cost"
-								name="Cost"
-								domain={[
-									(dataMin: number) => Math.round((dataMin - 5) / 5) * 5,
-									(dataMax: number) => Math.round((dataMax + 5) / 5) * 5,
-								]}
-								tickFormatter={(value) => formatCurrency(value)}>
-								<Label value="Cost" position="bottom" offset={0} />
-							</XAxis>
-							<YAxis
-								type="number"
-								dataKey="score"
-								name="Score"
-								domain={[
-									(dataMin: number) => Math.max(0, Math.round((dataMin - 5) / 5) * 5),
-									(dataMax: number) => Math.min(100, Math.round((dataMax + 5) / 5) * 5),
-								]}
-								tickFormatter={(value) => `${value}%`}>
-								<Label value="Score" angle={-90} position="left" dy={-15} />
-							</YAxis>
-							<ChartTooltip content={<ChartTooltipContent labelKey="label" hideIndicator />} />
-							<Customized component={renderQuadrant} />
-							{chartData.map((d, i) => (
-								<Scatter key={d.label} name={d.label} data={[d]} fill={`hsl(var(--chart-${i + 1}))`} />
-							))}
-							<ChartLegend content={<ChartLegendContent />} />
-						</ScatterChart>
-					</ChartContainer>
-					<div className="py-4 text-xs opacity-50">
-						(Note: Very expensive models are excluded from the scatter plot.)
-					</div>
+					<Plot tableData={tableData} />
 				</TableCaption>
 			</Table>
 		</div>
 	)
 }
-
-// eslint-disable-next-line @typescript-eslint/no-explicit-any
-const renderQuadrant = (props: any) => (
-	<Cross
-		width={props.width}
-		height={props.height}
-		x={props.width / 2 + 35}
-		y={props.height / 2 - 15}
-		top={0}
-		left={0}
-		stroke="currentColor"
-		opacity={0.1}
-	/>
-)

+ 336 - 0
apps/web-roo-code/src/app/evals/plot.tsx

@@ -0,0 +1,336 @@
+"use client"
+
+import { useMemo } from "react"
+import { ScatterChart, Scatter, XAxis, YAxis, Customized, Cross, LabelList } from "recharts"
+
+import { formatCurrency } from "@/lib"
+import { ChartContainer, ChartTooltip, ChartConfig } from "@/components/ui"
+
+import type { EvalRun } from "./types"
+
+type PlotProps = {
+	tableData: (EvalRun & { label: string; cost: number })[]
+}
+
+type LabelPosition = "top" | "bottom" | "left" | "right"
+
+export const Plot = ({ tableData }: PlotProps) => {
+	const chartData = useMemo(() => tableData.filter(({ cost }) => cost < 50), [tableData])
+
+	const chartConfig = useMemo(
+		() => chartData.reduce((acc, run) => ({ ...acc, [run.label]: run }), {} as ChartConfig),
+		[chartData],
+	)
+
+	// Calculate label positions to avoid overlaps.
+	const labelPositions = useMemo(() => {
+		const positions: Record<string, LabelPosition> = {}
+
+		// Track placed labels with their approximate bounds.
+		const placedLabels: Array<{
+			cost: number
+			score: number
+			label: string
+			position: LabelPosition
+		}> = []
+
+		// Helper function to check if two labels would overlap.
+		const wouldLabelsOverlap = (
+			p1: { cost: number; score: number; position: LabelPosition },
+			p2: { cost: number; score: number; position: LabelPosition },
+		): boolean => {
+			// Approximate thresholds for overlap detection.
+			const horizontalThreshold = 4 // Cost units.
+			const verticalThreshold = 5 // Score units.
+
+			const costDiff = Math.abs(p1.cost - p2.cost)
+			const scoreDiff = Math.abs(p1.score - p2.score)
+
+			// If points are far apart, no overlap.
+			if (costDiff > horizontalThreshold * 2 || scoreDiff > verticalThreshold * 2) {
+				return false
+			}
+
+			// Check specific position combinations for overlap.
+			// Same position for nearby points definitely overlaps.
+			if (p1.position === p2.position && costDiff < horizontalThreshold && scoreDiff < verticalThreshold) {
+				return true
+			}
+
+			// Check adjacent position overlaps.
+			const p1IsTop = p1.position === "top"
+			const p1IsBottom = p1.position === "bottom"
+			const p2IsTop = p2.position === "top"
+			const p2IsBottom = p2.position === "bottom"
+
+			// If both labels are on the same vertical side and points are close
+			// horizontally.
+			if ((p1IsTop && p2IsTop) || (p1IsBottom && p2IsBottom)) {
+				if (costDiff < horizontalThreshold && scoreDiff < verticalThreshold / 2) {
+					return true
+				}
+			}
+
+			return false
+		}
+
+		// Helper function to check if position would overlap with a data point.
+		const wouldOverlapPoint = (point: (typeof chartData)[0], position: LabelPosition): boolean => {
+			for (const other of chartData) {
+				if (other.label === point.label) {
+					continue
+				}
+
+				const costDiff = Math.abs(point.cost - other.cost)
+				const scoreDiff = Math.abs(point.score - other.score)
+
+				// Check if label would be placed on top of another point.
+				switch (position) {
+					case "top":
+						// Label is above, check if there's a point above.
+						if (costDiff < 3 && other.score > point.score && other.score - point.score < 6) {
+							return true
+						}
+						break
+					case "bottom":
+						// Label is below, check if there's a point below.
+						if (costDiff < 3 && other.score < point.score && point.score - other.score < 6) {
+							return true
+						}
+						break
+					case "left":
+						// Label is to the left, check if there's a point to the left.
+						if (scoreDiff < 3 && other.cost < point.cost && point.cost - other.cost < 4) {
+							return true
+						}
+						break
+					case "right":
+						// Label is to the right, check if there's a point to the right.
+						if (scoreDiff < 3 && other.cost > point.cost && other.cost - point.cost < 4) {
+							return true
+						}
+						break
+				}
+			}
+			return false
+		}
+
+		// Sort points to process them in a consistent order.
+		// Process from top-left to bottom-right.
+		const sortedData = [...chartData].sort((a, b) => {
+			// First by score (higher first).
+			const scoreDiff = b.score - a.score
+			if (Math.abs(scoreDiff) > 1) return scoreDiff
+			// Then by cost (lower first).
+			return a.cost - b.cost
+		})
+
+		// Process each point and find the best position.
+		sortedData.forEach((point) => {
+			// Try positions in order of preference.
+			const positionPreferences: LabelPosition[] = ["top", "bottom", "right", "left"]
+
+			let bestPosition: LabelPosition = "top"
+
+			for (const position of positionPreferences) {
+				// Check if this position would overlap with any placed labels.
+				let hasLabelOverlap = false
+
+				for (const placed of placedLabels) {
+					if (
+						wouldLabelsOverlap(
+							{ cost: point.cost, score: point.score, position },
+							{ cost: placed.cost, score: placed.score, position: placed.position },
+						)
+					) {
+						hasLabelOverlap = true
+						break
+					}
+				}
+
+				// Check if this position would overlap with any data points.
+				const hasPointOverlap = wouldOverlapPoint(point, position)
+
+				// If no overlaps, use this position.
+				if (!hasLabelOverlap && !hasPointOverlap) {
+					bestPosition = position
+					break
+				}
+			}
+
+			// Use the best position found
+			positions[point.label] = bestPosition
+			placedLabels.push({
+				cost: point.cost,
+				score: point.score,
+				label: point.label,
+				position: bestPosition,
+			})
+		})
+
+		return positions
+	}, [chartData])
+
+	return (
+		<>
+			<div className="pt-4 pb-8 font-mono">Cost x Score</div>
+			<ChartContainer config={chartConfig} className="h-[500px] w-full">
+				<ScatterChart margin={{ top: 20, right: 0, bottom: 0, left: 20 }}>
+					<XAxis
+						type="number"
+						dataKey="cost"
+						name="Cost"
+						domain={[
+							(dataMin: number) => Math.max(0, Math.round((dataMin - 5) / 5) * 5),
+							(dataMax: number) => Math.round((dataMax + 5) / 5) * 5,
+						]}
+						tickFormatter={(value) => formatCurrency(value)}
+					/>
+					<YAxis
+						type="number"
+						dataKey="score"
+						name="Score"
+						domain={[
+							(dataMin: number) => Math.max(0, Math.round((dataMin - 5) / 5) * 5),
+							(dataMax: number) => Math.min(100, Math.round((dataMax + 5) / 5) * 5),
+						]}
+						tickFormatter={(value) => `${value}%`}
+					/>
+					<ChartTooltip
+						content={({ active, payload }) => {
+							if (!active || !payload || !payload.length || !payload[0]) {
+								return null
+							}
+
+							const { label, cost, score } = payload[0].payload
+
+							return (
+								<div className="bg-background border rounded-sm p-2 shadow-sm text-left">
+									<div className="border-b pb-1">{label}</div>
+									<div className="pt-1">
+										<div>
+											Score: <span className="font-mono">{Math.round(score)}%</span>
+										</div>
+										<div>
+											Cost: <span className="font-mono">{formatCurrency(cost)}</span>
+										</div>
+									</div>
+								</div>
+							)
+						}}
+					/>
+					<Customized component={renderQuadrant} />
+					{chartData.map((d, index) => (
+						<Scatter
+							key={d.label}
+							name={d.label}
+							data={[d]}
+							fill={generateSpectrumColor(index, chartData.length)}>
+							<LabelList
+								dataKey="label"
+								content={(props) => renderCustomLabel(props, labelPositions[d.label] || "top")}
+							/>
+						</Scatter>
+					))}
+				</ScatterChart>
+			</ChartContainer>
+			<div className="py-4 text-xs opacity-50">
+				(Note: Models with a cost of $50 or more are excluded from the scatter plot.)
+			</div>
+		</>
+	)
+}
+
+// eslint-disable-next-line @typescript-eslint/no-explicit-any
+const renderQuadrant = (props: any) => (
+	<Cross
+		width={props.width}
+		height={props.height}
+		x={props.width / 2 + 35}
+		y={props.height / 2 - 15}
+		top={0}
+		left={0}
+		stroke="currentColor"
+		opacity={0.1}
+	/>
+)
+
+// eslint-disable-next-line @typescript-eslint/no-explicit-any
+const renderCustomLabel = (props: any, position: LabelPosition) => {
+	const { x, y, value } = props
+	const maxWidth = 80 // Maximum width in pixels - adjust as needed.
+
+	const truncateText = (text: string, maxChars: number = 20) => {
+		if (text.length <= maxChars) {
+			return text
+		}
+
+		return text.substring(0, maxChars - 1) + "…"
+	}
+
+	// Calculate position offsets based on label position.
+	let xOffset = 0
+	let yOffset = 0
+	let textAnchor: "middle" | "start" | "end" = "middle"
+	let dominantBaseline: "auto" | "hanging" | "middle" = "auto"
+
+	switch (position) {
+		case "top":
+			yOffset = -8
+			textAnchor = "middle"
+			dominantBaseline = "auto"
+			break
+		case "bottom":
+			yOffset = 15
+			textAnchor = "middle"
+			dominantBaseline = "hanging"
+			break
+		case "left":
+			xOffset = -8
+			yOffset = 5
+			textAnchor = "end"
+			dominantBaseline = "middle"
+			break
+		case "right":
+			xOffset = 15
+			yOffset = 5
+			textAnchor = "start"
+			dominantBaseline = "middle"
+			break
+	}
+
+	return (
+		<text
+			x={x + xOffset}
+			y={y + yOffset}
+			fontSize="11"
+			fontWeight="500"
+			fill="currentColor"
+			opacity="0.8"
+			textAnchor={textAnchor}
+			dominantBaseline={dominantBaseline}
+			style={{
+				pointerEvents: "none",
+				maxWidth: `${maxWidth}px`,
+				overflow: "hidden",
+				textOverflow: "ellipsis",
+				whiteSpace: "nowrap",
+			}}>
+			{truncateText(value)}
+		</text>
+	)
+}
+
+const generateSpectrumColor = (index: number, total: number): string => {
+	// Distribute hues evenly across the color wheel (0-360 degrees).
+	// Start at 0 (red) and distribute evenly.
+	const hue = (index * 360) / total
+
+	// Use high saturation for vibrant colors.
+	const saturation = 70
+
+	// Use medium lightness for good visibility on both light and dark backgrounds.
+	const lightness = 50
+
+	return `hsl(${Math.round(hue)}, ${saturation}%, ${lightness}%)`
+}

+ 9 - 0
apps/web-roo-code/src/app/evals/types.ts

@@ -0,0 +1,9 @@
+import type { TaskMetrics, Run } from "@roo-code/evals"
+
+export type EvalRun = Run & {
+	label: string
+	score: number
+	languageScores?: Record<"go" | "java" | "javascript" | "python" | "rust", number>
+	taskMetrics: TaskMetrics
+	modelId?: string
+}

+ 15 - 26
apps/web-roo-code/src/components/chromes/nav-bar.tsx

@@ -69,19 +69,13 @@ export function NavBar({ stars, downloads }: NavBarProps) {
 						className="text-muted-foreground transition-transform duration-200 hover:scale-105 hover:text-foreground">
 						Community
 					</a>
-					<div className="flex items-center rounded-full bg-gradient-to-r from-blue-400 to-cyan-400 p-0.5 text-xs">
-						<div className="rounded-full bg-background px-2 py-1.5">
-							<span className="text-muted-foreground border-r-2 border-foreground/50 pr-1.5">
-								Roo Code Cloud is coming
-							</span>
-							<a
-								href="/cloud-waitlist"
-								rel="noopener noreferrer"
-								className="font-medium text-primary hover:underline pl-1.5">
-								Sign up
-							</a>
-						</div>
-					</div>
+					<a
+						href={EXTERNAL_LINKS.CLOUD_APP}
+						target="_blank"
+						rel="noopener noreferrer"
+						className="text-muted-foreground transition-transform duration-200 hover:scale-105 hover:text-foreground">
+						Cloud
+					</a>
 				</nav>
 
 				<div className="hidden md:flex md:items-center md:space-x-4">
@@ -121,19 +115,6 @@ export function NavBar({ stars, downloads }: NavBarProps) {
 			<div
 				className={`absolute left-0 right-0 top-16 z-50 transform border-b border-border bg-background shadow-lg backdrop-blur-none transition-all duration-200 md:hidden ${isMenuOpen ? "translate-y-0 opacity-100" : "pointer-events-none -translate-y-2 opacity-0"}`}>
 				<nav className="flex flex-col py-2">
-					<div className="mx-5 mb-2 flex items-center rounded-full bg-gradient-to-r from-blue-400 to-cyan-400 p-0.5 text-xs">
-						<div className="flex-grow text-center rounded-full bg-background px-2 py-1.5">
-							<span className="text-muted-foreground border-r-2 border-foreground/50 pr-3">
-								Roo Code Cloud is coming
-							</span>
-							<a
-								href="/cloud-waitlist"
-								rel="noopener noreferrer"
-								className="font-medium text-primary hover:underline pl-3">
-								Sign up
-							</a>
-						</div>
-					</div>
 					<ScrollButton
 						targetId="features"
 						className="w-full px-8 py-3 text-left text-sm font-medium text-foreground/80 transition-colors hover:bg-accent hover:text-foreground"
@@ -181,6 +162,14 @@ export function NavBar({ stars, downloads }: NavBarProps) {
 						onClick={() => setIsMenuOpen(false)}>
 						Community
 					</a>
+					<a
+						href={EXTERNAL_LINKS.CLOUD_APP}
+						target="_blank"
+						rel="noopener noreferrer"
+						className="w-full px-8 py-3 text-left text-sm font-medium text-foreground/80 transition-colors hover:bg-accent hover:text-foreground"
+						onClick={() => setIsMenuOpen(false)}>
+						Cloud
+					</a>
 
 					<hr className="mx-8 my-2 border-t border-border/50" />
 

+ 1 - 0
apps/web-roo-code/src/lib/constants.ts

@@ -24,6 +24,7 @@ export const EXTERNAL_LINKS = {
 	OFFICE_HOURS_PODCAST: "https://www.youtube.com/@RooCodeYT/podcasts",
 	FAQ: "https://roocode.com/#faq",
 	TESTIMONIALS: "https://roocode.com/#testimonials",
+	CLOUD_APP: "https://app.roocode.com",
 }
 
 export const INTERNAL_LINKS = {

+ 7 - 1
apps/web-roo-code/src/lib/format-currency.ts

@@ -3,6 +3,12 @@ const formatter = new Intl.NumberFormat("en-US", {
 	currency: "USD",
 })
 
-export const formatCurrency = (amount: number) => formatter.format(amount)
+export const formatCurrency = (amount: number | null | undefined) => {
+	if (amount === null || amount === undefined) {
+		return "-"
+	}
+
+	return formatter.format(amount)
+}
 
 export const parsePrice = (price?: string) => (price ? parseFloat(price) * 1_000_000 : undefined)

+ 5 - 1
apps/web-roo-code/src/lib/format-duration.ts

@@ -1,4 +1,8 @@
-export const formatDuration = (durationMs: number) => {
+export const formatDuration = (durationMs: number | null | undefined) => {
+	if (durationMs === null || durationMs === undefined) {
+		return "-"
+	}
+
 	const seconds = Math.floor(durationMs / 1000)
 	const hours = Math.floor(seconds / 3600)
 	const minutes = Math.floor((seconds % 3600) / 60)

+ 5 - 1
apps/web-roo-code/src/lib/format-tokens.ts

@@ -1,4 +1,8 @@
-export const formatTokens = (tokens: number, decimals = 0) => {
+export const formatTokens = (tokens: number | null | undefined, decimals = 0) => {
+	if (tokens === null || tokens === undefined) {
+		return "-"
+	}
+
 	if (tokens < 1000) {
 		return tokens.toString()
 	}

+ 1 - 1
apps/web-roo-code/src/lib/hooks/use-open-router-models.ts

@@ -49,7 +49,7 @@ export const getOpenRouterModels = async (): Promise<OpenRouterModelRecord> => {
 
 	return result.data.data
 		.filter((rawModel) => {
-			// Skip image generation models (models that output images)
+			// Skip image generation models (models that output images).
 			return !rawModel.architecture?.output_modalities?.includes("image")
 		})
 		.sort((a, b) => a.name.localeCompare(b.name))

+ 0 - 1
jetbrains/plugin/.gitignore

@@ -52,4 +52,3 @@ plugins
 ### Generated build artifacts ###
 gradle.properties
 src/main/resources/META-INF/plugin.xml
-platform.zip

+ 12 - 6
jetbrains/plugin/genPlatform.gradle

@@ -7,7 +7,18 @@ import java.security.MessageDigest
 
 
 tasks.register('genPlatform', Zip) {
-    download(project)
+    doFirst {
+        // Delete existing platform.zip right before regeneration
+        def zipFile = new File(project.projectDir, "platform.zip")
+        if (zipFile.exists()) {
+            zipFile.delete()
+            println "Deleted existing platform.zip before regeneration"
+        }
+        
+        // Download platform files only when task actually executes
+        download(project)
+    }
+    
     from(new File(project.buildDir, "genPlatform/gen"))
     into("")
     destinationDirectory = project.projectDir
@@ -93,11 +104,6 @@ def download(Project project){
     def targetDir = new File(projectBuild, "gen/node_modules")
     def txtFile = new File(projectBuild, "gen/platform.txt")
     mergeDirectories(list, targetDir,txtFile)
-
-    def zipFile = new File(project.projectDir,"platform.zip")
-    if(zipFile.exists()) {
-        zipFile.delete()
-    }
 }
 
 

+ 14 - 7
jetbrains/plugin/src/main/kotlin/ai/kilocode/jetbrains/actors/MainThreadConsoleShape.kt

@@ -6,6 +6,7 @@ package ai.kilocode.jetbrains.actors
 
 import com.intellij.openapi.Disposable
 import com.intellij.openapi.diagnostic.Logger
+import ai.kilocode.jetbrains.logging.LogLevelConfig
 
 /**
  * Remote console log.
@@ -47,7 +48,7 @@ class MainThreadConsole : MainThreadConsoleShape {
      */
     override fun logExtensionHostMessage(msg: Map<String, Any>) {
         val type = msg["type"]
-        val severity = msg["severity"]
+        val severity = msg["severity"] as? String ?: "info"
         val arguments = msg["arguments"]?.let { args ->
             if (args is List<*>) {
                 args.joinToString(", ") { it.toString() }
@@ -56,16 +57,22 @@ class MainThreadConsole : MainThreadConsoleShape {
             }
         } ?: return
 
+        // Check if this log level should be logged based on current debug mode
+        if (!LogLevelConfig.shouldLog(severity)) {
+            return
+        }
+
         try {
             when (severity) {
-//                "log", "info" -> logger.info("[Extension Host] $arguments")
-                "warn" -> logger.warn("[Extension Host] $arguments")
-                "error" -> logger.warn("[Extension Host] ERROR: $arguments")
-//                "debug" -> logger.debug("[Extension Host] $arguments")
-//                else -> logger.info("[Extension Host] $arguments")
+                "log", "info" -> logger.info("[Extension Host] $arguments")
+                "warn" -> logger.warn("[Host] $arguments")
+                "error" -> logger.warn("[ERROR]: $arguments")
+                "debug" -> logger.debug("[Host] $arguments")
+                else -> logger.info("[Host] $arguments")
             }
         } catch (e: Exception) {
-            logger.error("Failed to process extension host log message", e)
+            val errorMsg = "Failed to process extension host log message"
+            logger.error(errorMsg, e) // IntelliJ logger handles both file and terminal
         }
     }
 

+ 31 - 12
jetbrains/plugin/src/main/kotlin/ai/kilocode/jetbrains/core/ExtensionSocketServer.kt

@@ -90,33 +90,52 @@ class ExtensionSocketServer() : ISocketServer {
         if (!isRunning) {
             return
         }
-        
-        isRunning = false
+
         logger.info("Stopping socket server")
-        
-        // Close all client managers
-        clientManagers.forEach { (_, manager) ->
+        isRunning = false
+
+        // First, interrupt the server thread to stop accepting new connections
+        serverThread?.interrupt()
+
+        // Wait for the server thread to finish
+        try {
+            serverThread?.join(5000) // Wait up to 5 seconds
+        } catch (e: InterruptedException) {
+            logger.warn("Interrupted while waiting for server thread to finish")
+            Thread.currentThread().interrupt()
+        }
+
+        // Close all client managers and wait for them to finish
+        clientManagers.forEach { (socket, manager) ->
             try {
+                logger.info("Disposing client manager for socket: ${socket.inetAddress}")
                 manager.dispose()
+                logger.info("Client manager disposed for socket: ${socket.inetAddress}")
             } catch (e: Exception) {
                 logger.warn("Failed to dispose client manager", e)
             }
         }
         clientManagers.clear()
-        
-        // Close the server
+
+        // Wait a bit for all client connections to be properly closed
+        try {
+            Thread.sleep(1000)
+        } catch (e: InterruptedException) {
+            Thread.currentThread().interrupt()
+        }
+
+        // Close the server socket
         try {
             serverSocket?.close()
         } catch (e: IOException) {
             logger.warn("Failed to close server socket", e)
         }
-        
-        // Interrupt the server thread
-        serverThread?.interrupt()
+
+        // Clean up references
         serverThread = null
         serverSocket = null
-        
-        logger.info("Socket server stopped")
+
+        logger.info("Socket server stopped completely")
     }
     
     /**

+ 35 - 9
jetbrains/plugin/src/main/kotlin/ai/kilocode/jetbrains/core/ExtensionUnixDomainSocketServer.kt

@@ -71,33 +71,59 @@ class ExtensionUnixDomainSocketServer : ISocketServer {
     // Stop UDS server, release resources
     override fun stop() {
         if (!isRunning) return
-        isRunning = false
         logger.info("Stopping UDS socket server")
-        // Close all client connections
-        clientManagers.forEach { (_, manager) ->
+        isRunning = false
+
+        // First, interrupt the server thread to stop accepting new connections
+        serverThread?.interrupt()
+
+        // Wait for the server thread to finish
+        try {
+            serverThread?.join(5000) // Wait up to 5 seconds
+        } catch (e: InterruptedException) {
+            logger.warn("[UDS] Interrupted while waiting for server thread to finish")
+            Thread.currentThread().interrupt()
+        }
+
+        // Close all client connections and wait for them to finish
+        clientManagers.forEach { (channel, manager) ->
             try {
+                logger.info("[UDS] Disposing client manager for channel")
                 manager.dispose()
+                logger.info("[UDS] Client manager disposed")
             } catch (e: Exception) {
-                logger.warn("Failed to dispose client manager", e)
+                logger.warn("[UDS] Failed to dispose client manager", e)
             }
         }
         clientManagers.clear()
+
+        // Wait a bit for all client connections to be properly closed
+        try {
+            Thread.sleep(1000)
+        } catch (e: InterruptedException) {
+            Thread.currentThread().interrupt()
+        }
+
+        // Close the server channel
         try {
             udsServerChannel?.close()
         } catch (e: Exception) {
-            logger.warn("Failed to close UDS server channel", e)
+            logger.warn("[UDS] Failed to close UDS server channel", e)
         }
+
+        // Delete the socket file
         try {
             udsSocketPath?.let { Files.deleteIfExists(it) }
         } catch (e: Exception) {
-            logger.warn("Failed to delete UDS socket file", e)
+            logger.warn("[UDS] Failed to delete UDS socket file", e)
         }
-        // Thread and channel cleanup
-        serverThread?.interrupt()
+
+        // Clean up references
         serverThread = null
         udsServerChannel = null
         udsSocketPath = null
-        logger.info("UDS socket server stopped")
+
+        logger.info("UDS socket server stopped completely")
     }
 
     override fun isRunning(): Boolean = isRunning

+ 30 - 7
jetbrains/plugin/src/main/kotlin/ai/kilocode/jetbrains/ipc/NodeSocket.kt

@@ -100,8 +100,14 @@ class NodeSocket : ISocket {
                         }
                     } catch (e: IOException) {
                         if (!isDisposed.get()) {
-                            // Only report errors when socket is not actively closed
-                            logger.error("Socket[$debugLabel] IO exception occurred while reading data", e)
+                            // Check if this is a connection reset during project switching
+                            val isConnectionReset = e.message?.contains("Connection reset") == true
+                            if (isConnectionReset) {
+                                logger.info("Socket[$debugLabel] Connection reset detected, likely due to project switching")
+                            } else {
+                                // Only report non-connection-reset errors when socket is not actively closed
+                                logger.error("Socket[$debugLabel] IO exception occurred while reading data", e)
+                            }
                             handleSocketError(e)
                         }
                         break
@@ -305,12 +311,31 @@ class NodeSocket : ISocket {
         traceSocketEvent(SocketDiagnosticsEventType.CLOSE)
         logger.info("Socket[$debugLabel] Releasing resources")
 
-        // Clean up listeners
+        // Clean up listeners first to prevent new events
         dataListeners.clear()
         closeListeners.clear()
         endListeners.clear()
 
-        // Close Socket
+        // Interrupt threads before closing socket to prevent race conditions
+        receiveThread?.interrupt()
+        endTimeoutHandle?.interrupt()
+
+        // Wait for threads to finish
+        try {
+            receiveThread?.join(2000) // Wait up to 2 seconds for receive thread
+        } catch (e: InterruptedException) {
+            logger.warn("Socket[$debugLabel] Interrupted while waiting for receive thread")
+            Thread.currentThread().interrupt()
+        }
+
+        try {
+            endTimeoutHandle?.join(1000) // Wait up to 1 second for timeout thread
+        } catch (e: InterruptedException) {
+            logger.warn("Socket[$debugLabel] Interrupted while waiting for timeout thread")
+            Thread.currentThread().interrupt()
+        }
+
+        // Close Socket after threads are stopped
         try {
             if (!isClosed()) {
                 closeAction()
@@ -319,10 +344,8 @@ class NodeSocket : ISocket {
             logger.warn("Socket[$debugLabel] Exception occurred while closing Socket during resource release", e)
         }
 
-        // Interrupt threads
-        receiveThread?.interrupt()
+        // Clean up thread references
         receiveThread = null
-        endTimeoutHandle?.interrupt()
         endTimeoutHandle = null
 
         logger.info("Socket[$debugLabel] Resource release completed")

+ 1 - 1
jetbrains/plugin/src/main/kotlin/ai/kilocode/jetbrains/ipc/proxy/RPCProtocol.kt

@@ -555,7 +555,7 @@ class RPCProtocol(
         args: List<Any?>,
         usesCancellationToken: Boolean
     ) {
-        LOG.info("receiveRequest:$req.$rpcId.$method()")
+        // LOG.info("receiveRequest:$req.$rpcId.$method()") // Removed: too noisy
         logger?.logIncoming(
             msgLength,
             req,

+ 75 - 34
jetbrains/plugin/src/main/kotlin/ai/kilocode/jetbrains/plugin/SystemObjectProvider.kt

@@ -5,65 +5,106 @@
 package ai.kilocode.jetbrains.plugin
 
 import com.intellij.openapi.application.ApplicationManager
+import com.intellij.openapi.components.Service
 import com.intellij.openapi.diagnostic.Logger
 import com.intellij.openapi.project.Project
+import com.intellij.openapi.Disposable
 import java.util.concurrent.ConcurrentHashMap
 
 /**
  * System Object Provider
  * Provides unified access to IDEA system objects
+ * Now project-scoped to prevent state sharing between projects
  */
-object SystemObjectProvider {
+@Service(Service.Level.PROJECT)
+class SystemObjectProvider(private val project: Project) : Disposable {
     private val logger = Logger.getInstance(SystemObjectProvider::class.java)
-    
-        // Mapping for storing system objects
-        private val systemObjects = ConcurrentHashMap<String, Any>()
 
-    
-        /**
-         * System object keys
-         */
+    // Mapping for storing system objects per project
+    private val systemObjects = ConcurrentHashMap<String, Any>()
+
+    /**
+     * System object keys
+     */
     object Keys {
         const val APPLICATION = "application"
-            // More system object keys can be added
+        const val PLUGIN_SERVICE = "pluginService"
+        // More system object keys can be added
     }
-    
-        /**
-         * Initialize the system object provider
-         * @param project current project
-         */
+
+    /**
+     * Initialize the system object provider
+     * @param project current project
+     */
     fun initialize(project: Project) {
-        logger.info("Initializing SystemObjectProvider with project: ${project.name}")
+        logger.info("Initializing SystemObjectProvider for project: ${project.name}")
 
+        // Register application-level objects
         register(Keys.APPLICATION, ApplicationManager.getApplication())
     }
-    
-        /**
-         * Register a system object
-         * @param key object key
-         * @param obj object instance
-         */
+
+    /**
+     * Register a system object
+     * @param key object key
+     * @param obj object instance
+     */
     fun register(key: String, obj: Any) {
         systemObjects[key] = obj
-        logger.debug("Registered system object: $key")
+        logger.debug("Registered system object for project ${project.name}: $key")
     }
-    
-        /**
-         * Get a system object
-         * @param key object key
-         * @return object instance or null
-         */
+
+    /**
+     * Get a system object
+     * @param key object key
+     * @return object instance or null
+     */
     @Suppress("UNCHECKED_CAST")
     fun <T> get(key: String): T? {
         return systemObjects[key] as? T
     }
-    
 
+    /**
+     * Remove a system object
+     * @param key object key
+     */
+    fun remove(key: String) {
+        systemObjects.remove(key)
+        logger.debug("Removed system object for project ${project.name}: $key")
+    }
+
+    /**
+     * Check if a system object exists
+     * @param key object key
+     * @return true if exists, false otherwise
+     */
+    fun has(key: String): Boolean {
+        return systemObjects.containsKey(key)
+    }
+
+    /**
+     * Get all registered keys
+     * @return set of registered keys
+     */
+    fun getKeys(): Set<String> {
+        return systemObjects.keys.toSet()
+    }
+
+    /**
+     * Clean up resources for this project
+     */
+    override fun dispose() {
+        logger.info("Disposing SystemObjectProvider for project: ${project.name}")
+        systemObjects.clear()
+    }
+
+    companion object {
         /**
-         * Clean up resources
+         * Get SystemObjectProvider instance for a project
+         * @param project the project
+         * @return SystemObjectProvider instance
          */
-    fun dispose() {
-        logger.info("Disposing SystemObjectProvider")
-        systemObjects.clear()
+        fun getInstance(project: Project): SystemObjectProvider {
+            return project.getService(SystemObjectProvider::class.java)
+        }
     }
-} 
+}

+ 113 - 27
jetbrains/plugin/src/main/kotlin/ai/kilocode/jetbrains/plugin/WecoderPlugin.kt

@@ -27,6 +27,9 @@ import com.intellij.openapi.extensions.PluginId
 import com.intellij.openapi.util.SystemInfo
 import com.intellij.ui.jcef.JBCefApp
 import com.intellij.openapi.application.ApplicationInfo
+import com.intellij.openapi.application.ApplicationManager
+import com.intellij.openapi.project.ProjectManager
+import com.intellij.openapi.project.ProjectManagerListener
 import ai.kilocode.jetbrains.core.*
 import ai.kilocode.jetbrains.util.ExtensionUtils
 import ai.kilocode.jetbrains.util.PluginConstants
@@ -41,6 +44,53 @@ class WecoderPlugin : StartupActivity.DumbAware {
     companion object {
         private val LOG = Logger.getInstance(WecoderPlugin::class.java)
 
+        // Project manager listener for handling project lifecycle events
+        private val projectManagerListener = object : ProjectManagerListener {
+            override fun projectOpened(project: Project) {
+                LOG.info("Project opened: ${project.name}")
+                // Initialize plugin service for newly opened project
+                try {
+                    val pluginService = getInstance(project)
+                    pluginService.initialize(project)
+                } catch (e: Exception) {
+                    LOG.error("Failed to initialize plugin for opened project: ${project.name}", e)
+                }
+            }
+
+            override fun projectClosed(project: Project) {
+                LOG.info("Project closed: ${project.name}")
+                // Clean up resources for closed project
+                try {
+                    val pluginService = getInstance(project)
+                    pluginService.dispose()
+                } catch (e: Exception) {
+                    LOG.error("Failed to dispose plugin for closed project: ${project.name}", e)
+                }
+            }
+
+            override fun projectClosing(project: Project) {
+                LOG.info("Project closing: ${project.name}")
+                // Perform any pre-close cleanup
+                try {
+                    // Notify WebViewManager about impending project close
+                    project.getService(WebViewManager::class.java).onProjectSwitch()
+                } catch (e: Exception) {
+                    LOG.error("Failed to handle project closing for: ${project.name}", e)
+                }
+            }
+        }
+
+        init {
+            // Register the project manager listener
+            try {
+                val messageBus = ApplicationManager.getApplication().messageBus
+                messageBus.connect().subscribe(ProjectManager.TOPIC, projectManagerListener)
+                LOG.info("Project manager listener registered successfully")
+            } catch (e: Exception) {
+                LOG.error("Failed to register project manager listener", e)
+            }
+        }
+
         /**
          * Get plugin service instance
          */
@@ -87,7 +137,7 @@ class WecoderPlugin : StartupActivity.DumbAware {
             Disposer.register(project, Disposable {
                 LOG.info("Disposing Kilo Code plugin for project: ${project.name}")
                 pluginService.dispose()
-                SystemObjectProvider.dispose()
+                // SystemObjectProvider is now project-scoped and will be disposed automatically
             })
 
             LOG.info("Kilo Code plugin initialized successfully for project: ${project.name}")
@@ -134,7 +184,7 @@ class WecoderPluginService(private var currentProject: Project) : Disposable {
     private var isInitialized = false
     
     // Plugin initialization complete flag
-    private val initializationComplete = CompletableFuture<Boolean>()
+    private var initializationComplete = CompletableFuture<Boolean>()
     
     // Coroutine scope
     private val coroutineScope = CoroutineScope(Dispatchers.IO + SupervisorJob())
@@ -207,22 +257,36 @@ class WecoderPluginService(private var currentProject: Project) : Disposable {
      * Initialize plugin service
      */
     fun initialize(project: Project) {
-        // DEBUG_MODE is no longer set directly in code, now read from config file
-        if (isInitialized) {
-            LOG.info("WecoderPluginService already initialized")
+        // Check if already initialized for the same project
+        if (isInitialized && this.currentProject == project) {
+            LOG.info("WecoderPluginService already initialized for project: ${project.name}")
             return
         }
-        
-        LOG.info("Initializing WecoderPluginService, debug mode: $DEBUG_TYPE")
+
+        // If initialized for a different project, clean up first
+        if (isInitialized && this.currentProject != project) {
+            LOG.info("Switching projects from ${this.currentProject?.name} to ${project.name}, cleaning up previous state")
+
+            // Notify WebViewManager about project switch
+            this.currentProject?.getService(WebViewManager::class.java)?.onProjectSwitch()
+
+            cleanup()
+            isInitialized = false
+            initializationComplete = CompletableFuture<Boolean>() // Reset completion flag
+        }
+
+        LOG.info("Initializing WecoderPluginService for project: ${project.name}, debug mode: $DEBUG_TYPE")
+
         // Initialize system object provider
-        SystemObjectProvider.initialize(project)
+        var systemObjectProvider = project.getService(SystemObjectProvider::class.java)
+        systemObjectProvider.initialize(project)
         this.currentProject = project
         socketServer.project = project
         udsSocketServer.project = project
-        
+
         // Register to system object provider
-        SystemObjectProvider.register("pluginService", this)
-        
+        systemObjectProvider.register("pluginService", this)
+
         // Start initialization in background thread
         coroutineScope.launch {
             try {
@@ -233,33 +297,33 @@ class WecoderPluginService(private var currentProject: Project) : Disposable {
                 // Initialize service registration
                 project.getService(ServiceProxyRegistry::class.java).initialize()
 //                ServiceProxyRegistry.getInstance().initialize()
-                
+
                 if (DEBUG_TYPE == ai.kilocode.jetbrains.plugin.DEBUG_MODE.ALL) {
                     // Debug mode: directly connect to extension process in debug
                     LOG.info("Running in debug mode: ${DEBUG_TYPE}, will directly connect to $DEBUG_HOST:$DEBUG_PORT")
-                    
+
                     // connet to debug port
                     socketServer.connectToDebugHost(DEBUG_HOST, DEBUG_PORT)
-                    
+
                     // Initialization successful
                     isInitialized = true
                     initializationComplete.complete(true)
-                    LOG.info("Debug mode connection successful, WecoderPluginService initialized")
+                    LOG.info("Debug mode connection successful, WecoderPluginService initialized for project: ${project.name}")
                 } else {
                     // Normal mode: start Socket server and extension process
                     // 1. Start Socket server according to system, use UDS except on Windows
                     val server: ISocketServer = if (SystemInfo.isWindows) socketServer else udsSocketServer
                     val portOrPath = server.start(projectPath)
                     if (!ExtensionUtils.isValidPortOrPath(portOrPath)) {
-                        LOG.error("Failed to start socket server")
+                        LOG.error("Failed to start socket server for project: ${project.name}")
                         initializationComplete.complete(false)
                         return@launch
                     }
 
-                    LOG.info("Socket server started on: $portOrPath")
+                    LOG.info("Socket server started on: $portOrPath for project: ${project.name}")
                     // 2. Start extension process
                     if (!processManager.start(portOrPath)) {
-                        LOG.error("Failed to start extension process")
+                        LOG.error("Failed to start extension process for project: ${project.name}")
                         server.stop()
                         initializationComplete.complete(false)
                         return@launch
@@ -267,10 +331,10 @@ class WecoderPluginService(private var currentProject: Project) : Disposable {
                     // Initialization successful
                     isInitialized = true
                     initializationComplete.complete(true)
-                    LOG.info("WecoderPluginService initialization completed")
+                    LOG.info("WecoderPluginService initialization completed for project: ${project.name}")
                 }
             } catch (e: Exception) {
-                LOG.error("Error during WecoderPluginService initialization", e)
+                LOG.error("Error during WecoderPluginService initialization for project: ${project.name}", e)
                 cleanup()
                 initializationComplete.complete(false)
             }
@@ -331,28 +395,50 @@ class WecoderPluginService(private var currentProject: Project) : Disposable {
      * Clean up resources
      */
     private fun cleanup() {
+        LOG.info("Starting cleanup for project: ${currentProject?.name}")
+
+        // First, stop the extension process to prevent new connections
         try {
-            // Stop extension process, only needed in non-debug mode
             if (DEBUG_TYPE == ai.kilocode.jetbrains.plugin.DEBUG_MODE.NONE) {
+                LOG.info("Stopping extension process")
                 processManager.stop()
             }
         } catch (e: Exception) {
             LOG.error("Error stopping process manager", e)
         }
-        
+
+        // Wait a bit for the process to fully stop
         try {
-            // Stop Socket server
+            Thread.sleep(500)
+        } catch (e: InterruptedException) {
+            Thread.currentThread().interrupt()
+        }
+
+        // Then stop socket servers - this will close all client connections
+        try {
+            LOG.info("Stopping socket servers")
             socketServer.stop()
             udsSocketServer.stop()
         } catch (e: Exception) {
             LOG.error("Error stopping socket server", e)
         }
 
-        // Unregister workspace file change listener
-        currentProject.getService(WorkspaceFileChangeManager::class.java).dispose()
-//        WorkspaceFileChangeManager.disposeInstance()
-        
+        // Wait for socket connections to be fully closed
+        try {
+            Thread.sleep(1000)
+        } catch (e: InterruptedException) {
+            Thread.currentThread().interrupt()
+        }
+
+        // Finally, clean up workspace file change listener
+        try {
+            currentProject?.getService(WorkspaceFileChangeManager::class.java)?.dispose()
+        } catch (e: Exception) {
+            LOG.error("Error disposing workspace file change manager", e)
+        }
+
         isInitialized = false
+        LOG.info("Cleanup completed for project: ${currentProject?.name}")
     }
     
     /**

+ 31 - 9
jetbrains/plugin/src/main/kotlin/ai/kilocode/jetbrains/terminal/WeCoderTerminalCustomizer.kt

@@ -161,18 +161,40 @@ class WeCoderTerminalCustomizer : LocalTerminalCustomizer() {
   }
 
   /**
-   * Inject VSCode integration script for Zsh
+   * Inject VSCode integration script for Zsh (safe with JetBrains shell integration)
    */
-  private fun injectZshScript(command: Array<String>, envs: MutableMap<String, String>, scriptPath: String): Array<String> {
-    // Save user's original ZDOTDIR environment variable
-    val userZdotdir = envs["ZDOTDIR"]
-      ?: System.getenv("ZDOTDIR")
-      ?: System.getProperty("user.home")
-    
+  private fun injectZshScript(
+    command: Array<String>,
+    envs: MutableMap<String, String>,
+    scriptPath: String
+  ): Array<String> {
+    // 1) If JetBrains' built-in zsh shell integration is already in place, avoid modifying ZDOTDIR to prevent conflicts.
+    val jetbrainsZshDir = envs["JETBRAINS_INTELLIJ_ZSH_DIR"] ?: System.getenv("JETBRAINS_INTELLIJ_ZSH_DIR")
+    val shellExeName = File(command[0]).name
+    val looksLikeJbZsh = command[0].contains("/plugins/terminal/shell-integrations/zsh")
+
+    if (jetbrainsZshDir != null || looksLikeJbZsh) {
+      logger.info("🔒 Detected JetBrains Zsh integration (JETBRAINS_INTELLIJ_ZSH_DIR=$jetbrainsZshDir, looksLikeJbZsh=$looksLikeJbZsh). Skip overriding ZDOTDIR.")
+      // Still retain the user's original ZDOTDIR in the environment for on-demand use within scripts.
+      val userZdotdir = envs["ZDOTDIR"] ?: System.getenv("ZDOTDIR") ?: System.getProperty("user.home")
+      envs["USER_ZDOTDIR"] = userZdotdir
+      return command
+    }
+
+    // 2) Inject only when `scriptPath` appears to be a valid `ZDOTDIR` (at least containing `.zshrc`).
+    val dir = File(scriptPath)
+    val hasZshrc = File(dir, ".zshrc").exists()
+    if (!dir.isDirectory || !hasZshrc) {
+      logger.warn("🚫 Zsh script dir '$scriptPath' is invalid (dir=$dir, hasZshrc=$hasZshrc). Skip overriding ZDOTDIR.")
+      return command
+    }
+
+    // 3) Record and securely overwrite.
+    val userZdotdir = envs["ZDOTDIR"] ?: System.getenv("ZDOTDIR") ?: System.getProperty("user.home")
     envs["USER_ZDOTDIR"] = userZdotdir
     envs["ZDOTDIR"] = scriptPath
-    
-    logger.info("🔧 Saved original ZDOTDIR: $userZdotdir, set new ZDOTDIR: $scriptPath")
+
+    logger.info("🔧 Set ZDOTDIR to '$scriptPath' (saved original as USER_ZDOTDIR='$userZdotdir'), shell=$shellExeName")
     return command
   }
 

+ 2 - 2
jetbrains/plugin/src/main/kotlin/ai/kilocode/jetbrains/ui/RooToolWindowFactory.kt

@@ -99,7 +99,7 @@ class RooToolWindowFactory : ToolWindowFactory {
             val isLinuxArm = osName.lowercase().contains("linux") && (osArch.lowercase().contains("aarch64") || osArch.lowercase().contains("arm"))
             
             return buildString {
-                append("<html><body style='width: 300px;'>")
+                append("<html><body style='width: 300px; padding: 8px;'>")
                 append("<p>Kilo Code is initializing...")
                 append("<h3>System Information</h3>")
                 append("<table>")
@@ -124,7 +124,7 @@ class RooToolWindowFactory : ToolWindowFactory {
                     append("<div style='background-color: #f8d7da; border: 1px solid #f5c6cb; padding: 10px; border-radius: 4px; color: #721c24;'>")
                     append("<b>⚠️ JCEF Not Supported</b><br>")
                     append("Your IDE runtime does not support JCEF. Please use a JCEF-enabled runtime.<br>")
-                    append("See known issues doc for more information.")
+                    append("<a href='https://kilocode.ai/docs/jetbrains-troubleshooting' target='_blank' style='color: #721c24; text-decoration: underline;'>See JetBrains docs for how to enable JCEF in your IDE</a>")
                     append("</div>")
                     append("<br>")
                 }

+ 74 - 0
jetbrains/plugin/src/main/kotlin/ai/kilocode/jetbrains/util/LogLevelConfig.kt

@@ -0,0 +1,74 @@
+// SPDX-FileCopyrightText: 2025 Weibo, Inc.
+//
+// SPDX-License-Identifier: Apache-2.0
+
+package ai.kilocode.jetbrains.logging
+
+import ai.kilocode.jetbrains.plugin.DEBUG_MODE
+import ai.kilocode.jetbrains.plugin.WecoderPluginService
+
+/**
+ * Log level enumeration matching console log severity levels
+ */
+enum class LogLevel(val value: Int) {
+    DEBUG(0),
+    LOG(1),
+    INFO(2),
+    WARN(3),
+    ERROR(4);
+    
+    companion object {
+        fun fromString(level: String): LogLevel {
+            return when (level.lowercase()) {
+                "debug" -> DEBUG
+                "log" -> LOG
+                "info" -> INFO
+                "warn" -> WARN
+                "error" -> ERROR
+                else -> INFO
+            }
+        }
+    }
+}
+
+/**
+ * Configuration for dynamic log level filtering based on debug mode
+ */
+object LogLevelConfig {
+    
+    /**
+     * Get the minimum log level that should be logged based on current debug mode
+     * - DEBUG/ALL mode: Show all logs (DEBUG and above)
+     * - Production mode: Show only warnings and errors (WARN and above)
+     */
+    fun getMinimumLogLevel(): LogLevel {
+        return when (WecoderPluginService.getDebugMode()) {
+            // Debug mode: show all log levels including debug, log, info
+            DEBUG_MODE.ALL, DEBUG_MODE.IDEA -> { 
+                LogLevel.DEBUG
+            }
+            // Production mode: only show warnings and errors
+            DEBUG_MODE.NONE -> {
+                LogLevel.WARN 
+            }
+        }
+    }
+    
+    /**
+     * Check if a log level should be logged based on current configuration
+     * @param level The log level to check
+     * @return true if the level should be logged, false otherwise
+     */
+    fun shouldLog(level: LogLevel): Boolean {
+        return level.value >= getMinimumLogLevel().value
+    }
+    
+    /**
+     * Check if a log level should be logged based on current configuration
+     * @param levelString The log level string to check
+     * @return true if the level should be logged, false otherwise
+     */
+    fun shouldLog(levelString: String): Boolean {
+        return shouldLog(LogLevel.fromString(levelString))
+    }
+}

+ 156 - 94
jetbrains/plugin/src/main/kotlin/ai/kilocode/jetbrains/webview/WebViewManager.kt

@@ -67,20 +67,23 @@ class WebViewManager(var project: Project) : Disposable, ThemeChangeListener {
     // Latest created WebView instance
     @Volatile
     private var latestWebView: WebViewInstance? = null
-    
+
     // Store WebView creation callbacks
     private val creationCallbacks = mutableListOf<WebViewCreationCallback>()
 
     // Resource root directory path
     @Volatile
     private var resourceRootDir: Path? = null
-    
+
     // Current theme configuration
     private var currentThemeConfig: JsonObject? = null
-    
+
     // Current theme type
     private var isDarkTheme: Boolean = true
-    
+
+    // Current body theme class
+    private var bodyThemeClass: String = "vscode-dark"
+
     // Prevent repeated dispose
     private var isDisposed = false
     private var themeInitialized = false
@@ -91,41 +94,57 @@ class WebViewManager(var project: Project) : Disposable, ThemeChangeListener {
      */
     fun initializeThemeManager(resourceRoot: String) {
         if (isDisposed or themeInitialized) return
-        
+
         logger.info("Initialize theme manager")
         val themeManager = ThemeManager.getInstance()
         themeManager.initialize(resourceRoot)
         themeManager.addThemeChangeListener(this)
         themeInitialized = true
     }
-    
+
     /**
      * Implement ThemeChangeListener interface, handle theme change events
      */
     override fun onThemeChanged(themeConfig: JsonObject, isDarkTheme: Boolean) {
         logger.info("Received theme change event, isDarkTheme: $isDarkTheme, config: ${themeConfig.size()}")
         this.currentThemeConfig = themeConfig
+        this.bodyThemeClass = if (isDarkTheme) "vscode-dark" else "vscode-light"
         this.isDarkTheme = isDarkTheme
-        
+
         // Send theme config to all WebView instances
         sendThemeConfigToWebViews(themeConfig)
     }
-    
+
     /**
      * Send theme config to all WebView instances
      */
     private fun sendThemeConfigToWebViews(themeConfig: JsonObject) {
         logger.info("Send theme config to WebView")
-        
+
 //        getAllWebViews().forEach { webView ->
             try {
-                getLatestWebView()?.sendThemeConfigToWebView(themeConfig)
+                getLatestWebView()?.sendThemeConfigToWebView(themeConfig, this.bodyThemeClass)
             } catch (e: Exception) {
                 logger.error("Failed to send theme config to WebView", e)
             }
 //        }
     }
-    
+
+    /**
+     * Dispose the latest WebView instance
+     */
+    private fun disposeLatestWebView() {
+        latestWebView?.let { webView ->
+            try {
+                logger.info("Disposing latest WebView instance: ${webView.viewType}/${webView.viewId}")
+                webView.dispose()
+            } catch (e: Exception) {
+                logger.error("Failed to dispose latest WebView", e)
+            }
+        }
+        latestWebView = null
+    }
+
     /**
      * Save HTML content to resource directory
      * @param html HTML content
@@ -137,9 +156,9 @@ class WebViewManager(var project: Project) : Disposable, ThemeChangeListener {
             logger.warn("Resource root directory does not exist, cannot save HTML content")
             throw IOException("Resource root directory does not exist")
         }
-        
+
         val filePath = resourceRootDir?.resolve(filename)
-        
+
         try {
             if (filePath != null) {
                 logger.info("HTML content saved to: $filePath")
@@ -152,7 +171,7 @@ class WebViewManager(var project: Project) : Disposable, ThemeChangeListener {
             throw e
         }
     }
-    
+
     /**
      * Register WebView creation callback
      * @param callback Callback object
@@ -161,7 +180,7 @@ class WebViewManager(var project: Project) : Disposable, ThemeChangeListener {
     fun addCreationCallback(callback: WebViewCreationCallback, disposable: Disposable? = null) {
         synchronized(creationCallbacks) {
             creationCallbacks.add(callback)
-            
+
             // If Disposable is provided, automatically remove callback when disposed
             if (disposable != null) {
                 Disposer.register(disposable, Disposable {
@@ -169,7 +188,7 @@ class WebViewManager(var project: Project) : Disposable, ThemeChangeListener {
                 })
             }
         }
-        
+
         // If there is already a latest created WebView, notify immediately
         latestWebView?.let { webview ->
             ApplicationManager.getApplication().invokeLater {
@@ -177,7 +196,7 @@ class WebViewManager(var project: Project) : Disposable, ThemeChangeListener {
             }
         }
     }
-    
+
     /**
      * Remove WebView creation callback
      * @param callback Callback object to remove
@@ -187,7 +206,7 @@ class WebViewManager(var project: Project) : Disposable, ThemeChangeListener {
             creationCallbacks.remove(callback)
         }
     }
-    
+
     /**
      * Notify all callbacks that WebView has been created
      * @param instance Created WebView instance
@@ -196,7 +215,7 @@ class WebViewManager(var project: Project) : Disposable, ThemeChangeListener {
         val callbacks = synchronized(creationCallbacks) {
             creationCallbacks.toList() // Create a copy to avoid concurrent modification
         }
-        
+
         // Safely call callbacks in UI thread
         ApplicationManager.getApplication().invokeLater {
             callbacks.forEach { callback ->
@@ -208,33 +227,36 @@ class WebViewManager(var project: Project) : Disposable, ThemeChangeListener {
             }
         }
     }
-    
+
     /**
      * Register WebView provider and create WebView instance
      */
     fun registerProvider(data: WebviewViewProviderData) {
-        logger.info("Register WebView provider and create WebView instance: ${data.viewType}")
+        logger.info("Register WebView provider and create WebView instance: ${data.viewType} for project: ${project.name}")
         val extension = data.extension
-        
+
+        // Clean up any existing WebView for this project before creating a new one
+        disposeLatestWebView()
+
         // Get location info from extension and set resource root directory
         try {
             @Suppress("UNCHECKED_CAST")
             val location = extension?.get("location") as? Map<String, Any?>
             val fsPath = location?.get("fsPath") as? String
-            
+
             if (fsPath != null) {
                 // Set resource root directory
                 val path = Paths.get(fsPath)
                 logger.info("Get resource directory path from extension: $path")
-                
+
                 // Ensure the resource directory exists
                 if (!path.exists()) {
                     path.createDirectories()
                 }
-                
+
                  // Update resource root directory
                 resourceRootDir = path
-                
+
                 // Initialize theme manager
                 initializeThemeManager(fsPath)
 
@@ -253,7 +275,7 @@ class WebViewManager(var project: Project) : Disposable, ThemeChangeListener {
 
         val title = data.options["title"] as? String ?: data.viewType
         val state = data.options["state"] as? Map<String, Any?> ?: emptyMap()
-        
+
         val webview = WebViewInstance(data.viewType, viewId, title, state,project,data.extension)
         // DEBUG HERE!
         // webview.showDebugWindow()
@@ -264,13 +286,13 @@ class WebViewManager(var project: Project) : Disposable, ThemeChangeListener {
 
         // Set as the latest created WebView
         latestWebView = webview
-        
-        logger.info("Create WebView instance: viewType=${data.viewType}, viewId=$viewId")
+
+        logger.info("Create WebView instance: viewType=${data.viewType}, viewId=$viewId for project: ${project.name}")
 
         // Notify callback
         notifyWebViewCreated(webview)
     }
-    
+
     /**
          * Get the latest created WebView instance
          */
@@ -297,13 +319,13 @@ class WebViewManager(var project: Project) : Disposable, ThemeChangeListener {
                             const msgStr = JSON.stringify(message);
                             ${getLatestWebView()?.jsQuery?.inject("msgStr")}
                         };
-                        
+
                         // Inject VSCode API mock
                         globalThis.acquireVsCodeApi = (function() {
                             let acquired = false;
-                        
+
                             let state = JSON.parse('${encodedState}');
-                        
+
                             if (typeof window !== "undefined" && !window.receiveMessageFromPlugin) {
                                 console.log("VSCodeAPIWrapper: Setting up receiveMessageFromPlugin for IDEA plugin compatibility");
                                 window.receiveMessageFromPlugin = (message) => {
@@ -315,7 +337,7 @@ class WebViewManager(var project: Project) : Disposable, ThemeChangeListener {
                                     window.dispatchEvent(event);
                                 };
                             }
-                        
+
                             return () => {
                                 if (acquired) {
                                     throw new Error('An instance of the VS Code API has already been acquired');
@@ -337,21 +359,21 @@ class WebViewManager(var project: Project) : Disposable, ThemeChangeListener {
                                 });
                             };
                         })();
-                        
+
                         // Clean up references to window parent for security
                         delete window.parent;
                         delete window.top;
                         delete window.frameElement;
-                        
+
                         console.log("VSCode API mock injected");
                         """)
 
 
 
         logger.info("Received HTML update event: handle=${data.handle}, html length: ${data.htmlContent.length}")
-        
+
         val webView = getLatestWebView()
-        
+
         if (webView != null) {
             try {
                 // If HTTP server is running
@@ -380,7 +402,7 @@ class WebViewManager(var project: Project) : Disposable, ThemeChangeListener {
                     // Delay sending theme config to ensure HTML is loaded
                     ApplicationManager.getApplication().invokeLater {
                         try {
-                            webView.sendThemeConfigToWebView(currentThemeConfig!!)
+                            webView.sendThemeConfigToWebView(currentThemeConfig!!, this.bodyThemeClass)
                         } catch (e: Exception) {
                             logger.error("Failed to send theme config to WebView", e)
                         }
@@ -396,23 +418,46 @@ class WebViewManager(var project: Project) : Disposable, ThemeChangeListener {
         }
     }
 
-    
+
+    /**
+     * Handle project switching by cleaning up current state
+     */
+    fun onProjectSwitch() {
+        logger.info("Handling project switch for WebViewManager")
+
+        // Dispose current WebView
+        disposeLatestWebView()
+
+        // Reset theme initialization flag to allow re-initialization
+        themeInitialized = false
+
+        // Clear theme data
+        currentThemeConfig = null
+
+        // Clear resource directory reference
+        resourceRootDir = null
+
+        logger.info("Project switch handled, WebViewManager state reset")
+    }
+
     override fun dispose() {
         if (isDisposed) {
             logger.info("WebViewManager has already been disposed, ignoring repeated call")
             return
         }
         isDisposed = true
-        
-        logger.info("Releasing WebViewManager resources...")
+
+        logger.info("Releasing WebViewManager resources for project: ${project.name}")
 
         // Remove listener from theme manager
         try {
-            ThemeManager.getInstance().removeThemeChangeListener(this)
+            if (themeInitialized) {
+                ThemeManager.getInstance().removeThemeChangeListener(this)
+            }
         } catch (e: Exception) {
             logger.error("Failed to remove listener from theme manager", e)
         }
-        
+
         // Clean up resource directory
         try {
             // Only delete index.html file, keep other files
@@ -434,21 +479,18 @@ class WebViewManager(var project: Project) : Disposable, ThemeChangeListener {
             logger.error("Failed to clean up index.html file", e)
         }
 
-        try {
-            latestWebView?.dispose()
-        } catch (e: Exception) {
-            logger.error("Failed to release WebView resources", e)
-        }
-        
+        // Dispose WebView
+        disposeLatestWebView()
+
         // Reset theme data
         currentThemeConfig = null
-        
+
         // Clear callback list
         synchronized(creationCallbacks) {
             creationCallbacks.clear()
         }
-        
-        logger.info("WebViewManager released")
+
+        logger.info("WebViewManager released for project: ${project.name}")
     }
 
 
@@ -466,10 +508,10 @@ class WebViewInstance(
     val extension: Map<String, Any?>
 ) : Disposable {
     private val logger = Logger.getInstance(WebViewInstance::class.java)
-    
+
     // JCEF browser instance
     val browser = JBCefBrowser.createBuilder().setOffScreenRendering(true).build()
-    
+
     // WebView state
     private var isDisposed = false
 
@@ -479,16 +521,20 @@ class WebViewInstance(
     // JSON serialization
     private val gson = Gson()
 
+    // Body theme class (e.g., "vscode-dark" or "vscode-light")
+    private var bodyThemeClass: String = "vscode-dark"
+
     // Coroutine scope
     private val coroutineScope = CoroutineScope(SupervisorJob() + Dispatchers.IO)
 
     private var isPageLoaded = false
+    private var isInitialPageLoad = true
 
     private var currentThemeConfig: JsonObject? = null
-    
+
     // Callback for page load completion
     private var pageLoadCallback: (() -> Unit)? = null
-    
+
     init {
         setupJSBridge()
         // Enable resource loading interception
@@ -498,8 +544,9 @@ class WebViewInstance(
     /**
      * Send theme config to the specified WebView instance
      */
-    fun sendThemeConfigToWebView(themeConfig: JsonObject) {
+    fun sendThemeConfigToWebView(themeConfig: JsonObject, bodyThemeClass: String) {
         currentThemeConfig = themeConfig
+        this.bodyThemeClass = bodyThemeClass
         if(isDisposed or !isPageLoaded) {
             logger.warn("WebView has been disposed or not loaded, cannot send theme config:${isDisposed},${isPageLoaded}")
             return
@@ -514,7 +561,7 @@ class WebViewInstance(
     fun isPageLoaded(): Boolean {
         return isPageLoaded
     }
-    
+
     /**
      * Set callback for page load completion
      * @param callback Callback function to be called when page is loaded
@@ -522,7 +569,7 @@ class WebViewInstance(
     fun setPageLoadCallback(callback: (() -> Unit)?) {
         pageLoadCallback = callback
     }
-    
+
     private fun injectTheme() {
         if(currentThemeConfig == null) {
             return
@@ -542,15 +589,17 @@ class WebViewInstance(
                 if (cssContent != null) {
                     val injectThemeScript = """
                         (function() {
-                            console.log("Ready to inject CSS variables into WebView")
                             function injectCSSVariables() {
+                                if (window.__cssVariablesInjected) {
+                                    return;
+                                }
                                 if(document.documentElement) {
                                     // Convert cssContent to style attribute of html tag
                                     try {
                                         // Extract CSS variables (format: --name:value;)
                                         const cssLines = `$cssContent`.split('\n');
                                         const cssVariables = [];
-                                        
+
                                         // Process each line, extract CSS variable declarations
                                         for (const line of cssLines) {
                                             const trimmedLine = line.trim();
@@ -563,17 +612,25 @@ class WebViewInstance(
                                                 cssVariables.push(trimmedLine);
                                             }
                                         }
-                                        
+
                                         // Merge extracted CSS variables into style attribute string
                                         const styleAttrValue = cssVariables.join(' ');
-                                        
+
                                         // Set as style attribute of html tag
                                         document.documentElement.setAttribute('style', styleAttrValue);
                                         console.log("CSS variables set as style attribute of HTML tag");
+
+                                        // Add theme class to body element for styled-components compatibility
+                                        // Remove existing theme classes
+                                        document.body.classList.remove('vscode-dark', 'vscode-light');
+
+                                        // Add appropriate theme class based on current theme
+                                        document.body.classList.add('$bodyThemeClass');
+                                        console.log("Added theme class to body: $bodyThemeClass");
                                     } catch (error) {
-                                        console.error("Error processing CSS variables:", error);
+                                        console.error("Error processing CSS variables and theme classes:", error);
                                     }
-                                    
+
                                     // Keep original default style injection logic
                                     if(document.head) {
                                         // Inject default theme style into head, use id="_defaultStyles"
@@ -583,14 +640,14 @@ class WebViewInstance(
                                             defaultStylesElement.id = '_defaultStyles';
                                             document.head.appendChild(defaultStylesElement);
                                         }
-                                        
+
                                         // Add default_themes.css content
                                         defaultStylesElement.textContent = `
                                             html {
                                                 background: var(--vscode-sideBar-background);
                                                 scrollbar-color: var(--vscode-scrollbarSlider-background) var(--vscode-sideBar-background);
                                             }
-                                            
+
                                             body {
                                                 overscroll-behavior-x: none;
                                                 background-color: transparent;
@@ -601,24 +658,24 @@ class WebViewInstance(
                                                 margin: 0;
                                                 padding: 0 20px;
                                             }
-                                            
+
                                             img, video {
                                                 max-width: 100%;
                                                 max-height: 100%;
                                             }
-                                            
+
                                             a, a code {
                                                 color: var(--vscode-textLink-foreground);
                                             }
-                                            
+
                                             p > a {
                                                 text-decoration: var(--text-link-decoration);
                                             }
-                                            
+
                                             a:hover {
                                                 color: var(--vscode-textLink-activeForeground);
                                             }
-                                            
+
                                             a:focus,
                                             input:focus,
                                             select:focus,
@@ -626,7 +683,7 @@ class WebViewInstance(
                                                 outline: 1px solid -webkit-focus-ring-color;
                                                 outline-offset: -1px;
                                             }
-                                            
+
                                             code {
                                                 font-family: var(--monaco-monospace-font);
                                                 color: var(--vscode-textPreformat-foreground);
@@ -634,16 +691,16 @@ class WebViewInstance(
                                                 padding: 1px 3px;
                                                 border-radius: 4px;
                                             }
-                                            
+
                                             pre code {
                                                 padding: 0;
                                             }
-                                            
+
                                             blockquote {
                                                 background: var(--vscode-textBlockQuote-background);
                                                 border-color: var(--vscode-textBlockQuote-border);
                                             }
-                                            
+
                                             kbd {
                                                 background-color: var(--vscode-keybindingLabel-background);
                                                 color: var(--vscode-keybindingLabel-foreground);
@@ -656,16 +713,16 @@ class WebViewInstance(
                                                 vertical-align: middle;
                                                 padding: 1px 3px;
                                             }
-                                            
+
                                             ::-webkit-scrollbar {
                                                 width: 10px;
                                                 height: 10px;
                                             }
-                                            
+
                                             ::-webkit-scrollbar-corner {
                                                 background-color: var(--vscode-editor-background);
                                             }
-                                            
+
                                             ::-webkit-scrollbar-thumb {
                                                 background-color: var(--vscode-scrollbarSlider-background);
                                             }
@@ -683,6 +740,7 @@ class WebViewInstance(
                                             }
                                         `;
                                         console.log("Default style injected to id=_defaultStyles");
+                                        window.__cssVariablesInjected = true;
                                     }
                                 } else {
                                     // If html tag does not exist yet, wait for DOM to load and try again
@@ -801,7 +859,7 @@ class WebViewInstance(
                     return true
                 }
             }, browser.cefBrowser)
-            
+
             // Register load handler
             client.addLoadHandler(object : CefLoadHandlerAdapter() {
                 override fun onLoadingStateChange(
@@ -812,7 +870,7 @@ class WebViewInstance(
                 ) {
                     logger.info("WebView loading state changed: isLoading=$isLoading, canGoBack=$canGoBack, canGoForward=$canGoForward")
                 }
-                
+
                 override fun onLoadStart(
                     browser: CefBrowser?,
                     frame: CefFrame?,
@@ -820,8 +878,9 @@ class WebViewInstance(
                 ) {
                     logger.info("WebView started loading: ${frame?.url}, transition type: $transitionType")
                     isPageLoaded = false
+                    isInitialPageLoad = true
                 }
-                
+
                 override fun onLoadEnd(
                     browser: CefBrowser?,
                     frame: CefFrame?,
@@ -829,11 +888,14 @@ class WebViewInstance(
                 ) {
                     logger.info("WebView finished loading: ${frame?.url}, status code: $httpStatusCode")
                     isPageLoaded = true
-                    injectTheme()
-                    // Notify page load completion
-                    pageLoadCallback?.invoke()
+
+                    if (isInitialPageLoad) {
+                        injectTheme()
+                        pageLoadCallback?.invoke()
+                        isInitialPageLoad = false
+                    }
                 }
-                
+
                 override fun onLoadError(
                     browser: CefBrowser?,
                     frame: CefFrame?,
@@ -887,7 +949,7 @@ class WebViewInstance(
             logger.error("Failed to enable WebView resource interception", e)
         }
     }
-    
+
     /**
          * Load URL
          */
@@ -897,7 +959,7 @@ class WebViewInstance(
             browser.loadURL(url)
         }
     }
-    
+
     /**
          * Load HTML content
          */
@@ -911,7 +973,7 @@ class WebViewInstance(
             }
         }
     }
-    
+
     /**
          * Execute JavaScript
          */
@@ -921,7 +983,7 @@ class WebViewInstance(
             browser.cefBrowser.executeJavaScript(script, browser.cefBrowser.url, 0)
         }
     }
-    
+
     /**
          * Open developer tools
          */
@@ -939,7 +1001,7 @@ class WebViewInstance(
                 frame.add(browser.component)
                 frame.setSize(800, 600)
                 frame.isVisible = true
-                
+
                 // Optional: Add dev tools button
                 val toolbar = JPanel()
                 val devToolsButton = JButton("Open DevTools")
@@ -949,7 +1011,7 @@ class WebViewInstance(
             }
         }
     }
-    
+
     override fun dispose() {
         if (!isDisposed) {
             browser.dispose()
@@ -957,4 +1019,4 @@ class WebViewInstance(
             logger.info("WebView instance released: $viewType/$viewId")
         }
     }
-}
+}

+ 5 - 0
packages/cloud/src/CloudService.ts

@@ -248,6 +248,11 @@ export class CloudService extends EventEmitter<CloudServiceEvents> implements Di
 		return this.settingsService!.updateUserSettings(settings)
 	}
 
+	public isTaskSyncEnabled(): boolean {
+		this.ensureInitialized()
+		return this.settingsService!.isTaskSyncEnabled()
+	}
+
 	// TelemetryClient
 
 	public captureEvent(event: TelemetryEvent): void {

+ 15 - 0
packages/cloud/src/CloudSettingsService.ts

@@ -266,6 +266,21 @@ export class CloudSettingsService extends EventEmitter<SettingsServiceEvents> im
 		}
 	}
 
+	public isTaskSyncEnabled(): boolean {
+		// Org settings take precedence
+		if (this.authService.getStoredOrganizationId()) {
+			return this.settings?.cloudSettings?.recordTaskMessages ?? false
+		}
+
+		// User settings default to true if unspecified
+		const userSettings = this.userSettings
+		if (userSettings) {
+			return userSettings.settings.taskSyncEnabled ?? true
+		}
+
+		return false
+	}
+
 	private async removeSettings(): Promise<void> {
 		this.settings = undefined
 		this.userSettings = undefined

+ 7 - 0
packages/cloud/src/StaticSettingsService.ts

@@ -51,6 +51,7 @@ export class StaticSettingsService implements SettingsService {
 			},
 			settings: {
 				extensionBridgeEnabled: true,
+				taskSyncEnabled: true,
 			},
 			version: 1,
 		}
@@ -65,6 +66,7 @@ export class StaticSettingsService implements SettingsService {
 	public getUserSettingsConfig(): UserSettingsConfig {
 		return {
 			extensionBridgeEnabled: true,
+			taskSyncEnabled: true,
 		}
 	}
 
@@ -72,6 +74,11 @@ export class StaticSettingsService implements SettingsService {
 		throw new Error("User settings updates are not supported in static mode")
 	}
 
+	public isTaskSyncEnabled(): boolean {
+		// Static settings always enable task sync
+		return true
+	}
+
 	public dispose(): void {
 		// No resources to clean up for static settings.
 	}

+ 2 - 2
packages/cloud/src/TelemetryClient.ts

@@ -246,9 +246,9 @@ export class CloudTelemetryClient extends BaseTelemetryClient {
 			return false
 		}
 
-		// Only record message telemetry if a cloud account is present and explicitly configured to record messages
+		// Only record message telemetry if task sync is enabled
 		if (eventName === TelemetryEventName.TASK_MESSAGE) {
-			return this.settingsService.getSettings()?.cloudSettings?.recordTaskMessages || false
+			return this.settingsService.isTaskSyncEnabled()
 		}
 
 		// Other telemetry types are capturable at this point

+ 8 - 0
packages/cloud/src/__tests__/CloudService.test.ts

@@ -59,6 +59,7 @@ describe("CloudService", () => {
 		initialize: ReturnType<typeof vi.fn>
 		getSettings: ReturnType<typeof vi.fn>
 		getAllowList: ReturnType<typeof vi.fn>
+		isTaskSyncEnabled: ReturnType<typeof vi.fn>
 		dispose: ReturnType<typeof vi.fn>
 		on: ReturnType<typeof vi.fn>
 		off: ReturnType<typeof vi.fn>
@@ -130,6 +131,7 @@ describe("CloudService", () => {
 			initialize: vi.fn(),
 			getSettings: vi.fn(),
 			getAllowList: vi.fn(),
+			isTaskSyncEnabled: vi.fn().mockReturnValue(true),
 			dispose: vi.fn(),
 			on: vi.fn(),
 			off: vi.fn(),
@@ -343,6 +345,12 @@ describe("CloudService", () => {
 			cloudService.getAllowList()
 			expect(mockSettingsService.getAllowList).toHaveBeenCalled()
 		})
+
+		it("should delegate isTaskSyncEnabled to SettingsService", () => {
+			const result = cloudService.isTaskSyncEnabled()
+			expect(mockSettingsService.isTaskSyncEnabled).toHaveBeenCalled()
+			expect(result).toBe(true)
+		})
 	})
 
 	describe("error handling", () => {

+ 189 - 0
packages/cloud/src/__tests__/CloudSettingsService.test.ts

@@ -20,6 +20,7 @@ describe("CloudSettingsService", () => {
 		getSessionToken: ReturnType<typeof vi.fn>
 		hasActiveSession: ReturnType<typeof vi.fn>
 		on: ReturnType<typeof vi.fn>
+		getStoredOrganizationId: ReturnType<typeof vi.fn>
 	}
 	let mockRefreshTimer: {
 		start: ReturnType<typeof vi.fn>
@@ -63,6 +64,7 @@ describe("CloudSettingsService", () => {
 			getSessionToken: vi.fn(),
 			hasActiveSession: vi.fn().mockReturnValue(false),
 			on: vi.fn(),
+			getStoredOrganizationId: vi.fn().mockReturnValue(null),
 		}
 
 		mockRefreshTimer = {
@@ -532,4 +534,191 @@ describe("CloudSettingsService", () => {
 			expect(mockContext.globalState.update).toHaveBeenCalledWith("user-settings", undefined)
 		})
 	})
+
+	describe("isTaskSyncEnabled", () => {
+		beforeEach(async () => {
+			await cloudSettingsService.initialize()
+		})
+
+		it("should return true when org recordTaskMessages is true", () => {
+			// Set up mock settings with org recordTaskMessages = true
+			const mockSettings = {
+				version: 1,
+				cloudSettings: {
+					recordTaskMessages: true,
+				},
+				defaultSettings: {},
+				allowList: { allowAll: true, providers: {} },
+			}
+
+			// Mock that user has organization ID (indicating org settings should be used)
+			mockAuthService.getStoredOrganizationId.mockReturnValue("org-123")
+
+			// Use reflection to set private settings
+			;(cloudSettingsService as unknown as { settings: typeof mockSettings }).settings = mockSettings
+
+			expect(cloudSettingsService.isTaskSyncEnabled()).toBe(true)
+		})
+
+		it("should return false when org recordTaskMessages is false", () => {
+			// Set up mock settings with org recordTaskMessages = false
+			const mockSettings = {
+				version: 1,
+				cloudSettings: {
+					recordTaskMessages: false,
+				},
+				defaultSettings: {},
+				allowList: { allowAll: true, providers: {} },
+			}
+
+			// Mock that user has organization ID (indicating org settings should be used)
+			mockAuthService.getStoredOrganizationId.mockReturnValue("org-123")
+
+			// Use reflection to set private settings
+			;(cloudSettingsService as unknown as { settings: typeof mockSettings }).settings = mockSettings
+
+			expect(cloudSettingsService.isTaskSyncEnabled()).toBe(false)
+		})
+
+		it("should fall back to user taskSyncEnabled when org recordTaskMessages is undefined", () => {
+			// Set up mock settings with org recordTaskMessages undefined
+			const mockSettings = {
+				version: 1,
+				cloudSettings: {},
+				defaultSettings: {},
+				allowList: { allowAll: true, providers: {} },
+			}
+
+			const mockUserSettings = {
+				version: 1,
+				features: {},
+				settings: {
+					taskSyncEnabled: true,
+				},
+			}
+
+			// Mock that user has no organization ID (indicating user settings should be used)
+			mockAuthService.getStoredOrganizationId.mockReturnValue(null)
+
+			// Use reflection to set private settings
+			;(cloudSettingsService as unknown as { settings: typeof mockSettings }).settings = mockSettings
+			;(cloudSettingsService as unknown as { userSettings: typeof mockUserSettings }).userSettings =
+				mockUserSettings
+
+			expect(cloudSettingsService.isTaskSyncEnabled()).toBe(true)
+		})
+
+		it("should return false when user taskSyncEnabled is false", () => {
+			// Set up mock settings with org recordTaskMessages undefined
+			const mockSettings = {
+				version: 1,
+				cloudSettings: {},
+				defaultSettings: {},
+				allowList: { allowAll: true, providers: {} },
+			}
+
+			const mockUserSettings = {
+				version: 1,
+				features: {},
+				settings: {
+					taskSyncEnabled: false,
+				},
+			}
+
+			// Mock that user has no organization ID (indicating user settings should be used)
+			mockAuthService.getStoredOrganizationId.mockReturnValue(null)
+
+			// Use reflection to set private settings
+			;(cloudSettingsService as unknown as { settings: typeof mockSettings }).settings = mockSettings
+			;(cloudSettingsService as unknown as { userSettings: typeof mockUserSettings }).userSettings =
+				mockUserSettings
+
+			expect(cloudSettingsService.isTaskSyncEnabled()).toBe(false)
+		})
+
+		it("should return true when user taskSyncEnabled is undefined (default)", () => {
+			// Set up mock settings with org recordTaskMessages undefined
+			const mockSettings = {
+				version: 1,
+				cloudSettings: {},
+				defaultSettings: {},
+				allowList: { allowAll: true, providers: {} },
+			}
+
+			const mockUserSettings = {
+				version: 1,
+				features: {},
+				settings: {},
+			}
+
+			// Mock that user has no organization ID (indicating user settings should be used)
+			mockAuthService.getStoredOrganizationId.mockReturnValue(null)
+
+			// Use reflection to set private settings
+			;(cloudSettingsService as unknown as { settings: typeof mockSettings }).settings = mockSettings
+			;(cloudSettingsService as unknown as { userSettings: typeof mockUserSettings }).userSettings =
+				mockUserSettings
+
+			expect(cloudSettingsService.isTaskSyncEnabled()).toBe(true)
+		})
+
+		it("should return false when no settings are available", () => {
+			// Mock that user has no organization ID
+			mockAuthService.getStoredOrganizationId.mockReturnValue(null)
+
+			// Clear both settings
+			;(cloudSettingsService as unknown as { settings: undefined }).settings = undefined
+			;(cloudSettingsService as unknown as { userSettings: undefined }).userSettings = undefined
+
+			expect(cloudSettingsService.isTaskSyncEnabled()).toBe(false)
+		})
+
+		it("should return false when only org settings are available but cloudSettings is undefined", () => {
+			const mockSettings = {
+				version: 1,
+				defaultSettings: {},
+				allowList: { allowAll: true, providers: {} },
+			}
+
+			// Mock that user has organization ID (indicating org settings should be used)
+			mockAuthService.getStoredOrganizationId.mockReturnValue("org-123")
+
+			// Use reflection to set private settings
+			;(cloudSettingsService as unknown as { settings: typeof mockSettings }).settings = mockSettings
+			;(cloudSettingsService as unknown as { userSettings: undefined }).userSettings = undefined
+
+			expect(cloudSettingsService.isTaskSyncEnabled()).toBe(false)
+		})
+
+		it("should prioritize org settings over user settings", () => {
+			// Set up conflicting settings: org = false, user = true
+			const mockSettings = {
+				version: 1,
+				cloudSettings: {
+					recordTaskMessages: false,
+				},
+				defaultSettings: {},
+				allowList: { allowAll: true, providers: {} },
+			}
+
+			const mockUserSettings = {
+				version: 1,
+				features: {},
+				settings: {
+					taskSyncEnabled: true,
+				},
+			}
+
+			// Mock that user has organization ID (indicating org settings should be used)
+			mockAuthService.getStoredOrganizationId.mockReturnValue("org-123")
+
+			// Use reflection to set private settings
+			;(cloudSettingsService as unknown as { settings: typeof mockSettings }).settings = mockSettings
+			;(cloudSettingsService as unknown as { userSettings: typeof mockUserSettings }).userSettings =
+				mockUserSettings
+
+			// Should return false (org setting takes precedence)
+			expect(cloudSettingsService.isTaskSyncEnabled()).toBe(false)
+		})
+	})
 })

+ 23 - 0
packages/cloud/src/__tests__/StaticSettingsService.test.ts

@@ -98,5 +98,28 @@ describe("StaticSettingsService", () => {
 
 			expect(mockLog).not.toHaveBeenCalled()
 		})
+
+		describe("isTaskSyncEnabled", () => {
+			it("should always return true", () => {
+				const service = new StaticSettingsService(validBase64)
+				expect(service.isTaskSyncEnabled()).toBe(true)
+			})
+
+			it("should return true regardless of settings content", () => {
+				// Create settings with different content
+				const differentSettings = {
+					version: 2,
+					cloudSettings: {
+						recordTaskMessages: false,
+					},
+					defaultSettings: {},
+					allowList: { allowAll: false, providers: {} },
+				}
+				const differentBase64 = Buffer.from(JSON.stringify(differentSettings)).toString("base64")
+
+				const service = new StaticSettingsService(differentBase64)
+				expect(service.isTaskSyncEnabled()).toBe(true)
+			})
+		})
 	})
 })

+ 22 - 64
packages/cloud/src/__tests__/TelemetryClient.test.ts

@@ -9,7 +9,8 @@ import { CloudTelemetryClient as TelemetryClient } from "../TelemetryClient.js"
 const mockFetch = vi.fn()
 global.fetch = mockFetch as any
 
-describe("TelemetryClient", () => {
+// kilocode_change - skip these tests since we don't use this code
+describe.skip("TelemetryClient", () => {
 	const getPrivateProperty = <T>(instance: any, propertyName: string): T => {
 		return instance[propertyName]
 	}
@@ -35,6 +36,14 @@ describe("TelemetryClient", () => {
 					recordTaskMessages: true,
 				},
 			}),
+			getUserSettings: vi.fn().mockReturnValue({
+				features: {},
+				settings: {
+					taskSyncEnabled: true,
+				},
+				version: 1,
+			}),
+			isTaskSyncEnabled: vi.fn().mockReturnValue(true),
 		}
 
 		mockFetch.mockResolvedValue({
@@ -76,12 +85,8 @@ describe("TelemetryClient", () => {
 			expect(isEventCapturable(TelemetryEventName.TASK_CONVERSATION_MESSAGE)).toBe(false)
 		})
 
-		it("should return true for TASK_MESSAGE events when recordTaskMessages is true", () => {
-			mockSettingsService.getSettings.mockReturnValue({
-				cloudSettings: {
-					recordTaskMessages: true,
-				},
-			})
+		it("should return true for TASK_MESSAGE events when isTaskSyncEnabled returns true", () => {
+			mockSettingsService.isTaskSyncEnabled.mockReturnValue(true)
 
 			const client = new TelemetryClient(mockAuthService, mockSettingsService)
 
@@ -91,55 +96,11 @@ describe("TelemetryClient", () => {
 			).bind(client)
 
 			expect(isEventCapturable(TelemetryEventName.TASK_MESSAGE)).toBe(true)
+			expect(mockSettingsService.isTaskSyncEnabled).toHaveBeenCalled()
 		})
 
-		it("should return false for TASK_MESSAGE events when recordTaskMessages is false", () => {
-			mockSettingsService.getSettings.mockReturnValue({
-				cloudSettings: {
-					recordTaskMessages: false,
-				},
-			})
-
-			const client = new TelemetryClient(mockAuthService, mockSettingsService)
-
-			const isEventCapturable = getPrivateProperty<(eventName: TelemetryEventName) => boolean>(
-				client,
-				"isEventCapturable",
-			).bind(client)
-
-			expect(isEventCapturable(TelemetryEventName.TASK_MESSAGE)).toBe(false)
-		})
-
-		it("should return false for TASK_MESSAGE events when recordTaskMessages is undefined", () => {
-			mockSettingsService.getSettings.mockReturnValue({
-				cloudSettings: {},
-			})
-
-			const client = new TelemetryClient(mockAuthService, mockSettingsService)
-
-			const isEventCapturable = getPrivateProperty<(eventName: TelemetryEventName) => boolean>(
-				client,
-				"isEventCapturable",
-			).bind(client)
-
-			expect(isEventCapturable(TelemetryEventName.TASK_MESSAGE)).toBe(false)
-		})
-
-		it("should return false for TASK_MESSAGE events when cloudSettings is undefined", () => {
-			mockSettingsService.getSettings.mockReturnValue({})
-
-			const client = new TelemetryClient(mockAuthService, mockSettingsService)
-
-			const isEventCapturable = getPrivateProperty<(eventName: TelemetryEventName) => boolean>(
-				client,
-				"isEventCapturable",
-			).bind(client)
-
-			expect(isEventCapturable(TelemetryEventName.TASK_MESSAGE)).toBe(false)
-		})
-
-		it("should return false for TASK_MESSAGE events when getSettings returns undefined", () => {
-			mockSettingsService.getSettings.mockReturnValue(undefined)
+		it("should return false for TASK_MESSAGE events when isTaskSyncEnabled returns false", () => {
+			mockSettingsService.isTaskSyncEnabled.mockReturnValue(false)
 
 			const client = new TelemetryClient(mockAuthService, mockSettingsService)
 
@@ -149,6 +110,7 @@ describe("TelemetryClient", () => {
 			).bind(client)
 
 			expect(isEventCapturable(TelemetryEventName.TASK_MESSAGE)).toBe(false)
+			expect(mockSettingsService.isTaskSyncEnabled).toHaveBeenCalled()
 		})
 	})
 
@@ -273,10 +235,8 @@ describe("TelemetryClient", () => {
 			expect(mockFetch).not.toHaveBeenCalled()
 		})
 
-		it("should not capture TASK_MESSAGE events when recordTaskMessages is undefined", async () => {
-			mockSettingsService.getSettings.mockReturnValue({
-				cloudSettings: {},
-			})
+		it("should not capture TASK_MESSAGE events when isTaskSyncEnabled returns false", async () => {
+			mockSettingsService.isTaskSyncEnabled.mockReturnValue(false)
 
 			const client = new TelemetryClient(mockAuthService, mockSettingsService)
 
@@ -294,6 +254,7 @@ describe("TelemetryClient", () => {
 			})
 
 			expect(mockFetch).not.toHaveBeenCalled()
+			expect(mockSettingsService.isTaskSyncEnabled).toHaveBeenCalled()
 		})
 
 		it("should not send request when schema validation fails", async () => {
@@ -353,12 +314,8 @@ describe("TelemetryClient", () => {
 			)
 		})
 
-		it("should attempt to capture TASK_MESSAGE events when recordTaskMessages is true", async () => {
-			mockSettingsService.getSettings.mockReturnValue({
-				cloudSettings: {
-					recordTaskMessages: true,
-				},
-			})
+		it("should attempt to capture TASK_MESSAGE events when isTaskSyncEnabled returns true", async () => {
+			mockSettingsService.isTaskSyncEnabled.mockReturnValue(true)
 
 			const eventProperties = {
 				appName: "roo-code",
@@ -389,6 +346,7 @@ describe("TelemetryClient", () => {
 				properties: eventProperties,
 			})
 
+			expect(mockSettingsService.isTaskSyncEnabled).toHaveBeenCalled()
 			expect(mockFetch).toHaveBeenCalledWith(
 				"https://app.roocode.com/api/events",
 				expect.objectContaining({

+ 19 - 4
packages/cloud/src/bridge/BridgeOrchestrator.ts

@@ -59,13 +59,28 @@ export class BridgeOrchestrator {
 		return BridgeOrchestrator.instance
 	}
 
-	public static isEnabled(user?: CloudUserInfo | null, remoteControlEnabled?: boolean): boolean {
-		return !!(user?.id && user.extensionBridgeEnabled && remoteControlEnabled)
+	public static isEnabled(user: CloudUserInfo | null, remoteControlEnabled: boolean): boolean {
+		// Always disabled if signed out.
+		if (!user) {
+			return false
+		}
+
+		// Disabled by the user's organization?
+		if (!user.extensionBridgeEnabled) {
+			return false
+		}
+
+		// Disabled by the user?
+		if (!remoteControlEnabled) {
+			return false
+		}
+
+		return true
 	}
 
 	public static async connectOrDisconnect(
-		userInfo: CloudUserInfo | null,
-		remoteControlEnabled: boolean | undefined,
+		userInfo: CloudUserInfo,
+		remoteControlEnabled: boolean,
 		options: BridgeOrchestratorOptions,
 	): Promise<void> {
 		if (BridgeOrchestrator.isEnabled(userInfo, remoteControlEnabled)) {

+ 2 - 0
packages/evals/package.json

@@ -15,6 +15,8 @@
 		"drizzle-kit:production": "dotenvx run -f .env.production -- tsx node_modules/drizzle-kit/bin.cjs",
 		"db:generate": "pnpm drizzle-kit generate",
 		"db:migrate": "pnpm drizzle-kit migrate",
+		"db:test:migrate": "pnpm drizzle-kit:test migrate",
+		"db:production:migrate": "pnpm drizzle-kit:production migrate",
 		"db:push": "pnpm drizzle-kit push",
 		"db:test:push": "pnpm drizzle-kit:test push",
 		"db:production:push": "pnpm drizzle-kit:production push",

+ 6 - 0
packages/evals/src/db/migrations/0002_bouncy_blazing_skull.sql

@@ -0,0 +1,6 @@
+ALTER TABLE "runs" ADD COLUMN "name" text;--> statement-breakpoint
+ALTER TABLE "runs" ADD COLUMN "contextWindow" integer;--> statement-breakpoint
+ALTER TABLE "runs" ADD COLUMN "inputPrice" real;--> statement-breakpoint
+ALTER TABLE "runs" ADD COLUMN "outputPrice" real;--> statement-breakpoint
+ALTER TABLE "runs" ADD COLUMN "cacheWritesPrice" real;--> statement-breakpoint
+ALTER TABLE "runs" ADD COLUMN "cacheReadsPrice" real;

+ 453 - 0
packages/evals/src/db/migrations/meta/0002_snapshot.json

@@ -0,0 +1,453 @@
+{
+	"id": "3d2b8423-6170-4cb2-9f62-1c86756da97a",
+	"prevId": "43b197c4-ff4f-48c1-908b-a330e66a162d",
+	"version": "7",
+	"dialect": "postgresql",
+	"tables": {
+		"public.runs": {
+			"name": "runs",
+			"schema": "",
+			"columns": {
+				"id": {
+					"name": "id",
+					"type": "integer",
+					"primaryKey": true,
+					"notNull": true,
+					"identity": {
+						"type": "always",
+						"name": "runs_id_seq",
+						"schema": "public",
+						"increment": "1",
+						"startWith": "1",
+						"minValue": "1",
+						"maxValue": "2147483647",
+						"cache": "1",
+						"cycle": false
+					}
+				},
+				"task_metrics_id": {
+					"name": "task_metrics_id",
+					"type": "integer",
+					"primaryKey": false,
+					"notNull": false
+				},
+				"model": {
+					"name": "model",
+					"type": "text",
+					"primaryKey": false,
+					"notNull": true
+				},
+				"name": {
+					"name": "name",
+					"type": "text",
+					"primaryKey": false,
+					"notNull": false
+				},
+				"description": {
+					"name": "description",
+					"type": "text",
+					"primaryKey": false,
+					"notNull": false
+				},
+				"contextWindow": {
+					"name": "contextWindow",
+					"type": "integer",
+					"primaryKey": false,
+					"notNull": false
+				},
+				"inputPrice": {
+					"name": "inputPrice",
+					"type": "real",
+					"primaryKey": false,
+					"notNull": false
+				},
+				"outputPrice": {
+					"name": "outputPrice",
+					"type": "real",
+					"primaryKey": false,
+					"notNull": false
+				},
+				"cacheWritesPrice": {
+					"name": "cacheWritesPrice",
+					"type": "real",
+					"primaryKey": false,
+					"notNull": false
+				},
+				"cacheReadsPrice": {
+					"name": "cacheReadsPrice",
+					"type": "real",
+					"primaryKey": false,
+					"notNull": false
+				},
+				"settings": {
+					"name": "settings",
+					"type": "jsonb",
+					"primaryKey": false,
+					"notNull": false
+				},
+				"pid": {
+					"name": "pid",
+					"type": "integer",
+					"primaryKey": false,
+					"notNull": false
+				},
+				"socket_path": {
+					"name": "socket_path",
+					"type": "text",
+					"primaryKey": false,
+					"notNull": true
+				},
+				"concurrency": {
+					"name": "concurrency",
+					"type": "integer",
+					"primaryKey": false,
+					"notNull": true,
+					"default": 2
+				},
+				"timeout": {
+					"name": "timeout",
+					"type": "integer",
+					"primaryKey": false,
+					"notNull": true,
+					"default": 5
+				},
+				"passed": {
+					"name": "passed",
+					"type": "integer",
+					"primaryKey": false,
+					"notNull": true,
+					"default": 0
+				},
+				"failed": {
+					"name": "failed",
+					"type": "integer",
+					"primaryKey": false,
+					"notNull": true,
+					"default": 0
+				},
+				"created_at": {
+					"name": "created_at",
+					"type": "timestamp",
+					"primaryKey": false,
+					"notNull": true
+				}
+			},
+			"indexes": {},
+			"foreignKeys": {
+				"runs_task_metrics_id_taskMetrics_id_fk": {
+					"name": "runs_task_metrics_id_taskMetrics_id_fk",
+					"tableFrom": "runs",
+					"tableTo": "taskMetrics",
+					"columnsFrom": ["task_metrics_id"],
+					"columnsTo": ["id"],
+					"onDelete": "no action",
+					"onUpdate": "no action"
+				}
+			},
+			"compositePrimaryKeys": {},
+			"uniqueConstraints": {},
+			"policies": {},
+			"checkConstraints": {},
+			"isRLSEnabled": false
+		},
+		"public.taskMetrics": {
+			"name": "taskMetrics",
+			"schema": "",
+			"columns": {
+				"id": {
+					"name": "id",
+					"type": "integer",
+					"primaryKey": true,
+					"notNull": true,
+					"identity": {
+						"type": "always",
+						"name": "taskMetrics_id_seq",
+						"schema": "public",
+						"increment": "1",
+						"startWith": "1",
+						"minValue": "1",
+						"maxValue": "2147483647",
+						"cache": "1",
+						"cycle": false
+					}
+				},
+				"tokens_in": {
+					"name": "tokens_in",
+					"type": "integer",
+					"primaryKey": false,
+					"notNull": true
+				},
+				"tokens_out": {
+					"name": "tokens_out",
+					"type": "integer",
+					"primaryKey": false,
+					"notNull": true
+				},
+				"tokens_context": {
+					"name": "tokens_context",
+					"type": "integer",
+					"primaryKey": false,
+					"notNull": true
+				},
+				"cache_writes": {
+					"name": "cache_writes",
+					"type": "integer",
+					"primaryKey": false,
+					"notNull": true
+				},
+				"cache_reads": {
+					"name": "cache_reads",
+					"type": "integer",
+					"primaryKey": false,
+					"notNull": true
+				},
+				"cost": {
+					"name": "cost",
+					"type": "real",
+					"primaryKey": false,
+					"notNull": true
+				},
+				"duration": {
+					"name": "duration",
+					"type": "integer",
+					"primaryKey": false,
+					"notNull": true
+				},
+				"tool_usage": {
+					"name": "tool_usage",
+					"type": "jsonb",
+					"primaryKey": false,
+					"notNull": false
+				},
+				"created_at": {
+					"name": "created_at",
+					"type": "timestamp",
+					"primaryKey": false,
+					"notNull": true
+				}
+			},
+			"indexes": {},
+			"foreignKeys": {},
+			"compositePrimaryKeys": {},
+			"uniqueConstraints": {},
+			"policies": {},
+			"checkConstraints": {},
+			"isRLSEnabled": false
+		},
+		"public.tasks": {
+			"name": "tasks",
+			"schema": "",
+			"columns": {
+				"id": {
+					"name": "id",
+					"type": "integer",
+					"primaryKey": true,
+					"notNull": true,
+					"identity": {
+						"type": "always",
+						"name": "tasks_id_seq",
+						"schema": "public",
+						"increment": "1",
+						"startWith": "1",
+						"minValue": "1",
+						"maxValue": "2147483647",
+						"cache": "1",
+						"cycle": false
+					}
+				},
+				"run_id": {
+					"name": "run_id",
+					"type": "integer",
+					"primaryKey": false,
+					"notNull": true
+				},
+				"task_metrics_id": {
+					"name": "task_metrics_id",
+					"type": "integer",
+					"primaryKey": false,
+					"notNull": false
+				},
+				"language": {
+					"name": "language",
+					"type": "text",
+					"primaryKey": false,
+					"notNull": true
+				},
+				"exercise": {
+					"name": "exercise",
+					"type": "text",
+					"primaryKey": false,
+					"notNull": true
+				},
+				"passed": {
+					"name": "passed",
+					"type": "boolean",
+					"primaryKey": false,
+					"notNull": false
+				},
+				"started_at": {
+					"name": "started_at",
+					"type": "timestamp",
+					"primaryKey": false,
+					"notNull": false
+				},
+				"finished_at": {
+					"name": "finished_at",
+					"type": "timestamp",
+					"primaryKey": false,
+					"notNull": false
+				},
+				"created_at": {
+					"name": "created_at",
+					"type": "timestamp",
+					"primaryKey": false,
+					"notNull": true
+				}
+			},
+			"indexes": {
+				"tasks_language_exercise_idx": {
+					"name": "tasks_language_exercise_idx",
+					"columns": [
+						{
+							"expression": "run_id",
+							"isExpression": false,
+							"asc": true,
+							"nulls": "last"
+						},
+						{
+							"expression": "language",
+							"isExpression": false,
+							"asc": true,
+							"nulls": "last"
+						},
+						{
+							"expression": "exercise",
+							"isExpression": false,
+							"asc": true,
+							"nulls": "last"
+						}
+					],
+					"isUnique": true,
+					"concurrently": false,
+					"method": "btree",
+					"with": {}
+				}
+			},
+			"foreignKeys": {
+				"tasks_run_id_runs_id_fk": {
+					"name": "tasks_run_id_runs_id_fk",
+					"tableFrom": "tasks",
+					"tableTo": "runs",
+					"columnsFrom": ["run_id"],
+					"columnsTo": ["id"],
+					"onDelete": "no action",
+					"onUpdate": "no action"
+				},
+				"tasks_task_metrics_id_taskMetrics_id_fk": {
+					"name": "tasks_task_metrics_id_taskMetrics_id_fk",
+					"tableFrom": "tasks",
+					"tableTo": "taskMetrics",
+					"columnsFrom": ["task_metrics_id"],
+					"columnsTo": ["id"],
+					"onDelete": "no action",
+					"onUpdate": "no action"
+				}
+			},
+			"compositePrimaryKeys": {},
+			"uniqueConstraints": {},
+			"policies": {},
+			"checkConstraints": {},
+			"isRLSEnabled": false
+		},
+		"public.toolErrors": {
+			"name": "toolErrors",
+			"schema": "",
+			"columns": {
+				"id": {
+					"name": "id",
+					"type": "integer",
+					"primaryKey": true,
+					"notNull": true,
+					"identity": {
+						"type": "always",
+						"name": "toolErrors_id_seq",
+						"schema": "public",
+						"increment": "1",
+						"startWith": "1",
+						"minValue": "1",
+						"maxValue": "2147483647",
+						"cache": "1",
+						"cycle": false
+					}
+				},
+				"run_id": {
+					"name": "run_id",
+					"type": "integer",
+					"primaryKey": false,
+					"notNull": false
+				},
+				"task_id": {
+					"name": "task_id",
+					"type": "integer",
+					"primaryKey": false,
+					"notNull": false
+				},
+				"tool_name": {
+					"name": "tool_name",
+					"type": "text",
+					"primaryKey": false,
+					"notNull": true
+				},
+				"error": {
+					"name": "error",
+					"type": "text",
+					"primaryKey": false,
+					"notNull": true
+				},
+				"created_at": {
+					"name": "created_at",
+					"type": "timestamp",
+					"primaryKey": false,
+					"notNull": true
+				}
+			},
+			"indexes": {},
+			"foreignKeys": {
+				"toolErrors_run_id_runs_id_fk": {
+					"name": "toolErrors_run_id_runs_id_fk",
+					"tableFrom": "toolErrors",
+					"tableTo": "runs",
+					"columnsFrom": ["run_id"],
+					"columnsTo": ["id"],
+					"onDelete": "no action",
+					"onUpdate": "no action"
+				},
+				"toolErrors_task_id_tasks_id_fk": {
+					"name": "toolErrors_task_id_tasks_id_fk",
+					"tableFrom": "toolErrors",
+					"tableTo": "tasks",
+					"columnsFrom": ["task_id"],
+					"columnsTo": ["id"],
+					"onDelete": "no action",
+					"onUpdate": "no action"
+				}
+			},
+			"compositePrimaryKeys": {},
+			"uniqueConstraints": {},
+			"policies": {},
+			"checkConstraints": {},
+			"isRLSEnabled": false
+		}
+	},
+	"enums": {},
+	"schemas": {},
+	"sequences": {},
+	"roles": {},
+	"policies": {},
+	"views": {},
+	"_meta": {
+		"columns": {},
+		"schemas": {},
+		"tables": {}
+	}
+}

+ 7 - 0
packages/evals/src/db/migrations/meta/_journal.json

@@ -15,6 +15,13 @@
 			"when": 1753198630651,
 			"tag": "0001_lowly_captain_flint",
 			"breakpoints": true
+		},
+		{
+			"idx": 2,
+			"version": "7",
+			"when": 1757191027855,
+			"tag": "0002_bouncy_blazing_skull",
+			"breakpoints": true
 		}
 	]
 }

+ 6 - 0
packages/evals/src/db/schema.ts

@@ -13,7 +13,13 @@ export const runs = pgTable("runs", {
 	id: integer().primaryKey().generatedAlwaysAsIdentity(),
 	taskMetricsId: integer("task_metrics_id").references(() => taskMetrics.id),
 	model: text().notNull(),
+	name: text(),
 	description: text(),
+	contextWindow: integer(),
+	inputPrice: real(),
+	outputPrice: real(),
+	cacheWritesPrice: real(),
+	cacheReadsPrice: real(),
 	settings: jsonb().$type<RooCodeSettings>(),
 	pid: integer(),
 	socketPath: text("socket_path").notNull(),

+ 4 - 3
packages/telemetry/src/TelemetryService.ts

@@ -36,14 +36,14 @@ export class TelemetryService {
 
 	/**
 	 * Updates the telemetry state based on user preferences and VSCode settings
-	 * @param didUserOptIn Whether the user has explicitly opted into telemetry
+	 * @param isOptedIn Whether the user is opted into telemetry
 	 */
-	public updateTelemetryState(didUserOptIn: boolean): void {
+	public updateTelemetryState(isOptedIn: boolean): void {
 		if (!this.isReady) {
 			return
 		}
 
-		this.clients.forEach((client) => client.updateTelemetryState(didUserOptIn))
+		this.clients.forEach((client) => client.updateTelemetryState(isOptedIn))
 	}
 
 	// kilocode_change start
@@ -96,6 +96,7 @@ export class TelemetryService {
 			cacheWriteTokens: number
 			cacheReadTokens: number
 			cost?: number
+			completionTime?: number // kilocode_change
 		},
 	): void {
 		this.captureEvent(TelemetryEventName.LLM_COMPLETION, { taskId, ...properties })

+ 1 - 1
packages/types/npm/package.metadata.json

@@ -1,6 +1,6 @@
 {
 	"name": "@roo-code/types",
-	"version": "1.74.0",
+	"version": "1.75.0",
 	"description": "TypeScript type definitions for Roo Code.",
 	"publishConfig": {
 		"access": "public",

+ 9 - 0
packages/types/src/cloud.ts

@@ -162,6 +162,7 @@ export type UserFeatures = z.infer<typeof userFeaturesSchema>
 
 export const userSettingsConfigSchema = z.object({
 	extensionBridgeEnabled: z.boolean().optional(),
+	taskSyncEnabled: z.boolean().optional(),
 })
 
 export type UserSettingsConfig = z.infer<typeof userSettingsConfigSchema>
@@ -302,6 +303,14 @@ export interface SettingsService {
 	 */
 	updateUserSettings(settings: Partial<UserSettingsConfig>): Promise<boolean>
 
+	/**
+	 * Determines if task sync/recording is enabled based on organization and user settings
+	 * Organization settings take precedence over user settings.
+	 * User settings default to true if unspecified.
+	 * @returns true if task sync is enabled, false otherwise
+	 */
+	isTaskSyncEnabled(): boolean
+
 	/**
 	 * Dispose of the settings service and clean up resources
 	 */

+ 1 - 4
packages/types/src/global-settings.ts

@@ -42,6 +42,7 @@ export const globalSettingsSchema = z.object({
 	lastShownAnnouncementId: z.string().optional(),
 	customInstructions: z.string().optional(),
 	taskHistory: z.array(historyItemSchema).optional(),
+	dismissedUpsells: z.array(z.string()).optional(),
 
 	// Image generation settings (experimental) - flattened for simplicity
 	openRouterImageApiKey: z.string().optional(),
@@ -151,8 +152,6 @@ export const globalSettingsSchema = z.object({
 	enableMcpServerCreation: z.boolean().optional(),
 	mcpMarketplaceCatalog: z.any().optional(), // kilocode_change: MCP marketplace catalog
 
-	remoteControlEnabled: z.boolean().optional(),
-
 	mode: z.string().optional(),
 	modeApiConfigs: z.record(z.string(), z.string()).optional(),
 	customModes: z.array(modeConfigSchema).optional(),
@@ -340,8 +339,6 @@ export const EVALS_SETTINGS: RooCodeSettings = {
 
 	mcpEnabled: false,
 
-	remoteControlEnabled: false,
-
 	mode: "code", // "architect",
 
 	customModes: [],

+ 1 - 0
packages/types/src/kilocode.ts

@@ -8,6 +8,7 @@ export const ghostServiceSettingsSchema = z
 		enableSmartInlineTaskKeybinding: z.boolean().optional(),
 		enableCustomProvider: z.boolean().optional(),
 		apiConfigId: z.string().optional(),
+		showGutterAnimation: z.boolean().optional(),
 	})
 	.optional()
 

+ 6 - 1
packages/types/src/provider-settings.ts

@@ -331,6 +331,7 @@ const kilocodeSchema = baseProviderSettingsSchema.extend({
 	openRouterSpecificProvider: z.string().optional(),
 	openRouterProviderDataCollection: openRouterProviderDataCollectionSchema.optional(),
 	openRouterProviderSort: openRouterProviderSortSchema.optional(),
+	kilocodeTesterWarningsDisabledUntil: z.number().optional(), // Timestamp for disabling KILOCODE-TESTER warnings
 })
 
 export const virtualQuotaFallbackProfileDataSchema = z.object({
@@ -351,11 +352,15 @@ export const virtualQuotaFallbackProfileDataSchema = z.object({
 const virtualQuotaFallbackSchema = baseProviderSettingsSchema.extend({
 	profiles: z.array(virtualQuotaFallbackProfileDataSchema).optional(),
 })
+
+export const zaiApiLineSchema = z.enum(["international_coding", "international", "china_coding", "china"])
+
+export type ZaiApiLine = z.infer<typeof zaiApiLineSchema>
 // kilocode_change end
 
 const zaiSchema = apiModelIdProviderModelSchema.extend({
 	zaiApiKey: z.string().optional(),
-	zaiApiLine: z.union([z.literal("china"), z.literal("international")]).optional(),
+	zaiApiLine: zaiApiLineSchema.optional(), // kilocode_change
 })
 
 const fireworksSchema = apiModelIdProviderModelSchema.extend({

+ 105 - 21
packages/types/src/providers/chutes.ts

@@ -18,22 +18,35 @@ export type ChutesModelId =
 	| "deepseek-ai/DeepSeek-V3-Base"
 	| "deepseek-ai/DeepSeek-R1-Zero"
 	| "deepseek-ai/DeepSeek-V3-0324"
+	// kilocode_change start
+	| "deepseek-ai/DeepSeek-V3.1-Terminus"
+	| "deepseek-ai/DeepSeek-V3.1-turbo"
+	| "deepseek-ai/DeepSeek-V3-0324-turbo"
+	// kilocode_change end
 	| "Qwen/Qwen3-235B-A22B"
 	| "Qwen/Qwen3-235B-A22B-Instruct-2507"
-	| "Qwen/Qwen3-235B-A22B-Thinking-2507"
-	| "Qwen/Qwen3-Coder-480B-A35B-Instruct-FP8"
-	| "Qwen/Qwen3-Next-80B-A3B-Instruct"
-	| "Qwen/Qwen3-Next-80B-A3B-Thinking"
 	| "Qwen/Qwen3-32B"
 	| "Qwen/Qwen3-30B-A3B"
 	| "Qwen/Qwen3-14B"
 	| "Qwen/Qwen3-8B"
+	| "Qwen/Qwen3-Coder-480B-A35B-Instruct-FP8"
 	| "microsoft/MAI-DS-R1-FP8"
 	| "tngtech/DeepSeek-R1T-Chimera"
 	| "zai-org/GLM-4.5-Air"
 	| "zai-org/GLM-4.5-FP8"
+	// kilocode_change start
+	| "zai-org/GLM-4.5-turbo"
+	| "zai-org/GLM-4.5V"
+	// kilocode_change end
 	| "moonshotai/Kimi-K2-Instruct-75k"
 	| "moonshotai/Kimi-K2-Instruct-0905"
+	// kilocode_change start
+	| "moonshotai/Kimi-Dev-72B"
+	| "moonshotai/Kimi-VL-A3B-Thinking"
+	// kilocode_change end
+	| "Qwen/Qwen3-235B-A22B-Thinking-2507"
+	| "Qwen/Qwen3-Next-80B-A3B-Instruct"
+	| "Qwen/Qwen3-Next-80B-A3B-Thinking"
 
 export const chutesDefaultModelId: ChutesModelId = "deepseek-ai/DeepSeek-R1-0528"
 
@@ -182,15 +195,35 @@ export const chutesModels = {
 		outputPrice: 0,
 		description: "DeepSeek V3 (0324) model.",
 	},
-	"Qwen/Qwen3-235B-A22B": {
+	// kilocode_change start
+	"deepseek-ai/DeepSeek-V3.1-Terminus": {
 		maxTokens: 32768,
-		contextWindow: 40960,
+		contextWindow: 163840,
 		supportsImages: false,
 		supportsPromptCache: false,
-		inputPrice: 0,
-		outputPrice: 0,
-		description: "Qwen3 235B A22B model.",
+		inputPrice: 0.25,
+		outputPrice: 1.0,
+		description: "DeepSeek V3.1 Terminus model.",
+	},
+	"deepseek-ai/DeepSeek-V3.1-turbo": {
+		maxTokens: 32768,
+		contextWindow: 163840,
+		supportsImages: false,
+		supportsPromptCache: false,
+		inputPrice: 1.0,
+		outputPrice: 3.0,
+		description: "DeepSeek V3.1 Turbo model.",
 	},
+	"deepseek-ai/DeepSeek-V3-0324-turbo": {
+		maxTokens: 32768,
+		contextWindow: 163840,
+		supportsImages: false,
+		supportsPromptCache: false,
+		inputPrice: 1.0,
+		outputPrice: 3.0,
+		description: "DeepSeek V3 (0324) Turbo model.",
+	},
+	// kilocode_change end
 	"Qwen/Qwen3-235B-A22B-Instruct-2507": {
 		maxTokens: 32768,
 		contextWindow: 262144,
@@ -200,6 +233,15 @@ export const chutesModels = {
 		outputPrice: 0,
 		description: "Qwen3 235B A22B Instruct 2507 model with 262K context window.",
 	},
+	"Qwen/Qwen3-235B-A22B": {
+		maxTokens: 32768,
+		contextWindow: 40960,
+		supportsImages: false,
+		supportsPromptCache: false,
+		inputPrice: 0,
+		outputPrice: 0,
+		description: "Qwen3 235B A22B model.",
+	},
 	"Qwen/Qwen3-32B": {
 		maxTokens: 32768,
 		contextWindow: 40960,
@@ -274,6 +316,35 @@ export const chutesModels = {
 		description:
 			"GLM-4.5-FP8 model with 128k token context window, optimized for agent-based applications with MoE architecture.",
 	},
+	// kilocode_change start
+	"zai-org/GLM-4.5-turbo": {
+		maxTokens: 32768,
+		contextWindow: 131072,
+		supportsImages: false,
+		supportsPromptCache: false,
+		inputPrice: 1.0,
+		outputPrice: 3.0,
+		description: "GLM-4.5-Turbo model.",
+	},
+	"zai-org/GLM-4.5V": {
+		maxTokens: 32768,
+		contextWindow: 131072,
+		supportsImages: false,
+		supportsPromptCache: false,
+		inputPrice: 0.08,
+		outputPrice: 0.33,
+		description: "GLM-4.5V model.",
+	},
+	// kilocode_change end
+	"Qwen/Qwen3-Coder-480B-A35B-Instruct-FP8": {
+		maxTokens: 32768,
+		contextWindow: 262144,
+		supportsImages: false,
+		supportsPromptCache: false,
+		inputPrice: 0,
+		outputPrice: 0,
+		description: "Qwen3 Coder 480B A35B Instruct FP8 model, optimized for coding tasks.",
+	},
 	"moonshotai/Kimi-K2-Instruct-75k": {
 		maxTokens: 32768,
 		contextWindow: 75000,
@@ -292,40 +363,53 @@ export const chutesModels = {
 		outputPrice: 0.8001,
 		description: "Moonshot AI Kimi K2 Instruct 0905 model with 256k context window.",
 	},
-	"Qwen/Qwen3-235B-A22B-Thinking-2507": {
+	// kilocode_change start
+	"moonshotai/Kimi-Dev-72B": {
 		maxTokens: 32768,
 		contextWindow: 262144,
 		supportsImages: false,
 		supportsPromptCache: false,
-		inputPrice: 0.077968332,
-		outputPrice: 0.31202496,
-		description: "Qwen3 235B A22B Thinking 2507 model with 262K context window.",
+		inputPrice: 0.07,
+		outputPrice: 0.26,
+		description: "Moonshot AI Kimi Dev 72B model.",
 	},
-	"Qwen/Qwen3-Coder-480B-A35B-Instruct-FP8": {
+	"moonshotai/Kimi-VL-A3B-Thinking": {
 		maxTokens: 32768,
 		contextWindow: 262144,
 		supportsImages: false,
 		supportsPromptCache: false,
-		inputPrice: 0,
-		outputPrice: 0,
-		description: "Qwen3 Coder 480B A35B Instruct FP8 model, optimized for coding tasks.",
+		inputPrice: 0.02,
+		outputPrice: 0.07,
+		description: "Moonshot AI Kimi VL A3B Thinking model.",
 	},
-	"Qwen/Qwen3-Next-80B-A3B-Instruct": {
+	// kilocode_change end
+	"Qwen/Qwen3-235B-A22B-Thinking-2507": {
 		maxTokens: 32768,
 		contextWindow: 262144,
 		supportsImages: false,
 		supportsPromptCache: false,
+		inputPrice: 0.077968332,
+		outputPrice: 0.31202496,
+		description: "Qwen3 235B A22B Thinking 2507 model with 262K context window.",
+	},
+	"Qwen/Qwen3-Next-80B-A3B-Instruct": {
+		maxTokens: 32768,
+		contextWindow: 131072,
+		supportsImages: false,
+		supportsPromptCache: false,
 		inputPrice: 0,
 		outputPrice: 0,
-		description: "Qwen3 Next 80B A3B Instruct model with 262K context window.",
+		description:
+			"Fast, stable instruction-tuned model optimized for complex tasks, RAG, and tool use without thinking traces.",
 	},
 	"Qwen/Qwen3-Next-80B-A3B-Thinking": {
 		maxTokens: 32768,
-		contextWindow: 262144,
+		contextWindow: 131072,
 		supportsImages: false,
 		supportsPromptCache: false,
 		inputPrice: 0,
 		outputPrice: 0,
-		description: "Qwen3 Next 80B A3B Thinking model with 262K context window.",
+		description:
+			"Reasoning-first model with structured thinking traces for multi-step problems, math proofs, and code synthesis.",
 	},
 } as const satisfies Record<string, ModelInfo>

+ 8 - 8
packages/types/src/providers/deepseek.ts

@@ -11,10 +11,10 @@ export const deepSeekModels = {
 		contextWindow: 128_000,
 		supportsImages: false,
 		supportsPromptCache: true,
-		inputPrice: 0.27, // $0.27 per million tokens (cache miss)
-		outputPrice: 1.1, // $1.10 per million tokens
-		cacheWritesPrice: 0.27, // $0.27 per million tokens (cache miss)
-		cacheReadsPrice: 0.07, // $0.07 per million tokens (cache hit).
+		inputPrice: 0.56, // $0.56 per million tokens (cache miss) - Updated Sept 5, 2025
+		outputPrice: 1.68, // $1.68 per million tokens - Updated Sept 5, 2025
+		cacheWritesPrice: 0.56, // $0.56 per million tokens (cache miss) - Updated Sept 5, 2025
+		cacheReadsPrice: 0.07, // $0.07 per million tokens (cache hit) - Updated Sept 5, 2025
 		description: `DeepSeek-V3 achieves a significant breakthrough in inference speed over previous models. It tops the leaderboard among open-source models and rivals the most advanced closed-source models globally.`,
 	},
 	"deepseek-reasoner": {
@@ -22,10 +22,10 @@ export const deepSeekModels = {
 		contextWindow: 128_000,
 		supportsImages: false,
 		supportsPromptCache: true,
-		inputPrice: 0.55, // $0.55 per million tokens (cache miss)
-		outputPrice: 2.19, // $2.19 per million tokens
-		cacheWritesPrice: 0.55, // $0.55 per million tokens (cache miss)
-		cacheReadsPrice: 0.14, // $0.14 per million tokens (cache hit)
+		inputPrice: 0.56, // $0.56 per million tokens (cache miss) - Updated Sept 5, 2025
+		outputPrice: 1.68, // $1.68 per million tokens - Updated Sept 5, 2025
+		cacheWritesPrice: 0.56, // $0.56 per million tokens (cache miss) - Updated Sept 5, 2025
+		cacheReadsPrice: 0.07, // $0.07 per million tokens (cache hit) - Updated Sept 5, 2025
 		description: `DeepSeek-R1 achieves performance comparable to OpenAI-o1 across math, code, and reasoning tasks. Supports Chain of Thought reasoning with up to 64K output tokens.`,
 	},
 } as const satisfies Record<string, ModelInfo>

+ 14 - 0
packages/types/src/providers/openai.ts

@@ -70,6 +70,20 @@ export const openAiNativeModels = {
 		supportsTemperature: false,
 		tiers: [{ name: "flex", contextWindow: 400000, inputPrice: 0.025, outputPrice: 0.2, cacheReadsPrice: 0.0025 }],
 	},
+	"gpt-5-codex": {
+		maxTokens: 128000,
+		contextWindow: 400000,
+		supportsImages: true,
+		supportsPromptCache: true,
+		supportsReasoningEffort: true,
+		reasoningEffort: "medium",
+		inputPrice: 1.25,
+		outputPrice: 10.0,
+		cacheReadsPrice: 0.13,
+		description: "GPT-5-Codex: A version of GPT-5 optimized for agentic coding in Codex",
+		supportsVerbosity: true,
+		supportsTemperature: false,
+	},
 	"gpt-4.1": {
 		maxTokens: 32_768,
 		contextWindow: 1_047_576,

+ 20 - 0
packages/types/src/providers/sambanova.ts

@@ -6,10 +6,12 @@ export type SambaNovaModelId =
 	| "Meta-Llama-3.3-70B-Instruct"
 	| "DeepSeek-R1"
 	| "DeepSeek-V3-0324"
+	| "DeepSeek-V3.1"
 	| "DeepSeek-R1-Distill-Llama-70B"
 	| "Llama-4-Maverick-17B-128E-Instruct"
 	| "Llama-3.3-Swallow-70B-Instruct-v0.4"
 	| "Qwen3-32B"
+	| "gpt-oss-120b"
 
 export const sambaNovaDefaultModelId: SambaNovaModelId = "Meta-Llama-3.3-70B-Instruct"
 
@@ -51,6 +53,15 @@ export const sambaNovaModels = {
 		outputPrice: 4.5,
 		description: "DeepSeek V3 model with 32K context window.",
 	},
+	"DeepSeek-V3.1": {
+		maxTokens: 8192,
+		contextWindow: 32768,
+		supportsImages: false,
+		supportsPromptCache: false,
+		inputPrice: 3.0,
+		outputPrice: 4.5,
+		description: "DeepSeek V3.1 model with 32K context window.",
+	},
 	"DeepSeek-R1-Distill-Llama-70B": {
 		maxTokens: 8192,
 		contextWindow: 131072,
@@ -87,4 +98,13 @@ export const sambaNovaModels = {
 		outputPrice: 0.8,
 		description: "Alibaba Qwen 3 32B model with 8K context window.",
 	},
+	"gpt-oss-120b": {
+		maxTokens: 8192,
+		contextWindow: 131072,
+		supportsImages: false,
+		supportsPromptCache: false,
+		inputPrice: 0.22,
+		outputPrice: 0.59,
+		description: "OpenAI gpt oss 120b model with 128k context window.",
+	},
 } as const satisfies Record<string, ModelInfo>

+ 55 - 0
packages/types/src/providers/vertex.ts

@@ -294,6 +294,60 @@ export const vertexModels = {
 		outputPrice: 1.15,
 		description: "Meta Llama 4 Maverick 17B Instruct model, 128K context.",
 	},
+	"deepseek-r1-0528-maas": {
+		maxTokens: 32_768,
+		contextWindow: 163_840,
+		supportsImages: false,
+		supportsPromptCache: false,
+		inputPrice: 1.35,
+		outputPrice: 5.4,
+		description: "DeepSeek R1 (0528). Available in us-central1",
+	},
+	"deepseek-v3.1-maas": {
+		maxTokens: 32_768,
+		contextWindow: 163_840,
+		supportsImages: false,
+		supportsPromptCache: false,
+		inputPrice: 0.6,
+		outputPrice: 1.7,
+		description: "DeepSeek V3.1. Available in us-west2",
+	},
+	"gpt-oss-120b-maas": {
+		maxTokens: 32_768,
+		contextWindow: 131_072,
+		supportsImages: false,
+		supportsPromptCache: false,
+		inputPrice: 0.15,
+		outputPrice: 0.6,
+		description: "OpenAI gpt-oss 120B. Available in us-central1",
+	},
+	"gpt-oss-20b-maas": {
+		maxTokens: 32_768,
+		contextWindow: 131_072,
+		supportsImages: false,
+		supportsPromptCache: false,
+		inputPrice: 0.075,
+		outputPrice: 0.3,
+		description: "OpenAI gpt-oss 20B. Available in us-central1",
+	},
+	"qwen3-coder-480b-a35b-instruct-maas": {
+		maxTokens: 32_768,
+		contextWindow: 262_144,
+		supportsImages: false,
+		supportsPromptCache: false,
+		inputPrice: 1.0,
+		outputPrice: 4.0,
+		description: "Qwen3 Coder 480B A35B Instruct. Available in us-south1",
+	},
+	"qwen3-235b-a22b-instruct-2507-maas": {
+		maxTokens: 16_384,
+		contextWindow: 262_144,
+		supportsImages: false,
+		supportsPromptCache: false,
+		inputPrice: 0.25,
+		outputPrice: 1.0,
+		description: "Qwen3 235B A22B Instruct. Available in us-south1",
+	},
 } as const satisfies Record<string, ModelInfo>
 
 export const VERTEX_REGIONS = [
@@ -302,6 +356,7 @@ export const VERTEX_REGIONS = [
 	{ value: "us-east1", label: "us-east1" },
 	{ value: "us-east4", label: "us-east4" },
 	{ value: "us-east5", label: "us-east5" },
+	{ value: "us-south1", label: "us-south1" },
 	{ value: "us-west1", label: "us-west1" },
 	{ value: "us-west2", label: "us-west2" },
 	{ value: "us-west3", label: "us-west3" },

+ 50 - 0
packages/types/src/providers/xai.ts

@@ -28,6 +28,56 @@ export const xaiModels = {
 		cacheReadsPrice: 0.75,
 		description: "xAI's Grok-4 model with 256K context window",
 	},
+	// kilocode_change start
+	"grok-4-fast": {
+		maxTokens: 30_000,
+		contextWindow: 2_000_000,
+		supportsImages: true,
+		supportsPromptCache: true,
+		inputPrice: 0.4, // This is the pricing for prompts above 128K context
+		outputPrice: 1.0,
+		cacheReadsPrice: 0.05,
+		description: "xAI's Grok-4-Fast model with reasonning and a 2M context window",
+		tiers: [
+			{
+				contextWindow: 128_000,
+				inputPrice: 0.2,
+				outputPrice: 0.5,
+				cacheReadsPrice: 0.05,
+			},
+			{
+				contextWindow: Infinity,
+				inputPrice: 0.4,
+				outputPrice: 1,
+				cacheReadsPrice: 0.05,
+			},
+		],
+	},
+	"grok-4-fast-non-reasoning": {
+		maxTokens: 30_000,
+		contextWindow: 2_000_000,
+		supportsImages: true,
+		supportsPromptCache: true,
+		inputPrice: 0.4, // This is the pricing for prompts above 128K context
+		outputPrice: 1.0,
+		cacheReadsPrice: 0.05,
+		description: "xAI's Grok-4-Fast model without reasonning and with a 2M context window",
+		tiers: [
+			{
+				contextWindow: 128_000,
+				inputPrice: 0.2,
+				outputPrice: 0.5,
+				cacheReadsPrice: 0.05,
+			},
+			{
+				contextWindow: Infinity,
+				inputPrice: 0.4,
+				outputPrice: 1,
+				cacheReadsPrice: 0.05,
+			},
+		],
+	},
+	// kilocode_change end
 	"grok-3": {
 		maxTokens: 8192,
 		contextWindow: 131072,

+ 14 - 0
packages/types/src/providers/zai.ts

@@ -1,4 +1,5 @@
 import type { ModelInfo } from "../model.js"
+import { ZaiApiLine } from "../provider-settings.js" // kilocode_change
 
 // Z AI
 // https://docs.z.ai/guides/llm/glm-4.5
@@ -125,3 +126,16 @@ export const mainlandZAiModels = {
 } as const satisfies Record<string, ModelInfo>
 
 export const ZAI_DEFAULT_TEMPERATURE = 0
+
+// kilocode_change start
+export const zaiApiLineConfigs = {
+	international_coding: {
+		name: "International Coding Plan",
+		baseUrl: "https://api.z.ai/api/coding/paas/v4",
+		isChina: false,
+	},
+	international: { name: "International Standard", baseUrl: "https://api.z.ai/api/paas/v4", isChina: false },
+	china_coding: { name: "China Coding Plan", baseUrl: "https://open.bigmodel.cn/api/coding/paas/v4", isChina: true },
+	china: { name: "China Standard", baseUrl: "https://open.bigmodel.cn/api/paas/v4", isChina: true },
+} satisfies Record<ZaiApiLine, { name: string; baseUrl: string; isChina: boolean }>
+// kilocode_change end

+ 7 - 2
packages/types/src/telemetry.ts

@@ -25,7 +25,8 @@ export enum TelemetryEventName {
 	INLINE_ASSIST_ACCEPT_SUGGESTION = "Inline Assist Accept Suggestion",
 	INLINE_ASSIST_REJECT_SUGGESTION = "Inline Assist Reject Suggestion",
 	CHECKPOINT_FAILURE = "Checkpoint Failure",
-	EXCESSIVE_RECURSION = "Excessive Recursion",
+	TOOL_ERROR = "Tool Error",
+	MAX_COMPLETION_TOKENS_REACHED_ERROR = "Max Completion Tokens Reached Error",
 	NOTIFICATION_CLICKED = "Notification Clicked",
 	WEBVIEW_MEMORY_USAGE = "Webview Memory Usage",
 	FREE_MODELS_LINK_CLICKED = "Free Models Link Clicked",
@@ -139,6 +140,10 @@ export const taskPropertiesSchema = z.object({
 			pending: z.number(),
 		})
 		.optional(),
+	// kilocode_change start
+	currentTaskSize: z.number().optional(),
+	taskHistorySize: z.number().optional(),
+	// kilocode_change end
 })
 
 export type TaskProperties = z.infer<typeof taskPropertiesSchema>
@@ -272,11 +277,11 @@ export interface TelemetryClient {
 
 	setProvider(provider: TelemetryPropertiesProvider): void
 	capture(options: TelemetryEvent): Promise<void>
-	updateTelemetryState(didUserOptIn: boolean): void
 	// kilocode_change start
 	captureException(error: Error, properties?: Record<string | number, unknown>): void
 	updateIdentity(kilocodeToken: string): Promise<void>
 	// kilocode_change end
+	updateTelemetryState(isOptedIn: boolean): void
 	isTelemetryEnabled(): boolean
 	shutdown(): Promise<void>
 }

Rozdílová data souboru nebyla zobrazena, protože soubor je příliš velký
+ 158 - 273
pnpm-lock.yaml


binární
releases/3.28.0-release.png


binární
releases/3.28.1-release.png


binární
releases/3.28.2-release.png


+ 170 - 0
scripts/update-contributors.js

@@ -0,0 +1,170 @@
+#!/usr/bin/env node
+
+import fs from "fs"
+import path from "path"
+import https from "https"
+import { fileURLToPath } from "url"
+
+// Configuration
+const __filename = fileURLToPath(import.meta.url)
+const __dirname = path.dirname(__filename)
+const README_FILE = path.join(__dirname, "../README.md")
+const CONTRIBUTORS_JSON_URL = "https://kilocode.ai/contributors.json"
+const MAX_CONTRIBUTORS_DISPLAY = 9
+const CONTRIBUTORS_PAGE_URL = "https://kilocode.ai/#contributors"
+
+// Function to make HTTP requests
+function makeRequest(url) {
+	return new Promise((resolve, reject) => {
+		https
+			.get(url, (res) => {
+				let data = ""
+
+				res.on("data", (chunk) => {
+					data += chunk
+				})
+
+				res.on("end", () => {
+					try {
+						const jsonData = JSON.parse(data)
+						resolve(jsonData)
+					} catch (error) {
+						reject(error)
+					}
+				})
+			})
+			.on("error", (error) => {
+				reject(error)
+			})
+	})
+}
+
+// Function to generate Markdown contributor list
+function generateContributorMarkdown(contributors) {
+	let markdown = "## Contributors\n\n"
+	markdown += "Thanks to all the contributors who help make Kilo Code better!\n\n"
+
+	// Map the kilocode.ai format to expected format
+	const validContributors = contributors.map((contributor) => {
+		// Convert kilocode.ai format to GitHub-like format
+		return {
+			login: contributor.username,
+			html_url: `https://github.com/${contributor.username}`,
+			avatar_url: `https://avatars.githubusercontent.com/u/${contributor.avatarId}`,
+		}
+	})
+
+	// Limit to MAX_CONTRIBUTORS_DISPLAY contributors
+	const displayContributors = validContributors.slice(0, MAX_CONTRIBUTORS_DISPLAY)
+	const hasMore = validContributors.length > MAX_CONTRIBUTORS_DISPLAY
+
+	// Create a grid of contributor avatars in rows of 5
+	let contributorGrid = "<table>\n"
+
+	for (let i = 0; i < displayContributors.length; i++) {
+		const contributor = displayContributors[i]
+
+		// Start a new row every 5 contributors
+		if (i % 5 === 0) {
+			if (i > 0) {
+				contributorGrid += "\n  </tr>\n"
+			}
+			contributorGrid += "  <tr>\n"
+		}
+
+		const contributorCell = `    <td align="center">
+      <a href="${contributor.html_url}">
+        <img src="${contributor.avatar_url}?size=100" width="100" height="100" alt="${contributor.login}" style="border-radius: 50%;" />
+      </a>
+    </td>`
+
+		contributorGrid += contributorCell
+	}
+
+	// Add "more..." cell if there are more contributors
+	if (hasMore) {
+		// Check if we need to add it to current row or start new row
+		const lastRowCount = displayContributors.length % 5
+
+		if (lastRowCount === 0) {
+			// Start a new row for "more..."
+			contributorGrid += "\n  </tr>\n  <tr>\n"
+		}
+
+		const moreCell = `    <td align="center">
+      <a href="${CONTRIBUTORS_PAGE_URL}">
+        <b>more ...</b>
+      </a>
+    </td>`
+
+		contributorGrid += moreCell
+	}
+
+	// Close the last row
+	contributorGrid += "\n  </tr>\n</table>\n"
+
+	markdown += contributorGrid
+	return markdown
+}
+
+// Function to update the contributors section in the README
+async function updateContributorsSection() {
+	try {
+		console.log("Fetching contributors from kilocode.ai...")
+
+		// Fetch contributors from external JSON
+		const contributors = await makeRequest(CONTRIBUTORS_JSON_URL)
+
+		if (!Array.isArray(contributors)) {
+			throw new Error("Failed to fetch contributors data")
+		}
+
+		console.log(`Found ${contributors.length} contributors`)
+
+		// Generate Markdown content
+		const contributorMarkdown = generateContributorMarkdown(contributors)
+
+		// Read the existing README
+		let readmeContent = fs.readFileSync(README_FILE, "utf8")
+
+		// Find the contributors section markers or add at the end
+		const contributorsStartMarker = "## Contributors"
+		const contributorsEndMarker = "<!-- END CONTRIBUTORS SECTION -->"
+
+		let newContent
+
+		if (readmeContent.includes(contributorsStartMarker)) {
+			// Replace existing contributors section
+			const startIndex = readmeContent.indexOf(contributorsStartMarker)
+			const endIndex = readmeContent.indexOf(contributorsEndMarker)
+
+			if (endIndex === -1) {
+				// No end marker, look for any existing table or content after the header
+				// and replace everything from the header to the end of file
+				const afterHeaderIndex = startIndex + contributorsStartMarker.length
+				const beforeContent = readmeContent.substring(0, startIndex)
+				newContent = beforeContent + contributorMarkdown + "\n<!-- END CONTRIBUTORS SECTION -->\n"
+			} else {
+				// Replace between markers - clean replacement between the header and end marker
+				const beforeContent = readmeContent.substring(0, startIndex)
+				const afterContent = readmeContent.substring(endIndex + contributorsEndMarker.length)
+				newContent = beforeContent + contributorMarkdown + "\n" + contributorsEndMarker + afterContent
+			}
+		} else {
+			// Add contributors section at the end
+			newContent = readmeContent.trim() + "\n\n" + contributorMarkdown + "\n<!-- END CONTRIBUTORS SECTION -->\n"
+		}
+
+		// Write the updated content back to the README
+		fs.writeFileSync(README_FILE, newContent)
+
+		console.log("Contributors section updated successfully!")
+	} catch (error) {
+		console.error("Error updating contributors section:", error.message)
+		console.error("Stack trace:", error.stack)
+		process.exit(1)
+	}
+}
+
+// Run the script
+updateContributorsSection()

Některé soubory nejsou zobrazeny, neboť je v těchto rozdílových datech změněno mnoho souborů