mirror of
https://github.com/Kilo-Org/kilocode.git
synced 2026-09-19 10:02:04 +08:00
Merge branch 'dev' into feat/review-apply-fixes
This commit is contained in:
@@ -23,7 +23,7 @@
|
||||
},
|
||||
"packages/app": {
|
||||
"name": "@opencode-ai/app",
|
||||
"version": "1.0.22",
|
||||
"version": "1.0.23",
|
||||
"dependencies": {
|
||||
"@kilocode/kilo-i18n": "workspace:*",
|
||||
"@kilocode/kilo-ui": "workspace:*",
|
||||
@@ -75,7 +75,7 @@
|
||||
},
|
||||
"packages/desktop": {
|
||||
"name": "@opencode-ai/desktop",
|
||||
"version": "1.0.22",
|
||||
"version": "1.0.23",
|
||||
"dependencies": {
|
||||
"@opencode-ai/app": "workspace:*",
|
||||
"@opencode-ai/ui": "workspace:*",
|
||||
@@ -108,7 +108,7 @@
|
||||
},
|
||||
"packages/kilo-gateway": {
|
||||
"name": "@kilocode/kilo-gateway",
|
||||
"version": "1.0.22",
|
||||
"version": "1.0.23",
|
||||
"dependencies": {
|
||||
"@clack/prompts": "1.0.0-alpha.1",
|
||||
"@kilocode/plugin": "workspace:*",
|
||||
@@ -140,7 +140,7 @@
|
||||
},
|
||||
"packages/kilo-i18n": {
|
||||
"name": "@kilocode/kilo-i18n",
|
||||
"version": "1.0.22",
|
||||
"version": "1.0.23",
|
||||
"devDependencies": {
|
||||
"@tsconfig/node22": "catalog:",
|
||||
"@types/bun": "catalog:",
|
||||
@@ -150,7 +150,7 @@
|
||||
},
|
||||
"packages/kilo-telemetry": {
|
||||
"name": "@kilocode/kilo-telemetry",
|
||||
"version": "1.0.22",
|
||||
"version": "1.0.23",
|
||||
"dependencies": {
|
||||
"@kilocode/kilo-gateway": "workspace:*",
|
||||
"@opentelemetry/api": "1.9.0",
|
||||
@@ -170,7 +170,7 @@
|
||||
},
|
||||
"packages/kilo-ui": {
|
||||
"name": "@kilocode/kilo-ui",
|
||||
"version": "1.0.22",
|
||||
"version": "1.0.23",
|
||||
"devDependencies": {
|
||||
"@tsconfig/node22": "catalog:",
|
||||
"@types/bun": "catalog:",
|
||||
@@ -183,7 +183,7 @@
|
||||
},
|
||||
"packages/kilo-vscode": {
|
||||
"name": "kilo-code",
|
||||
"version": "7.0.22",
|
||||
"version": "7.0.23",
|
||||
"dependencies": {
|
||||
"@anthropic-ai/sdk": "^0.39.0",
|
||||
"@kilocode/kilo-i18n": "workspace:*",
|
||||
@@ -224,7 +224,7 @@
|
||||
},
|
||||
"packages/opencode": {
|
||||
"name": "@kilocode/cli",
|
||||
"version": "1.0.22",
|
||||
"version": "1.0.23",
|
||||
"bin": {
|
||||
"kilo": "./bin/kilo",
|
||||
"kilocode": "./bin/kilocode",
|
||||
@@ -333,7 +333,7 @@
|
||||
},
|
||||
"packages/plugin": {
|
||||
"name": "@kilocode/plugin",
|
||||
"version": "1.0.22",
|
||||
"version": "1.0.23",
|
||||
"dependencies": {
|
||||
"@kilocode/sdk": "workspace:*",
|
||||
"zod": "catalog:",
|
||||
@@ -347,14 +347,14 @@
|
||||
},
|
||||
"packages/script": {
|
||||
"name": "@opencode-ai/script",
|
||||
"version": "1.0.22",
|
||||
"version": "1.0.23",
|
||||
"devDependencies": {
|
||||
"@types/bun": "catalog:",
|
||||
},
|
||||
},
|
||||
"packages/sdk/js": {
|
||||
"name": "@kilocode/sdk",
|
||||
"version": "1.0.22",
|
||||
"version": "1.0.23",
|
||||
"devDependencies": {
|
||||
"@hey-api/openapi-ts": "0.90.10",
|
||||
"@tsconfig/node22": "catalog:",
|
||||
@@ -365,7 +365,7 @@
|
||||
},
|
||||
"packages/ui": {
|
||||
"name": "@opencode-ai/ui",
|
||||
"version": "1.0.22",
|
||||
"version": "1.0.23",
|
||||
"dependencies": {
|
||||
"@kilocode/sdk": "workspace:*",
|
||||
"@kobalte/core": "catalog:",
|
||||
@@ -407,7 +407,7 @@
|
||||
},
|
||||
"packages/util": {
|
||||
"name": "@opencode-ai/util",
|
||||
"version": "1.0.22",
|
||||
"version": "1.0.23",
|
||||
"dependencies": {
|
||||
"zod": "catalog:",
|
||||
},
|
||||
|
||||
+1
-1
@@ -102,6 +102,6 @@
|
||||
"@standard-community/standard-openapi@0.2.9": "patches/@standard-community%2Fstandard-openapi@0.2.9.patch",
|
||||
"ghostty-web@0.3.0": "patches/ghostty-web@0.3.0.patch"
|
||||
},
|
||||
"version": "1.0.22",
|
||||
"version": "1.0.23",
|
||||
"peerDependencies": {}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@opencode-ai/app",
|
||||
"version": "1.0.22",
|
||||
"version": "1.0.23",
|
||||
"description": "",
|
||||
"type": "module",
|
||||
"exports": {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@opencode-ai/desktop",
|
||||
"private": true,
|
||||
"version": "1.0.22",
|
||||
"version": "1.0.23",
|
||||
"type": "module",
|
||||
"license": "MIT",
|
||||
"scripts": {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
id = "kilo"
|
||||
name = "Kilo"
|
||||
description = "The open source coding agent."
|
||||
version = "1.0.22"
|
||||
version = "1.0.23"
|
||||
schema_version = 1
|
||||
authors = ["Anomaly"]
|
||||
repository = "https://github.com/Kilo-Org/kilo"
|
||||
@@ -11,26 +11,26 @@ name = "Kilo"
|
||||
icon = "./icons/opencode.svg"
|
||||
|
||||
[agent_servers.opencode.targets.darwin-aarch64]
|
||||
archive = "https://github.com/Kilo-Org/kilo/releases/download/v1.0.22/opencode-darwin-arm64.zip"
|
||||
archive = "https://github.com/Kilo-Org/kilo/releases/download/v1.0.23/opencode-darwin-arm64.zip"
|
||||
cmd = "./opencode"
|
||||
args = ["acp"]
|
||||
|
||||
[agent_servers.opencode.targets.darwin-x86_64]
|
||||
archive = "https://github.com/Kilo-Org/kilo/releases/download/v1.0.22/opencode-darwin-x64.zip"
|
||||
archive = "https://github.com/Kilo-Org/kilo/releases/download/v1.0.23/opencode-darwin-x64.zip"
|
||||
cmd = "./opencode"
|
||||
args = ["acp"]
|
||||
|
||||
[agent_servers.opencode.targets.linux-aarch64]
|
||||
archive = "https://github.com/Kilo-Org/kilo/releases/download/v1.0.22/opencode-linux-arm64.tar.gz"
|
||||
archive = "https://github.com/Kilo-Org/kilo/releases/download/v1.0.23/opencode-linux-arm64.tar.gz"
|
||||
cmd = "./opencode"
|
||||
args = ["acp"]
|
||||
|
||||
[agent_servers.opencode.targets.linux-x86_64]
|
||||
archive = "https://github.com/Kilo-Org/kilo/releases/download/v1.0.22/opencode-linux-x64.tar.gz"
|
||||
archive = "https://github.com/Kilo-Org/kilo/releases/download/v1.0.23/opencode-linux-x64.tar.gz"
|
||||
cmd = "./opencode"
|
||||
args = ["acp"]
|
||||
|
||||
[agent_servers.opencode.targets.windows-x86_64]
|
||||
archive = "https://github.com/Kilo-Org/kilo/releases/download/v1.0.22/opencode-windows-x64.zip"
|
||||
archive = "https://github.com/Kilo-Org/kilo/releases/download/v1.0.23/opencode-windows-x64.zip"
|
||||
cmd = "./opencode.exe"
|
||||
args = ["acp"]
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"$schema": "https://json.schemastore.org/package.json",
|
||||
"name": "@kilocode/kilo-gateway",
|
||||
"version": "1.0.22",
|
||||
"version": "1.0.23",
|
||||
"type": "module",
|
||||
"license": "MIT",
|
||||
"description": "Unified Kilo Gateway package for OpenCode - authentication, provider, and API integration",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"$schema": "https://json.schemastore.org/package.json",
|
||||
"name": "@kilocode/kilo-i18n",
|
||||
"version": "1.0.22",
|
||||
"version": "1.0.23",
|
||||
"type": "module",
|
||||
"license": "MIT",
|
||||
"description": "Kilo-specific i18n translations and overrides",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"$schema": "https://json.schemastore.org/package.json",
|
||||
"name": "@kilocode/kilo-telemetry",
|
||||
"version": "1.0.22",
|
||||
"version": "1.0.23",
|
||||
"type": "module",
|
||||
"license": "MIT",
|
||||
"description": "Telemetry for Kilo CLI - PostHog analytics integration",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@kilocode/kilo-ui",
|
||||
"version": "1.0.22",
|
||||
"version": "1.0.23",
|
||||
"type": "module",
|
||||
"license": "MIT",
|
||||
"exports": {
|
||||
|
||||
@@ -0,0 +1,370 @@
|
||||
# Telemetry Implementation Plan
|
||||
|
||||
## Architecture Overview
|
||||
|
||||
The extension uses a **proxy-based telemetry architecture**: all telemetry flows through the CLI's `kilo-telemetry` package, which uses `posthog-node` internally. The extension host and webview never talk to PostHog directly.
|
||||
|
||||
```mermaid
|
||||
graph TD
|
||||
B[Webview - SolidJS/Browser] -->|postMessage| A[Extension Host - Node.js]
|
||||
A -->|POST /telemetry/capture| CLI[CLI Server - kilo-telemetry]
|
||||
CLI -->|posthog-node| P[PostHog US]
|
||||
A -->|enriches with VS Code properties| A
|
||||
KP[KiloProvider - TelemetryPropertiesProvider] -->|getTelemetryProperties| A
|
||||
D[TelemetryService - Singleton] --> E[CLITelemetryClient]
|
||||
D --> F[DebugTelemetryClient - dev only]
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Architecture — New Extension
|
||||
|
||||
Key architectural decisions for telemetry in the new extension:
|
||||
|
||||
1. **CLI/core owns all telemetry sending** — The `kilo-telemetry` package (which uses `posthog-node` internally) is the single point of contact with PostHog. Neither the extension host nor the webview include any PostHog SDK.
|
||||
|
||||
2. **Extension proxies UI events through CLI** — The extension host forwards telemetry events to the CLI via `POST /telemetry/capture`. The CLI enriches them with server-side context and sends to PostHog.
|
||||
|
||||
3. **Webview sends events via `postMessage`** — The webview has no PostHog SDK. Instead, it fires `postMessage({ type: "telemetry", event, properties })` to the extension host, which enriches the event with VS Code properties and forwards to the CLI endpoint.
|
||||
|
||||
4. **VS Code telemetry consent is the master kill switch** — `vscode.env.telemetryLevel` is passed as a startup parameter to the CLI server. If VS Code telemetry is disabled, the CLI suppresses all PostHog sending.
|
||||
|
||||
5. **Same PostHog project as old extension** — Events go to the same PostHog project. Old vs new extension events are distinguished via properties (e.g., `extensionVersion`, `architecture: "new"`).
|
||||
|
||||
6. **All events from old plan are kept** — Every event category is preserved, including autocomplete, agent manager, marketplace, and memory tracking events.
|
||||
|
||||
---
|
||||
|
||||
## 1. Core Components
|
||||
|
||||
Telemetry is split across two layers: the **CLI** (which owns all agent/LLM/tool events) and the **extension** (which captures UI-only events and proxies them to the CLI).
|
||||
|
||||
### 1.1 Extension-side: `TelemetryProxy` — Simple Singleton
|
||||
|
||||
A single class that captures UI events from the extension host and webview, then forwards them to the CLI.
|
||||
|
||||
- One singleton instance, created at extension activation
|
||||
- `capture(event, properties)` — sends to CLI via `POST /telemetry/capture` and logs to `console.log`
|
||||
- Receives webview events via `postMessage` handler
|
||||
- Enriches all events with VS Code properties (`vscodeVersion`, `editorName`, `machineId`)
|
||||
- No inheritance, no pluggable backends, no abstract classes
|
||||
- Respects `vscode.env.telemetryLevel` — if disabled, `capture()` is a no-op
|
||||
|
||||
### 1.2 CLI-side: `kilo-telemetry` Package
|
||||
|
||||
Already exists at `packages/kilo-telemetry/`. Handles:
|
||||
|
||||
- All PostHog communication (`posthog-node`)
|
||||
- OpenTelemetry integration for AI SDK spans
|
||||
- Identity management (machineId → email upgrade on auth)
|
||||
- Privacy filtering (git info stripped, error details filtered for org users)
|
||||
- The `POST /telemetry/capture` endpoint (to be added) routes through this package
|
||||
|
||||
### 1.5 Webview Telemetry — `postMessage` Proxy
|
||||
|
||||
**File:** `webview-ui/src/utils/telemetry.ts`
|
||||
|
||||
The webview does **not** use any PostHog SDK. Instead:
|
||||
|
||||
1. The webview calls a thin helper that fires `postMessage({ type: "telemetry", event, properties })` to the extension host.
|
||||
2. The extension host receives the message, enriches it with VS Code-specific properties (from `KiloProvider.getTelemetryProperties()`), and forwards the event to the CLI via `POST /telemetry/capture`.
|
||||
3. The CLI's `kilo-telemetry` package sends the event to PostHog.
|
||||
|
||||
This keeps the webview dependency-free for analytics and ensures all telemetry flows through a single, auditable path.
|
||||
|
||||
---
|
||||
|
||||
## 2. Initialization Flow
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant Ext as extension.ts
|
||||
participant TS as TelemetryService
|
||||
participant CLI as CLI Server
|
||||
participant KP as KiloProvider
|
||||
participant WV as Webview
|
||||
|
||||
Ext->>CLI: Start CLI server (pass telemetryLevel from vscode.env)
|
||||
Ext->>TS: createInstance()
|
||||
Ext->>TS: register CLITelemetryClient (points to CLI endpoint)
|
||||
KP->>TS: setProvider(this)
|
||||
Note over TS,KP: KiloProvider implements TelemetryPropertiesProvider
|
||||
WV->>Ext: postMessage({ type: "telemetry", event, properties })
|
||||
Ext->>Ext: Enrich with VS Code properties
|
||||
Ext->>CLI: POST /telemetry/capture
|
||||
CLI->>CLI: kilo-telemetry → posthog-node → PostHog
|
||||
```
|
||||
|
||||
**Key code paths:**
|
||||
|
||||
1. `extension.ts` — Start CLI server with `telemetryLevel` from `vscode.env.telemetryLevel`
|
||||
2. `extension.ts` — `TelemetryService.createInstance()`, register `CLITelemetryClient`
|
||||
3. `KiloProvider` constructor — `TelemetryService.instance.setProvider(this)`
|
||||
4. Webview posts telemetry messages via `postMessage` → extension host → CLI
|
||||
5. Extension shutdown — `TelemetryService.instance.shutdown()`
|
||||
|
||||
---
|
||||
|
||||
## 3. Telemetry Events
|
||||
|
||||
All events are defined in `TelemetryEventName` enum (`packages/types/src/telemetry.ts`). Here are the categories:
|
||||
|
||||
### 3.1 Task Lifecycle
|
||||
|
||||
| Event | Properties | Capture Method |
|
||||
| ---------------------- | ----------------------------------- | ------------------------------ |
|
||||
| `Task Created` | `taskId` | `captureTaskCreated()` |
|
||||
| `Task Reopened` | `taskId` | `captureTaskRestarted()` |
|
||||
| `Task Completed` | `taskId` | `captureTaskCompleted()` |
|
||||
| `Conversation Message` | `taskId`, `source` (user/assistant) | `captureConversationMessage()` |
|
||||
|
||||
### 3.2 LLM & AI
|
||||
|
||||
| Event | Properties |
|
||||
| --------------------------- | ----------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `LLM Completion` | `taskId`, `inputTokens`, `outputTokens`, `cacheWriteTokens`, `cacheReadTokens`, `cost`, `completionTime`, `inferenceProvider` |
|
||||
| `Context Condensed` | `taskId`, `isAutomaticTrigger`, `usedCustomPrompt`, `usedCustomApiHandler` |
|
||||
| `Sliding Window Truncation` | `taskId` |
|
||||
|
||||
### 3.3 Tools & Modes
|
||||
|
||||
| Event | Properties |
|
||||
| ---------------------- | -------------------------------- |
|
||||
| `Tool Used` | `taskId`, `tool`, `toolProtocol` |
|
||||
| `Mode Switched` | `taskId`, `newMode` |
|
||||
| `Mode Setting Changed` | `settingName` |
|
||||
| `Custom Mode Created` | `modeSlug`, `modeName` |
|
||||
| `Code Action Used` | `actionType` |
|
||||
|
||||
### 3.4 Checkpoints
|
||||
|
||||
| Event | Properties |
|
||||
| --------------------- | ---------- |
|
||||
| `Checkpoint Created` | `taskId` |
|
||||
| `Checkpoint Restored` | `taskId` |
|
||||
| `Checkpoint Diffed` | `taskId` |
|
||||
|
||||
### 3.5 UI Interactions
|
||||
|
||||
| Event | Properties |
|
||||
| ---------------------- | ------------------- |
|
||||
| `Tab Shown` | `tab` |
|
||||
| `Title Button Clicked` | `button` |
|
||||
| `Prompt Enhanced` | `taskId` (optional) |
|
||||
|
||||
### 3.6 Marketplace
|
||||
|
||||
| Event | Properties |
|
||||
| ---------------------------- | ------------------------------------------------------------ |
|
||||
| `Marketplace Item Installed` | `itemId`, `itemType`, `itemName`, `target`, additional props |
|
||||
| `Marketplace Item Removed` | `itemId`, `itemType`, `itemName`, `target` |
|
||||
|
||||
### 3.7 Account & Auth
|
||||
|
||||
| Event | Properties |
|
||||
| ------------------------- | ---------- |
|
||||
| `Account Connect Clicked` | — |
|
||||
| `Account Connect Success` | — |
|
||||
| `Account Logout Clicked` | — |
|
||||
| `Account Logout Success` | — |
|
||||
|
||||
### 3.8 Error Tracking
|
||||
|
||||
| Event | Properties |
|
||||
| ----------------------------------- | --------------------------------------------------------------- |
|
||||
| `Schema Validation Error` | `schemaName`, `error` (Zod formatted) |
|
||||
| `Diff Application Error` | `taskId`, `consecutiveMistakeCount` |
|
||||
| `Shell Integration Error` | `taskId` |
|
||||
| `Consecutive Mistake Error` | `taskId` |
|
||||
| Exceptions via `captureException()` | Structured via `ApiProviderError` and `ConsecutiveMistakeError` |
|
||||
|
||||
### 3.9 Autocomplete (Kilo-specific)
|
||||
|
||||
| Event | Properties |
|
||||
| -------------------------------------- | ----------------------------------------------------------- |
|
||||
| `Autocomplete Suggestion Requested` | `languageId`, `modelId`, `provider`, `autocompleteType` |
|
||||
| `Autocomplete LLM Request Completed` | `latencyMs`, `cost`, `inputTokens`, `outputTokens`, context |
|
||||
| `Autocomplete LLM Request Failed` | `latencyMs`, `error`, context |
|
||||
| `Autocomplete LLM Suggestion Returned` | context, `suggestionLength` |
|
||||
| `Autocomplete Suggestion Cache Hit` | `matchType`, `suggestionLength`, context |
|
||||
| `Autocomplete Accept Suggestion` | `suggestionLength` |
|
||||
| `Autocomplete Suggestion Filtered` | `reason`, context |
|
||||
| `Autocomplete Unique Suggestion Shown` | context (only after 300ms visibility) |
|
||||
|
||||
### 3.10 Other (Kilo-specific)
|
||||
|
||||
- `Commit Message Generated`
|
||||
- `Agent Manager Opened/Session Started/Session Completed/Session Stopped/Session Error/Login Issue`
|
||||
- `Auto Purge Started/Completed/Failed`, `Manual Purge Triggered`
|
||||
- `Webview Memory Usage`, `Memory Warning Shown`
|
||||
- `Ask Approval`
|
||||
- `Notification Clicked`
|
||||
- `Suggestion Button Clicked`
|
||||
- `Free Models Link Clicked`, `Create Organization Link Clicked`
|
||||
- `Ghost Service Disabled`
|
||||
|
||||
---
|
||||
|
||||
## 4. Properties Attached to Every Event
|
||||
|
||||
Every event gets enriched with properties from `KiloProvider.getTelemetryProperties()`:
|
||||
|
||||
### Static App Properties (computed once)
|
||||
|
||||
- `appName`, `appVersion`, `vscodeVersion`, `platform`, `editorName`
|
||||
- `wrapped`, `wrapper`, `wrapperCode`, `wrapperVersion`, `wrapperTitle` — wrapper/IDE detection
|
||||
- `machineId`, `vscodeIsTelemetryEnabled`
|
||||
|
||||
### Dynamic Properties (per-event)
|
||||
|
||||
- `language`, `mode`, `taskId`, `parentTaskId`, `apiProvider`, `modelId`, `diffStrategy`, `isSubtask`
|
||||
- `currentTaskSize`, `taskHistorySize`, `toolStyle` (XML vs native)
|
||||
- `todos` object: `{ total, completed, inProgress, pending }`
|
||||
- `memory` (process memory usage)
|
||||
- `fastApply` settings, `openRouter` routing config, `autoApprove` settings
|
||||
- `kilocodeOrganizationId` (when present)
|
||||
|
||||
### Git Properties (computed once)
|
||||
|
||||
- `repositoryUrl`, `repositoryName`, `defaultBranch` (filtered out before sending by telemetry client)
|
||||
|
||||
---
|
||||
|
||||
## 5. Privacy & Consent
|
||||
|
||||
### User Opt-in Model
|
||||
|
||||
- Three states: `"unset"`, `"enabled"`, `"disabled"` (see `TelemetrySetting` type)
|
||||
- Full telemetry enabled only when: **VS Code telemetry level = `"all"`** AND **user setting ≠ `"disabled"`**
|
||||
- Wrapper apps can force telemetry enabled via environment variable
|
||||
|
||||
### VS Code Telemetry Level as Master Control
|
||||
|
||||
`vscode.env.telemetryLevel` is passed as a startup parameter to the CLI server.
|
||||
|
||||
Telemetry is only active when `vscode.env.telemetryLevel` is `"all"`. Any other level (`"off"`, `"crash"`, `"error"`) disables all telemetry — the CLI startup parameter will indicate telemetry is disabled and no events will be sent.
|
||||
|
||||
This ensures the user's VS Code telemetry preference is respected across the entire stack — webview, extension host, and CLI.
|
||||
|
||||
### Identity Management
|
||||
|
||||
- Default: `vscode.env.machineId` (anonymous)
|
||||
- Authenticated: user email fetched from `api.kilo.ai/api/profile` via `updateIdentity()`
|
||||
- Identity updates are race-safe (counter-based)
|
||||
|
||||
### Data Filtering
|
||||
|
||||
- Git repository info is **always** stripped before sending
|
||||
- Error details and file paths are stripped for **organization** users
|
||||
- `TASK_MESSAGE` events are excluded from PostHog (contain full conversation)
|
||||
- Expected API errors (429, 402) are not reported via `shouldReportApiErrorToTelemetry()`
|
||||
|
||||
### Identity Continuity (Old → New Extension)
|
||||
|
||||
Users switching from the old extension to the new one must not appear as two different users in PostHog.
|
||||
|
||||
- **Old extension**: Uses `vscode.env.machineId` as PostHog `distinct_id`
|
||||
- **CLI (`kilo-telemetry`)**: Generates a random UUID stored in `~/.config/kilo/telemetry-id`
|
||||
- **Authenticated users**: Both old and new call `alias()` on auth, which merges identities server-side — no issue
|
||||
- **Unauthenticated users**: Will have completely different `distinct_id` values with no link
|
||||
|
||||
**Action required:** The extension must pass `vscode.env.machineId` to the CLI at startup so `kilo-telemetry` uses it as the `distinct_id` instead of (or aliased with) its own generated UUID. This ensures unauthenticated users maintain identity continuity.
|
||||
|
||||
---
|
||||
|
||||
## 6. Structured Error Classes
|
||||
|
||||
The extension defines reusable error classes for structured exception tracking:
|
||||
|
||||
### `ApiProviderError`
|
||||
|
||||
```typescript
|
||||
class ApiProviderError extends Error {
|
||||
provider: string
|
||||
modelId: string
|
||||
operation: string
|
||||
errorCode?: number
|
||||
}
|
||||
```
|
||||
|
||||
### `ConsecutiveMistakeError`
|
||||
|
||||
```typescript
|
||||
class ConsecutiveMistakeError extends Error {
|
||||
taskId: string
|
||||
consecutiveMistakeCount: number
|
||||
consecutiveMistakeLimit: number
|
||||
reason: "no_tools_used" | "tool_repetition" | "unknown"
|
||||
provider?: string
|
||||
modelId?: string
|
||||
}
|
||||
```
|
||||
|
||||
Both have type guards (`isApiProviderError()`, `isConsecutiveMistakeError()`) and property extractors for telemetry.
|
||||
|
||||
---
|
||||
|
||||
## 7. Implementation Recommendations for New Extension
|
||||
|
||||
1. **Use `kilo-telemetry` via CLI proxy** — all PostHog communication goes through the CLI's `POST /telemetry/capture` endpoint. The extension does not include `posthog-node` or `posthog-js` directly.
|
||||
2. **Singleton service pattern** — single `TelemetryService` instance, multiple pluggable clients
|
||||
3. **Properties provider pattern** — `KiloProvider` implements `TelemetryPropertiesProvider` to inject VS Code context into every event
|
||||
4. **Typed events** — all event names in an enum, with typed capture methods on the service
|
||||
5. **Event subscription/filtering** — clients can include/exclude specific events
|
||||
6. **Property filtering** — per-client property filtering (privacy controls)
|
||||
7. **Dual opt-in** — respect both IDE-level and extension-level telemetry settings
|
||||
8. **Identity upgrade** — anonymous by default, upgrade to user identity on auth
|
||||
9. **Graceful degradation** — never crash on telemetry failures; all capture calls are fire-and-forget
|
||||
10. **Debug client** — separate console-logging client for development
|
||||
|
||||
---
|
||||
|
||||
## 8. Package Dependencies
|
||||
|
||||
### Server-side (CLI)
|
||||
|
||||
- `kilo-telemetry` package (already in monorepo, uses `posthog-node` internally)
|
||||
|
||||
### Client-side (Webview)
|
||||
|
||||
- No PostHog SDK needed — telemetry events are sent via `postMessage` to the extension host
|
||||
|
||||
### Extension Host
|
||||
|
||||
- No PostHog SDK — events are proxied through the CLI's `POST /telemetry/capture` endpoint
|
||||
|
||||
### Shared Types
|
||||
|
||||
- `zod` — for schema validation of telemetry properties
|
||||
|
||||
---
|
||||
|
||||
## 9. Gateway Integration
|
||||
|
||||
### 9.1 `editor_name` Header
|
||||
|
||||
The Kilo Gateway records `editor_name` on every API request for cost attribution by feature. The CLI currently defaults to `"Kilo CLI"`, so when the extension spawns the CLI server, all gateway usage gets misattributed as CLI usage.
|
||||
|
||||
**Action required:** The extension must pass `editor_name` (e.g., `"Kilo VSCode"`) as a CLI startup parameter so the gateway correctly attributes requests to the VS Code extension.
|
||||
|
||||
### 9.2 `platform` Field in Session Ingest
|
||||
|
||||
The session ingest API records a `platform` field per session, which also defaults to `"cli"`. This must be set to identify the new extension (e.g., `"vscode"`) so sessions are correctly attributed.
|
||||
|
||||
**Action required:** Pass `platform` as a CLI startup parameter alongside `editor_name`.
|
||||
|
||||
### 9.3 `X-KILOCODE-TASKID` Persistence (Nice-to-have)
|
||||
|
||||
The gateway already sends `X-KILOCODE-TASKID` (session ID) on every request, but the backend doesn't persist it in `microdollar_usage_metadata`. If persisted, usage records could be joined to sessions for deterministic cost attribution, eliminating the time-matching heuristic currently used for ~20% of records. This is a backend change, not an extension change.
|
||||
|
||||
---
|
||||
|
||||
## 10. Schema Change Process
|
||||
|
||||
If any telemetry events or properties are intentionally dropped, renamed, or have their semantics changed, the analytics team (Pedro) must be notified **before** the change ships. This allows analytics dashboards and queries to be updated in parallel.
|
||||
|
||||
Checklist for schema changes:
|
||||
|
||||
- [ ] Document the change in this file
|
||||
- [ ] Create a GitHub issue tagged with `telemetry`
|
||||
- [ ] Notify @pedroheyerdahl in the PR description
|
||||
@@ -96,6 +96,7 @@ The rebuild has a working foundation:
|
||||
| [Skills System](non-agent-features/skills-system.md) | ❌ Not started | No skill discovery, management, or hot-reload in extension. | CLI has skills runtime; extension provides packaging/UI | P2 |
|
||||
| [Speech-to-Text](non-agent-features/speech-to-text.md) | ❌ Not started | No voice input or streaming STT. | Webview (mic capture); CLI-compatible STT optional | P3 |
|
||||
| [Task History](non-agent-features/task-history.md) | 🔨 Partial | Session list exists but lacks search, metadata, and full persistence. [#167](https://github.com/Kilo-Org/kilo/issues/167) | CLI session storage; extension provides history UI | P1 |
|
||||
| [Telemetry](non-agent-features/telemetry.md) | ❌ Not started | Dual-layer telemetry (PostHog Node server-side + PostHog JS client-side) for extension usage, errors, LLM completions, and AI interactions. Includes privacy controls, typed events, and structured error tracking. See detailed plan. | Extension-side (PostHog + kilo-telemetry) | P1 |
|
||||
| [Terminal / Shell Integration](non-agent-features/terminal-shell-integration.md) | ❌ Not started | No VS Code terminal integration for command execution display, exit code tracking, or working directory changes. | CLI executes commands; extension provides terminal UX | P1 |
|
||||
|
||||
---
|
||||
|
||||
@@ -128,6 +128,31 @@ async function main() {
|
||||
plugins: [esbuildProblemMatcherPlugin],
|
||||
})
|
||||
|
||||
// Build Agent Manager webview (SolidJS, shares components with sidebar)
|
||||
const agentManagerCtx = await esbuild.context({
|
||||
entryPoints: ["webview-ui/agent-manager/index.tsx"],
|
||||
bundle: true,
|
||||
format: "iife",
|
||||
minify: production,
|
||||
sourcemap: !production,
|
||||
sourcesContent: false,
|
||||
platform: "browser",
|
||||
outfile: "dist/agent-manager.js",
|
||||
logLevel: "silent",
|
||||
loader: {
|
||||
".woff": "file",
|
||||
".woff2": "file",
|
||||
".ttf": "file",
|
||||
},
|
||||
plugins: [
|
||||
solidDedupePlugin,
|
||||
pierreWorkerStubPlugin,
|
||||
cssPackageResolvePlugin,
|
||||
solidPlugin(),
|
||||
esbuildProblemMatcherPlugin,
|
||||
],
|
||||
})
|
||||
|
||||
// Build webview
|
||||
const webviewCtx = await esbuild.context({
|
||||
entryPoints: ["webview-ui/src/index.tsx"],
|
||||
@@ -154,10 +179,10 @@ async function main() {
|
||||
})
|
||||
|
||||
if (watch) {
|
||||
await Promise.all([extensionCtx.watch(), webviewCtx.watch()])
|
||||
await Promise.all([extensionCtx.watch(), webviewCtx.watch(), agentManagerCtx.watch()])
|
||||
} else {
|
||||
await Promise.all([extensionCtx.rebuild(), webviewCtx.rebuild()])
|
||||
await Promise.all([extensionCtx.dispose(), webviewCtx.dispose()])
|
||||
await Promise.all([extensionCtx.rebuild(), webviewCtx.rebuild(), agentManagerCtx.rebuild()])
|
||||
await Promise.all([extensionCtx.dispose(), webviewCtx.dispose(), agentManagerCtx.dispose()])
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
"name": "kilo-code",
|
||||
"displayName": "Kilo Code",
|
||||
"description": "Kilo Code AI Agent & Autocomplete",
|
||||
"version": "7.0.22",
|
||||
"version": "7.0.23",
|
||||
"publisher": "kilocode",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
|
||||
@@ -1,97 +0,0 @@
|
||||
import * as vscode from "vscode"
|
||||
|
||||
/**
|
||||
* AgentManagerProvider manages the Agent Manager webview panel.
|
||||
* Opens in the main editor area (not sidebar).
|
||||
*/
|
||||
export class AgentManagerProvider implements vscode.Disposable {
|
||||
public static readonly viewType = "kilo-code.new.AgentManagerPanel"
|
||||
|
||||
private panel: vscode.WebviewPanel | undefined
|
||||
private panelDisposables: vscode.Disposable[] = []
|
||||
|
||||
constructor(private readonly _extensionUri: vscode.Uri) {}
|
||||
|
||||
/**
|
||||
* Open or focus the Agent Manager panel
|
||||
*/
|
||||
public openPanel(): void {
|
||||
if (this.panel) {
|
||||
this.panel.reveal(vscode.ViewColumn.One)
|
||||
return
|
||||
}
|
||||
|
||||
this.panel = vscode.window.createWebviewPanel(
|
||||
AgentManagerProvider.viewType,
|
||||
"Agent Manager",
|
||||
vscode.ViewColumn.One,
|
||||
{
|
||||
enableScripts: true,
|
||||
retainContextWhenHidden: true,
|
||||
localResourceRoots: [this._extensionUri],
|
||||
},
|
||||
)
|
||||
|
||||
this.panel.webview.html = this._getHtmlForWebview(this.panel.webview)
|
||||
|
||||
this.panel.onDidDispose(
|
||||
() => {
|
||||
// Clean up panel-specific disposables when panel is closed
|
||||
this.panelDisposables.forEach((d) => d.dispose())
|
||||
this.panelDisposables = []
|
||||
this.panel = undefined
|
||||
},
|
||||
null,
|
||||
this.panelDisposables,
|
||||
)
|
||||
|
||||
console.log("Agent Manager panel opened")
|
||||
}
|
||||
|
||||
private _getHtmlForWebview(webview: vscode.Webview): string {
|
||||
const nonce = getNonce()
|
||||
|
||||
return `<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta http-equiv="Content-Security-Policy" content="default-src 'none'; style-src 'unsafe-inline'; script-src 'nonce-${nonce}';">
|
||||
<title>Agent Manager</title>
|
||||
<style>
|
||||
body {
|
||||
padding: 20px;
|
||||
color: var(--vscode-foreground);
|
||||
font-family: var(--vscode-font-family);
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
min-height: 100vh;
|
||||
margin: 0;
|
||||
}
|
||||
h1 {
|
||||
font-size: 2em;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Agent Manager</h1>
|
||||
</body>
|
||||
</html>`
|
||||
}
|
||||
|
||||
public dispose(): void {
|
||||
this.panel?.dispose()
|
||||
this.panelDisposables.forEach((d) => d.dispose())
|
||||
this.panelDisposables = []
|
||||
}
|
||||
}
|
||||
|
||||
function getNonce(): string {
|
||||
let text = ""
|
||||
const possible = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
|
||||
for (let i = 0; i < 32; i++) {
|
||||
text += possible.charAt(Math.floor(Math.random() * possible.length))
|
||||
}
|
||||
return text
|
||||
}
|
||||
@@ -3,6 +3,7 @@ import { z } from "zod"
|
||||
import { type HttpClient, type SessionInfo, type SSEEvent, type KiloConnectionService } from "./services/cli-backend"
|
||||
import { handleChatCompletionRequest } from "./services/autocomplete/chat-autocomplete/handleChatCompletionRequest"
|
||||
import { handleChatCompletionAccepted } from "./services/autocomplete/chat-autocomplete/handleChatCompletionAccepted"
|
||||
import { buildWebviewHtml } from "./utils"
|
||||
|
||||
export class KiloProvider implements vscode.WebviewViewProvider {
|
||||
public static readonly viewType = "kilo-code.new.sidebarView"
|
||||
@@ -143,6 +144,17 @@ export class KiloProvider implements vscode.WebviewViewProvider {
|
||||
this.initializeConnection()
|
||||
}
|
||||
|
||||
/**
|
||||
* Attach to a webview that already has its own HTML set.
|
||||
* Sets up message handling and connection without overriding HTML content.
|
||||
*/
|
||||
public attachToWebview(webview: vscode.Webview): void {
|
||||
this.isWebviewReady = false
|
||||
this.webview = webview
|
||||
this.setupWebviewMessageHandler(webview)
|
||||
this.initializeConnection()
|
||||
}
|
||||
|
||||
/**
|
||||
* Set up the shared message handler for both sidebar and tab webviews.
|
||||
* Handles ALL message types so tabs have full functionality.
|
||||
@@ -1270,64 +1282,14 @@ export class KiloProvider implements vscode.WebviewViewProvider {
|
||||
}
|
||||
|
||||
private _getHtmlForWebview(webview: vscode.Webview): string {
|
||||
const scriptUri = webview.asWebviewUri(vscode.Uri.joinPath(this.extensionUri, "dist", "webview.js"))
|
||||
const styleUri = webview.asWebviewUri(vscode.Uri.joinPath(this.extensionUri, "dist", "webview.css"))
|
||||
const iconsBaseUri = webview.asWebviewUri(vscode.Uri.joinPath(this.extensionUri, "assets", "icons"))
|
||||
|
||||
const nonce = getNonce()
|
||||
|
||||
// CSP allows:
|
||||
// - default-src 'none': Block everything by default
|
||||
// - style-src: Allow inline styles and our CSS file
|
||||
// - script-src 'nonce-...': Only allow scripts with our nonce
|
||||
// - connect-src: Allow connections to localhost for API calls
|
||||
// - img-src: Allow images from webview and data URIs
|
||||
const csp = [
|
||||
"default-src 'none'",
|
||||
`style-src 'unsafe-inline' ${webview.cspSource}`,
|
||||
`script-src 'nonce-${nonce}' 'wasm-unsafe-eval'`,
|
||||
`font-src ${webview.cspSource}`,
|
||||
"connect-src http://127.0.0.1:* http://localhost:* ws://127.0.0.1:* ws://localhost:*",
|
||||
`img-src ${webview.cspSource} data: https:`,
|
||||
].join("; ")
|
||||
|
||||
return `<!DOCTYPE html>
|
||||
<html lang="en" data-theme="kilo-vscode">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta http-equiv="Content-Security-Policy" content="${csp}">
|
||||
<title>Kilo Code</title>
|
||||
<link rel="stylesheet" href="${styleUri}">
|
||||
<style>
|
||||
html, body {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
}
|
||||
body {
|
||||
background-color: var(--vscode-editor-background);
|
||||
color: var(--vscode-foreground);
|
||||
font-family: var(--vscode-font-family);
|
||||
}
|
||||
#root {
|
||||
height: 100%;
|
||||
}
|
||||
.container {
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100vh;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
<script nonce="${nonce}">window.ICONS_BASE_URI = "${iconsBaseUri}";</script>
|
||||
<script nonce="${nonce}" src="${scriptUri}"></script>
|
||||
</body>
|
||||
</html>`
|
||||
return buildWebviewHtml(webview, {
|
||||
scriptUri: webview.asWebviewUri(vscode.Uri.joinPath(this.extensionUri, "dist", "webview.js")),
|
||||
styleUri: webview.asWebviewUri(vscode.Uri.joinPath(this.extensionUri, "dist", "webview.css")),
|
||||
iconsBaseUri: webview.asWebviewUri(vscode.Uri.joinPath(this.extensionUri, "assets", "icons")),
|
||||
title: "Kilo Code",
|
||||
port: this.connectionService.getServerInfo()?.port,
|
||||
extraStyles: `.container { height: 100%; display: flex; flex-direction: column; height: 100vh; }`,
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1341,12 +1303,3 @@ export class KiloProvider implements vscode.WebviewViewProvider {
|
||||
this.trackedSessionIds.clear()
|
||||
}
|
||||
}
|
||||
|
||||
function getNonce(): string {
|
||||
let text = ""
|
||||
const possible = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
|
||||
for (let i = 0; i < 32; i++) {
|
||||
text += possible.charAt(Math.floor(Math.random() * possible.length))
|
||||
}
|
||||
return text
|
||||
}
|
||||
|
||||
@@ -0,0 +1,90 @@
|
||||
import * as vscode from "vscode"
|
||||
import type { KiloConnectionService } from "../services/cli-backend"
|
||||
import { KiloProvider } from "../KiloProvider"
|
||||
import { buildWebviewHtml } from "../utils"
|
||||
|
||||
/**
|
||||
* AgentManagerProvider opens the Agent Manager panel.
|
||||
*
|
||||
* The panel runs a SolidJS app that shares the sidebar's component library
|
||||
* and provider chain (VSCodeProvider, SessionProvider, etc.). It renders a
|
||||
* session list on the left and the ChatView on the right.
|
||||
*
|
||||
* Communication with kilo serve happens through a KiloProvider instance,
|
||||
* identical to the sidebar and "Open in Tab" panels.
|
||||
*/
|
||||
export class AgentManagerProvider implements vscode.Disposable {
|
||||
public static readonly viewType = "kilo-code.new.AgentManagerPanel"
|
||||
|
||||
private panel: vscode.WebviewPanel | undefined
|
||||
private provider: KiloProvider | undefined
|
||||
private outputChannel: vscode.OutputChannel
|
||||
|
||||
constructor(
|
||||
private readonly extensionUri: vscode.Uri,
|
||||
private readonly connectionService: KiloConnectionService,
|
||||
) {
|
||||
this.outputChannel = vscode.window.createOutputChannel("Kilo Agent Manager")
|
||||
}
|
||||
|
||||
private log(...args: unknown[]) {
|
||||
const msg = args.map((a) => (typeof a === "string" ? a : JSON.stringify(a))).join(" ")
|
||||
this.outputChannel.appendLine(`${new Date().toISOString()} ${msg}`)
|
||||
}
|
||||
|
||||
public openPanel(): void {
|
||||
if (this.panel) {
|
||||
this.log("Panel already open, revealing")
|
||||
this.panel.reveal(vscode.ViewColumn.One)
|
||||
return
|
||||
}
|
||||
this.log("Opening Agent Manager panel")
|
||||
|
||||
this.panel = vscode.window.createWebviewPanel(
|
||||
AgentManagerProvider.viewType,
|
||||
"Agent Manager",
|
||||
vscode.ViewColumn.One,
|
||||
{
|
||||
enableScripts: true,
|
||||
retainContextWhenHidden: true,
|
||||
localResourceRoots: [this.extensionUri],
|
||||
},
|
||||
)
|
||||
|
||||
this.panel.iconPath = {
|
||||
light: vscode.Uri.joinPath(this.extensionUri, "assets", "icons", "kilo-light.svg"),
|
||||
dark: vscode.Uri.joinPath(this.extensionUri, "assets", "icons", "kilo-dark.svg"),
|
||||
}
|
||||
|
||||
// Set the HTML first (agent-manager bundle instead of the sidebar's webview bundle)
|
||||
this.panel.webview.html = this.getHtml(this.panel.webview)
|
||||
|
||||
// Attach a KiloProvider for message handling, SSE subscription, and connection init
|
||||
// without overriding our HTML.
|
||||
this.provider = new KiloProvider(this.extensionUri, this.connectionService)
|
||||
this.provider.attachToWebview(this.panel.webview)
|
||||
|
||||
this.panel.onDidDispose(() => {
|
||||
this.log("Panel disposed")
|
||||
this.provider?.dispose()
|
||||
this.provider = undefined
|
||||
this.panel = undefined
|
||||
})
|
||||
}
|
||||
|
||||
private getHtml(webview: vscode.Webview): string {
|
||||
return buildWebviewHtml(webview, {
|
||||
scriptUri: webview.asWebviewUri(vscode.Uri.joinPath(this.extensionUri, "dist", "agent-manager.js")),
|
||||
styleUri: webview.asWebviewUri(vscode.Uri.joinPath(this.extensionUri, "dist", "agent-manager.css")),
|
||||
iconsBaseUri: webview.asWebviewUri(vscode.Uri.joinPath(this.extensionUri, "assets", "icons")),
|
||||
title: "Agent Manager",
|
||||
port: this.connectionService.getServerInfo()?.port,
|
||||
})
|
||||
}
|
||||
|
||||
public dispose(): void {
|
||||
this.provider?.dispose()
|
||||
this.panel?.dispose()
|
||||
this.outputChannel.dispose()
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
import * as vscode from "vscode"
|
||||
import { KiloProvider } from "./KiloProvider"
|
||||
import { AgentManagerProvider } from "./AgentManagerProvider"
|
||||
import { AgentManagerProvider } from "./agent-manager/AgentManagerProvider"
|
||||
import { EXTENSION_DISPLAY_NAME } from "./constants"
|
||||
import { KiloConnectionService } from "./services/cli-backend"
|
||||
import { registerAutocompleteProvider } from "./services/autocomplete"
|
||||
@@ -35,7 +35,7 @@ export function activate(context: vscode.ExtensionContext) {
|
||||
)
|
||||
|
||||
// Create Agent Manager provider for editor panel
|
||||
const agentManagerProvider = new AgentManagerProvider(context.extensionUri)
|
||||
const agentManagerProvider = new AgentManagerProvider(context.extensionUri, connectionService)
|
||||
context.subscriptions.push(agentManagerProvider)
|
||||
|
||||
// Register toolbar button command handlers
|
||||
|
||||
@@ -0,0 +1,64 @@
|
||||
import * as crypto from "crypto"
|
||||
import * as vscode from "vscode"
|
||||
|
||||
export function getNonce(): string {
|
||||
return crypto.randomBytes(16).toString("hex")
|
||||
}
|
||||
|
||||
export function buildWebviewHtml(
|
||||
webview: vscode.Webview,
|
||||
opts: {
|
||||
scriptUri: vscode.Uri
|
||||
styleUri: vscode.Uri
|
||||
iconsBaseUri: vscode.Uri
|
||||
title: string
|
||||
port?: number
|
||||
extraStyles?: string
|
||||
},
|
||||
): string {
|
||||
const nonce = getNonce()
|
||||
const connectSrc = opts.port
|
||||
? `http://127.0.0.1:${opts.port} http://localhost:${opts.port} ws://127.0.0.1:${opts.port} ws://localhost:${opts.port}`
|
||||
: "http://127.0.0.1:* http://localhost:* ws://127.0.0.1:* ws://localhost:*"
|
||||
|
||||
const csp = [
|
||||
"default-src 'none'",
|
||||
`style-src 'unsafe-inline' ${webview.cspSource}`,
|
||||
`script-src 'nonce-${nonce}' 'wasm-unsafe-eval'`,
|
||||
`font-src ${webview.cspSource}`,
|
||||
`connect-src ${connectSrc}`,
|
||||
`img-src ${webview.cspSource} data: https:`,
|
||||
].join("; ")
|
||||
|
||||
return `<!DOCTYPE html>
|
||||
<html lang="en" data-theme="kilo-vscode">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta http-equiv="Content-Security-Policy" content="${csp}">
|
||||
<link rel="stylesheet" href="${opts.styleUri}">
|
||||
<title>${opts.title}</title>
|
||||
<style>
|
||||
html, body {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
}
|
||||
body {
|
||||
background-color: var(--vscode-editor-background);
|
||||
color: var(--vscode-foreground);
|
||||
font-family: var(--vscode-font-family);
|
||||
}
|
||||
#root {
|
||||
height: 100%;
|
||||
}${opts.extraStyles ? `\n ${opts.extraStyles}` : ""}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
<script nonce="${nonce}">window.ICONS_BASE_URI = "${opts.iconsBaseUri}";</script>
|
||||
<script nonce="${nonce}" src="${opts.scriptUri}"></script>
|
||||
</body>
|
||||
</html>`
|
||||
}
|
||||
@@ -0,0 +1,84 @@
|
||||
// Agent Manager root component
|
||||
|
||||
import { Component, For } from "solid-js"
|
||||
import { ThemeProvider } from "@kilocode/kilo-ui/theme"
|
||||
import { DialogProvider } from "@kilocode/kilo-ui/context/dialog"
|
||||
import { MarkedProvider } from "@kilocode/kilo-ui/context/marked"
|
||||
import { CodeComponentProvider } from "@kilocode/kilo-ui/context/code"
|
||||
import { DiffComponentProvider } from "@kilocode/kilo-ui/context/diff"
|
||||
import { Code } from "@kilocode/kilo-ui/code"
|
||||
import { Diff } from "@kilocode/kilo-ui/diff"
|
||||
import { Toast } from "@kilocode/kilo-ui/toast"
|
||||
import { Button } from "@kilocode/kilo-ui/button"
|
||||
import { VSCodeProvider } from "../src/context/vscode"
|
||||
import { ServerProvider } from "../src/context/server"
|
||||
import { ProviderProvider } from "../src/context/provider"
|
||||
import { ConfigProvider } from "../src/context/config"
|
||||
import { SessionProvider, useSession } from "../src/context/session"
|
||||
import { ChatView } from "../src/components/chat"
|
||||
import { LanguageBridge, DataBridge } from "../src/App"
|
||||
import { formatRelativeDate } from "../src/utils/date"
|
||||
import "./agent-manager.css"
|
||||
|
||||
const AgentManagerContent: Component = () => {
|
||||
const session = useSession()
|
||||
|
||||
return (
|
||||
<div class="am-layout">
|
||||
<div class="am-sidebar">
|
||||
<div class="am-sidebar-header">AGENT MANAGER</div>
|
||||
<Button variant="primary" size="large" onClick={() => session.clearCurrentSession()}>
|
||||
+ New Agent
|
||||
</Button>
|
||||
<div class="am-sessions-header">SESSIONS</div>
|
||||
<div class="am-list">
|
||||
<For each={session.sessions()}>
|
||||
{(s) => (
|
||||
<button
|
||||
class={`am-item ${s.id === session.currentSessionID() ? "am-item-active" : ""}`}
|
||||
onClick={() => session.selectSession(s.id)}
|
||||
>
|
||||
<span class="am-item-title">{s.title || "Untitled"}</span>
|
||||
<span class="am-item-time">{formatRelativeDate(s.updatedAt)}</span>
|
||||
</button>
|
||||
)}
|
||||
</For>
|
||||
</div>
|
||||
</div>
|
||||
<div class="am-detail">
|
||||
<ChatView onSelectSession={(id) => session.selectSession(id)} />
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export const AgentManagerApp: Component = () => {
|
||||
return (
|
||||
<ThemeProvider defaultTheme="kilo-vscode">
|
||||
<DialogProvider>
|
||||
<VSCodeProvider>
|
||||
<ServerProvider>
|
||||
<LanguageBridge>
|
||||
<MarkedProvider>
|
||||
<DiffComponentProvider component={Diff}>
|
||||
<CodeComponentProvider component={Code}>
|
||||
<ProviderProvider>
|
||||
<ConfigProvider>
|
||||
<SessionProvider>
|
||||
<DataBridge>
|
||||
<AgentManagerContent />
|
||||
</DataBridge>
|
||||
</SessionProvider>
|
||||
</ConfigProvider>
|
||||
</ProviderProvider>
|
||||
</CodeComponentProvider>
|
||||
</DiffComponentProvider>
|
||||
</MarkedProvider>
|
||||
</LanguageBridge>
|
||||
</ServerProvider>
|
||||
</VSCodeProvider>
|
||||
<Toast.Region />
|
||||
</DialogProvider>
|
||||
</ThemeProvider>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,98 @@
|
||||
/* Agent Manager layout */
|
||||
|
||||
.am-layout {
|
||||
display: flex;
|
||||
height: 100vh;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.am-sidebar {
|
||||
width: 260px;
|
||||
min-width: 200px;
|
||||
border-right: 1px solid var(--border-weak-base, var(--vscode-panel-border, #333));
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
flex-shrink: 0;
|
||||
background: var(--surface-base, var(--vscode-sideBar-background, #181818));
|
||||
padding: 8px;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.am-sidebar-header {
|
||||
font-size: 11px;
|
||||
font-weight: 600;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.5px;
|
||||
color: var(--text-weak, var(--vscode-descriptionForeground, #888));
|
||||
padding: 4px 8px 0;
|
||||
}
|
||||
|
||||
.am-sessions-header {
|
||||
font-size: 11px;
|
||||
font-weight: 600;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.5px;
|
||||
color: var(--text-weak, var(--vscode-descriptionForeground, #888));
|
||||
padding: 4px 8px 0;
|
||||
}
|
||||
|
||||
.am-list {
|
||||
flex: 1;
|
||||
overflow-y: auto;
|
||||
min-height: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 2px;
|
||||
}
|
||||
|
||||
.am-item {
|
||||
display: flex;
|
||||
align-items: baseline;
|
||||
gap: 8px;
|
||||
padding: 6px 8px;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
background: none;
|
||||
border: none;
|
||||
width: 100%;
|
||||
text-align: left;
|
||||
color: var(--text-base, var(--vscode-foreground));
|
||||
font-size: 13px;
|
||||
font-family: inherit;
|
||||
}
|
||||
|
||||
.am-item:hover {
|
||||
background: var(--surface-inset-base-hover, var(--vscode-list-hoverBackground, #2a2d2e));
|
||||
}
|
||||
|
||||
.am-item-active {
|
||||
background: var(--surface-interactive-base, var(--vscode-list-activeSelectionBackground, #04395e));
|
||||
color: var(--text-on-interactive-base, var(--vscode-list-activeSelectionForeground, #fff));
|
||||
}
|
||||
|
||||
.am-item-title {
|
||||
flex: 1;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.am-item-time {
|
||||
font-size: 11px;
|
||||
white-space: nowrap;
|
||||
color: var(--text-weaker, var(--vscode-descriptionForeground, #888));
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.am-item-active .am-item-time {
|
||||
color: var(--text-on-interactive-weak, rgba(255, 255, 255, 0.7));
|
||||
}
|
||||
|
||||
.am-detail {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
overflow: hidden;
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
// Agent Manager SolidJS entry point
|
||||
// Shares components and providers with the sidebar webview
|
||||
// webviewReady is sent by ServerProvider inside the component tree
|
||||
|
||||
import { render } from "solid-js/web"
|
||||
import "@kilocode/kilo-ui/styles"
|
||||
import "../src/styles/chat.css"
|
||||
import { AgentManagerApp } from "./AgentManagerApp"
|
||||
|
||||
const root = document.getElementById("root")
|
||||
if (root) {
|
||||
render(() => <AgentManagerApp />, root)
|
||||
}
|
||||
@@ -45,26 +45,27 @@ const DummyView: Component<{ title: string }> = (props) => {
|
||||
/**
|
||||
* Bridge our session store to the DataProvider's expected Data shape.
|
||||
*/
|
||||
const DataBridge: Component<{ children: any }> = (props) => {
|
||||
export const DataBridge: Component<{ children: any }> = (props) => {
|
||||
const session = useSession()
|
||||
|
||||
const data = createMemo(() => ({
|
||||
session: session.sessions().map((s) => ({ ...s, id: s.id, role: "user" as const })),
|
||||
session_status: {} as Record<string, any>,
|
||||
session_diff: {} as Record<string, any[]>,
|
||||
message: {
|
||||
[session.currentSessionID() ?? ""]: session.messages() as unknown as SDKMessage[],
|
||||
},
|
||||
part: Object.fromEntries(
|
||||
session
|
||||
.messages()
|
||||
.map((msg) => [msg.id, session.getParts(msg.id) as unknown as SDKPart[]])
|
||||
.filter(([, parts]) => (parts as SDKPart[]).length > 0),
|
||||
),
|
||||
permission: {
|
||||
[session.currentSessionID() ?? ""]: session.permissions() as unknown as any[],
|
||||
},
|
||||
}))
|
||||
const data = createMemo(() => {
|
||||
const id = session.currentSessionID()
|
||||
return {
|
||||
session: session.sessions().map((s) => ({ ...s, id: s.id, role: "user" as const })),
|
||||
session_status: {} as Record<string, any>,
|
||||
session_diff: {} as Record<string, any[]>,
|
||||
message: id ? { [id]: session.messages() as unknown as SDKMessage[] } : {},
|
||||
part: id
|
||||
? Object.fromEntries(
|
||||
session
|
||||
.messages()
|
||||
.map((msg) => [msg.id, session.getParts(msg.id) as unknown as SDKPart[]])
|
||||
.filter(([, parts]) => (parts as SDKPart[]).length > 0),
|
||||
)
|
||||
: {},
|
||||
permission: id ? { [id]: session.permissions() as unknown as any[] } : {},
|
||||
}
|
||||
})
|
||||
|
||||
const respond = (input: { sessionID: string; permissionID: string; response: "once" | "always" | "reject" }) => {
|
||||
session.respondToPermission(input.permissionID, input.response)
|
||||
@@ -81,7 +82,7 @@ const DataBridge: Component<{ children: any }> = (props) => {
|
||||
* Wraps children in LanguageProvider, passing server-side language info.
|
||||
* Must be below ServerProvider in the hierarchy.
|
||||
*/
|
||||
const LanguageBridge: Component<{ children: any }> = (props) => {
|
||||
export const LanguageBridge: Component<{ children: any }> = (props) => {
|
||||
const server = useServer()
|
||||
return (
|
||||
<LanguageProvider vscodeLanguage={server.vscodeLanguage} languageOverride={server.languageOverride}>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"$schema": "https://json.schemastore.org/package.json",
|
||||
"version": "1.0.22",
|
||||
"version": "1.0.23",
|
||||
"name": "@kilocode/cli",
|
||||
"type": "module",
|
||||
"license": "MIT",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"$schema": "https://json.schemastore.org/package.json",
|
||||
"name": "@kilocode/plugin",
|
||||
"version": "1.0.22",
|
||||
"version": "1.0.23",
|
||||
"type": "module",
|
||||
"license": "MIT",
|
||||
"repository": {
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
"exports": {
|
||||
".": "./src/index.ts"
|
||||
},
|
||||
"version": "1.0.22",
|
||||
"version": "1.0.23",
|
||||
"dependencies": {},
|
||||
"peerDependencies": {}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"$schema": "https://json.schemastore.org/package.json",
|
||||
"name": "@kilocode/sdk",
|
||||
"version": "1.0.22",
|
||||
"version": "1.0.23",
|
||||
"type": "module",
|
||||
"license": "MIT",
|
||||
"repository": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@opencode-ai/ui",
|
||||
"version": "1.0.22",
|
||||
"version": "1.0.23",
|
||||
"type": "module",
|
||||
"license": "MIT",
|
||||
"exports": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@opencode-ai/util",
|
||||
"version": "1.0.22",
|
||||
"version": "1.0.23",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"license": "MIT",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@kilocode/upstream-merge",
|
||||
"version": "1.0.22",
|
||||
"version": "1.0.23",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"description": "Scripts for automating upstream opencode merges into Kilo",
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
"name": "opencode",
|
||||
"displayName": "opencode",
|
||||
"description": "opencode for VS Code",
|
||||
"version": "1.0.22",
|
||||
"version": "1.0.23",
|
||||
"publisher": "sst-dev",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
|
||||
Reference in New Issue
Block a user