Merge remote-tracking branch 'origin/main' into accidental-matrix

This commit is contained in:
kirillk
2026-05-09 15:32:18 -04:00
727 changed files with 34038 additions and 11845 deletions
@@ -0,0 +1,5 @@
---
"@kilocode/cli": patch
---
Fix garbled diff and additions/deletions counts shown by `apply_patch` when updating a non-UTF-8 file.
-6
View File
@@ -1,6 +0,0 @@
---
"@kilocode/cli": patch
"kilo-code": patch
---
Preserve the selected thinking level after compacting a session.
-5
View File
@@ -1,5 +0,0 @@
---
"kilo-code": patch
---
Update regular Kilo editor tab titles to follow the current session title.
+5
View File
@@ -0,0 +1,5 @@
---
"@kilocode/cli": patch
---
Speed up reading large files: the `read` tool now streams UTF-8 content from disk and stops once the line/byte cap is reached, instead of loading the whole file into memory first.
-7
View File
@@ -1,7 +0,0 @@
---
"kilo-code": patch
"@kilocode/cli": patch
"@kilocode/sdk": patch
---
Remove custom providers from settings when disconnecting them so they do not reappear after being disabled and re-enabled.
-5
View File
@@ -1,5 +0,0 @@
---
"kilo-code": patch
---
Keep Agent Manager model, mode, and reasoning effort selections independent per session and preserve them after restart.
-5
View File
@@ -1,5 +0,0 @@
---
"kilo-code": minor
---
Unify the Changes panel into a single tab with a source dropdown to switch between workspace and current-session diffs. Show the session's additions and deletions on the sidebar changes badge, and warn in the panel when snapshots are disabled for the repository.
+21 -2
View File
@@ -96,6 +96,16 @@ jobs:
echo "cli_url=$URL" >> "$GITHUB_OUTPUT"
echo "::notice::Testing CLI v${VERSION} via asset API: $URL"
# Harbor's default agent-setup timeout is 360s. The hello-world container
# (FROM ubuntu:24.04) needs apt-get update + apt-get install + a NodeSource
# curl|bash + apt install nodejs before the CLI even downloads, and on
# the Blacksmith runners that occasionally pushes past 6 min when an apt
# mirror or NodeSource cdn is slow, killing the run with
# AgentSetupTimeoutError. Doubling the multiplier to 2 gives enough
# headroom for transient mirror/cdn slowness while still finishing well
# under timeout-minutes. Note: --timeout-multiplier scales every timeout
# uniformly (setup, agent, verifier, env build), but they're all upper
# limits so this is harmless.
- name: Run smoke test — hello-world
env:
KILO_CLI_URL: ${{ steps.cli.outputs.cli_url }}
@@ -104,7 +114,8 @@ jobs:
./scripts/run_eval.sh \
-m kilo/anthropic/claude-sonnet-4.6 \
-d hello-world \
--job-name smoke-test-hello-world
--job-name smoke-test-hello-world \
--timeout-multiplier 2
- name: Run smoke test — log-summary-date-ranges
env:
@@ -115,11 +126,18 @@ jobs:
-m kilo/anthropic/claude-sonnet-4.6 \
-d terminal-bench-sample \
-t "log-summary-date-ranges" \
--job-name smoke-test-log-summary
--job-name smoke-test-log-summary \
--timeout-multiplier 2
- name: Validate results
run: python3 scripts/validate_smoke_test.py jobs/smoke-test-*/
# Also upload the agent setup logs (stdout/stderr/return-code from the
# CLI install script) so we can see exactly which step stalls when the
# next AgentSetupTimeoutError happens. These come from the install
# script in kilo-bench, which uses `set -euo pipefail` (no `set -x`)
# and never echoes auth tokens or API keys, so the captured output is
# safe to upload.
- name: Upload results
if: always()
uses: actions/upload-artifact@v7 # kilocode_change
@@ -128,5 +146,6 @@ jobs:
path: |
jobs/smoke-test-*/**/result.json
jobs/smoke-test-*/**/trajectory.json
jobs/smoke-test-*/**/agent/setup/*.txt
retention-days: 30
if-no-files-found: warn
@@ -1,14 +1,53 @@
---
description: Resolve upstream merge conflicts
description: Resolve upstream opencode merge conflicts interactively
mode: primary
permission:
read: ask
edit: ask
webfetch: ask
bash:
"*": ask
"git status *": allow
"git log *": allow
"git diff *": allow
"git show *": allow
"git ls-files *": allow
"git ls-tree *": allow
"git grep *": allow
"git hash-object *": allow
"git remote -v *": allow
"git rev-parse *": allow
"git merge-base *": allow
"git show-ref *": allow
"git worktree list": allow
"git branch --show-current": allow
"grep *": allow
"rg *": allow
"head *": allow
"tail *": allow
"cat *": allow
"wc *": allow
"ls *": allow
"pwd *": allow
"diff *": allow
"gh pr view *": allow
"gh run view *": allow
"gh api \"repos/sst/opencode/commits/dev\" *": allow
"axiom *": allow
"bun test *": allow
"bun run typecheck *": allow
"bun run lint *": allow
"bun run script/check-opencode-annotations.ts *": allow
"script/upstream/find-conflict-markers.sh *": allow
"./script/upstream/find-conflict-markers.sh *": allow
---
Resolve the manual part of an upstream merge.
Arguments: `$ARGUMENTS`
Use the first argument as the upstream version, for example `v1.1.50` or
`1.1.50`. If no argument is provided, infer the version from the current branch
name, `upstream-merge-report-<version>.md`, or the newest relevant report file.
The user will provide the upstream version (for example `v1.1.50` or `1.1.50`)
in their first message. If they don't, infer it from the current branch name,
from `upstream-merge-report-<version>.md`, or from the newest relevant report
file.
## Workflow
@@ -44,6 +83,11 @@ git log --all --oneline -- <kilo-file>
Look at the commit message and any PR reference. "We wrote our own because of
PR #NNNN" is a real constraint; "we wrote our own because of a typo" is not.
When upstream narrows an externally-visible compatibility list (models,
providers, routes, config keys, file formats), verify the intent from upstream
PRs, issues, release notes, or current docs before dropping entries. Treat
silent list shrinkage during a refactor as suspicious until proven intentional.
### 3. Write a plan in chat and get approval
For every conflicted file (and any adjacent file the resolution forces you to
@@ -112,6 +156,9 @@ Apply in order:
there and list both paths in the final summary. Verify the new file already
carries the Kilo-renamed symbols (e.g. `x-kilo-directory`) by diffing against
pristine upstream.
- if upstream extracts shared policy into a helper, move Kilo-specific additions
into the helper when possible instead of keeping a pre-check at the old call
site. The extracted helper should stay the source of truth for all callers.
- if upstream deleted a file, analyse whether the Kilo behaviour should be
ported elsewhere or removed rather than restoring the deleted file
- if tests fail only because upstream intentionally removed behaviour, remove
@@ -151,6 +198,26 @@ ported in too. After resolving the flagged file, check:
Add any such files to the plan as `hybrid` or `take-ours` with the same
approval flow.
### 6.5. Scan auto-merged files for latent bugs
Files not in `--diff-filter=U` merged without conflict markers but may still
be broken. Check every auto-merged file for:
- **Duplicate declarations in the same scope.** If both sides added equivalent
code independently, auto-merge keeps both. Grep touched functions for
repeated identifiers before trusting the merge.
- **Duplicate keys in config/manifest files.** If both sides added the same
entry to a shared manifest (dependencies, scripts, workflow lists), the
merged file may have the key twice. This often breaks install/setup before
any test runs — a cheap early win to scan for.
- **Orphaned imports and references.** A rename upstream may leave a Kilo
callsite pointing at a now-missing export. Run full typecheck from the repo
root; references that silently survived the merge surface there.
- **Partial auto-merges.** Upstream may have refactored a region Kilo
deliberately stubbed out (commented blocks, removed fallbacks). If the
auto-merge pulled in references to names that only exist in the removed
path, the file compiles upstream but breaks on Kilo.
### 7. Verify each resolution before moving on
- confirm `script/upstream/find-conflict-markers.sh <file>` prints nothing
@@ -189,6 +256,49 @@ git commit -m "resolve merge conflicts"
The default `git merge` auto-message (`Merge branch '…' into …`) is also fine,
but `resolve merge conflicts` is the convention for these PRs.
### 9.5. Handle downstream API renames as separate commits
Upstream often renames exported APIs. The rename itself auto-merges cleanly in
shared code, but the change cascades into Kilo-only files (kilocode tests,
kilo-specific source, plugins) that still reference the old symbol. Those
files don't appear in `--diff-filter=U` because their own content didn't
conflict.
Keep the behavioural merge commit focused on resolution decisions. Land the
cascade in one or more follow-up commits:
- after the merge commit, run full repo typecheck and collect every "cannot
find name" / "property does not exist" error
- bulk-rename with a mechanical transform when the rename is one-to-one
- restructure or parameter-thread when upstream changed semantics, not just
the name (e.g. moved a helper behind a dependency-injected surface, so
callers now need the injected handle)
- split large downstream refactors into their own commits with messages that
name the rename
Reviewers can then skim the behavioural commit without untangling mechanical
rename noise from merge decisions.
### 9.6. Handle upstream-added tests that diverge from Kilo
Upstream sometimes adds tests that encode design contracts Kilo intentionally
breaks. These auto-merge cleanly and then fail. Three resolution patterns:
- **Rewrite the test** when the test is a contract assertion and Kilo has a
different but equally valid contract. Invert or adjust the assertion with a
`kilocode_change` marker explaining the divergence.
- **Skip the test** when the test relies on patterns that Kilo has replaced
(interception seams that are bypassed by dependency injection, fixture
helpers bound to a removed API, assumptions about serialization shape that
Kilo's extensions break). Mark with `kilocode_change` and a rationale
explaining what would need to change for the test to run.
- **Delete the test** when it covers functionality Kilo deliberately removed
(fallback paths, deprecated endpoints, products Kilo doesn't ship). Note
the deletion in the PR body.
Never silently delete; always leave a breadcrumb. A future reviewer should be
able to understand why this one upstream test is treated differently.
### 10. Resync version strings in a separate commit
Upstream stamps its own version into shared files — notably
@@ -261,3 +371,37 @@ config schema behaviour, migrations, provider routing, or security posture.
- Stricter DOM lib types (upstream TS upgrade) can surface latent casting
issues around `WebSocket.send`, `Headers`, etc. — prefer narrowing the Kilo
type over adding `any` casts.
- Auto-merge can duplicate the same declaration twice in one scope when both
sides added equivalent code independently. Silent for git, caught by
typecheck. Same hazard for duplicated object keys in config/manifest files —
those can break install before any test runs.
- Kilo code may rely on ambient context (async-local storage, globally-set
flags, process env) being populated at a lifecycle moment that upstream
refactors away. If Kilo behaviour reads ambient state during init, forked
work, or event handlers, check the refactor still establishes that state at
the right time. Fix by restoring the ambient state, or by threading the
needed value through explicitly.
- Tests that intercept via process-global or module-global spies can become
no-ops after upstream moves the intercepted code path through dependency
injection. The production code no longer touches the spied symbol. Fixing
the test usually means injecting a mock at the new seam rather than tweaking
the spy.
- When Kilo extends a shared data shape with extra optional fields, different
serialization paths for that shape can diverge on whether missing values are
omitted or emitted as null. Parity tests between two such paths break on
every Kilo addition — audit the encoding assumption before adding fields.
- Rule ordering in allowlist/permission evaluation is usually last-match-wins.
Re-declaring a catch-all rule "for safety" in a later ruleset silently
overrides more specific allow rules from an earlier ruleset. Treat a
redundant catch-all as destructive, not defensive.
- Upstream-added tests can encode a design contract Kilo deliberately breaks.
The test passes upstream because upstream doesn't share Kilo's requirement.
Decide between refactoring Kilo to match the upstream contract or rewriting
the test to assert Kilo's divergent contract — with a `kilocode_change`
marker explaining the divergence.
- CI and local can show different test failures. Tests that read user-local
state (home dir, global config, auth tokens) pass in one environment but
fail in the other. A green local run does not imply green CI.
- Dependency manifests and lockfiles move together. When the merge edits one,
regenerate and commit the other in the same change — otherwise CI breaks on
the follow-up setup step.
+68
View File
@@ -0,0 +1,68 @@
---
name: gh-issues
description: Use when creating, triaging, or commenting on GitHub issues for the Kilo VS Code extension or JetBrains plugin via `gh`. Covers issue templates, project board assignment, title conventions, and required `gh` scopes.
---
# GitHub Issues
Use this skill whenever you create or manage a GitHub issue with `gh` for either the VS Code extension or the JetBrains plugin.
## Templates
The repo defines issue templates in `.github/ISSUE_TEMPLATE/`. Pick the matching template instead of opening a blank issue:
| Template | When to use |
|---|---|
| `Bug report` (`bug-report.yml`) | Reproducible defects with steps, expected, and actual behavior |
| `Feature Request` (`feature-request.yml`) | New capabilities, enhancements, or behavior changes |
| `Question` (`question.yml`) | Usage or design questions that aren't obviously bugs or feature requests |
Pass the template title to `gh issue create --template`.
## Title Conventions
- Use a plain, descriptive title that reads cleanly as a standalone sentence.
- Do not add platform-specific prefixes such as `[JetBrains]`, `[Jetbrains]`, `[JB]`, `[VS Code]`, `[VSCode]`, or similar. Routing happens through project boards, not the title.
## Project Boards
Every new issue must land on the correct project board:
| Surface | Project | URL |
|---|---|---|
| VS Code extension | `VS Code Extension` | https://github.com/orgs/Kilo-Org/projects/25 |
| JetBrains plugin | `Jetbrains Plugin` | https://github.com/orgs/Kilo-Org/projects/39 |
Pass the project title to `gh issue create --project`.
## Recipes
Create a VS Code extension bug report and add it to the board:
```bash
gh issue create \
--template "Bug report" \
--project "VS Code Extension" \
--title "Sidebar chat fails to render after reload" \
--body "..."
```
Create a JetBrains feature request:
```bash
gh issue create \
--template "Feature Request" \
--project "Jetbrains Plugin" \
--title "Support Kotlin Multiplatform target detection" \
--body "..."
```
## Scope Errors
If `gh` reports a missing scope when assigning a project, refresh the auth token and retry:
```bash
gh auth refresh -s project
```
After the refresh succeeds, re-run the original `gh issue create` command. Do not fall back to creating the issue without the project — the board assignment is required.
+8
View File
@@ -28,3 +28,11 @@ Use the current Effect v4 / effect-smol source, not memory or older Effect v2/v3
- In tests, prefer the repo's existing Effect test helpers and live tests for filesystem, git, child process, locks, or timing behavior.
- Do not introduce `any`, non-null assertions, unchecked casts, or older Effect APIs just to satisfy types.
- Do not answer from memory. Verify against `.opencode/references/effect-smol` or nearby code first.
## Testing Patterns
- Use `testEffect(...)` from `packages/opencode/test/lib/effect.ts` for tests that exercise Effect services, layers, runtime context, scoped resources, or platform integrations.
- Use `it.live(...)` for filesystem, git repositories, HTTP servers, sockets, child processes, locks, real time, and other live platform behavior.
- Run tests from package directories such as `packages/opencode`; never run package tests from the repo root.
- Prefer explicit test layers over ad hoc managed runtimes. Keep dependency provisioning visible in the test file.
- Use scoped fixtures and finalizers for resources that must be cleaned up, including temporary directories, flags, databases, fibers, servers, and global state.
+8 -50
View File
@@ -223,12 +223,7 @@ PR descriptions should be 2-3 lines covering **what** changed and **why**. Focus
## GitHub Issues
- When creating a GitHub issue for the VS Code extension or JetBrains plugin, use the repo's existing issue templates in `.github/ISSUE_TEMPLATE/`. Pick the matching template (`Bug report`, `Feature Request`, or `Question`) instead of opening a blank issue.
- Do not add platform-specific title prefixes such as `[JetBrains]`, `[Jetbrains]`, `[JB]`, `[VS Code]`, `[VSCode]`, or similar. Use a plain, descriptive title.
- Always add VS Code extension issues to the GitHub project `VS Code Extension`: https://github.com/orgs/Kilo-Org/projects/25
- Always add JetBrains plugin issues to the GitHub project `Jetbrains Plugin`: https://github.com/orgs/Kilo-Org/projects/39
- When using `gh`, prefer `gh issue create --template "..." --project "..."` with the matching project title.
- If project assignment fails because `gh` is missing the required scope, run `gh auth refresh -s project` and retry.
When creating or managing GitHub issues for the VS Code extension or JetBrains plugin via `gh`, load `.kilo/skills/gh-issues/SKILL.md`. It covers templates, project boards (`VS Code Extension`, `Jetbrains Plugin`), title conventions, and the `gh auth refresh -s project` recovery path.
## Fork Merge Process
@@ -262,50 +257,13 @@ The goal is to keep our diff from upstream as small as possible, making regular
### Kilocode Change Markers
To minimize merge conflicts when syncing with upstream, mark Kilo Code-specific changes in shared code with `kilocode_change` comments.
When editing shared upstream files, mark Kilo-specific lines with `kilocode_change` comments so future merges can find them. The basic forms are:
**Single line:**
- Single line: `const value = 42 // kilocode_change`
- Multi-line block: wrap with `// kilocode_change start` / `// kilocode_change end`
- New file in a shared path: `// kilocode_change - new file` at the top
- JSX/TSX: use `{/* kilocode_change */}` (and `{/* kilocode_change start */}` / `end`)
```typescript
const value = 42 // kilocode_change
```
Markers are NOT needed in paths that contain `kilocode` in the name (e.g. `packages/opencode/src/kilocode/`, `packages/opencode/test/kilocode/`) — these are entirely Kilo Code additions and won't conflict with upstream.
**Multi-line:**
```typescript
// kilocode_change start
const foo = 1
const bar = 2
// kilocode_change end
```
**New files:**
```typescript
// kilocode_change - new file
```
<!-- prettier-ignore -->
**JSX/TSX (inside JSX templates):**
<!-- prettier-ignore -->
```tsx
{/* kilocode_change */}
```
<!-- prettier-ignore -->
```tsx
{/* kilocode_change start */}
<MyComponent />
{/* kilocode_change end */}
```
#### When markers are NOT needed
Code in these paths is Kilo Code-specific and does NOT need `kilocode_change` markers:
- `packages/opencode/src/kilocode/` - All files in this directory
- `packages/opencode/test/kilocode/` - All test files for kilocode
- Any other path containing `kilocode` in filename or directory name
These paths are entirely Kilo Code additions and won't conflict with upstream.
For decision rules on when to keep changes inline vs. extract Kilo logic, marker placement guidance, and verification commands, load `.kilo/skills/kilocode-merge-minimizer/SKILL.md`.
+1 -1
View File
@@ -38,7 +38,7 @@
## Get Started in Visual Studio Code
1. Install the Kilo Code extension from the [VS Code Marketplace](https://marketplace.visualstudio.com/items?itemName=kilocode.Kilo-Code).
2. Create your account to access 500+ cutting-edge AI models including Gemini 3.1 Pro, Claude 4.6 Sonnet & Opus, and GPT-5.4 – with transparent pricing that matches provider rates exactly.
2. Create your account to access 500+ cutting-edge AI models including GPT-5.5, Claude Opus 4.7, Claude Sonnet 4.6, and Gemini 3.1 Pro Preview, with transparent pricing that matches provider rates exactly.
3. Start coding with AI that adapts to your workflow. Watch our quick-start guide to see Kilo in action:
<a href="https://youtu.be/pqGfYXgrhig"><img src="https://img.youtube.com/vi/pqGfYXgrhig/maxresdefault.jpg" alt="Watch the video" width="640" height="360"></a>
+84 -77
View File
@@ -33,7 +33,7 @@
},
"packages/core": {
"name": "@opencode-ai/core",
"version": "7.2.42",
"version": "7.2.48",
"bin": {
"opencode": "./bin/opencode",
},
@@ -68,13 +68,14 @@
},
"packages/kilo-docs": {
"name": "@kilocode/kilo-docs",
"version": "7.2.42",
"version": "7.2.48",
"dependencies": {
"@docsearch/css": "^4",
"@docsearch/js": "^4",
"@markdoc/markdoc": "^0.5.4",
"@markdoc/next.js": "^0.5.0",
"@vscode/codicons": "^0.0.44",
"@xyflow/react": "12.10.2",
"js-yaml": "^4.1.0",
"mermaid": "11.12.3",
"next": "^16.1.5",
@@ -97,7 +98,7 @@
},
"packages/kilo-gateway": {
"name": "@kilocode/kilo-gateway",
"version": "7.2.42",
"version": "7.2.48",
"dependencies": {
"@ai-sdk/alibaba": "1.0.17",
"@ai-sdk/anthropic": "3.0.71",
@@ -133,7 +134,7 @@
},
"packages/kilo-i18n": {
"name": "@kilocode/kilo-i18n",
"version": "7.2.42",
"version": "7.2.48",
"devDependencies": {
"@tsconfig/node22": "catalog:",
"@types/bun": "catalog:",
@@ -143,7 +144,7 @@
},
"packages/kilo-indexing": {
"name": "@kilocode/kilo-indexing",
"version": "7.2.42",
"version": "7.2.48",
"dependencies": {
"@aws-sdk/client-bedrock-runtime": "3.1005.0",
"@aws-sdk/credential-provider-ini": "3.972.31",
@@ -177,7 +178,7 @@
},
"packages/kilo-telemetry": {
"name": "@kilocode/kilo-telemetry",
"version": "7.2.42",
"version": "7.2.48",
"dependencies": {
"@kilocode/kilo-gateway": "workspace:*",
"posthog-node": "4.4.0",
@@ -191,8 +192,9 @@
},
"packages/kilo-ui": {
"name": "@kilocode/kilo-ui",
"version": "7.2.42",
"version": "7.2.48",
"dependencies": {
"@kilocode/sdk": "workspace:*",
"@kobalte/core": "0.13.11",
"@opencode-ai/core": "workspace:*",
"@pierre/diffs": "catalog:",
@@ -200,6 +202,7 @@
"@solid-primitives/media": "2.3.3",
"@solid-primitives/resize-observer": "2.1.5",
"@solid-primitives/rootless": "1.5.2",
"diff": "catalog:",
"lucide-solid": "0.576.0",
"motion": "12.34.5",
"motion-dom": "12.34.3",
@@ -226,9 +229,10 @@
},
"packages/kilo-vscode": {
"name": "kilo-code",
"version": "7.2.42",
"version": "7.2.48",
"dependencies": {
"@anthropic-ai/sdk": "^0.39.0",
"@kilocode/kilo-gateway": "workspace:*",
"@kilocode/kilo-i18n": "workspace:*",
"@kilocode/kilo-indexing": "workspace:*",
"@kilocode/kilo-ui": "workspace:*",
@@ -244,14 +248,15 @@
"dotenv": "^16.4.7",
"fastest-levenshtein": "^1.0.16",
"friendly-words": "1.3.1",
"fuzzysort": "3.1.0",
"ignore": "^7.0.3",
"js-tiktoken": "^1.0.18",
"lru-cache": "^11.0.2",
"marked": "catalog:",
"openai": "^4.85.4",
"quick-lru": "^7.0.0",
"simple-git": "3.35.2",
"simple-git": "3.36.0",
"solid-js": "^1.9.11",
"stream-chat": "9.38.0",
"uri-js": "^4.4.1",
"virtua": "catalog:",
"web-tree-sitter": "^0.24.7",
@@ -287,7 +292,7 @@
},
"packages/opencode": {
"name": "@kilocode/cli",
"version": "7.2.42",
"version": "7.2.48",
"bin": {
"kilo": "./bin/kilo",
"kilocode": "./bin/kilo",
@@ -390,9 +395,8 @@
"ripgrep": "0.3.1",
"rotating-file-stream": "3.2.9",
"semver": "^7.6.3",
"simple-git": "3.35.2",
"simple-git": "3.36.0",
"solid-js": "catalog:",
"stream-chat": "9.38.0",
"strip-ansi": "7.1.2",
"tree-sitter-bash": "0.25.0",
"tree-sitter-powershell": "0.25.10",
@@ -446,7 +450,7 @@
},
"packages/plugin": {
"name": "@kilocode/plugin",
"version": "7.2.42",
"version": "7.2.48",
"dependencies": {
"@kilocode/sdk": "workspace:*",
"effect": "catalog:",
@@ -461,8 +465,8 @@
"typescript": "catalog:",
},
"peerDependencies": {
"@opentui/core": ">=0.1.105",
"@opentui/solid": ">=0.1.105",
"@opentui/core": ">=0.2.2",
"@opentui/solid": ">=0.2.2",
},
"optionalPeers": [
"@opentui/core",
@@ -471,7 +475,7 @@
},
"packages/script": {
"name": "@opencode-ai/script",
"version": "7.2.42",
"version": "7.2.48",
"dependencies": {
"semver": "^7.6.3",
},
@@ -482,7 +486,7 @@
},
"packages/sdk/js": {
"name": "@kilocode/sdk",
"version": "7.2.42",
"version": "7.2.48",
"dependencies": {
"cross-spawn": "catalog:",
},
@@ -497,7 +501,7 @@
},
"packages/storybook": {
"name": "@opencode-ai/storybook",
"version": "7.2.42",
"version": "7.2.48",
"devDependencies": {
"@opencode-ai/ui": "workspace:*",
"@solidjs/meta": "catalog:",
@@ -520,7 +524,7 @@
},
"packages/ui": {
"name": "@opencode-ai/ui",
"version": "7.2.42",
"version": "7.2.48",
"dependencies": {
"@kilocode/sdk": "workspace:*",
"@kobalte/core": "catalog:",
@@ -535,7 +539,7 @@
"@solidjs/router": "catalog:",
"@typescript/native-preview": "catalog:",
"diff": "catalog:",
"dompurify": "3.3.3",
"dompurify": "3.4.2",
"fuzzysort": "catalog:",
"katex": "0.16.27",
"luxon": "catalog:",
@@ -580,7 +584,6 @@
"patchedDependencies": {
"@standard-community/standard-openapi@0.2.9": "patches/@standard-community%2Fstandard-openapi@0.2.9.patch",
"@npmcli/agent@4.0.0": "patches/@npmcli%2Fagent@4.0.0.patch",
"stream-chat@9.38.0": "patches/stream-chat@9.38.0.patch",
},
"overrides": {
"@effect/platform-node-shared": "4.0.0-beta.46",
@@ -589,7 +592,7 @@
"@xmldom/xmldom": ">=0.8.12",
"defu": "6.1.6",
"diff": "8.0.4",
"dompurify": "3.3.3",
"dompurify": "3.4.2",
"effect": "catalog:",
"fastify": ">=5.8.3",
"happy-dom": ">=20.8.9",
@@ -608,10 +611,12 @@
"@npmcli/arborist": "9.4.0",
"@octokit/rest": "22.0.0",
"@openauthjs/openauth": "0.0.0-20250322224806",
"@opentui/core": "0.1.105",
"@opentui/solid": "0.1.105",
"@opentui/core": "0.2.2",
"@opentui/solid": "0.2.2",
"@pierre/diffs": "1.1.0-beta.18",
"@playwright/test": "1.59.1",
"@sentry/solid": "10.36.0",
"@sentry/vite-plugin": "4.6.0",
"@solid-primitives/storage": "4.3.3",
"@solidjs/meta": "0.29.4",
"@solidjs/router": "0.15.4",
@@ -628,7 +633,7 @@
"ai": "6.0.168",
"cross-spawn": "7.0.6",
"diff": "8.0.4",
"dompurify": "3.3.3",
"dompurify": "3.4.2",
"drizzle-kit": "1.0.0-beta.19-d95b7a4",
"drizzle-orm": "1.0.0-beta.19-d95b7a4",
"effect": "4.0.0-beta.57",
@@ -1504,21 +1509,21 @@
"@opentelemetry/semantic-conventions": ["@opentelemetry/semantic-conventions@1.40.0", "", {}, "sha512-cifvXDhcqMwwTlTK04GBNeIe7yyo28Mfby85QXFe1Yk8nmi36Ab/5UQwptOx84SsoGNRg+EVSjwzfSZMy6pmlw=="],
"@opentui/core": ["@opentui/core@0.1.105", "", { "dependencies": { "bun-ffi-structs": "0.1.2", "diff": "8.0.2", "jimp": "1.6.0", "marked": "17.0.1", "yoga-layout": "3.2.1" }, "optionalDependencies": { "@dimforge/rapier2d-simd-compat": "^0.17.3", "@opentui/core-darwin-arm64": "0.1.105", "@opentui/core-darwin-x64": "0.1.105", "@opentui/core-linux-arm64": "0.1.105", "@opentui/core-linux-x64": "0.1.105", "@opentui/core-win32-arm64": "0.1.105", "@opentui/core-win32-x64": "0.1.105", "bun-webgpu": "0.1.5", "planck": "^1.4.2", "three": "0.177.0" }, "peerDependencies": { "web-tree-sitter": "0.25.10" } }, "sha512-vllSOOCW6VIThV/96GRLJ1IxIBuR+ci6FDvnPIAG4s7SJ/FW6zAkqDn1xrtBwwk/lM3QWjLqy8BZc+zwWvveJA=="],
"@opentui/core": ["@opentui/core@0.2.2", "", { "dependencies": { "bun-ffi-structs": "0.2.2", "diff": "9.0.0", "marked": "17.0.1", "string-width": "7.2.0", "strip-ansi": "7.1.2", "yoga-layout": "3.2.1" }, "optionalDependencies": { "@opentui/core-darwin-arm64": "0.2.2", "@opentui/core-darwin-x64": "0.2.2", "@opentui/core-linux-arm64": "0.2.2", "@opentui/core-linux-x64": "0.2.2", "@opentui/core-win32-arm64": "0.2.2", "@opentui/core-win32-x64": "0.2.2" }, "peerDependencies": { "web-tree-sitter": "0.25.10" } }, "sha512-wxg1CD58SVrowu+WgbhZNi3UP/wWxPio2Kj2IeTjomoIE+6EXLxR8eCCxHYVuQUd9E4fknrKkY5HmiSsp6oPow=="],
"@opentui/core-darwin-arm64": ["@opentui/core-darwin-arm64@0.1.105", "", { "os": "darwin", "cpu": "arm64" }, "sha512-1pIL7aer9amwj8EpYoMNtvavKetIe+nX8uBRmYsMQb+KvJoUAZUqENfRW+qHE5WrsOyxx8/QoyXTHw15GG5iLQ=="],
"@opentui/core-darwin-arm64": ["@opentui/core-darwin-arm64@0.2.2", "", { "os": "darwin", "cpu": "arm64" }, "sha512-tY5n3ZRQx+b0kyhQJJLsyJMeZ+0w4FV37YZc/Qqv3qvOqE9kZPw/7adR77FYwWDm/7fax94mLMrR8Y5bKUkDmw=="],
"@opentui/core-darwin-x64": ["@opentui/core-darwin-x64@0.1.105", "", { "os": "darwin", "cpu": "x64" }, "sha512-hLIRSWlK3gY2NRXJGWiTBiMYSmRDjOYFZF6WtUVXhY2SL3sp08dhmr/6dmAVH+3pKCsCipLEsrrcQX6SAihCTA=="],
"@opentui/core-darwin-x64": ["@opentui/core-darwin-x64@0.2.2", "", { "os": "darwin", "cpu": "x64" }, "sha512-W/R7OnqY30FXcTG0tiP2JkQFmgtYbIte5afQ5PC12TliRoee1RqG3iCG6kY1jxW+3Vg6jge88uiSjUEDpeV2gA=="],
"@opentui/core-linux-arm64": ["@opentui/core-linux-arm64@0.1.105", "", { "os": "linux", "cpu": "arm64" }, "sha512-jlRKfPkozTZEkHEePuCWYcTIUtPm+ieInAwGVqGmjbvqjxdVv1/W/Dt6LEZ/9jpRiOPd+FjXAfLe6wa/XWHr+w=="],
"@opentui/core-linux-arm64": ["@opentui/core-linux-arm64@0.2.2", "", { "os": "linux", "cpu": "arm64" }, "sha512-1pzTYFEZauYuw6AGycw2TYGtAlZVGjuUtSdxH1fP51kBPS3oVWduUY2j7GKREz3SU5NulvO2Wc6HWsm3feMqwQ=="],
"@opentui/core-linux-x64": ["@opentui/core-linux-x64@0.1.105", "", { "os": "linux", "cpu": "x64" }, "sha512-kfWS1WMg6qHShmxZX9s1tZc/8JcXw6uyy2UtyTbJdRFExtXGH37oKHi8QK8iPL2ExCx4z7zqVnVJfO3X/Wh7lA=="],
"@opentui/core-linux-x64": ["@opentui/core-linux-x64@0.2.2", "", { "os": "linux", "cpu": "x64" }, "sha512-ucVwUtUYeOYGVFPBLbPoxzbrPdhD0PDyKNQ2X4n1AJ9jlQX4gqBZRcXMEF8hiXDjFxsikZwef7De0ciCcWvAMg=="],
"@opentui/core-win32-arm64": ["@opentui/core-win32-arm64@0.1.105", "", { "os": "win32", "cpu": "arm64" }, "sha512-UFx6A8OpBVbGWK6OAw4GqAqKZgIITJfSOd35pG9yDVKQouHN2OGc2HeeXrH2A4h42p40Xl6IfcqqfllkpC13Dg=="],
"@opentui/core-win32-arm64": ["@opentui/core-win32-arm64@0.2.2", "", { "os": "win32", "cpu": "arm64" }, "sha512-MPhYdJNdxmC5Bqsq6sis/+VkjRgkEjm+bQ1Tl++NSKLuiTU32Re0ImcZlgHbe+LZtZoGMZHVSgZlkGd3oYXO2g=="],
"@opentui/core-win32-x64": ["@opentui/core-win32-x64@0.1.105", "", { "os": "win32", "cpu": "x64" }, "sha512-f9FqqUmxehwhF+cgyazm0YT0v0BYTTCPzd6eztqhl74N3x/kC+jOOz2rdJDC/tTBo1JVsF64KupOnhIs6/Cogg=="],
"@opentui/core-win32-x64": ["@opentui/core-win32-x64@0.2.2", "", { "os": "win32", "cpu": "x64" }, "sha512-19BroLfn2h0RDYfJS5o96Fc8kYCDhRBcseIXtHIkoKIsKMxx62KiDLo/byVye6rp+yQRRB7Xkd2uWqsbdiWo9w=="],
"@opentui/solid": ["@opentui/solid@0.1.105", "", { "dependencies": { "@babel/core": "7.28.0", "@babel/preset-typescript": "7.27.1", "@opentui/core": "0.1.105", "babel-plugin-module-resolver": "5.0.2", "babel-preset-solid": "1.9.10", "entities": "7.0.1", "s-js": "^0.4.9" }, "peerDependencies": { "solid-js": "1.9.11" } }, "sha512-uxnaMP802sCI487pv/Hk9xdFdIj9mkg3eNliAqbqR0Shmd4phcjKEZvPRpijjmI99j4s9nul71jzF3h1oz31Nw=="],
"@opentui/solid": ["@opentui/solid@0.2.2", "", { "dependencies": { "@babel/core": "7.28.0", "@babel/preset-typescript": "7.27.1", "@opentui/core": "0.2.2", "babel-plugin-module-resolver": "5.0.2", "babel-preset-solid": "1.9.12", "entities": "7.0.1", "s-js": "^0.4.9" }, "peerDependencies": { "solid-js": "1.9.12" } }, "sha512-ZBVfCoVAhcUGQWPAWOTdzuVldMaRkuPpCu4U1VZCqmIw9DtbCuiVr0WnDocDxKhJLbTu8bl3qEWtVCf6lTSi3w=="],
"@oslojs/asn1": ["@oslojs/asn1@1.0.0", "", { "dependencies": { "@oslojs/binary": "1.0.0" } }, "sha512-zw/wn0sj0j0QKbIXfIlnEcTviaCzYOY3V5rAyjR6YtOByFtJiT574+8p9Wlach0lZH9fddD4yb9laEAIl4vXQA=="],
@@ -2134,8 +2139,6 @@
"@types/json-schema": ["@types/json-schema@7.0.15", "", {}, "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA=="],
"@types/jsonwebtoken": ["@types/jsonwebtoken@9.0.10", "", { "dependencies": { "@types/ms": "*", "@types/node": "*" } }, "sha512-asx5hIG9Qmf/1oStypjanR7iKTv0gXQ1Ov/jfrX6kS/EO0OFni8orbmGCn0672NHR3kXHwpAwR+B368ZGN/2rA=="],
"@types/katex": ["@types/katex@0.16.7", "", {}, "sha512-HMwFiRujE5PjrgwHQ25+bsLJgowjGjm5Z8FVSf0N6PwgJrwxH0QxzHYDcKsTfV3wva0vzrpqMTJS2jXPr5BMEQ=="],
"@types/linkify-it": ["@types/linkify-it@3.0.5", "", {}, "sha512-yg6E+u0/+Zjva+buc3EIb+29XEg4wltq7cSmd4Uc2EE/1nUVmxyzpX6gUXD0V8jIrG0r7YeOGVIbYRkxeooCtw=="],
@@ -2202,8 +2205,6 @@
"@types/vscode": ["@types/vscode@1.116.0", "", {}, "sha512-sYHp4MO6BqJ2PD7Hjt0hlIS3tMaYsVPJrd0RUjDJ8HtOYnyJIEej0bLSccM8rE77WrC+Xox/kdBwEFDO8MsxNA=="],
"@types/whatwg-mimetype": ["@types/whatwg-mimetype@3.0.2", "", {}, "sha512-c2AKvDT8ToxLIOUlN51gTiHXflsfIFisS4pO7pDPoKouJCESkhZnEy623gwP9laCy5lnLDAw1vAzu2vM2YLOrA=="],
"@types/which": ["@types/which@3.0.4", "", {}, "sha512-liyfuo/106JdlgSchJzXEQCVArk0CvevqPote8F8HgWgJ3dRCcTHgJIsLDuee0kxk/mhbInzIZk3QWSZJ8R+2w=="],
"@types/ws": ["@types/ws@8.18.1", "", { "dependencies": { "@types/node": "*" } }, "sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg=="],
@@ -2318,6 +2319,10 @@
"@xterm/xterm": ["@xterm/xterm@6.0.0", "", {}, "sha512-TQwDdQGtwwDt+2cgKDLn0IRaSxYu1tSUjgKarSDkUM0ZNiSRXFpjxEsvc/Zgc5kq5omJ+V0a8/kIM2WD3sMOYg=="],
"@xyflow/react": ["@xyflow/react@12.10.2", "", { "dependencies": { "@xyflow/system": "0.0.76", "classcat": "^5.0.3", "zustand": "^4.4.0" }, "peerDependencies": { "react": ">=17", "react-dom": ">=17" } }, "sha512-CgIi6HwlcHXwlkTpr0fxLv/0sRVNZ8IdwKLzzeCscaYBwpvfcH1QFOCeaTCuEn1FQEs/B8CjnTSjhs8udgmBgQ=="],
"@xyflow/system": ["@xyflow/system@0.0.76", "", { "dependencies": { "@types/d3-drag": "^3.0.7", "@types/d3-interpolate": "^3.0.4", "@types/d3-selection": "^3.0.10", "@types/d3-transition": "^3.0.8", "@types/d3-zoom": "^3.0.8", "d3-drag": "^3.0.0", "d3-interpolate": "^3.0.1", "d3-selection": "^3.0.0", "d3-zoom": "^3.0.0" } }, "sha512-hvwvnRS1B3REwVDlWexsq7YQaPZeG3/mKo1jv38UmnpWmxihp14bW6VtEOuHEwJX2FvzFw8k77LyKSk/wiZVNA=="],
"@zip.js/zip.js": ["@zip.js/zip.js@2.7.62", "", {}, "sha512-OaLvZ8j4gCkLn048ypkZu29KX30r8/OfFF2w4Jo5WXFr+J04J+lzJ5TKZBVgFXhlvSkqNFQdfnY1Q8TMTCyBVA=="],
"abbrev": ["abbrev@4.0.0", "", {}, "sha512-a1wflyaL0tHtJSmLSOVybYhy22vRih4eduhhrkcjgrWGnRfrZtovJ2FRjxuTtkkj47O/baf0R86QU5OuYpz8fA=="],
@@ -2426,7 +2431,7 @@
"babel-plugin-module-resolver": ["babel-plugin-module-resolver@5.0.2", "", { "dependencies": { "find-babel-config": "^2.1.1", "glob": "^9.3.3", "pkg-up": "^3.1.0", "reselect": "^4.1.7", "resolve": "^1.22.8" } }, "sha512-9KtaCazHee2xc0ibfqsDeamwDps6FZNo5S0Q81dUqEuFzVwPhcT4J5jOqIVvgCA3Q/wO9hKYxN/Ds3tIsp5ygg=="],
"babel-preset-solid": ["babel-preset-solid@1.9.10", "", { "dependencies": { "babel-plugin-jsx-dom-expressions": "^0.40.3" }, "peerDependencies": { "@babel/core": "^7.0.0", "solid-js": "^1.9.10" }, "optionalPeers": ["solid-js"] }, "sha512-HCelrgua/Y+kqO8RyL04JBWS/cVdrtUv/h45GntgQY+cJl4eBcKkCDV3TdMjtKx1nXwRaR9QXslM/Npm1dxdZQ=="],
"babel-preset-solid": ["babel-preset-solid@1.9.12", "", { "dependencies": { "babel-plugin-jsx-dom-expressions": "^0.40.6" }, "peerDependencies": { "@babel/core": "^7.0.0", "solid-js": "^1.9.12" }, "optionalPeers": ["solid-js"] }, "sha512-LLqnuKVDlKpyBlMPcH6qEvs/wmS9a+NczppxJ3ryS/c0O5IiSFOIBQi9GzyiGDSbcJpx4Gr87jyFTos1MyEuWg=="],
"balanced-match": ["balanced-match@4.0.4", "", {}, "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA=="],
@@ -2494,13 +2499,13 @@
"buffers": ["buffers@0.1.1", "", {}, "sha512-9q/rDEGSb/Qsvv2qvzIzdluL5k7AaJOTrw23z9reQthrbF7is4CtlT0DXyO1oei2DCp4uojjzQ7igaSHp1kAEQ=="],
"bun-ffi-structs": ["bun-ffi-structs@0.1.2", "", { "peerDependencies": { "typescript": "^5" } }, "sha512-Lh1oQAYHDcnesJauieA4UNkWGXY9hYck7OA5IaRwE3Bp6K2F2pJSNYqq+hIy7P3uOvo3km3oxS8304g5gDMl/w=="],
"bun-ffi-structs": ["bun-ffi-structs@0.2.2", "", { "peerDependencies": { "typescript": "^5" } }, "sha512-N/ZWtyN0piZlrXQT7TO0V+q952orYqkfhXRXM1Hcbb+R3QSiBH4vLnib187Mrs1H7pWIYECAmPeapGYDOMCl+w=="],
"bun-pty": ["bun-pty@0.4.8", "", {}, "sha512-rO70Mrbr13+jxHHHu2YBkk2pNqrJE5cJn29WE++PUr+GFA0hq/VgtQPZANJ8dJo6d7XImvBk37Innt8GM7O28w=="],
"bun-types": ["bun-types@1.3.12", "", { "dependencies": { "@types/node": "*" } }, "sha512-HqOLj5PoFajAQciOMRiIZGNoKxDJSr6qigAttOX40vJuSp6DN/CxWp9s3C1Xwm4oH7ybueITwiaOcWXoYVoRkA=="],
"bun-webgpu": ["bun-webgpu@0.1.5", "", { "dependencies": { "@webgpu/types": "^0.1.60" }, "optionalDependencies": { "bun-webgpu-darwin-arm64": "^0.1.5", "bun-webgpu-darwin-x64": "^0.1.5", "bun-webgpu-linux-x64": "^0.1.5", "bun-webgpu-win32-x64": "^0.1.5" } }, "sha512-91/K6S5whZKX7CWAm9AylhyKrLGRz6BUiiPiM/kXadSnD4rffljCD/q9cNFftm5YXhx4MvLqw33yEilxogJvwA=="],
"bun-webgpu": ["bun-webgpu@0.1.4", "", { "dependencies": { "@webgpu/types": "^0.1.60" }, "optionalDependencies": { "bun-webgpu-darwin-arm64": "^0.1.4", "bun-webgpu-darwin-x64": "^0.1.4", "bun-webgpu-linux-x64": "^0.1.4", "bun-webgpu-win32-x64": "^0.1.4" } }, "sha512-Kw+HoXl1PMWJTh9wvh63SSRofTA8vYBFCw0XEP1V1fFdQEDhI8Sgf73sdndE/oDpN/7CMx0Yv/q8FCvO39ROMQ=="],
"bun-webgpu-darwin-arm64": ["bun-webgpu-darwin-arm64@0.1.6", "", { "os": "darwin", "cpu": "arm64" }, "sha512-lIsDkPzJzPl6yrB5CUOINJFPnTRv6fF/Q8J1mAr43ogSp86WZEg9XZKaT6f3EUJ+9ETogGoMnoj1q0AwHUTbAQ=="],
@@ -2574,6 +2579,8 @@
"citty": ["citty@0.1.6", "", { "dependencies": { "consola": "^3.2.3" } }, "sha512-tskPPKEs8D2KPafUypv2gxwJP8h/OaJmC82QQGGDQcHvXX43xF2VDACcJVmZ0EuSxkpO9Kc4MlrA3q0+FG58AQ=="],
"classcat": ["classcat@5.0.5", "", {}, "sha512-JhZUT7JFcQy/EzW605k/ktHtncoo9vnyW/2GspNYwFlN1C/WmjuV/xtS04e9SOkL2sTdw0VAZ2UGCcQ9lR6p6w=="],
"clean-git-ref": ["clean-git-ref@2.0.1", "", {}, "sha512-bLSptAy2P0s6hU4PzuIMKmMJJSE6gLXGH1cntDu7bWJUksvuM+7ReOK61mozULErYvP6a15rnYl0zFDef+pyPw=="],
"clean-stack": ["clean-stack@4.2.0", "", { "dependencies": { "escape-string-regexp": "5.0.0" } }, "sha512-LYv6XPxoyODi36Dp976riBtSY27VmFo+MKqEU9QCCWyTrdEPDog+RWA7xQWHi6Vbp61j5c4cdzzX1NidnwtUWg=="],
@@ -2818,7 +2825,7 @@
"domhandler": ["domhandler@5.0.3", "", { "dependencies": { "domelementtype": "^2.3.0" } }, "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w=="],
"dompurify": ["dompurify@3.3.3", "", { "optionalDependencies": { "@types/trusted-types": "^2.0.7" } }, "sha512-Oj6pzI2+RqBfFG+qOaOLbFXLQ90ARpcGG6UePL82bJLtdsa6CYJD7nmiU8MW9nQNOtCHV3lZ/Bzq1X0QYbBZCA=="],
"dompurify": ["dompurify@3.4.2", "", { "optionalDependencies": { "@types/trusted-types": "^2.0.7" } }, "sha512-lHeS9SA/IKeIFFyYciHBr2n0v1VMPlSj843HdLOwjb2OxNwdq9Xykxqhk+FE42MzAdHvInbAolSE4mhahPpjXA=="],
"domutils": ["domutils@3.2.2", "", { "dependencies": { "dom-serializer": "^2.0.0", "domelementtype": "^2.3.0", "domhandler": "^5.0.3" } }, "sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw=="],
@@ -3114,8 +3121,6 @@
"hachure-fill": ["hachure-fill@0.5.2", "", {}, "sha512-3GKBOn+m2LX9iq+JC1064cSFprJY4jL1jCXTcpnfER5HYE2l/4EfWSGzkPa/ZDBmYI0ZOEj5VHV/eKnPGkHuOg=="],
"happy-dom": ["happy-dom@20.9.0", "", { "dependencies": { "@types/node": ">=20.0.0", "@types/whatwg-mimetype": "^3.0.2", "@types/ws": "^8.18.1", "entities": "^7.0.1", "whatwg-mimetype": "^3.0.0", "ws": "^8.18.3" } }, "sha512-GZZ9mKe8r646NUAf/zemnGbjYh4Bt8/MqASJY+pSm5ZDtc3YQox+4gsLI7yi1hba6o+eCsGxpHn5+iEVn31/FQ=="],
"has-flag": ["has-flag@4.0.0", "", {}, "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ=="],
"has-property-descriptors": ["has-property-descriptors@1.0.2", "", { "dependencies": { "es-define-property": "^1.0.0" } }, "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg=="],
@@ -3404,8 +3409,6 @@
"linkify-it": ["linkify-it@5.0.0", "", { "dependencies": { "uc.micro": "^2.0.0" } }, "sha512-5aHCbzQRADcdP+ATqnDuhhJ/MRIqDkZX5pyjFHRRysS8vZ5AbqGEoFIb6pYHPZ+L/OC2Lc+xT8uHVVR5CAK/wQ=="],
"linkifyjs": ["linkifyjs@4.3.2", "", {}, "sha512-NT1CJtq3hHIreOianA8aSXn6Cw0JzYOuDQbOrSPe7gqFnCpKP++MQe3ODgO3oh2GJFORkAAdqredOa60z63GbA=="],
"load-json-file": ["load-json-file@7.0.1", "", {}, "sha512-Gnxj3ev3mB5TkVBGad0JM6dmLiQL+o0t23JPBZ9sd+yvSLk05mFoqKBw5N8gbbkU4TNXyqCgIrl/VM17OgUIgQ=="],
"locate-path": ["locate-path@6.0.0", "", { "dependencies": { "p-locate": "^5.0.0" } }, "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw=="],
@@ -4064,7 +4067,7 @@
"simple-get": ["simple-get@4.0.1", "", { "dependencies": { "decompress-response": "^6.0.0", "once": "^1.3.1", "simple-concat": "^1.0.0" } }, "sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA=="],
"simple-git": ["simple-git@3.35.2", "", { "dependencies": { "@kwsites/file-exists": "^1.1.1", "@kwsites/promise-deferred": "^1.1.1", "@simple-git/args-pathspec": "^1.0.2", "@simple-git/argv-parser": "^1.0.3", "debug": "^4.4.0" } }, "sha512-ZMjl06lzTm1EScxEGuM6+mEX+NQd14h/B3x0vWU+YOXAMF8sicyi1K4cjTfj5is+35ChJEHDl1EjypzYFWH2FA=="],
"simple-git": ["simple-git@3.36.0", "", { "dependencies": { "@kwsites/file-exists": "^1.1.1", "@kwsites/promise-deferred": "^1.1.1", "@simple-git/args-pathspec": "^1.0.3", "@simple-git/argv-parser": "^1.1.0", "debug": "^4.4.0" } }, "sha512-cGQjLjK8bxJw4QuYT7gxHw3/IouVESbhahSsHrX97MzCL1gu2u7oy38W6L2ZIGECEfIBG4BabsWDPjBxJENv9Q=="],
"simple-xml-to-json": ["simple-xml-to-json@1.2.7", "", {}, "sha512-mz9VXphOxQWX3eQ/uXCtm6upltoN0DLx8Zb5T4TFC4FHB7S9FDPGre8CfLWqPWQQH/GrQYd2AXhhVM5LDpYx6Q=="],
@@ -4156,8 +4159,6 @@
"storybook-solidjs-vite": ["storybook-solidjs-vite@10.0.9", "", { "dependencies": { "@joshwooding/vite-plugin-react-docgen-typescript": "^0.6.1", "@storybook/builder-vite": "^10.0.0", "@storybook/global": "^5.0.0", "vite-plugin-solid": "^2.11.8" }, "peerDependencies": { "solid-js": "^1.9.0", "storybook": "^0.0.0-0 || ^10.0.0", "typescript": ">= 4.9.x", "vite": "^5.0.0 || ^6.0.0 || ^7.0.0" }, "optionalPeers": ["typescript"] }, "sha512-n6MwWCL9mK/qIaUutE9vhGB0X1I1hVnKin2NL+iVC5oXfAiuaABVZlr/1oEeEypsgCdyDOcbEbhJmDWmaqGpPw=="],
"stream-chat": ["stream-chat@9.38.0", "", { "dependencies": { "@types/jsonwebtoken": "^9.0.8", "@types/ws": "^8.5.14", "axios": "^1.12.2", "base64-js": "^1.5.1", "form-data": "^4.0.4", "isomorphic-ws": "^5.0.0", "jsonwebtoken": "^9.0.3", "linkifyjs": "^4.3.2", "ws": "^8.18.1" } }, "sha512-nyTFKHnhGfk1Op/xuZzPKzM9uNTy4TBma69+ApwGj/UtrK2pT6rSaU0Qy/oAqub+Bh7jR2/5vlV/8FWJ2BObFg=="],
"streamx": ["streamx@2.25.0", "", { "dependencies": { "events-universal": "^1.0.0", "fast-fifo": "^1.3.2", "text-decoder": "^1.1.0" } }, "sha512-0nQuG6jf1w+wddNEEXCF4nTg3LtufWINB5eFEN+5TNZW7KWJp6x87+JFL43vaAUPyCfH1wID+mNVyW6OHtFamg=="],
"string-width": ["string-width@7.2.0", "", { "dependencies": { "emoji-regex": "^10.3.0", "get-east-asian-width": "^1.0.0", "strip-ansi": "^7.1.0" } }, "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ=="],
@@ -4454,7 +4455,7 @@
"whatwg-encoding": ["whatwg-encoding@3.1.1", "", { "dependencies": { "iconv-lite": "0.6.3" } }, "sha512-6qN4hJdMwfYBtE3YBTTHhoeuUrDBPZmbQaxWAqSALV/MeEnR5z1xd8UKud2RAkFoPkmB+hli1TZSnyi84xz1vQ=="],
"whatwg-mimetype": ["whatwg-mimetype@3.0.0", "", {}, "sha512-nt+N2dzIutVRxARx1nghPKGv1xHikU7HKdfafKkLNLindmPU/ch3U31NOCGGA/dmPcmb1VlofO0vnKAcsm0o/Q=="],
"whatwg-mimetype": ["whatwg-mimetype@4.0.0", "", {}, "sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg=="],
"whatwg-url": ["whatwg-url@5.0.0", "", { "dependencies": { "tr46": "~0.0.3", "webidl-conversions": "^3.0.0" } }, "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw=="],
@@ -4522,6 +4523,8 @@
"zod-to-json-schema": ["zod-to-json-schema@3.24.5", "", { "peerDependencies": { "zod": "^3.24.1" } }, "sha512-/AuWwMP+YqiPbsJx5D6TfgRTc4kTLjsh5SOcd4bLsfUg2RcEXrFMJl1DGgdHy2aCfsIA/cr/1JM0xcB2GZji8g=="],
"zustand": ["zustand@4.5.7", "", { "dependencies": { "use-sync-external-store": "^1.2.2" }, "peerDependencies": { "@types/react": ">=16.8", "immer": ">=9.0.6", "react": ">=16.8" }, "optionalPeers": ["@types/react", "immer", "react"] }, "sha512-CHOUy7mu3lbD6o6LJLfllpjkzhHXSBlX8B9+qPddUsIfeF5S/UZ5q0kmCsnRqT1UHFQZchNFDDzMbQsuesHWlw=="],
"zwitch": ["zwitch@2.0.4", "", {}, "sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A=="],
"@actions/artifact/@actions/core": ["@actions/core@2.0.3", "", { "dependencies": { "@actions/exec": "^2.0.0", "@actions/http-client": "^3.0.2" } }, "sha512-Od9Thc3T1mQJYddvVPM4QGiLUewdh+3txmDYHHxoNdkqysR1MbCT+rFOtNUxYAz+7+6RIsqipVahY2GJqGPyxA=="],
@@ -4730,8 +4733,6 @@
"@openauthjs/openauth/jose": ["jose@5.9.6", "", {}, "sha512-AMlnetc9+CV9asI19zHmrgS/WYsWUwCn2R7RzlbJWD7F9eWYUTGyBmU9o6PxngtLGOiDGPRu+Uc4fhKzbpteZQ=="],
"@opencode-ai/plugin/effect": ["effect@4.0.0-beta.46", "", { "dependencies": { "@standard-schema/spec": "^1.1.0", "fast-check": "^4.5.3", "find-my-way-ts": "^0.1.6", "ini": "^6.0.0", "kubernetes-types": "^1.30.0", "msgpackr": "^1.11.8", "multipasta": "^0.2.7", "toml": "^3.0.0", "uuid": "^13.0.0", "yaml": "^2.8.2" } }, "sha512-3f6gXvvUMtEueCRY0tU76Vq2Pej1SAwwE+s0Owd5nD53yS5n4RZhUA1rlCGFuSbQFA225pGy8vO72+lpvu7u5A=="],
"@opencode-ai/storybook/@storybook/addon-a11y": ["@storybook/addon-a11y@10.3.5", "", { "dependencies": { "@storybook/global": "^5.0.0", "axe-core": "^4.2.0" }, "peerDependencies": { "storybook": "^10.3.5" } }, "sha512-5k6lpgfIeLxvNhE8v3wEzdiu73ONKjF4gmH1AHvfqYd8kIVzQJai0KCDxgvqNncXHQhIWkaf1fg6+9hKaYJyaw=="],
"@opencode-ai/storybook/@storybook/addon-docs": ["@storybook/addon-docs@10.3.5", "", { "dependencies": { "@mdx-js/react": "^3.0.0", "@storybook/csf-plugin": "10.3.5", "@storybook/icons": "^2.0.1", "@storybook/react-dom-shim": "10.3.5", "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", "ts-dedent": "^2.0.0" }, "peerDependencies": { "storybook": "^10.3.5" } }, "sha512-WuHbxia/o5TX4Rg/IFD0641K5qId/Nk0dxhmAUNoFs5L0+yfZUwh65XOBbzXqrkYmYmcVID4v7cgDRmzstQNkA=="],
@@ -4780,10 +4781,6 @@
"@solid-primitives/resize-observer/@solid-primitives/rootless": ["@solid-primitives/rootless@1.5.3", "", { "dependencies": { "@solid-primitives/utils": "^6.4.0" }, "peerDependencies": { "solid-js": "^1.6.12" } }, "sha512-N8cIDAHbWcLahNRLr0knAAQvXyEdEMoAZvIMZKmhNb1mlx9e2UOv9BRD5YNwQUJwbNoYVhhLwFOEOcVXFx0HqA=="],
"@standard-community/standard-json/effect": ["effect@4.0.0-beta.46", "", { "dependencies": { "@standard-schema/spec": "^1.1.0", "fast-check": "^4.5.3", "find-my-way-ts": "^0.1.6", "ini": "^6.0.0", "kubernetes-types": "^1.30.0", "msgpackr": "^1.11.8", "multipasta": "^0.2.7", "toml": "^3.0.0", "uuid": "^13.0.0", "yaml": "^2.8.2" } }, "sha512-3f6gXvvUMtEueCRY0tU76Vq2Pej1SAwwE+s0Owd5nD53yS5n4RZhUA1rlCGFuSbQFA225pGy8vO72+lpvu7u5A=="],
"@standard-community/standard-openapi/effect": ["effect@4.0.0-beta.46", "", { "dependencies": { "@standard-schema/spec": "^1.1.0", "fast-check": "^4.5.3", "find-my-way-ts": "^0.1.6", "ini": "^6.0.0", "kubernetes-types": "^1.30.0", "msgpackr": "^1.11.8", "multipasta": "^0.2.7", "toml": "^3.0.0", "uuid": "^13.0.0", "yaml": "^2.8.2" } }, "sha512-3f6gXvvUMtEueCRY0tU76Vq2Pej1SAwwE+s0Owd5nD53yS5n4RZhUA1rlCGFuSbQFA225pGy8vO72+lpvu7u5A=="],
"@storybook/addon-links/storybook": ["storybook@10.3.5", "", { "dependencies": { "@storybook/global": "^5.0.0", "@storybook/icons": "^2.0.1", "@testing-library/jest-dom": "^6.9.1", "@testing-library/user-event": "^14.6.1", "@vitest/expect": "3.2.4", "@vitest/spy": "3.2.4", "@webcontainer/env": "^1.1.1", "esbuild": "^0.18.0 || ^0.19.0 || ^0.20.0 || ^0.21.0 || ^0.22.0 || ^0.23.0 || ^0.24.0 || ^0.25.0 || ^0.26.0 || ^0.27.0", "open": "^10.2.0", "recast": "^0.23.5", "semver": "^7.7.3", "use-sync-external-store": "^1.5.0", "ws": "^8.18.0" }, "peerDependencies": { "prettier": "^2 || ^3" }, "optionalPeers": ["prettier"], "bin": "./dist/bin/dispatcher.js" }, "sha512-uBSZu/GZa9aEIW3QMGvdQPMZWhGxSe4dyRWU8B3/Vd47Gy/XLC7tsBxRr13txmmPOEDHZR94uLuq0H50fvuqBw=="],
"@storybook/addon-onboarding/storybook": ["storybook@10.3.5", "", { "dependencies": { "@storybook/global": "^5.0.0", "@storybook/icons": "^2.0.1", "@testing-library/jest-dom": "^6.9.1", "@testing-library/user-event": "^14.6.1", "@vitest/expect": "3.2.4", "@vitest/spy": "3.2.4", "@webcontainer/env": "^1.1.1", "esbuild": "^0.18.0 || ^0.19.0 || ^0.20.0 || ^0.21.0 || ^0.22.0 || ^0.23.0 || ^0.24.0 || ^0.25.0 || ^0.26.0 || ^0.27.0", "open": "^10.2.0", "recast": "^0.23.5", "semver": "^7.7.3", "use-sync-external-store": "^1.5.0", "ws": "^8.18.0" }, "peerDependencies": { "prettier": "^2 || ^3" }, "optionalPeers": ["prettier"], "bin": "./dist/bin/dispatcher.js" }, "sha512-uBSZu/GZa9aEIW3QMGvdQPMZWhGxSe4dyRWU8B3/Vd47Gy/XLC7tsBxRr13txmmPOEDHZR94uLuq0H50fvuqBw=="],
@@ -4908,8 +4905,6 @@
"cheerio/undici": ["undici@7.25.0", "", {}, "sha512-xXnp4kTyor2Zq+J1FfPI6Eq3ew5h6Vl0F/8d9XU5zZQf1tX9s2Su1/3PiMmUANFULpmksxkClamIZcaUqryHsQ=="],
"cheerio/whatwg-mimetype": ["whatwg-mimetype@4.0.0", "", {}, "sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg=="],
"clean-stack/escape-string-regexp": ["escape-string-regexp@5.0.0", "", {}, "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw=="],
"cli-truncate/string-width": ["string-width@5.1.2", "", { "dependencies": { "eastasianwidth": "^0.2.0", "emoji-regex": "^9.2.2", "strip-ansi": "^7.0.1" } }, "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA=="],
@@ -4950,6 +4945,8 @@
"enquirer/strip-ansi": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="],
"esbuild-plugin-solid/babel-preset-solid": ["babel-preset-solid@1.9.10", "", { "dependencies": { "babel-plugin-jsx-dom-expressions": "^0.40.3" }, "peerDependencies": { "@babel/core": "^7.0.0", "solid-js": "^1.9.10" }, "optionalPeers": ["solid-js"] }, "sha512-HCelrgua/Y+kqO8RyL04JBWS/cVdrtUv/h45GntgQY+cJl4eBcKkCDV3TdMjtKx1nXwRaR9QXslM/Npm1dxdZQ=="],
"eslint/ajv": ["ajv@6.14.0", "", { "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", "json-schema-traverse": "^0.4.1", "uri-js": "^4.2.2" } }, "sha512-IWrosm/yrn43eiKqkfkHis7QioDleaXQHdDVPKg0FSwwd/DuvyX79TZnFOnYpB7dcsFAMmtFztZuXPDvSePkFw=="],
"eslint/chalk": ["chalk@4.1.2", "", { "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" } }, "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA=="],
@@ -5074,6 +5071,10 @@
"openid-client/lru-cache": ["lru-cache@6.0.0", "", { "dependencies": { "yallist": "^4.0.0" } }, "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA=="],
"opentui-spinner/@opentui/core": ["@opentui/core@0.1.105", "", { "dependencies": { "bun-ffi-structs": "0.1.2", "diff": "8.0.2", "jimp": "1.6.0", "marked": "17.0.1", "yoga-layout": "3.2.1" }, "optionalDependencies": { "@dimforge/rapier2d-simd-compat": "^0.17.3", "@opentui/core-darwin-arm64": "0.1.105", "@opentui/core-darwin-x64": "0.1.105", "@opentui/core-linux-arm64": "0.1.105", "@opentui/core-linux-x64": "0.1.105", "@opentui/core-win32-arm64": "0.1.105", "@opentui/core-win32-x64": "0.1.105", "bun-webgpu": "0.1.5", "planck": "^1.4.2", "three": "0.177.0" }, "peerDependencies": { "web-tree-sitter": "0.25.10" } }, "sha512-vllSOOCW6VIThV/96GRLJ1IxIBuR+ci6FDvnPIAG4s7SJ/FW6zAkqDn1xrtBwwk/lM3QWjLqy8BZc+zwWvveJA=="],
"opentui-spinner/@opentui/solid": ["@opentui/solid@0.1.105", "", { "dependencies": { "@babel/core": "7.28.0", "@babel/preset-typescript": "7.27.1", "@opentui/core": "0.1.105", "babel-plugin-module-resolver": "5.0.2", "babel-preset-solid": "1.9.10", "entities": "7.0.1", "s-js": "^0.4.9" }, "peerDependencies": { "solid-js": "1.9.11" } }, "sha512-uxnaMP802sCI487pv/Hk9xdFdIj9mkg3eNliAqbqR0Shmd4phcjKEZvPRpijjmI99j4s9nul71jzF3h1oz31Nw=="],
"ora/cli-spinners": ["cli-spinners@2.9.2", "", {}, "sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg=="],
"ora/log-symbols": ["log-symbols@6.0.0", "", { "dependencies": { "chalk": "^5.3.0", "is-unicode-supported": "^1.3.0" } }, "sha512-i24m8rpwhmPIS4zscNzK6MSEhk0DUWa/8iYQWxhffV8jkI4Phvs3F+quL5xvS0gdQR0FyTCMMH33Y78dDTzzIw=="],
@@ -5188,6 +5189,8 @@
"vite-plugin-icons-spritesheet/glob": ["glob@11.1.0", "", { "dependencies": { "foreground-child": "^3.3.1", "jackspeak": "^4.1.1", "minimatch": "^10.1.1", "minipass": "^7.1.2", "package-json-from-dist": "^1.0.0", "path-scurry": "^2.0.0" }, "bin": { "glob": "dist/esm/bin.mjs" } }, "sha512-vuNwKSaKiqm7g0THUBu2x7ckSs3XJLXE+2ssL7/MfTGPLLcrJQ/4Uq1CjPTtO5cCIiRxqvN6Twy1qOwhL0Xjcw=="],
"vite-plugin-solid/babel-preset-solid": ["babel-preset-solid@1.9.10", "", { "dependencies": { "babel-plugin-jsx-dom-expressions": "^0.40.3" }, "peerDependencies": { "@babel/core": "^7.0.0", "solid-js": "^1.9.10" }, "optionalPeers": ["solid-js"] }, "sha512-HCelrgua/Y+kqO8RyL04JBWS/cVdrtUv/h45GntgQY+cJl4eBcKkCDV3TdMjtKx1nXwRaR9QXslM/Npm1dxdZQ=="],
"vitest/why-is-node-running": ["why-is-node-running@2.3.0", "", { "dependencies": { "siginfo": "^2.0.0", "stackback": "0.0.2" }, "bin": { "why-is-node-running": "cli.js" } }, "sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w=="],
"vscode-languageserver-protocol/vscode-jsonrpc": ["vscode-jsonrpc@8.2.0", "", {}, "sha512-C+r0eKJUIfiDIfwJhria30+TYWPtuHJXHtI7J0YlOmKAo7ogxP20T0zxB7HZQIFhIyvoBPwWskjxrvAtfjyZfA=="],
@@ -5262,7 +5265,7 @@
"@kilocode/kilo-gateway/@opentui/core/@opentui/core-win32-x64": ["@opentui/core-win32-x64@0.1.75", "", { "os": "win32", "cpu": "x64" }, "sha512-ESpVZVGewe3JkB2TwrG3VRbkxT909iPdtvgNT7xTCIYH2VB4jqZomJfvERPTE0tvqAZJm19mHECzJFI8asSJgQ=="],
"@kilocode/kilo-gateway/@opentui/core/bun-webgpu": ["bun-webgpu@0.1.4", "", { "dependencies": { "@webgpu/types": "^0.1.60" }, "optionalDependencies": { "bun-webgpu-darwin-arm64": "^0.1.4", "bun-webgpu-darwin-x64": "^0.1.4", "bun-webgpu-linux-x64": "^0.1.4", "bun-webgpu-win32-x64": "^0.1.4" } }, "sha512-Kw+HoXl1PMWJTh9wvh63SSRofTA8vYBFCw0XEP1V1fFdQEDhI8Sgf73sdndE/oDpN/7CMx0Yv/q8FCvO39ROMQ=="],
"@kilocode/kilo-gateway/@opentui/core/bun-ffi-structs": ["bun-ffi-structs@0.1.2", "", { "peerDependencies": { "typescript": "^5" } }, "sha512-Lh1oQAYHDcnesJauieA4UNkWGXY9hYck7OA5IaRwE3Bp6K2F2pJSNYqq+hIy7P3uOvo3km3oxS8304g5gDMl/w=="],
"@kilocode/kilo-gateway/@opentui/solid/@babel/core": ["@babel/core@7.28.0", "", { "dependencies": { "@ampproject/remapping": "^2.2.0", "@babel/code-frame": "^7.27.1", "@babel/generator": "^7.28.0", "@babel/helper-compilation-targets": "^7.27.2", "@babel/helper-module-transforms": "^7.27.3", "@babel/helpers": "^7.27.6", "@babel/parser": "^7.28.0", "@babel/template": "^7.27.2", "@babel/traverse": "^7.28.0", "@babel/types": "^7.28.0", "convert-source-map": "^2.0.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", "json5": "^2.2.3", "semver": "^6.3.1" } }, "sha512-UlLAnTPrFdNGoFtbSXwcGFQBtQZJCNjaN6hQNP3UPvuNXT1i82N26KL3dZeIpNalWywr9IuQuncaAfUaS1g6sQ=="],
@@ -5306,12 +5309,6 @@
"@octokit/rest/@octokit/core/before-after-hook": ["before-after-hook@4.0.0", "", {}, "sha512-q6tR3RPqIB1pMiTRMFcZwuG5T8vwp+vUvEG0vuI6B+Rikh5BfPp2fQ82c925FOs+b0lcFQ8CFrL+KbilfZFhOQ=="],
"@opencode-ai/plugin/effect/@standard-schema/spec": ["@standard-schema/spec@1.1.0", "", {}, "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w=="],
"@opencode-ai/plugin/effect/toml": ["toml@3.0.0", "", {}, "sha512-y/mWCZinnvxjTKYhJ+pYxwD0mRLVvOtdS2Awbgxln6iEnt4rk0yBxeSBHkGJcPucRiG0e55mwWp+g/05rsrd6w=="],
"@opencode-ai/plugin/effect/uuid": ["uuid@13.0.0", "", { "bin": { "uuid": "dist-node/bin/uuid" } }, "sha512-XQegIaBTVUjSHliKqcnFqYypAd4S+WCYt5NIeRs6w/UAry7z8Y9j5ZwRRL4kzq9U3sD6v+85er9FvkEaBpji2w=="],
"@opencode-ai/storybook/@storybook/addon-docs/@storybook/csf-plugin": ["@storybook/csf-plugin@10.3.5", "", { "dependencies": { "unplugin": "^2.3.5" }, "peerDependencies": { "esbuild": "*", "rollup": "*", "storybook": "^10.3.5", "vite": "*", "webpack": "*" }, "optionalPeers": ["esbuild", "rollup", "vite", "webpack"] }, "sha512-qlEzNKxOjq86pvrbuMwiGD/bylnsXk1dg7ve0j77YFjEEchqtl7qTlrXvFdNaLA89GhW6D/EV6eOCu/eobPDgw=="],
"@opencode-ai/storybook/@storybook/addon-docs/@storybook/react-dom-shim": ["@storybook/react-dom-shim@10.3.5", "", { "peerDependencies": { "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", "storybook": "^10.3.5" } }, "sha512-Gw8R7XZm0zSUH0XAuxlQJhmizsLzyD6x00KOlP6l7oW9eQHXGfxg3seNDG3WrSAcW07iP1/P422kuiriQlOv7g=="],
@@ -5336,18 +5333,6 @@
"@opentui/solid/@babel/core/semver": ["semver@6.3.1", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA=="],
"@standard-community/standard-json/effect/@standard-schema/spec": ["@standard-schema/spec@1.1.0", "", {}, "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w=="],
"@standard-community/standard-json/effect/toml": ["toml@3.0.0", "", {}, "sha512-y/mWCZinnvxjTKYhJ+pYxwD0mRLVvOtdS2Awbgxln6iEnt4rk0yBxeSBHkGJcPucRiG0e55mwWp+g/05rsrd6w=="],
"@standard-community/standard-json/effect/uuid": ["uuid@13.0.0", "", { "bin": { "uuid": "dist-node/bin/uuid" } }, "sha512-XQegIaBTVUjSHliKqcnFqYypAd4S+WCYt5NIeRs6w/UAry7z8Y9j5ZwRRL4kzq9U3sD6v+85er9FvkEaBpji2w=="],
"@standard-community/standard-openapi/effect/@standard-schema/spec": ["@standard-schema/spec@1.1.0", "", {}, "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w=="],
"@standard-community/standard-openapi/effect/toml": ["toml@3.0.0", "", {}, "sha512-y/mWCZinnvxjTKYhJ+pYxwD0mRLVvOtdS2Awbgxln6iEnt4rk0yBxeSBHkGJcPucRiG0e55mwWp+g/05rsrd6w=="],
"@standard-community/standard-openapi/effect/uuid": ["uuid@13.0.0", "", { "bin": { "uuid": "dist-node/bin/uuid" } }, "sha512-XQegIaBTVUjSHliKqcnFqYypAd4S+WCYt5NIeRs6w/UAry7z8Y9j5ZwRRL4kzq9U3sD6v+85er9FvkEaBpji2w=="],
"@storybook/addon-links/storybook/open": ["open@10.2.0", "", { "dependencies": { "default-browser": "^5.2.1", "define-lazy-prop": "^3.0.0", "is-inside-container": "^1.0.0", "wsl-utils": "^0.1.0" } }, "sha512-YgBpdJHPyQ2UE5x+hlSXcnejzAvD0b22U2OuAP+8OnlJT+PjWPxtgmGqKKc+RgTM63U9gN0YzrYc71R2WT/hTA=="],
"@storybook/addon-onboarding/storybook/open": ["open@10.2.0", "", { "dependencies": { "default-browser": "^5.2.1", "define-lazy-prop": "^3.0.0", "is-inside-container": "^1.0.0", "wsl-utils": "^0.1.0" } }, "sha512-YgBpdJHPyQ2UE5x+hlSXcnejzAvD0b22U2OuAP+8OnlJT+PjWPxtgmGqKKc+RgTM63U9gN0YzrYc71R2WT/hTA=="],
@@ -5600,6 +5585,26 @@
"opencontrol/@modelcontextprotocol/sdk/zod-to-json-schema": ["zod-to-json-schema@3.25.2", "", { "peerDependencies": { "zod": "^3.25.28 || ^4" } }, "sha512-O/PgfnpT1xKSDeQYSCfRI5Gy3hPf91mKVDuYLUHZJMiDFptvP41MSnWofm8dnCm0256ZNfZIM7DSzuSMAFnjHA=="],
"opentui-spinner/@opentui/core/@opentui/core-darwin-arm64": ["@opentui/core-darwin-arm64@0.1.105", "", { "os": "darwin", "cpu": "arm64" }, "sha512-1pIL7aer9amwj8EpYoMNtvavKetIe+nX8uBRmYsMQb+KvJoUAZUqENfRW+qHE5WrsOyxx8/QoyXTHw15GG5iLQ=="],
"opentui-spinner/@opentui/core/@opentui/core-darwin-x64": ["@opentui/core-darwin-x64@0.1.105", "", { "os": "darwin", "cpu": "x64" }, "sha512-hLIRSWlK3gY2NRXJGWiTBiMYSmRDjOYFZF6WtUVXhY2SL3sp08dhmr/6dmAVH+3pKCsCipLEsrrcQX6SAihCTA=="],
"opentui-spinner/@opentui/core/@opentui/core-linux-arm64": ["@opentui/core-linux-arm64@0.1.105", "", { "os": "linux", "cpu": "arm64" }, "sha512-jlRKfPkozTZEkHEePuCWYcTIUtPm+ieInAwGVqGmjbvqjxdVv1/W/Dt6LEZ/9jpRiOPd+FjXAfLe6wa/XWHr+w=="],
"opentui-spinner/@opentui/core/@opentui/core-linux-x64": ["@opentui/core-linux-x64@0.1.105", "", { "os": "linux", "cpu": "x64" }, "sha512-kfWS1WMg6qHShmxZX9s1tZc/8JcXw6uyy2UtyTbJdRFExtXGH37oKHi8QK8iPL2ExCx4z7zqVnVJfO3X/Wh7lA=="],
"opentui-spinner/@opentui/core/@opentui/core-win32-arm64": ["@opentui/core-win32-arm64@0.1.105", "", { "os": "win32", "cpu": "arm64" }, "sha512-UFx6A8OpBVbGWK6OAw4GqAqKZgIITJfSOd35pG9yDVKQouHN2OGc2HeeXrH2A4h42p40Xl6IfcqqfllkpC13Dg=="],
"opentui-spinner/@opentui/core/@opentui/core-win32-x64": ["@opentui/core-win32-x64@0.1.105", "", { "os": "win32", "cpu": "x64" }, "sha512-f9FqqUmxehwhF+cgyazm0YT0v0BYTTCPzd6eztqhl74N3x/kC+jOOz2rdJDC/tTBo1JVsF64KupOnhIs6/Cogg=="],
"opentui-spinner/@opentui/core/bun-ffi-structs": ["bun-ffi-structs@0.1.2", "", { "peerDependencies": { "typescript": "^5" } }, "sha512-Lh1oQAYHDcnesJauieA4UNkWGXY9hYck7OA5IaRwE3Bp6K2F2pJSNYqq+hIy7P3uOvo3km3oxS8304g5gDMl/w=="],
"opentui-spinner/@opentui/core/bun-webgpu": ["bun-webgpu@0.1.5", "", { "dependencies": { "@webgpu/types": "^0.1.60" }, "optionalDependencies": { "bun-webgpu-darwin-arm64": "^0.1.5", "bun-webgpu-darwin-x64": "^0.1.5", "bun-webgpu-linux-x64": "^0.1.5", "bun-webgpu-win32-x64": "^0.1.5" } }, "sha512-91/K6S5whZKX7CWAm9AylhyKrLGRz6BUiiPiM/kXadSnD4rffljCD/q9cNFftm5YXhx4MvLqw33yEilxogJvwA=="],
"opentui-spinner/@opentui/solid/@babel/core": ["@babel/core@7.28.0", "", { "dependencies": { "@ampproject/remapping": "^2.2.0", "@babel/code-frame": "^7.27.1", "@babel/generator": "^7.28.0", "@babel/helper-compilation-targets": "^7.27.2", "@babel/helper-module-transforms": "^7.27.3", "@babel/helpers": "^7.27.6", "@babel/parser": "^7.28.0", "@babel/template": "^7.27.2", "@babel/traverse": "^7.28.0", "@babel/types": "^7.28.0", "convert-source-map": "^2.0.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", "json5": "^2.2.3", "semver": "^6.3.1" } }, "sha512-UlLAnTPrFdNGoFtbSXwcGFQBtQZJCNjaN6hQNP3UPvuNXT1i82N26KL3dZeIpNalWywr9IuQuncaAfUaS1g6sQ=="],
"opentui-spinner/@opentui/solid/babel-preset-solid": ["babel-preset-solid@1.9.10", "", { "dependencies": { "babel-plugin-jsx-dom-expressions": "^0.40.3" }, "peerDependencies": { "@babel/core": "^7.0.0", "solid-js": "^1.9.10" }, "optionalPeers": ["solid-js"] }, "sha512-HCelrgua/Y+kqO8RyL04JBWS/cVdrtUv/h45GntgQY+cJl4eBcKkCDV3TdMjtKx1nXwRaR9QXslM/Npm1dxdZQ=="],
"ora/log-symbols/is-unicode-supported": ["is-unicode-supported@1.3.0", "", {}, "sha512-43r2mRvz+8JRIKnWJ+3j8JtjRKZ6GmjzfaE/qiBJnikNnYv/6bagRJ1kUhNk8R5EX/GkobD+r+sfxCPJsiKBLQ=="],
"p-locate/p-limit/yocto-queue": ["yocto-queue@0.1.0", "", {}, "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q=="],
@@ -5800,6 +5805,8 @@
"mocha/yargs/string-width/strip-ansi": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="],
"opentui-spinner/@opentui/solid/@babel/core/semver": ["semver@6.3.1", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA=="],
"pkg-conf/find-up/locate-path/p-locate": ["p-locate@6.0.0", "", { "dependencies": { "p-limit": "^4.0.0" } }, "sha512-wPrq66Llhl7/4AGC6I+cqxT07LhXvWL08LNXz1fENOw0Ap4sRZZ/gZpTTJ5jpurzzzfS2W/Ge9BY3LgLjCShcw=="],
"pkg-up/find-up/locate-path/p-locate": ["p-locate@3.0.0", "", { "dependencies": { "p-limit": "^2.0.0" } }, "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ=="],
+4 -4
View File
@@ -1,8 +1,8 @@
{
"nodeModules": {
"x86_64-linux": "sha256-BkFDOCEvivFrxKOvMjAIhj3QZwmb9vE/KvCZv5puw6E=",
"aarch64-linux": "sha256-sTkfQNjqCNCxmHbpQ8woA6azoiyD37e8Xy7UwB6eNsA=",
"aarch64-darwin": "sha256-lKuagsTHqij5C764DM3d+BOe4JKs2KV8XiwSlJItGMg=",
"x86_64-darwin": "sha256-g5gZMHLq8feSZHVaCOLGU6Tj2Qw56Zogif3pT9EIlz8="
"x86_64-linux": "sha256-n8qDAnmhHLO3PICGFAL6UzPi6k+Dwtl9Wg87PKqa/vY=",
"aarch64-linux": "sha256-XBCHg6BYL1J3M5JCUZda9e8ZKi8EEpI2hez6w3ICOYs=",
"aarch64-darwin": "sha256-3K35v/UghrM8CSpWvbvJVLeoF94MALWVA7F5B++Po00=",
"x86_64-darwin": "sha256-aTjNOPvGygJ34A1Q5+qziyyuSFa0w6WMUjUcsXSLtBU="
}
}
+13 -10
View File
@@ -7,11 +7,13 @@
"packageManager": "bun@1.3.13",
"scripts": {
"dev": "bun run --cwd packages/opencode --conditions=browser src/index.ts",
"dev-setup": "bun run --cwd packages/opencode --conditions=browser src/index.ts dev-setup",
"dev:desktop": "bun --cwd packages/desktop-electron dev",
"dev:web": "bun --cwd packages/app dev",
"dev:console": "ulimit -n 10240 2>/dev/null; bun run --cwd packages/console/app dev",
"dev:storybook": "bun --cwd packages/storybook storybook",
"lint": "oxlint",
"typecheck": "bun turbo typecheck",
"postinstall": "bun run --cwd packages/opencode fix-node-pty && bun run script/setup-git.ts",
"postinstall": "bun run --cwd packages/opencode fix-node-pty",
"prepare": "husky",
"random": "echo 'Random script'",
"hello": "echo 'Hello World!'",
@@ -31,8 +33,8 @@
"@types/cross-spawn": "6.0.6",
"@octokit/rest": "22.0.0",
"@hono/zod-validator": "0.4.2",
"@opentui/core": "0.1.105",
"@opentui/solid": "0.1.105",
"@opentui/core": "0.2.2",
"@opentui/solid": "0.2.2",
"ulid": "3.0.1",
"@kobalte/core": "0.13.11",
"@types/luxon": "3.7.1",
@@ -47,7 +49,7 @@
"@solid-primitives/storage": "4.3.3",
"@tailwindcss/vite": "4.1.11",
"diff": "8.0.4",
"dompurify": "3.3.3",
"dompurify": "3.4.2",
"drizzle-kit": "1.0.0-beta.19-d95b7a4",
"drizzle-orm": "1.0.0-beta.19-d95b7a4",
"effect": "4.0.0-beta.57",
@@ -74,6 +76,8 @@
"@solidjs/meta": "0.29.4",
"@solidjs/router": "0.15.4",
"@solidjs/start": "https://pkg.pr.new/@solidjs/start@dfb2020",
"@sentry/solid": "10.36.0",
"@sentry/vite-plugin": "4.6.0",
"solid-js": "1.9.12",
"vite-plugin-solid": "2.11.10",
"@lydell/node-pty": "1.2.0-beta.10"
@@ -82,7 +86,6 @@
"devDependencies": {
"@actions/artifact": "5.0.1",
"@tsconfig/bun": "catalog:",
"@types/bun": "catalog:",
"@types/mime-types": "3.0.1",
"@typescript/native-preview": "catalog:",
"glob": "13.0.5",
@@ -93,6 +96,7 @@
"semver": "^7.6.0",
"sst": "3.18.10",
"turbo": "2.8.13",
"@types/bun": "catalog:",
"@changesets/changelog-github": "^0.5.1",
"@changesets/cli": "^2.27.10"
},
@@ -137,15 +141,14 @@
"smol-toml": ">=1.6.1",
"fastify": ">=5.8.3",
"diff": "8.0.4",
"dompurify": "3.3.3",
"dompurify": "3.4.2",
"happy-dom": ">=20.8.9"
},
"patchedDependencies": {
"@npmcli/agent@4.0.0": "patches/@npmcli%2Fagent@4.0.0.patch",
"@standard-community/standard-openapi@0.2.9": "patches/@standard-community%2Fstandard-openapi@0.2.9.patch",
"solid-js@1.9.10": "patches/solid-js@1.9.10.patch",
"stream-chat@9.38.0": "patches/stream-chat@9.38.0.patch"
"solid-js@1.9.10": "patches/solid-js@1.9.10.patch"
},
"version": "7.2.42",
"version": "7.2.48",
"peerDependencies": {}
}
+1 -1
View File
@@ -1,6 +1,6 @@
{
"$schema": "https://json.schemastore.org/package.json",
"version": "7.2.42",
"version": "7.2.48",
"name": "@opencode-ai/core",
"type": "module",
"license": "MIT",
+32 -16
View File
@@ -5,6 +5,8 @@ import os from "os"
import { Context, Effect, Layer } from "effect"
import { Flock } from "./util/flock"
import { markNoIndex } from "./kilocode/spotlight" // kilocode_change
import { ensureRealDir } from "./kilocode/global" // kilocode_change
import { Flag } from "./flag/flag"
const app = "kilo" // kilocode_change
// kilocode_change start
@@ -21,6 +23,7 @@ const cache = path.join(clean(xdgCache)!, app)
const config = path.join(clean(xdgConfig)!, app)
const state = path.join(clean(xdgState)!, app)
// kilocode_change end
const tmp = path.join(os.tmpdir(), app)
const paths = {
get home() {
@@ -32,6 +35,7 @@ const paths = {
cache,
config,
state,
tmp,
}
export const Path = paths
@@ -39,11 +43,12 @@ export const Path = paths
Flock.setGlobal({ state })
await Promise.all([
fs.mkdir(Path.data, { recursive: true }),
fs.mkdir(Path.config, { recursive: true }),
fs.mkdir(Path.state, { recursive: true }),
fs.mkdir(Path.log, { recursive: true }),
fs.mkdir(Path.bin, { recursive: true }),
ensureRealDir(Path.data), // kilocode_change
ensureRealDir(Path.config), // kilocode_change
ensureRealDir(Path.state), // kilocode_change
ensureRealDir(Path.tmp), // kilocode_change
ensureRealDir(Path.log), // kilocode_change
ensureRealDir(Path.bin), // kilocode_change
])
// kilocode_change start - keep generated Kilo data out of macOS Spotlight
@@ -58,23 +63,34 @@ export interface Interface {
readonly cache: string
readonly config: string
readonly state: string
readonly tmp: string
readonly bin: string
readonly log: string
}
export function make(input: Partial<Interface> = {}): Interface {
return {
home: Path.home,
data: Path.data,
cache: Path.cache,
config: Flag.KILO_CONFIG_DIR ?? Path.config,
state: Path.state,
tmp: Path.tmp,
bin: Path.bin,
log: Path.log,
...input,
}
}
export const layer = Layer.effect(
Service,
Effect.gen(function* () {
return Service.of({
home: Path.home,
data: Path.data,
cache: Path.cache,
config: Path.config,
state: Path.state,
bin: Path.bin,
log: Path.log,
})
}),
Effect.sync(() => Service.of(make())),
)
export const layerWith = (input: Partial<Interface>) =>
Layer.effect(
Service,
Effect.sync(() => Service.of(make(input))),
)
export * as Global from "./global"
+20
View File
@@ -0,0 +1,20 @@
import fs from "fs/promises"
/**
* Like `fs.mkdir({ recursive: true })` but also repairs broken symlinks and
* junctions whose target no longer exists (a Windows edge-case where the user
* had a junction at e.g. `~/.kilocode` pointing to a deleted directory).
*
* `fs.mkdir({ recursive: true })` silently no-ops when a junction exists even
* if its target is gone, so subsequent writes inside that path fail with ENOENT.
* We detect this by calling `fs.stat` (which follows the symlink/junction) after
* mkdir: if stat fails the entry is broken and we remove + recreate it.
*/
export async function ensureRealDir(p: string) {
await fs.mkdir(p, { recursive: true })
const ok = await fs.stat(p).then(() => true).catch(() => false)
if (!ok) {
await fs.rm(p, { force: true })
await fs.mkdir(p, { recursive: true })
}
}
+6 -2
View File
@@ -120,13 +120,17 @@ export const layer = Layer.effect(
}
})()
if (yield* afs.existsSafe(dir)) {
if (yield* afs.existsSafe(path.join(dir, "node_modules", name))) {
return resolveEntryPoint(name, path.join(dir, "node_modules", name))
}
const tree = yield* reify({ dir, add: [pkg] })
const first = tree.edgesOut.values().next().value?.to
if (!first) return yield* new InstallFailedError({ add: [pkg], dir })
if (!first) {
const result = resolveEntryPoint(name, path.join(dir, "node_modules", name))
if (Option.isSome(result.entrypoint)) return result
return yield* new InstallFailedError({ add: [pkg], dir })
}
return resolveEntryPoint(first.name, first.path)
}, Effect.scoped)
@@ -18,20 +18,17 @@ function sleep(ms: number) {
return new Promise<void>((resolve) => setTimeout(resolve, ms))
}
const msg: Msg = JSON.parse(process.argv[2]!)
const msg: Msg = JSON.parse(process.argv[2])
const testGlobal = Layer.succeed(
Global.Service,
Global.Service.of({
home: os.homedir(),
data: os.tmpdir(),
cache: os.tmpdir(),
config: os.tmpdir(),
state: os.tmpdir(),
bin: os.tmpdir(),
log: os.tmpdir(),
}),
)
const testGlobal = Global.layerWith({
home: os.homedir(),
data: os.tmpdir(),
cache: os.tmpdir(),
config: os.tmpdir(),
state: os.tmpdir(),
bin: os.tmpdir(),
log: os.tmpdir(),
})
const testLayer = EffectFlock.layer.pipe(Layer.provide(testGlobal), Layer.provide(AppFileSystem.defaultLayer))
+16
View File
@@ -0,0 +1,16 @@
import { describe, expect, test } from "bun:test"
import fs from "fs/promises"
import os from "os"
import path from "path"
import { Global } from "@opencode-ai/core/global"
describe("global paths", () => {
test("tmp path is under the system temp directory", () => {
expect(Global.Path.tmp).toBe(path.join(os.tmpdir(), "opencode"))
expect(Global.make().tmp).toBe(Global.Path.tmp)
})
test("tmp path is created on module load", async () => {
expect((await fs.stat(Global.Path.tmp)).isDirectory()).toBe(true)
})
})
+35
View File
@@ -1,7 +1,12 @@
import fs from "fs/promises"
import path from "path"
import { describe, expect, test } from "bun:test"
import { NodeFileSystem } from "@effect/platform-node"
import { Effect, Layer, Option } from "effect"
import { AppFileSystem } from "@opencode-ai/core/filesystem"
import { Global } from "@opencode-ai/core/global"
import { Npm } from "@opencode-ai/core/npm"
import { EffectFlock } from "@opencode-ai/core/util/effect-flock"
import { tmpdir } from "./fixture/tmpdir"
const win = process.platform === "win32"
@@ -15,6 +20,14 @@ const writePackage = (dir: string, pkg: Record<string, unknown>) =>
}),
)
const npmLayer = (cache: string) =>
Npm.layer.pipe(
Layer.provide(EffectFlock.layer),
Layer.provide(AppFileSystem.layer),
Layer.provide(Global.layerWith({ cache, state: path.join(cache, "state") })),
Layer.provide(NodeFileSystem.layer),
)
describe("Npm.sanitize", () => {
test("keeps normal scoped package specs unchanged", () => {
expect(Npm.sanitize("@opencode/acme")).toBe("@opencode/acme")
@@ -29,6 +42,28 @@ describe("Npm.sanitize", () => {
})
})
describe("Npm.add", () => {
test("reifies when package cache directory exists without the package installed", async () => {
await using tmp = await tmpdir()
await fs.mkdir(path.join(tmp.path, "fixture-provider"))
await writePackage(path.join(tmp.path, "fixture-provider"), {
name: "fixture-provider",
main: "index.js",
})
await Bun.write(path.join(tmp.path, "fixture-provider", "index.js"), "export const fixture = true\n")
const spec = `fixture-provider@file:${path.join(tmp.path, "fixture-provider")}`
await fs.mkdir(path.join(tmp.path, "cache", "packages", Npm.sanitize(spec)), { recursive: true })
const entry = await Effect.gen(function* () {
const npm = yield* Npm.Service
return yield* npm.add(spec)
}).pipe(Effect.scoped, Effect.provide(npmLayer(path.join(tmp.path, "cache"))), Effect.runPromise)
expect(Option.isSome(entry.entrypoint)).toBe(true)
})
})
describe("Npm.install", () => {
test("respects omit from project .npmrc", async () => {
await using tmp = await tmpdir()
+9 -12
View File
@@ -93,18 +93,15 @@ async function waitForFile(file: string, timeout = 3_000) {
// Test layer
// ---------------------------------------------------------------------------
const testGlobal = Layer.succeed(
Global.Service,
Global.Service.of({
home: os.homedir(),
data: os.tmpdir(),
cache: os.tmpdir(),
config: os.tmpdir(),
state: os.tmpdir(),
bin: os.tmpdir(),
log: os.tmpdir(),
}),
)
const testGlobal = Global.layerWith({
home: os.homedir(),
data: os.tmpdir(),
cache: os.tmpdir(),
config: os.tmpdir(),
state: os.tmpdir(),
bin: os.tmpdir(),
log: os.tmpdir(),
})
const testLayer = EffectFlock.layer.pipe(Layer.provide(testGlobal), Layer.provide(AppFileSystem.defaultLayer))
+1 -8
View File
@@ -3,13 +3,6 @@
"extends": "@tsconfig/bun/tsconfig.json",
"compilerOptions": {
"noUncheckedIndexedAccess": false,
"types": ["bun"],
"plugins": [
{
"name": "@effect/language-service",
"transform": "@effect/language-service/transform",
"namespaceImportPackages": ["effect", "@effect/*"]
}
]
"types": ["bun"]
}
}
+6 -6
View File
@@ -1,7 +1,7 @@
id = "kilo"
name = "Kilo"
description = "The open source coding agent."
version = "7.2.42"
version = "7.2.48"
schema_version = 1
authors = ["Anomaly"]
repository = "https://github.com/Kilo-Org/kilocode"
@@ -11,26 +11,26 @@ name = "Kilo"
icon = "./icons/opencode.svg"
[agent_servers.opencode.targets.darwin-aarch64]
archive = "https://github.com/Kilo-Org/kilocode/releases/download/v7.2.42/opencode-darwin-arm64.zip"
archive = "https://github.com/Kilo-Org/kilocode/releases/download/v7.2.48/opencode-darwin-arm64.zip"
cmd = "./opencode"
args = ["acp"]
[agent_servers.opencode.targets.darwin-x86_64]
archive = "https://github.com/Kilo-Org/kilocode/releases/download/v7.2.42/opencode-darwin-x64.zip"
archive = "https://github.com/Kilo-Org/kilocode/releases/download/v7.2.48/opencode-darwin-x64.zip"
cmd = "./opencode"
args = ["acp"]
[agent_servers.opencode.targets.linux-aarch64]
archive = "https://github.com/Kilo-Org/kilocode/releases/download/v7.2.42/opencode-linux-arm64.tar.gz"
archive = "https://github.com/Kilo-Org/kilocode/releases/download/v7.2.48/opencode-linux-arm64.tar.gz"
cmd = "./opencode"
args = ["acp"]
[agent_servers.opencode.targets.linux-x86_64]
archive = "https://github.com/Kilo-Org/kilocode/releases/download/v7.2.42/opencode-linux-x64.tar.gz"
archive = "https://github.com/Kilo-Org/kilocode/releases/download/v7.2.48/opencode-linux-x64.tar.gz"
cmd = "./opencode"
args = ["acp"]
[agent_servers.opencode.targets.windows-x86_64]
archive = "https://github.com/Kilo-Org/kilocode/releases/download/v7.2.42/opencode-windows-x64.zip"
archive = "https://github.com/Kilo-Org/kilocode/releases/download/v7.2.48/opencode-windows-x64.zip"
cmd = "./opencode.exe"
args = ["acp"]
@@ -0,0 +1,38 @@
import { describe, it, expect } from "vitest"
import { buildSitemapXml } from "../pages/api/sitemap.xml"
describe("sitemap.xml", () => {
it("produces valid XML with urlset root", () => {
const xml = buildSitemapXml()
expect(xml).toMatch(/^<\?xml version="1\.0" encoding="UTF-8"\?>/)
expect(xml).toContain('<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">')
expect(xml).toContain("</urlset>")
})
it("all <loc> values start with https://kilo.ai/docs", () => {
const xml = buildSitemapXml()
const locs = [...xml.matchAll(/<loc>(.*?)<\/loc>/g)].map((m) => m[1])
expect(locs.length).toBeGreaterThan(0)
for (const loc of locs) {
expect(loc).toMatch(/^https:\/\/kilo\.ai\/docs/)
}
})
it("includes the docs root URL", () => {
const xml = buildSitemapXml()
expect(xml).toContain("<loc>https://kilo.ai/docs</loc>")
})
it("includes known pages from the nav", () => {
const xml = buildSitemapXml()
expect(xml).toContain("https://kilo.ai/docs/getting-started/installing")
expect(xml).toContain("https://kilo.ai/docs/getting-started")
})
it("has no duplicate <loc> entries", () => {
const xml = buildSitemapXml()
const locs = [...xml.matchAll(/<loc>(.*?)<\/loc>/g)].map((m) => m[1])
const unique = new Set(locs)
expect(locs.length).toBe(unique.size)
})
})
@@ -0,0 +1,107 @@
"use client"
import React from "react"
/**
* BrowserFrame wraps content (typically an image) in an abstract browser chrome.
* Provides a minimal title bar with traffic light dots and an optional URL bar.
*/
export function BrowserFrame({
children,
url,
caption,
}: {
children: React.ReactNode
url?: string
caption?: string
}) {
return (
<figure style={{ margin: "24px 0" }}>
<div
style={{
borderRadius: "12px",
overflow: "hidden",
border: "1px solid rgba(255,255,255,0.08)",
background: "#0f0f16",
boxShadow: "0 8px 32px rgba(0,0,0,0.4)",
}}
>
{/* Title bar */}
<div
style={{
display: "flex",
alignItems: "center",
gap: "8px",
padding: "12px 16px",
background: "#1a1a24",
borderBottom: "1px solid rgba(255,255,255,0.06)",
}}
>
{/* Traffic lights */}
<div style={{ display: "flex", gap: "6px" }}>
<div
style={{
width: "10px",
height: "10px",
borderRadius: "50%",
background: "#ff5f57",
}}
/>
<div
style={{
width: "10px",
height: "10px",
borderRadius: "50%",
background: "#febc2e",
}}
/>
<div
style={{
width: "10px",
height: "10px",
borderRadius: "50%",
background: "#28c840",
}}
/>
</div>
{/* URL bar */}
{url && (
<div
style={{
flex: 1,
marginLeft: "12px",
padding: "4px 12px",
borderRadius: "6px",
background: "rgba(255,255,255,0.04)",
border: "1px solid rgba(255,255,255,0.06)",
fontSize: "11px",
fontFamily: "'JetBrains Mono', monospace",
color: "rgba(255,255,255,0.4)",
overflow: "hidden",
textOverflow: "ellipsis",
whiteSpace: "nowrap",
}}
>
{url}
</div>
)}
</div>
{/* Content */}
<div style={{ lineHeight: 0 }}>{children}</div>
</div>
{caption && (
<figcaption
style={{
textAlign: "center",
fontSize: "13px",
color: "var(--text-muted, #888)",
marginTop: "8px",
fontStyle: "italic",
}}
>
{caption}
</figcaption>
)}
</figure>
)
}
+9
View File
@@ -0,0 +1,9 @@
declare module "*.css" {
const content: Record<string, string>
export default content
}
declare module "@xyflow/react/dist/style.css" {
const content: string
export default content
}
@@ -0,0 +1,149 @@
import type { Node, Edge, Position } from "@xyflow/react"
import type { DiagramDefinition } from "./index"
const baseStyle = {
padding: "14px 22px",
borderRadius: "8px",
fontSize: "13px",
fontFamily: "'JetBrains Mono', monospace",
fontWeight: 600,
textAlign: "center" as const,
minWidth: "160px",
}
const nodes: Node[] = [
{
id: "polecat-write",
position: { x: 0, y: 80 },
data: { label: "Polecat writes code" },
sourcePosition: "right" as Position,
targetPosition: "left" as Position,
style: {
...baseStyle,
background: "rgba(59,130,246,0.1)",
border: "1px solid rgba(59,130,246,0.5)",
color: "#3b82f6",
},
},
{
id: "push",
position: { x: 240, y: 80 },
data: { label: "Push branch" },
sourcePosition: "right" as Position,
targetPosition: "left" as Position,
style: {
...baseStyle,
background: "rgba(255,255,255,0.03)",
border: "1px solid rgba(255,255,255,0.15)",
color: "#ccc",
minWidth: "120px",
},
},
{
id: "refinery-review",
position: { x: 440, y: 80 },
data: { label: "Refinery reviews" },
sourcePosition: "right" as Position,
targetPosition: "left" as Position,
style: {
...baseStyle,
background: "rgba(251,191,36,0.1)",
border: "1px solid rgba(251,191,36,0.5)",
color: "#fbbf24",
},
},
{
id: "approve",
position: { x: 680, y: 20 },
data: { label: "✓ Approve & Merge" },
sourcePosition: "right" as Position,
targetPosition: "left" as Position,
style: {
...baseStyle,
background: "rgba(34,197,94,0.1)",
border: "1px solid rgba(34,197,94,0.5)",
color: "#22c55e",
},
},
{
id: "feedback",
position: { x: 680, y: 140 },
data: { label: "✗ Send feedback" },
sourcePosition: "bottom" as Position,
targetPosition: "left" as Position,
style: {
...baseStyle,
background: "rgba(249,115,22,0.1)",
border: "1px solid rgba(249,115,22,0.5)",
color: "#f97316",
},
},
{
id: "revise",
position: { x: 240, y: 220 },
data: { label: "Polecat revises" },
sourcePosition: "right" as Position,
targetPosition: "top" as Position,
style: {
...baseStyle,
background: "rgba(59,130,246,0.1)",
border: "1px solid rgba(59,130,246,0.5)",
color: "#3b82f6",
},
},
]
const edges: Edge[] = [
{
id: "write-push",
source: "polecat-write",
target: "push",
animated: true,
style: { strokeWidth: 2, stroke: "#3b82f6" },
},
{
id: "push-review",
source: "push",
target: "refinery-review",
animated: true,
style: { strokeWidth: 2, stroke: "#fbbf24" },
},
{
id: "review-approve",
source: "refinery-review",
target: "approve",
type: "smoothstep",
animated: true,
style: { strokeWidth: 2, stroke: "#22c55e" },
},
{
id: "review-feedback",
source: "refinery-review",
target: "feedback",
type: "smoothstep",
style: { strokeWidth: 2, stroke: "#f97316", strokeDasharray: "5 3" },
},
{
id: "feedback-revise",
source: "feedback",
target: "revise",
type: "smoothstep",
style: { strokeWidth: 2, stroke: "#f97316", strokeDasharray: "5 3" },
},
{
id: "revise-review",
source: "revise",
target: "refinery-review",
type: "smoothstep",
animated: true,
label: "re-submit",
labelStyle: { fontSize: 10, fontFamily: "'JetBrains Mono', monospace", fill: "#888" },
style: { strokeWidth: 2, stroke: "#3b82f6" },
},
]
export const adversarialLoop: DiagramDefinition = {
nodes,
edges,
caption: "The micro-adversarial loop — write, review, revise until quality is met",
}
@@ -0,0 +1,139 @@
import type { Node, Edge, Position } from "@xyflow/react"
import type { DiagramDefinition } from "./index"
const nodeBase = {
style: {
padding: "12px 20px",
borderRadius: "8px",
fontSize: "13px",
fontFamily: "'JetBrains Mono', monospace",
fontWeight: 600,
border: "1px solid",
textAlign: "center" as const,
minWidth: "120px",
},
} as const satisfies Pick<Node, "style">
const nodes = [
{
id: "open",
position: { x: 0, y: 120 },
data: { label: "open" },
// not sure why, but using Position.Top results in everything breaking at runtime
// we just settle for casing as Position instead
sourcePosition: "right" as Position,
targetPosition: "top" as Position,
style: {
...nodeBase.style,
background: "rgba(34,197,94,0.1)",
borderColor: "rgba(34,197,94,0.5)",
color: "#22c55e",
},
},
{
id: "in_progress",
position: { x: 200, y: 120 },
data: { label: "in_progress" },
sourcePosition: "right" as Position,
targetPosition: "top" as Position,
style: {
...nodeBase.style,
background: "rgba(59,130,246,0.1)",
borderColor: "rgba(59,130,246,0.5)",
color: "#3b82f6",
},
},
{
id: "in_review",
position: { x: 420, y: 120 },
data: { label: "in_review" },
sourcePosition: "right" as Position,
targetPosition: "top" as Position,
style: {
...nodeBase.style,
background: "rgba(251,191,36,0.1)",
borderColor: "rgba(251,191,36,0.5)",
color: "#fbbf24",
},
},
{
id: "closed",
position: { x: 640, y: 120 },
data: { label: "closed" },
sourcePosition: "top" as Position,
targetPosition: "top" as Position,
style: {
...nodeBase.style,
background: "rgba(139,92,246,0.1)",
borderColor: "rgba(139,92,246,0.5)",
color: "#8b5cf6",
},
},
{
id: "failed",
position: { x: 310, y: 280 },
data: { label: "failed" },
sourcePosition: "top" as Position,
targetPosition: "top" as Position,
style: {
...nodeBase.style,
background: "rgba(239,68,68,0.1)",
borderColor: "rgba(239,68,68,0.5)",
color: "#ef4444",
},
},
] as const satisfies Node[]
const edgeBase = {
style: { strokeWidth: 2 },
animated: true,
}
const edges = [
{
id: "open-progress",
source: "open",
target: "in_progress",
type: "smoothstep",
label: "agent dispatched",
labelStyle: { fontSize: 10, fontFamily: "'JetBrains Mono', monospace", fill: "#888" },
...edgeBase,
style: { ...edgeBase.style, stroke: "#3b82f6" },
},
{
id: "progress-review",
source: "in_progress",
target: "in_review",
type: "smoothstep",
label: "work complete",
labelStyle: { fontSize: 10, fontFamily: "'JetBrains Mono', monospace", fill: "#888", zIndex: 10 },
...edgeBase,
style: { ...edgeBase.style, stroke: "#fbbf24" },
},
{
id: "review-closed",
source: "in_review",
target: "closed",
type: "smoothstep",
label: "approved & merged",
labelStyle: { fontSize: 10, fontFamily: "'JetBrains Mono', monospace", fill: "#888" },
...edgeBase,
style: { ...edgeBase.style, stroke: "#8b5cf6" },
},
{
id: "progress-failed",
source: "in_progress",
target: "failed",
type: "smoothstep",
label: "max retries",
labelStyle: { fontSize: 10, fontFamily: "'JetBrains Mono', monospace", fill: "#888", marginTop: "100px" },
...edgeBase,
style: { ...edgeBase.style, strokeWidth: 2, stroke: "#ef4444", strokeDasharray: "5 3" },
},
] as const satisfies Edge[]
export const beadLifecycle = {
nodes,
edges,
caption: "The bead lifecycle — from open to closed, with adversarial review in between",
} as const satisfies DiagramDefinition
@@ -0,0 +1,174 @@
import type { Node, Edge, Position } from "@xyflow/react"
import type { DiagramDefinition } from "./index"
const stageStyle = (color: string) => ({
padding: "12px 18px",
borderRadius: "8px",
fontSize: "12px",
fontFamily: "'JetBrains Mono', monospace",
fontWeight: 600,
background: `rgba(${color},0.1)`,
border: `1px solid rgba(${color},0.5)`,
textAlign: "center" as const,
minWidth: "140px",
})
const closedColor = "139,92,246"
const activeColor = "59,130,246"
const pendingColor = "255,255,255"
const reviewColor = "251,191,36"
const mergeColor = "34,197,94"
const nodes: Node[] = [
{
id: "bead1",
position: { x: 0, y: 0 },
data: { label: "1. Explore codebase" },
sourcePosition: "bottom" as Position,
targetPosition: "left" as Position,
style: { ...stageStyle(closedColor), color: "#8b5cf6" },
},
{
id: "review1",
position: { x: 0, y: 100 },
data: { label: "✓ reviewed" },
sourcePosition: "right" as Position,
targetPosition: "top" as Position,
style: { ...stageStyle(mergeColor), color: "#22c55e", minWidth: "140px", fontSize: "11px" },
},
{
id: "bead2",
position: { x: 200, y: 0 },
data: { label: "2. Design schema" },
sourcePosition: "bottom" as Position,
targetPosition: "left" as Position,
style: { ...stageStyle(closedColor), color: "#8b5cf6" },
},
{
id: "review2",
position: { x: 200, y: 100 },
data: { label: "✓ reviewed" },
sourcePosition: "right" as Position,
targetPosition: "top" as Position,
style: { ...stageStyle(mergeColor), color: "#22c55e", minWidth: "140px", fontSize: "11px" },
},
{
id: "bead3",
position: { x: 400, y: 0 },
data: { label: "3. Implement API" },
sourcePosition: "bottom" as Position,
targetPosition: "left" as Position,
style: { ...stageStyle(activeColor), color: "#3b82f6" },
},
{
id: "review3",
position: { x: 400, y: 100 },
data: { label: "⟳ in review" },
sourcePosition: "right" as Position,
targetPosition: "top" as Position,
style: { ...stageStyle(reviewColor), color: "#fbbf24", minWidth: "140px", fontSize: "11px" },
},
{
id: "bead4",
position: { x: 600, y: 0 },
data: { label: "4. Write tests" },
sourcePosition: "bottom" as Position,
targetPosition: "left" as Position,
style: { ...stageStyle(pendingColor), color: "#666", opacity: 0.6 },
},
{
id: "landing",
position: { x: 800, y: 35 },
data: { label: "Landing Review" },
sourcePosition: "right" as Position,
targetPosition: "left" as Position,
style: {
...stageStyle(reviewColor),
color: "#fbbf24",
minWidth: "140px",
opacity: 0.4,
border: "1px dashed rgba(251,191,36,0.4)",
},
},
{
id: "main",
position: { x: 990, y: 35 },
data: { label: "→ main" },
sourcePosition: "right" as Position,
targetPosition: "left" as Position,
style: {
...stageStyle(mergeColor),
color: "#22c55e",
minWidth: "100px",
opacity: 0.4,
border: "1px dashed rgba(34,197,94,0.4)",
},
},
]
const edges: Edge[] = [
{
id: "b1-r1",
source: "bead1",
target: "review1",
type: "smoothstep",
style: { strokeWidth: 2, stroke: "#8b5cf6" },
},
{
id: "r1-b2",
source: "review1",
target: "bead2",
animated: true,
style: { strokeWidth: 2, stroke: "#22c55e" },
label: "builds on",
labelStyle: { fontSize: 9, fontFamily: "'JetBrains Mono', monospace", fill: "#666" },
},
{
id: "b2-r2",
source: "bead2",
target: "review2",
type: "smoothstep",
style: { strokeWidth: 2, stroke: "#8b5cf6" },
},
{
id: "r2-b3",
source: "review2",
target: "bead3",
animated: true,
style: { strokeWidth: 2, stroke: "#22c55e" },
label: "builds on",
labelStyle: { fontSize: 9, fontFamily: "'JetBrains Mono', monospace", fill: "#666" },
},
{
id: "b3-r3",
source: "bead3",
target: "review3",
type: "smoothstep",
animated: true,
style: { strokeWidth: 2, stroke: "#3b82f6" },
},
{
id: "r3-b4",
source: "review3",
target: "bead4",
style: { strokeWidth: 1, stroke: "#444", strokeDasharray: "4 4" },
},
{
id: "b4-landing",
source: "bead4",
target: "landing",
style: { strokeWidth: 1, stroke: "#444", strokeDasharray: "4 4" },
},
{
id: "landing-main",
source: "landing",
target: "main",
style: { strokeWidth: 1, stroke: "#444", strokeDasharray: "4 4" },
},
]
export const convoyExecution: DiagramDefinition = {
nodes,
edges,
caption: "A convoy executing — each bead builds on reviewed work from previous stages",
}
@@ -0,0 +1,16 @@
import type { Node, Edge } from "@xyflow/react"
import { beadLifecycle } from "./bead-lifecycle"
import { adversarialLoop } from "./adversarial-loop"
import { convoyExecution } from "./convoy-execution"
export interface DiagramDefinition {
nodes: Node[]
edges: Edge[]
caption?: string
}
export const diagrams: Record<string, DiagramDefinition> = {
"bead-lifecycle": beadLifecycle,
"adversarial-loop": adversarialLoop,
"convoy-execution": convoyExecution,
}
@@ -0,0 +1,115 @@
"use client"
import React, { useState, useEffect, useMemo } from "react"
import { diagrams } from "./diagrams"
/**
* FlowDiagram renders an interactive React Flow diagram.
* Loaded lazily to avoid bundling the entire @xyflow/react library on pages that don't use it.
*
* Usage in markdown:
* {% flowDiagram name="bead-lifecycle" /%}
* {% flowDiagram name="adversarial-loop" height="500px" /%}
*/
export function FlowDiagram({ name, height = "400px" }: { name: string; height?: string }) {
const [mod, setMod] = useState<typeof import("@xyflow/react") | null>(null)
const [cssLoaded, setCssLoaded] = useState(false)
useEffect(() => {
Promise.all([import("@xyflow/react"), import("@xyflow/react/dist/style.css").then(() => setCssLoaded(true))]).then(
([xyflow]) => {
setMod(xyflow)
},
)
}, [])
const diagram = diagrams[name]
if (!diagram) {
return (
<div
style={{
height,
display: "flex",
alignItems: "center",
justifyContent: "center",
border: "1px solid rgba(255,255,255,0.1)",
borderRadius: "8px",
color: "var(--text-muted, #888)",
fontFamily: "monospace",
fontSize: "14px",
}}
>
Diagram &quot;{name}&quot; not found
</div>
)
}
if (!mod || !cssLoaded) {
return (
<div
style={{
height,
display: "flex",
alignItems: "center",
justifyContent: "center",
border: "1px solid rgba(255,255,255,0.1)",
borderRadius: "8px",
background: "rgba(0,0,0,0.2)",
color: "var(--text-muted, #888)",
}}
>
Loading diagram...
</div>
)
}
const { ReactFlow, Background, BackgroundVariant } = mod
return (
<figure style={{ margin: "24px 0" }}>
<div
style={{
height,
width: "100%",
border: "1px solid rgba(255,255,255,0.08)",
borderRadius: "12px",
overflow: "hidden",
}}
>
<ReactFlow
nodes={diagram.nodes}
edges={diagram.edges}
fitView
fitViewOptions={{ padding: 0.2 }}
nodesDraggable={false}
nodesConnectable={false}
elementsSelectable={false}
panOnDrag={false}
panOnScroll={false}
zoomOnScroll={false}
zoomOnPinch={false}
zoomOnDoubleClick={false}
preventScrolling={false}
proOptions={{ hideAttribution: true }}
style={{ background: "#08080c" }}
>
<Background variant={BackgroundVariant.Dots} color="rgba(248,160,32,0.15)" gap={20} size={1} />
</ReactFlow>
</div>
{diagram.caption && (
<figcaption
style={{
textAlign: "center",
fontSize: "13px",
color: "var(--text-muted, #888)",
marginTop: "8px",
fontStyle: "italic",
}}
>
{diagram.caption}
</figcaption>
)}
</figure>
)
}
+2
View File
@@ -16,3 +16,5 @@ export * from "./ThemeToggle"
export * from "./TopNav"
export * from "./VideoEmbed"
export * from "./YouTube"
export * from "./FlowDiagram"
export * from "./BrowserFrame"
@@ -23,6 +23,19 @@ export const CodeWithAiNav: NavSection[] = [
{ href: "/code-with-ai/platforms/mobile", children: "Mobile Apps" },
{ href: "/code-with-ai/platforms/slack", children: "Slack" },
{ href: "/code-with-ai/app-builder", children: "App Builder" },
{
href: "/code-with-ai/gastown",
children: "Gas Town by Kilo",
subLinks: [
{ href: "/code-with-ai/gastown/quick-start", children: "Quick Start" },
{ href: "/code-with-ai/gastown/concepts", children: "Concepts" },
{ href: "/code-with-ai/gastown/mayor", children: "The Mayor" },
{ href: "/code-with-ai/gastown/sling-work", children: "Sling Work" },
{ href: "/code-with-ai/gastown/code-review", children: "Code Review" },
{ href: "/code-with-ai/gastown/settings", children: "Settings" },
{ href: "/code-with-ai/gastown/troubleshooting", children: "Troubleshooting" },
],
},
],
},
{
@@ -62,6 +75,7 @@ export const CodeWithAiNav: NavSection[] = [
},
],
},
{
title: "Productivity Tools",
links: [
@@ -80,6 +94,7 @@ export const CodeWithAiNav: NavSection[] = [
children: "Git Commit Generation",
},
{ href: "/code-with-ai/features/speech-to-text", children: "Voice Transcription" },
{ href: "/code-with-ai/features/message-feedback", children: "Message Feedback" },
{
href: "/code-with-ai/features/browser-use",
children: "Agent Behavior",
+2
View File
@@ -43,4 +43,6 @@ exclude = [
# Consistently times out in CI
'^https?://opncd\.ai',
'^https?://zod\.dev/v4/changelog',
# OpenAI docs return 404 to plain GET link checks but resolve in browsers
'^https?://platform\.openai\.com/docs/api-reference/responses/create',
]
@@ -0,0 +1,16 @@
import { BrowserFrame } from "../../components"
export const browserFrame = {
render: BrowserFrame,
children: ["paragraph", "tag", "list"],
attributes: {
url: {
type: String,
description: "Optional URL to display in the address bar",
},
caption: {
type: String,
description: "Optional caption below the frame",
},
},
}
@@ -0,0 +1,18 @@
import { FlowDiagram } from "../../components"
export const flowDiagram = {
render: FlowDiagram,
selfClosing: true,
attributes: {
name: {
type: String,
required: true,
description: "Name of the diagram to render (must match a key in diagrams/index.ts)",
},
height: {
type: String,
default: "400px",
description: "Height of the diagram container",
},
},
}
+2
View File
@@ -7,3 +7,5 @@ export * from "./image.markdoc"
export * from "./kilo-code-icon.markdoc"
export * from "./video.markdoc"
export * from "./youtube.markdoc"
export * from "./flow-diagram.markdoc"
export * from "./browser-frame.markdoc"
+5
View File
@@ -30,6 +30,11 @@ module.exports = withMarkdoc(/* config: https://markdoc.io/docs/nextjs#options *
source: "/llms.txt",
destination: "/api/llms.txt",
},
{
// Rewrite /docs/sitemap.xml to the API endpoint (internal to basePath)
source: "/sitemap.xml",
destination: "/api/sitemap.xml",
},
],
afterFiles: [
{
+3 -2
View File
@@ -1,6 +1,6 @@
{
"name": "@kilocode/kilo-docs",
"version": "7.2.42",
"version": "7.2.48",
"private": true,
"scripts": {
"dev": "next dev --webpack --port 3002",
@@ -13,8 +13,9 @@
"@docsearch/js": "^4",
"@markdoc/markdoc": "^0.5.4",
"@markdoc/next.js": "^0.5.0",
"js-yaml": "^4.1.0",
"@vscode/codicons": "^0.0.44",
"@xyflow/react": "12.10.2",
"js-yaml": "^4.1.0",
"mermaid": "11.12.3",
"next": "^16.1.5",
"posthog-js": "^1.335.3",
@@ -1,11 +1,26 @@
---
title: "Using ChatGPT Plus/Pro with Kilo Code"
description: "Use your ChatGPT Plus or Pro subscription with Kilo Code. Setup guide for authenticating without a separate API key."
description: "Use your ChatGPT Plus or Pro subscription with Kilo Code. Flat-rate access to OpenAI Codex models with OAuth login — no separate API key required."
sidebar_label: ChatGPT Plus/Pro
---
# Using ChatGPT Subscriptions With Kilo Code
If you already pay for ChatGPT Plus or Pro, you can use that subscription to run OpenAI's top coding models directly inside Kilo Code — with no extra API charges beyond your subscription.
## Why use your ChatGPT subscription?
- **Flat-rate access to OpenAI models:** Your subscription covers usage without pay-as-you-go API costs.
- **OAuth login — no API keys:** Click "Sign in to OpenAI Codex," authenticate in your browser, and you're done.
- **Full agentic workflows:** Generate, refactor, debug, edit files, and run terminal commands inside Kilo Code.
- **Multiple AI modes:** Switch between Code, Plan, Debug, and Ask modes for different tasks.
{% callout type="note" %}
Your ChatGPT subscription works with Kilo Code's core functionality (VS Code extension and CLI), but does **not** include cloud features such as Cloud Agents, Kilo Deploy, or KiloClaw. To use GPT models in those features, use the [Kilo Gateway](/docs/gateway).
{% /callout %}
## Setup
{% tabs %}
{% tab label="VSCode (Legacy)" %}
@@ -47,12 +62,31 @@ Then set your default model to one of the OpenAI Codex models available in Kilo
## Tips and Notes
- **Subscription Required:** You need an active ChatGPT Plus or Pro subscription. This provider won't work with free ChatGPT accounts. See [OpenAI's ChatGPT plans](https://chatgpt.com/pricing/) for more information.
- **Authentication Errors:** If you receive a CSRF or other error when completing OAuth authentication, ensure you do not have another application already listening on port 1455. You can check on Linux and Mac by using `lsof -i :1455`.
- **Subscription Required:** You need an active ChatGPT Plus or Pro subscription. This provider won't work with free ChatGPT accounts. [Codex is included](https://developers.openai.com/codex/pricing/) in ChatGPT Plus, Pro, Business, Edu, and Enterprise plans. See [OpenAI's ChatGPT plans](https://chatgpt.com/pricing/) for more information.
- **No API Costs:** Usage through this provider counts against your ChatGPT subscription, not separately billed API usage.
- **Authentication Errors:** If you receive a CSRF or other error when completing OAuth authentication, ensure you do not have another application already listening on port 1455. You can check on Linux and Mac by using `lsof -i :1455`.
- **Sign Out:** To disconnect in VS Code, use the "Disconnect" button in the provider settings. In the CLI, run `kilo auth logout` and choose OpenAI.
- **Switching providers:** You can switch to Claude, Gemini, or local models at any time — this provider is optional.
## Limitations
- **You can't use arbitrary OpenAI API models.** This provider only exposes the models listed in Kilo Code's Codex model catalog.
- **You can't export/migrate your sign-in state with settings export.** OAuth tokens are stored in VS Code SecretStorage, which isn't included in Kilo Code's settings export.
- **Codex catalog models only.** This provider only exposes the models listed in Kilo Code's Codex model catalog. It does not give access to every model available through the OpenAI API.
- **OAuth tokens can't be exported with settings.** Tokens are stored in VS Code SecretStorage, which isn't included in Kilo Code's settings export.
- **Cloud features not included.** Cloud Agents, Kilo Deploy, and KiloClaw require the [Kilo Gateway](/docs/gateway).
## FAQ
**Do I need a separate API key?**
No — just sign in with OAuth using your ChatGPT subscription.
**Which ChatGPT plans include Codex access?**
ChatGPT Plus, Pro, Business, Edu, and Enterprise. Free accounts are not supported.
**How is usage billed?**
Usage counts against your ChatGPT subscription limits — there are no separate API charges.
**Can I still switch to other AI providers?**
Yes. Use OpenAI when it fits and switch to Claude, Gemini, or any local model at any time.
**Can I use my ChatGPT subscription in KiloClaw?**
No. For KiloClaw and other cloud features, use GPT models through the [Kilo Gateway](/docs/gateway), which gives access to 500+ models including the latest GPT releases.
@@ -1,6 +1,6 @@
---
title: "Using OpenAI-Compatible Providers with Kilo Code"
description: "Connect any OpenAI-compatible API endpoint to Kilo Code, including Azure OpenAI, Together AI, and self-hosted servers."
description: "Connect OpenAI-compatible API endpoints like Together AI, Perplexity, and self-hosted servers."
sidebar_label: OpenAI Compatible
---
@@ -14,6 +14,12 @@ Kilo Code supports a wide range of AI model providers that offer APIs compatible
This document focuses on setting up providers _other than_ the official OpenAI API (which has its own [dedicated configuration page](/docs/ai-providers/openai)).
{% callout type="warning" %}
Do not use a custom OpenAI-compatible provider for Azure OpenAI GPT-5 deployments. Azure GPT-5 rejects the `max_tokens` parameter used by generic OpenAI-compatible providers and requires Azure-specific handling.
Use Kilo Code's native `azure` provider instead. If your Azure deployment name differs from the model name you select in Kilo, map it with the model `id` field in `kilo.json`.
{% /callout %}
## General Configuration
{% tabs %}
@@ -21,7 +27,7 @@ This document focuses on setting up providers _other than_ the official OpenAI A
The key to using an OpenAI-compatible provider is to configure two main settings:
1. **Base URL:** This is the API endpoint for the provider. It will _not_ be `https://api.openai.com/v1` (that's for the official OpenAI API).
1. **Base URL:** This is the API endpoint for the provider. It will _not_ be `https://api.openai.com/v1` (that's for the official OpenAI API). For Azure OpenAI GPT-5, do not enter your Azure endpoint here. Configure the native `azure` provider instead.
2. **API Key:** This is the secret key you obtain from the provider.
3. **Model ID:** This is the model name of the specific model.
@@ -53,7 +59,7 @@ You'll find these settings in the Kilo Code settings panel (click the {% codicon
- **Provider ID** — A unique identifier (e.g., `my-provider`).
- **Display name** — A human-readable name shown in the UI.
- **Base URL** — The provider's OpenAI-compatible API endpoint (e.g., `https://api.your-provider.com/v1`). Kilo auto-fetches available models when a valid URL is entered.
- **Base URL** — The provider's OpenAI-compatible API endpoint (e.g., `https://api.your-provider.com/v1`). Kilo auto-fetches available models when a valid URL is entered. For Azure OpenAI GPT-5, use the native `azure` provider instead.
- **API key** — Your API key. Optional — leave empty if authentication is handled via headers.
- **Models** — Add models manually or select from the auto-fetched list (see [Automatic Model Detection](#automatic-model-detection) below).
- **Headers** (optional) — Custom HTTP headers as key-value pairs.
@@ -114,7 +120,7 @@ Then set your default model using the `provider-id/model-id` format:
**Configuration fields:**
- **`models`** — A map of model IDs to model definitions. Each model should include a `name` and `limit` with `context` and `output` token counts. If `limit.context` or `limit.output` is omitted, it defaults to `0`, which limits context management.
- **`options.baseURL`** — The base URL of your OpenAI-compatible API endpoint.
- **`options.baseURL`** — The base URL of your OpenAI-compatible API endpoint. For Azure OpenAI GPT-5, configure `provider.azure` instead.
- **`options.apiKey`** — Your API key. Use any non-empty string (e.g., `"none"`) if the provider doesn't require authentication.
You can also set the API key via an environment variable instead of putting it in the config file. Use the `env` field to specify which variable to read:
@@ -172,6 +178,7 @@ This enhancement allows you to:
- **"Invalid API Key":** Double-check that you've entered the API key correctly.
- **"Model Not Found":** Make sure you're using a valid model ID for your chosen provider.
- **Connection Errors:** Verify the Base URL is correct and that your provider's API is accessible.
- **Azure GPT-5 rejects `max_tokens`:** Azure GPT-5 deployments must use Kilo Code's native `azure` provider. Generic OpenAI-compatible custom providers send `max_tokens`, which Azure GPT-5 rejects because it expects `max_completion_tokens`.
- **Unexpected Results:** If you're getting unexpected results, try a different model.
By using an OpenAI-compatible provider, you can leverage the flexibility of Kilo Code with a wider range of AI models. Remember to always consult your provider's documentation for the most accurate and up-to-date information.
@@ -10,6 +10,10 @@ Kilo Code supports accessing models directly through the official OpenAI API.
**Website:** [https://openai.com/](https://openai.com/)
{% callout type="tip" %}
**Already have a ChatGPT Plus or Pro subscription?** You can use it to access OpenAI's Codex models inside Kilo Code — no separate API key or pay-as-you-go charges needed. See the [ChatGPT Plus/Pro provider page](/docs/ai-providers/openai-chatgpt-plus-pro) for setup instructions.
{% /callout %}
## Getting an API Key
1. **Sign Up/Sign In:** Go to the [OpenAI Platform](https://platform.openai.com/). Create an account or sign in.
@@ -71,4 +75,4 @@ Then set your default model:
## Tips and Notes
- **Pricing:** Refer to the [OpenAI Pricing](https://openai.com/pricing) page for details on model costs.
- **Azure OpenAI Service:** If you'd like to use the Azure OpenAI service, please see our section on [OpenAI-compatible](/docs/ai-providers/openai-compatible) providers.
- **Azure OpenAI Service:** Use Kilo Code's native `azure` provider for Azure OpenAI, especially GPT-5 deployments. Do not configure Azure GPT-5 through a generic [OpenAI-compatible](/docs/ai-providers/openai-compatible) custom provider.
@@ -0,0 +1,35 @@
import type { NextApiRequest, NextApiResponse } from "next"
import { Nav } from "../../lib/nav"
import type { NavLink, NavSection } from "../../lib/types"
const origin = "https://kilo.ai"
const base = "/docs"
function links(sections: NavSection[]): string[] {
const out: string[] = []
function walk(items: NavLink[]) {
for (const item of items) {
out.push(item.href)
if (item.subLinks) walk(item.subLinks)
}
}
for (const s of sections) walk(s.links)
return out
}
export function buildSitemapXml(): string {
const all = Object.values(Nav).flatMap(links)
const hrefs = Array.from(new Set(["", ...all])).sort()
const urls = hrefs.map((h) => ` <url><loc>${origin}${base}${h}</loc></url>`).join("\n")
return `<?xml version="1.0" encoding="UTF-8"?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
${urls}
</urlset>`
}
export default function handler(_: NextApiRequest, res: NextApiResponse) {
const xml = buildSitemapXml()
res.setHeader("Content-Type", "application/xml; charset=utf-8")
res.setHeader("Cache-Control", "public, s-maxage=3600, stale-while-revalidate=86400")
res.status(200).send(xml)
}
@@ -309,6 +309,38 @@ If the model key in your config differs from what the provider expects, use the
Here `my-local-llama` is the key you use in your config and model picker, while `meta-llama-3.1-8b-instruct` is the actual model identifier sent to the LM Studio API.
For Azure OpenAI, use the native `azure` provider and set `id` to your Azure deployment name when it differs from the model key. Do not configure Azure GPT-5 family deployments under `openai-compatible`, because that provider sends `max_tokens` and Azure GPT-5 expects `max_completion_tokens`.
```jsonc
{
"$schema": "https://app.kilo.ai/config.json",
"model": "azure/gpt-5.5",
"provider": {
"azure": {
"options": {
"apiKey": "{env:AZURE_API_KEY}",
"resourceName": "my-azure-resource",
},
"models": {
"gpt-5.5": {
"id": "my-gpt-5-5-deployment",
"name": "GPT-5.5 on Azure",
"reasoning": true,
"tool_call": true,
"temperature": false,
"limit": {
"context": 400000,
"output": 128000,
},
},
},
},
},
}
```
Here `azure/gpt-5.5` is the model you select in Kilo Code, while `my-gpt-5-5-deployment` is the Azure deployment name sent to Azure. If you prefer to configure the full Azure endpoint instead of a resource name, replace `resourceName` with `baseURL`, for example `"baseURL": "https://my-resource.openai.azure.com/openai"`. If both are configured, Kilo Code uses `baseURL` and ignores `resourceName` to avoid sending conflicting Azure SDK options.
## Model Loading Priority
When Kilo starts, it resolves the active model in this order:
@@ -0,0 +1,74 @@
---
title: "Message Feedback"
description: "Rate assistant responses with thumbs up/down to help Kilo improve"
---
# Message Feedback
You can give a thumbs up or thumbs down to any response Kilo gives you. It's a quick way to tell us when something worked well, or when it didn't.
{% callout type="info" %}
Feedback is only available when telemetry is on. If you turn telemetry off, the buttons no longer display. See [Turning it off](#turning-it-off) below.
{% /callout %}
## How to use it
{% tabs %}
{% tab label="VSCode" %}
When Kilo finishes a response, you'll see thumbs-up and thumbs-down buttons next to the **Copy response** button. Click one to send your rating. Click the same button again to take it back, or click the other one to change your mind.
If you reload the window or switch to a different session, the feedback status will be reset.
{% /tab %}
{% tab label="CLI" %}
In the terminal, two keybinds rate the most recent assistant message:
| Action | Keybind |
|---|---|
| Helpful | `<leader>=` |
| Not helpful | `<leader>-` |
`<leader>` defaults to `Ctrl+X`, so you press `Ctrl+X` and then `=` for thumbs-up, or `Ctrl+X` and then `-` for thumbs-down.
{% /tab %}
{% /tabs %}
## Why it matters
Your feedback is the most direct way to tell us what's working and what isn't. We use it to tune the prompts behind Kilo's behavior, pick better default models, and find and fix problems faster than we could on our own.
## What we send
When you rate a response, here's what gets sent:
- That you rated a response thumbs up or thumbs down.
- The provider and model ID that produced the response (for example, `anthropic` and `claude-sonnet-4-5`).
- For responses that came through Kilo Gateway, the request ID so we can match the rating back to the session.
No further information is included in the rating feedback. For more information on telemetry, see [PRIVACY.md](https://github.com/Kilo-Org/kilocode/blob/main/PRIVACY.md).
## Turning it off
Feedback is part of telemetry, so turning telemetry off will cause the feedback buttons to no longer appear.
{% tabs %}
{% tab label="VSCode" %}
Open Settings (`Cmd+,` on Mac, `Ctrl+,` on Windows/Linux), search for **`telemetry.telemetryLevel`**, and set it to **Off**. The feedback buttons will no longer appear. Set it back to **All** to bring them back. (For more on what this setting controls, see [VS Code's telemetry docs](https://code.visualstudio.com/docs/getstarted/telemetry).)
{% /tab %}
{% tab label="CLI" %}
Set `KILO_TELEMETRY_LEVEL=off` in your environment before starting Kilo:
```bash
KILO_TELEMETRY_LEVEL=off kilo
```
With telemetry off, pressing the feedback keybinds shows a message saying feedback is disabled, and nothing is sent.
{% /tab %}
{% /tabs %}
@@ -0,0 +1,135 @@
---
title: "Code Review"
description: "How the refinery agent reviews and merges code"
---
# {% $markdoc.frontmatter.title %}
Every piece of code produced by Gas Town agents goes through automated review before merging. The **refinery** agent is dedicated to critiquing, verifying, and gatekeeping what lands in your codebase.
## The Review Pipeline
When a polecat finishes a bead and pushes its branch, the work enters the review pipeline:
{% flowDiagram name="adversarial-loop" height="340px" /%}
{% browserFrame url="app.kilo.ai/gastown/town/rigs/main" caption="A bead in review — the refinery is evaluating the polecat's work" %}
{% image src="/docs/img/gastown/gt-rig-page-convoy-bead-in-review.png" alt="Gas Town rig page showing a bead in review status" /%}
{% /browserFrame %}
The refinery evaluates:
- **Correctness** — does the code do what the task asked?
- **Style** — does it follow project conventions?
- **Completeness** — are tests included? Are edge cases handled?
- **Safety** — any security issues, data leaks, or breaking changes?
## Micro-Adversarial Loops
The core insight behind Gas Town's review system is **adversarial iteration**. Rather than one agent producing a final answer, two agents with different objectives improve the output through tension:
{% flowDiagram name="adversarial-loop" height="340px" /%}
This pattern is fundamentally different from having a single agent self-review:
- Self-review has a **confirmation bias** — the same "mind" that wrote the code evaluates it
- Adversarial review creates **genuine tension** — the refinery has different priorities than the polecat
- Each revision cycle **measurably improves** the output because feedback is specific and actionable
### The Loop in Practice
A typical bead goes through 1-2 revision cycles:
| Cycle | What happens |
|---|---|
| **Write** | Polecat reads the task, writes code, runs tests, pushes |
| **Review 1** | Refinery finds 2 issues: missing test case, inconsistent naming |
| **Revise 1** | Polecat adds the test, fixes naming, pushes again |
| **Review 2** | Refinery approves — code meets quality bar |
| **Merge** | Code lands on target branch |
After 3 failed revision cycles, the bead escalates rather than looping forever.
## Merge Strategies
Gas Town supports two merge strategies, configurable per-town or per-rig:
### Direct Merge
The refinery merges directly to the target branch (convoy feature branch or main) without creating a GitHub PR. This is faster but gives you less visibility into individual merges.
**Best for:** trusted agent output, internal projects, rapid iteration.
### Pull Request Mode
The refinery creates a GitHub PR for each merge. The PR includes:
- The diff
- Review comments from the refinery
- Status checks from CI
You can configure whether PRs auto-merge after refinery approval or require human approval.
**Best for:** production codebases, team environments, audit trails.
## Convoy-Level Review
Convoys add an additional review layer beyond per-bead review:
{% flowDiagram name="convoy-execution" height="200px" /%}
{% browserFrame url="app.kilo.ai/gastown/town/merges" caption="The merge queue — review detail showing refinery feedback" %}
{% image src="/docs/img/gastown/gt-merge-queue-page-review-detail.png" alt="Gas Town merge queue with review detail" /%}
{% /browserFrame %}
| Review Layer | What's checked | Who reviews |
|---|---|---|
| Per-bead | Individual contribution quality | Refinery agent |
| Landing | Combined feature coherence | Refinery agent |
| Human (optional) | Business logic, architecture | Your team |
## Combining with Kilo Code Review
Gas Town's refinery works independently, but combining it with [Kilo Code Review](/docs/code-with-ai/platforms/cloud-agent) creates an even stronger pipeline:
1. **Agent writes** → agent-level refinery reviews (fast, automated)
2. **Code lands as PR** → Kilo Code Review provides human-readable review (deeper, contextual)
3. **Human approves** → code ships
This gives you automated adversarial review for speed **plus** AI-assisted human review for judgment — the best of both approaches.
## The Merge Queue
The merge queue page shows all active and completed reviews in your town:
{% browserFrame url="app.kilo.ai/gastown/town/merges" caption="The merge queue — all reviews at a glance" %}
{% image src="/docs/img/gastown/gt-merge-queue-page.png" alt="Gas Town merge queue page" /%}
{% /browserFrame %}
## Review Configuration
Customize the refinery's behavior in **Town Settings** → **Review**:
| Setting | Options | Default |
|---|---|---|
| `review_mode` | `always` / `never` / `pr_only` | `always` |
| `merge_strategy` | `direct` / `pr` | `direct` |
| `auto_merge` | `true` / `false` | `true` |
| `review_gates` | Strictness level (1-5) | 3 |
| `max_review_cycles` | How many revision attempts | 3 |
### Review Mode
- **`always`** — every bead goes through refinery review (recommended)
- **`never`** — skip review, merge directly on polecat completion (fast but risky)
- **`pr_only`** — only review work that creates a PR
### Review Gates
Higher gate levels make the refinery stricter:
- **Level 1** — basic sanity (compiles, doesn't break tests)
- **Level 3** — standard (style, tests, correctness) — default
- **Level 5** — strict (architecture review, performance, security)
## Handling Review Feedback
When the refinery rejects a submission, it provides specific, actionable feedback. The polecat receives this feedback and revises accordingly. You can see the feedback exchange in the bead's event history.
If a bead fails review 3 times, it transitions to `failed` and creates an **escalation** for human attention. This prevents infinite loops while ensuring difficult code doesn't slip through without proper quality.
@@ -0,0 +1,156 @@
---
title: "Concepts"
description: "Understand the building blocks of Gas Town — towns, beads, convoys, rigs, and agents"
---
# {% $markdoc.frontmatter.title %}
Gas Town by Kilo is built around a small set of composable primitives. Understanding these concepts is key to getting the most out of agent orchestration.
## Towns
A **town** is a persistent workspace where agents operate on your code. It maintains:
- **Configuration** — models, merge strategy, review settings, custom instructions
- **Agent state** — which agents exist, what they're working on, their capabilities
- **Work history** — every bead that's been created, worked on, reviewed, and closed
- **Rig connections** — which repositories are connected and how they're configured
Think of a town as a team's workspace — it accumulates institutional knowledge over time. Agents in your town learn from the history of what's been built, reviewed, and merged.
A town can be personal (owned by you) or organizational (shared across a team).
## Beads
A **bead** is the fundamental unit of work. Every task, review, and coordination action is represented as a bead with a lifecycle:
{% flowDiagram name="bead-lifecycle" height="320px" /%}
| Status | What's happening |
|---|---|
| `open` | Waiting to be picked up by an agent |
| `in_progress` | An agent is actively working on it |
| `in_review` | Work is complete, awaiting review by the refinery |
| `closed` | Successfully completed and merged |
| `failed` | Could not be completed (agent exhausted retries) |
### Bead Types
| Type | Purpose |
|---|---|
| `issue` | A coding task — bug fix, feature, refactor |
| `merge_request` | A review task for the refinery |
| `convoy` | A container for multi-step workflows |
| `escalation` | An issue the agents couldn't resolve — needs human input |
| `message` | Inter-agent communication |
## Convoys
A **convoy** is a multi-bead workflow where tasks can depend on each other. Instead of slinging isolated tasks, convoys let you express complex work as a directed graph.
{% flowDiagram name="convoy-execution" height="200px" /%}
When you create a convoy, you define:
- **Tasks** — what needs to be done (each becomes a bead)
- **Dependencies** — which tasks must complete before others can start
- **Feature branch** — a shared branch that convoy work lands on
The reconciler ensures beads are only dispatched when their dependencies are met. This means agents naturally build on each other's work.
### Staged Convoys
Convoys can be **staged** — created but not started immediately. This lets you review the plan before agents begin executing. Un-stage when you're ready to go.
## Rigs
A **rig** connects a repository to your town. Each rig has:
- Its own set of agents (polecats, refinery)
- Branch configuration (default branch, merge target)
- Override settings (model, review mode, merge strategy)
A town can have **multiple rigs** — useful when your project spans several repositories.
## Agents
Agents are the workers in your town. Each has a specialized role:
### Polecats (Coding Agents)
Polecats do the actual software engineering:
- Read and understand your codebase
- Write code changes in isolated git worktrees
- Run tests and commands to verify their work
- Push branches when done
Multiple polecats can work in **parallel** on different beads. The default is 2 per rig, configurable up to 5+.
Each polecat gets its own git worktree — they never conflict with each other or with your local development.
### The Refinery (Review Agent)
The refinery is the quality gate. When a polecat finishes a bead:
1. The refinery reviews the diff
2. Checks for issues, style violations, missing tests
3. Either **approves and merges** or **sends feedback**
4. If feedback is sent, the polecat revises and resubmits
This creates a **micro-adversarial loop** — one agent writes, another critiques, forcing iterative improvement before code lands.
{% flowDiagram name="adversarial-loop" height="340px" /%}
### The Mayor (Coordination Agent)
The mayor is your interface to the town:
- Plans convoys from high-level descriptions
- Reports on status and progress
- Triages issues and escalations
- Manages agent configuration
- Answers questions about the codebase and work history
The mayor runs persistently — always available for conversation.
## The Reconciler
The reconciler is the engine that drives the town forward. It runs on every alarm tick (every 5 seconds when work is active) and:
1. **Drains events** — agent completions, status changes, failures
2. **Evaluates rules** — which beads need agents, which convoys are ready to advance
3. **Emits actions** — dispatch an agent, create a review, update convoy progress
4. **Enforces invariants** — no double-dispatch, no orphaned hooks, bounded retries
You don't interact with the reconciler directly — it's the autonomous engine that keeps the town moving.
## The Micro-Adversarial Loop
The most powerful concept in Gas Town is the **micro-adversarial loop**. Rather than trusting a single agent's output, every piece of work goes through an adversarial cycle:
{% flowDiagram name="adversarial-loop" height="340px" /%}
This pattern compounds when combined with **convoys**:
1. Bead 1: Explore the codebase → reviewed → merged to convoy branch
2. Bead 2: Design the schema (builds on bead 1's context) → reviewed → merged
3. Bead 3: Implement the feature (builds on beads 1+2) → reviewed → merged
4. **Landing review**: The full convoy branch is reviewed as a cohesive unit before merging to main
At every stage, work is critiqued and refined. Combined with Kilo's [Code Review](/docs/code-with-ai/gastown/code-review) product, this creates a pipeline where code is reviewed multiple times by different agents with different perspectives before it ever reaches your main branch.
## How It All Fits Together
{% browserFrame url="app.kilo.ai/gastown/town" caption="The complete Gas Town experience — Mayor chat, convoy progress, and agent coordination" %}
{% image src="/docs/img/gastown/gt-town-overview.png" alt="Gas Town overview showing the full architecture in action" /%}
{% /browserFrame %}
| Component | Responsibility |
|---|---|
| **You** | Describe work, review PRs, set direction |
| **Mayor** | Plan, coordinate, communicate |
| **Reconciler** | Schedule, dispatch, enforce rules |
| **Polecats** | Write code, run tests, push branches |
| **Refinery** | Review, critique, merge |
| **Container** | Isolated environment with git, tools, runtime |
| **Rig** | Repository connection and configuration |
@@ -0,0 +1,113 @@
---
title: "Gas Town by Kilo"
description: "Autonomous AI agent orchestration for your codebase"
---
# {% $markdoc.frontmatter.title %}
Gastown by Kilo is an autonomous agent orchestration platform that manages teams of AI agents working on your codebase. Built on [Gastown](https://gastown.dev) — the open protocol for agent orchestration — Kilo's implementation coordinates coding agents, a code review agent, and a conversational coordinator to ship features, fix bugs, and maintain your projects with minimal human intervention.
You describe the work. Agents figure out how to do it, write the code, review each other's output, and land clean PRs — while you stay in control of what ships.
{% browserFrame url="app.kilo.ai/gastown/town" caption="A Gas Town with active agents, convoy progress, and Mayor chat" %}
{% image src="/docs/img/gastown/gt-town-overview.png" alt="Gas Town overview showing active work and Mayor chat" /%}
{% /browserFrame %}
## What Makes Gastown Different
Unlike single-agent coding tools that handle one task at a time, Gastown orchestrates **multiple specialized agents** working in parallel:
| Agent | Role | What it does |
|---|---|---|
| **Polecats** | Coding | Write code, run tests, push branches. Multiple can work simultaneously on different tasks. |
| **Refinery** | Code Review | Reviews polecat output, runs quality checks, merges approved work. |
| **Mayor** | Coordination | Your conversational interface. Plans work, answers questions, triages issues, keeps things moving. |
These agents operate within a **town** — a persistent workspace connected to your repository. The town maintains state across sessions: work history, agent configuration, and institutional knowledge about your codebase.
{% browserFrame url="app.kilo.ai/gastown/town/rigs/main" caption="A staged convoy ready to be kicked off" %}
{% image src="/docs/img/gastown/gt-rig-page-staged-convoy.png" alt="Gas Town rig page with a staged convoy" /%}
{% /browserFrame %}
## How It Works
### 1. Create a town
Connect a GitHub repository to a new town. Gastown provisions a dedicated environment with agents ready to work.
### 2. Sling work
Describe what needs to be done — a bug to fix, a feature to build, a refactor to execute. You can sling a single task or a **convoy** (a multi-step plan where tasks depend on each other).
{% browserFrame url="app.kilo.ai/gastown/town/rigs/main" caption="Agents working on a convoy — beads flowing through the pipeline" %}
{% image src="/docs/img/gastown/gt-rig-page-convoy-in-progress.png" alt="Gas Town rig page with convoy in progress" /%}
{% /browserFrame %}
### 3. Agents pick it up
The reconciler assigns open work to available polecats. Each agent gets its own git worktree, writes code, runs commands, and pushes a branch when done.
### 4. Code gets reviewed
Completed work flows to the refinery for automated code review. Depending on your merge strategy, the refinery either merges directly or creates a PR for your approval.
### 5. You stay in control
Monitor progress from the town dashboard. Chat with the mayor. Review PRs. Adjust priorities. Intervene when agents need guidance. Everything the agents do is visible — branches, commits, review comments, and decision reasoning.
{% browserFrame url="app.kilo.ai/gastown/town/beads" caption="The beads page — all work items with status, type, and history" %}
{% image src="/docs/img/gastown/gt-beads-page.png" alt="Gas Town beads page showing beads in various states" /%}
{% /browserFrame %}
## Core Concepts
| Concept | What it is |
|---|---|
| **Town** | A persistent workspace connecting your repo to a team of agents. Maintains configuration, history, and state. |
| **Bead** | A unit of work — an issue to fix, a task to complete, a review to perform. Beads have a lifecycle: open, in progress, in review, closed. |
| **Convoy** | A multi-bead workflow. Beads within a convoy can depend on each other, forming a DAG that agents execute in order. |
| **Rig** | A repository connection within a town. Each rig has its own branch configuration, agents, and settings. A town can have multiple rigs. |
| **Mayor** | The coordination agent. Your primary interface for interacting with the town through natural language. |
| **Polecat** | A coding agent. Works on individual beads — reads code, makes changes, runs tests, pushes branches. |
| **Refinery** | The review agent. Checks polecat output for quality, performs merges, and gates what lands in your codebase. |
For a deeper dive, see [Concepts](/docs/code-with-ai/gastown/concepts).
## The Mayor — Your Interface to the Town
The mayor is how you interact with Gastown conversationally. Think of it as a technical lead that knows your codebase, tracks what all agents are doing, and can take action on your behalf.
You can ask the mayor to:
- Plan and create convoys from high-level descriptions
- Check on the status of in-progress work
- Investigate failures and stuck agents
- Update town settings and configuration
- Triage incoming issues or bug reports
The mayor runs persistently in your town — it's always available, even when no coding agents are active.
{% browserFrame url="app.kilo.ai/gastown/town/rigs/main" caption="Staged convoy detail — review the plan before agents start" %}
{% image src="/docs/img/gastown/gt-rig-page-staged-convoy-detail.png" alt="Gas Town staged convoy detail view" /%}
{% /browserFrame %}
## What You Can Build With Gastown
- **Feature development** — Describe a feature, get back a PR with tests
- **Bug fixes** — Point agents at an issue, they investigate and fix it
- **Refactoring** — Large-scale codebase changes executed methodically across convoys
- **Maintenance** — Dependency updates, lint fixes, documentation generation
- **Code review** — Automated first-pass review on all agent and human PRs
## Getting Started
- [Quick Start](/docs/code-with-ai/gastown/quick-start) — Create your first town and see agents work in under 5 minutes
- [Concepts](/docs/code-with-ai/gastown/concepts) — Detailed explanation of towns, beads, convoys, and the agent lifecycle
- [The Mayor](/docs/code-with-ai/gastown/mayor) — How to interact with the coordination agent effectively
## Guides
- [Sling Work](/docs/code-with-ai/gastown/sling-work) — Creating tasks, convoys, and staged workflows
- [Code Review](/docs/code-with-ai/gastown/code-review) — Merge strategies, review gates, and the refinery pipeline
- [Settings](/docs/code-with-ai/gastown/settings) — Model configuration, agent limits, custom instructions, and environment variables
- [Troubleshooting](/docs/code-with-ai/gastown/troubleshooting) — Common issues and how to resolve them
@@ -0,0 +1,143 @@
---
title: "The Mayor"
description: "How to interact with your town's coordination agent"
---
# {% $markdoc.frontmatter.title %}
The Mayor is your primary interface to a Gas Town. It's a persistent conversational agent that coordinates work, answers questions, and takes action on your behalf.
## What the Mayor Does
The Mayor operates as a technical lead that:
- **Plans work** — converts high-level descriptions into convoys and beads
- **Reports status** — knows what every agent is doing, what's stuck, what's shipped
- **Triages issues** — investigates failures, stuck agents, and escalations
- **Configures the town** — updates settings, manages rigs, adjusts agent behavior
- **Answers questions** — about the codebase, work history, and town state
Unlike polecats (which spin up to work on specific beads), the Mayor runs **persistently**. It's always available, even when no coding agents are active.
{% browserFrame url="app.kilo.ai/gastown/town" caption="The Mayor chat — always available for coordination and questions" %}
{% image src="/docs/img/gastown/gt-town-overview.png" alt="Gas Town Mayor chat interface" /%}
{% /browserFrame %}
## Talking to the Mayor
Open the Mayor panel from your town dashboard. The conversation is persistent — the Mayor remembers context from previous messages within the same session.
### Planning Work
Ask the Mayor to create work for the town:
> *"Create a convoy to add authentication to the API. We need JWT token generation, middleware for protected routes, and integration tests."*
The Mayor will:
1. Break this into individual beads with dependencies
2. Propose a convoy plan for your review
3. Create the convoy (staged by default, so you can review before agents start)
### Checking Status
> *"What's everyone working on?"*
> *"Is anything stuck?"*
> *"How did the last convoy go?"*
The Mayor has full visibility into agent state, bead progress, and recent history.
### Investigating Problems
> *"The auth refactor bead has been in progress for 20 minutes — what's happening?"*
> *"Why did the refinery reject the last review?"*
The Mayor can inspect agent status messages, review feedback, and container logs to diagnose issues.
### Updating Configuration
> *"Switch the model to Auto Frontier for this town"*
> *"Set max polecats to 4"*
> *"Add a custom instruction: always use TypeScript strict mode"*
The Mayor can modify town settings on your behalf through natural language.
## Mayor Tools
The Mayor has access to 21 specialized tools for town management:
### Work Creation
| Tool | What it does |
|---|---|
| `gt_sling` | Delegate a single task to a polecat agent in a specific rig |
| `gt_sling_batch` | Create a multi-bead convoy with dependency ordering, merge mode, and staging options |
### Convoy Management
| Tool | What it does |
|---|---|
| `gt_convoy_status` | Show detailed status of a convoy — each bead's progress and assignee |
| `gt_convoy_start` | Start a staged convoy — begin agent dispatch |
| `gt_convoy_close` | Force-close a convoy and optionally its tracked beads |
| `gt_convoy_update` | Edit convoy metadata (merge mode, feature branch) |
| `gt_convoy_add_bead` | Add an existing bead to a convoy's tracking |
| `gt_convoy_remove_bead` | Remove a bead from a convoy |
| `gt_list_convoys` | List active convoys with progress counts |
### Bead Management
| Tool | What it does |
|---|---|
| `gt_bead_update` | Edit a bead's status, title, body, priority, labels, or dependencies |
| `gt_bead_reassign` | Reassign a bead to a different agent |
| `gt_bead_delete` | Delete one or more beads (supports bulk — up to 5000) |
| `gt_list_beads` | List beads in a rig, filterable by status and type |
### Agent Management
| Tool | What it does |
|---|---|
| `gt_agent_reset` | Force-reset an agent to idle, unhooking it from any bead |
| `gt_nudge` | Send a real-time nudge to a polecat (immediate, wait-idle, or queued) |
| `gt_list_agents` | List all agents in a rig with their roles and status |
| `gt_mail_send` | Send a persistent mail message to any agent in any rig |
### Town & UI
| Tool | What it does |
|---|---|
| `gt_list_rigs` | List all rigs (repositories) in the town |
| `gt_ui_action` | Trigger UI actions — open drawers, navigate pages, highlight items |
| `gt_escalation_acknowledge` | Acknowledge an escalation as reviewed |
| `gt_report_bug` | File a bug report on the Gastown GitHub repo (checks for duplicates first) |
These tools are used automatically when you make requests — you don't need to invoke them directly.
## Tips for Effective Communication
### Be specific about scope
Instead of: *"Fix the bugs"*
Try: *"Fix the TypeScript type errors in src/auth/. There are 3 reported in the CI output."*
### Use convoys for complex work
Instead of: *"Add a user dashboard with charts, settings, and notifications"*
Try: *"Create a staged convoy for the user dashboard. Break it into: 1) dashboard layout and navigation, 2) chart components with mock data, 3) settings page, 4) notification system. Each should build on the previous."*
### Let the Mayor triage
When something goes wrong, ask the Mayor before investigating yourself:
> *"Bead abc123 has been stuck for 30 minutes — can you investigate?"*
The Mayor can often diagnose and resolve issues (reset agents, close stuck convoys, re-dispatch work) without you needing to dig into the admin panel.
## Mayor Limitations
- The Mayor coordinates but doesn't write code itself — that's what polecats are for
- It can only see what's happening inside your town, not external systems
- Complex multi-repo orchestration may need manual coordination between towns
- The Mayor's context window is bounded — very long conversations may lose early context
@@ -0,0 +1,100 @@
---
title: "Quick Start"
description: "Get your first Gas Town running in minutes"
---
# {% $markdoc.frontmatter.title %}
This guide walks you through creating your first town, connecting a repository, and watching agents work on real code.
## Prerequisites
- A [Kilo account](https://app.kilo.ai) (free tier works)
- A GitHub repository you want agents to work on
- A GitHub Personal Access Token (recommended)
## 1. Create a Town
When you first visit Gas Town with no existing towns, you'll be taken directly into the new town onboarding flow. Give your town a name — this is just for your reference.
{% browserFrame url="app.kilo.ai/gastown" caption="The new town onboarding flow" %}
{% image src="/docs/img/gastown/gt-new-town-onboarding.png" alt="Gas Town new town onboarding flow" /%}
{% /browserFrame %}
## 2. Connect a Repository
Add a **rig** to your town. A rig is a connection to a specific repository.
1. Click **Add Rig**
2. Select your GitHub repository (or paste the URL)
3. Choose the default branch (usually `main`)
4. Click **Connect**
Gastown uses the [Kilo GitHub App](https://github.com/apps/kilo-code) to access your repository. You'll be prompted to install it if you haven't already.
{% browserFrame url="app.kilo.ai/gastown/town/rigs/new" caption="Adding a new rig — connect your repository" %}
{% image src="/docs/img/gastown/gt-new-rig.png" alt="Gas Town new rig creation flow" /%}
{% /browserFrame %}
## 3. Add a GitHub Personal Access Token
{% callout type="tip" title="Recommended" %}
Adding a GitHub PAT means all commits, branches, and PRs created by your town's agents will appear as **you** — not a bot account. It also enables agents to use `gh` CLI commands (creating issues, commenting on PRs, etc.) on your behalf.
{% /callout %}
1. Go to **Town Settings** → **Git & Authentication**
2. Generate a [fine-grained personal access token](https://github.com/settings/personal-access-tokens/new) scoped to the repository your town is connected to
3. Required permissions: **Contents** (read/write), **Pull requests** (read/write), **Metadata** (read)
4. Optional: add **Actions** (read/write) if your repo uses GitHub Actions workflows
5. Paste the token and save
{% callout type="info" %}
Use a fine-grained token scoped to only the repository your town works on. Since agents act autonomously on your behalf, limiting the token's scope reduces risk.
{% /callout %}
Without a PAT, agents use the GitHub App installation token — functional but shows up as a bot in your git history.
## 4. Sling Your First Task
Now let's give the agents something to do. The easiest way is to ask the Mayor:
> *"Add a CONTRIBUTING.md file with basic setup instructions"*
Or use the **Sling Work** action in the town header to describe the task directly.
## 5. Watch Agents Work
The reconciler assigns your task to an available polecat agent. Head to the **rig page** to watch it in action:
1. A **bead** appears in the kanban board's open column
2. It moves to `in_progress` as a polecat picks it up
3. The agent reads your code, makes changes, and pushes a branch
4. The bead moves to `in_review` as the refinery checks the work
5. The refinery merges (or creates a PR depending on your settings)
6. The bead lands in the `closed` column
{% browserFrame url="app.kilo.ai/gastown/town/rigs/main" caption="The rig page — convoy tracker and kanban board showing beads in various states" %}
{% image src="/docs/img/gastown/gt-rig-page-convoy-in-progress.png" alt="Gas Town rig page with an active convoy and beads in progress" /%}
{% /browserFrame %}
The whole cycle typically takes 2-10 minutes depending on complexity and the model you're using.
## 6. Talk to the Mayor
Click the **Mayor** chat to interact with your town's coordinator. Try:
- *"What's the status of the town?"*
- *"Create a convoy to add unit tests for the auth module"*
- *"What settings should I change for faster reviews?"*
The Mayor is always running — it's your primary interface for managing the town conversationally.
{% browserFrame url="app.kilo.ai/gastown/town" caption="The Mayor — your conversational interface to the town" %}
{% image src="/docs/img/gastown/gt-town-overview.png" alt="Gas Town overview with Mayor chat" /%}
{% /browserFrame %}
## What's Next?
- [Concepts](/docs/code-with-ai/gastown/concepts) — Understand the building blocks in depth
- [Sling Work](/docs/code-with-ai/gastown/sling-work) — Learn about convoys and multi-step workflows
- [Settings](/docs/code-with-ai/gastown/settings) — Configure models, merge strategies, and agent behavior
@@ -0,0 +1,162 @@
---
title: "Settings"
description: "Configure models, merge strategies, and agent behavior"
---
# {% $markdoc.frontmatter.title %}
Gas Town settings control how your agents behave — which models they use, how they review code, and how they interact with your repositories.
Access settings from your town dashboard → **Settings**.
## Models
### Default Model
The primary model used by all agents (polecats, refinery, mayor). This affects quality, speed, and cost.
Popular choices:
- **Kilo Auto Frontier** — highest quality models, best results (recommended)
- **Kilo Auto Balanced** — good balance of quality and cost (minimum for Gas Town)
### Role-Specific Models
Override the default model for specific agent roles. By default, all roles use the town-level model. You can override per-role if you want to optimize cost vs quality for different tasks (e.g., a faster model for the mayor, a stronger model for the refinery).
### Small Model
Used for lightweight tasks (classification, routing, summarization). Usually a smaller, cheaper model.
## Git & Authentication
### GitHub Personal Access Token
{% callout type="tip" title="Strongly Recommended" %}
Adding a GitHub PAT ensures that all commits, branches, and PRs created by your agents appear as **you** in git history. Without it, activity shows up under the Kilo GitHub App bot account.
{% /callout %}
**To add a PAT:**
1. Go to **Settings** → **Git & Authentication**
2. Generate a [fine-grained personal access token](https://github.com/settings/personal-access-tokens/new) scoped to the connected repository
3. Required permissions: **Contents** (read/write), **Pull requests** (read/write), **Metadata** (read)
4. Optional: add **Actions** (read/write) if your repo uses GitHub Actions workflows
5. Paste the token and save
{% callout type="info" %}
Use a fine-grained token limited to only the repository your town is connected to. Agents act autonomously on your behalf, so limiting scope is a best practice.
{% /callout %}
**What the PAT enables:**
- Commits and PRs appear as you (your avatar, your username)
- Agents can use `gh` CLI commands on your behalf
- Access to the specific repository you scoped the token to
- Ability to trigger CI workflows (if Actions permission is granted)
**Without a PAT:**
- The GitHub App installation token is used (functional but less personal)
- PRs show as created by the Kilo bot
- Some `gh` CLI operations may not work
### GitHub App Installation
The [Kilo GitHub App](https://github.com/apps/kilo-code) provides base-level repository access. It's installed per-organization or per-repository and gives agents read/write access to code, PRs, and issues.
The GitHub App is **required** — it's how Gastown gets installation tokens for cloning and pushing. The PAT is **optional but recommended** — it provides user-level attribution.
## Merge Strategy
Controls how reviewed code lands in your repository:
| Strategy | Behavior | Best for |
|---|---|---|
| `direct` | Refinery merges directly (no PR) | Speed, trusted environments |
| `pr` | Refinery creates a PR | Audit trail, team visibility, CI integration |
For `pr` mode, you can also configure:
- **Auto-merge** — PRs merge automatically after refinery approval
- **Require human approval** — PRs wait for a human reviewer
## Refinery Configuration
### Review Mode
| Mode | Behavior |
|---|---|
| `always` | Every completed bead goes through review (default, recommended) |
| `never` | Skip review entirely — merge on polecat completion |
| `pr_only` | Only review work that generates a PR |
### Review Gates
Strictness level from 1 (lenient) to 5 (strict). Higher levels mean the refinery will reject more frequently but produce higher quality output.
### Max Review Cycles
How many write → review → revise cycles before a bead fails and escalates. Default: 3.
## Agent Limits
### Max Polecats Per Rig
How many coding agents can work simultaneously on a single rig. Default: 2.
Higher values mean more parallel work but also more resource consumption. Consider:
- **1-2** — conservative, good for small repos or limited budgets
- **3-4** — moderate parallelism, good for medium projects
- **5+** — aggressive, best for large repos with lots of independent work
### Alarm Intervals
Controls how frequently the reconciler checks for work:
- **Active interval** — when agents are working (default: 5s)
- **Idle interval** — when no work is pending (default: 60s)
Lower active intervals mean faster response to events but more compute usage.
## Environment Variables
Add environment variables that are available to all agents in the container. Useful for:
- API keys for external services agents might test against
- Database connection strings for test environments
- Feature flags or configuration values
{% callout type="warning" %}
Environment variables are visible to all agents in the town. Do not store production secrets here — use test/development credentials only.
{% /callout %}
## Custom Instructions
Free-form text injected into every agent's system prompt. Use this to communicate:
- Project conventions: *"Always use TypeScript strict mode"*
- Architecture decisions: *"The auth module uses JWT with RS256. Never use HS256."*
- Style preferences: *"Use functional components with hooks, never class components"*
- Constraints: *"Do not modify files in the `vendor/` directory"*
- Testing requirements: *"Every new function must have a corresponding unit test"*
Custom instructions are powerful — they let you encode institutional knowledge that agents follow consistently.
## Convoy Settings
### Staged Convoys Default
When `true`, new convoys are created as staged (paused until you un-stage). Default: `true`.
### Convoy Merge Mode
| Mode | Behavior |
|---|---|
| `review-then-land` | Each bead merges to a feature branch, then a landing review merges to main (default) |
| `review-and-merge` | Each bead goes through review and merges directly to main (no feature branch) |
`review-then-land` provides the strongest quality guarantees but takes longer. Use `review-and-merge` for simpler tasks or when you want immediate feedback.
## Per-Rig Overrides
Any town-level setting can be overridden at the rig level. This lets you:
- Use a more powerful model for a complex repository
- Set stricter review gates for a production codebase
- Allow more polecats on a repo with many independent modules
- Disable review for a documentation-only repo
Access per-rig settings from the rig detail page → **Settings**.
@@ -0,0 +1,158 @@
---
title: "Sling Work"
description: "Creating tasks and convoys for agents to work on"
---
# {% $markdoc.frontmatter.title %}
"Slinging work" is how you give agents something to do. You can sling a single task (a bead) or a structured multi-step plan (a convoy).
## Single Tasks
The simplest way to use Gas Town — describe what needs to be done, and an agent picks it up.
Ask the Mayor:
> *"Fix the 404 error on the /settings page — the route is missing from the router config"*
Or use the **Sling Work** action in the town header. Either way, the reconciler assigns the bead to an available polecat. The agent reads the relevant code, makes the fix, runs any tests, and pushes a branch.
### Writing Good Task Descriptions
The quality of agent output directly correlates with the clarity of your description:
| Approach | Example |
|---|---|
| **Vague** (avoid) | "Fix the auth" |
| **Specific** (better) | "Fix the JWT token expiration — tokens should last 24h, not 1h. The constant is in `src/auth/config.ts`" |
| **Contextual** (best) | "Fix #142: users are getting logged out after 1 hour. The issue is the JWT expiration in `src/auth/config.ts` is set to 3600 (1h) but should be 86400 (24h). Add a test to verify." |
Include:
- **What** needs to change
- **Where** in the codebase (file paths if you know them)
- **Why** it's needed (link to issues, error messages)
- **How to verify** (tests to run, behavior to check)
## Convoys
Convoys are where Gas Town really shines. Instead of one agent doing everything in one pass, convoys break complex work into stages where each builds on the last — with adversarial review at every step.
### Why Convoys?
Single-pass agent output has a quality ceiling. The longer an agent works on one task, the more likely it is to accumulate compounding errors. Convoys solve this by:
1. **Decomposing** complex work into focused, reviewable chunks
2. **Sequencing** so later steps build on reviewed, merged code
3. **Reviewing** each chunk independently before it becomes the foundation for the next step
4. **Containing failures** — if step 3 fails, steps 1 and 2 are already safely merged
{% flowDiagram name="convoy-execution" height="200px" /%}
### Creating a Convoy
**Via the Mayor:**
> *"Create a convoy to migrate the database from PostgreSQL to MySQL. Steps: 1) audit current schema and queries, 2) design the new schema with migration plan, 3) implement the migration scripts, 4) update the application layer, 5) add integration tests"*
The Mayor converts this into a convoy with proper dependencies.
{% browserFrame url="app.kilo.ai/gastown/town/rigs/main" caption="A staged convoy — review the task breakdown before agents begin" %}
{% image src="/docs/img/gastown/gt-rig-page-staged-convoy-detail.png" alt="Gas Town staged convoy detail showing task dependencies" /%}
{% /browserFrame %}
### Convoy Execution
Once started, the reconciler manages the convoy:
{% browserFrame url="app.kilo.ai/gastown/town/rigs/main" caption="Convoy in progress — review bead detail showing the refinery at work" %}
{% image src="/docs/img/gastown/gt-rig-page-convoy-review-bead-detail.png" alt="Gas Town convoy review bead detail" /%}
{% /browserFrame %}
Key behaviors:
- Each polecat starts from the **convoy feature branch**, which accumulates all previously merged work
- Beads only dispatch when their **dependencies are satisfied** (upstream beads closed)
- The refinery reviews each sub-PR against the convoy branch
- Once all beads close, a **landing review** checks the full combined diff before merging to main
### The Adversarial Advantage
The convoy pattern creates **layered adversarial review**:
1. **Per-bead review** — refinery critiques each individual contribution
2. **Context accumulation** — each agent builds on verified, reviewed code
3. **Landing review** — the complete feature is reviewed holistically
4. **Combined with Kilo Code Review** — if configured, human reviewers see the final PR too
This means code goes through **3-4 review passes** before landing in your main branch. Bugs get caught at the smallest possible scope where they're cheapest to fix.
### Staged Convoys
By default, convoys are created **staged** — the plan exists but agents don't start until you un-stage it. This lets you:
- Review the task breakdown before execution
- Adjust descriptions, add context, reorder
- Ensure the plan makes sense before burning compute
Un-stage via the convoy detail page or ask the Mayor: *"Start the database migration convoy"*
## Assigning Priority
Beads have priority levels: `low`, `medium` (default), `high`, `critical`.
Higher priority beads are dispatched first when multiple beads are waiting for agents. Set priority:
- In the Sling Work dialog
- Via the Mayor: *"Make the auth fix high priority"*
- By editing the bead after creation
## Watching Progress
### Rig Page — Convoy Tracker
The best place to observe your town in action is the **rig page**. At the top, active convoys show their progress as a visual tracker — each bead in the convoy displayed with its current status and dependency relationships. You can see exactly where in the DAG execution has reached and which beads are blocking downstream work.
{% browserFrame url="app.kilo.ai/gastown/town/rigs/main" caption="Convoy tracker — see exactly where execution has reached" %}
{% image src="/docs/img/gastown/gt-rig-page-convoy-in-progress.png" alt="Gas Town rig page convoy tracker with beads in various states" /%}
{% /browserFrame %}
### Rig Page — Kanban Board
Below the convoy tracker, a kanban board shows beads organized by status — open, in progress, in review, and closed — updating in real-time as agents move work through the pipeline.
{% browserFrame url="app.kilo.ai/gastown/town/rigs/main" caption="Kanban board — beads flow through columns as agents work" %}
{% image src="/docs/img/gastown/gt-rig-page-convoy-bead-in-review.png" alt="Gas Town rig page kanban board with a bead in review" /%}
{% /browserFrame %}
You can see at a glance:
- What's queued up (open column)
- What agents are actively working on (in progress)
- What's awaiting review (in review)
- What's shipped (closed)
Beads move through columns autonomously as the reconciler dispatches agents and work progresses.
### Beads Page
For a more detailed, filterable view across all rigs, the beads page shows every bead in your town. Filter by:
- Status (open, in progress, in review, closed, failed)
- Type (issue, merge_request, convoy)
- Rig (if you have multiple repos)
{% browserFrame url="app.kilo.ai/gastown/town/beads" caption="Beads page — filterable list of all work items" %}
{% image src="/docs/img/gastown/gt-beads-page.png" alt="Gas Town beads page" /%}
{% /browserFrame %}
Click any bead to see its full detail — description, event history, agent activity, and review feedback:
{% browserFrame url="app.kilo.ai/gastown/town/beads/detail" caption="Bead detail — full history and status" %}
{% image src="/docs/img/gastown/gt-beads-page-detail.png" alt="Gas Town bead detail view" /%}
{% /browserFrame %}
### Town Overview
The town overview shows a high-level summary:
- Active agents and what they're working on
- Recent completions
- Pending work queue
### Real-Time Events
The event timeline shows every state transition as it happens — bead dispatched, review submitted, merge completed. Useful for understanding the flow when you want to see exactly what's happening under the hood.
@@ -0,0 +1,136 @@
---
title: "Troubleshooting"
description: "Common issues and how to resolve them"
---
# {% $markdoc.frontmatter.title %}
Gas Town is a complex system with multiple agents, containers, and external integrations. When things go wrong, this guide helps you diagnose and fix common issues.
## Agents Not Picking Up Work
**Symptom:** Beads stay in `open` status. No agents transition to `working`.
**Common causes:**
| Cause | Fix |
|---|---|
| All polecats at max dispatch attempts | Ask the Mayor: *"Reset agent dispatch attempts"* |
| Reconciler paused (draining) | Wait for drain to complete |
| No available polecats | Check `max_polecats_per_rig` — you may need to increase it |
**Quick fix:** Ask the Mayor: *"Why aren't beads getting picked up?"* — it can diagnose and often resolve the issue.
## Container Not Starting
**Symptom:** The container status shows errors or stays in "starting" indefinitely.
**Common causes:**
- Git clone failure (bad credentials, repo not accessible)
- GitHub App not installed on the repository
- Environment variables causing startup crash
**Fix:**
1. Check that the [Kilo GitHub App](https://github.com/apps/kilo-code) is installed on your repository
2. Verify your GitHub PAT is valid (if configured)
3. Remove any environment variables that might cause issues during container init
4. Try a container restart from town settings
## Git Authentication Failures
**Symptom:** Agents can't clone, push, or fetch. Errors mention "authentication failed" or "permission denied".
**Common causes:**
| Cause | Fix |
|---|---|
| GitHub App uninstalled | Reinstall at [github.com/apps/kilo-code](https://github.com/apps/kilo-code) |
| PAT expired or revoked | Generate a new token and update in Settings → Git & Authentication |
| Repository visibility changed | Ensure the GitHub App has access to the repo |
| Org SSO not authorized | Authorize the token for your organization's SSO |
**Diagnosis:** The Mayor may report *"git credential refresh failed: no_installation_found"* — this definitively means the GitHub App needs to be reinstalled.
## Review Loop / Stuck in Review
**Symptom:** A bead cycles between `in_review` and `in_progress` repeatedly, or MR beads keep failing.
**Common causes:**
- The refinery finds issues the polecat can't fix (e.g., fundamental architecture problems)
- Missing PR URL on merge request beads
- Convoy feature branch deleted from remote
**Fix:**
1. Check the bead's event history for review feedback
2. If the feedback is a dead end, ask the Mayor to close the bead: *"Close bead [id] — the approach isn't working"*
3. For stuck convoys, the Mayor can force-close: *"Force close convoy [name]"*
{% callout type="info" %}
Beads automatically escalate after 3 failed review cycles. If a bead is genuinely stuck in a loop, it will eventually fail and notify you rather than running forever.
{% /callout %}
## The Mayor is Unresponsive
**Symptom:** Messages to the Mayor don't get responses, or the Mayor says it's "unauthenticated".
**Common causes:**
- Container sleeping (wakes up after ~30 seconds)
- KILOCODE_TOKEN expired
- Gateway authentication failure
**Fix:**
1. Wait 30 seconds — the container may be waking from sleep
2. If persistent: go to Settings → refresh the container token
3. If still failing: go to settings, force container shutdown
## Convoy Stuck / Never Completes
**Symptom:** A convoy shows open but no beads are being dispatched, or it never reaches "landed".
**Common causes:**
- Upstream dependency bead failed (blocks downstream)
- Convoy feature branch doesn't exist on remote
- Landing MR repeatedly failing
**Fix:**
1. Check convoy progress: which beads are closed? which are open/failed?
2. For failed dependencies: fix or close the blocking bead, then downstream beads will dispatch
3. Ask the Mayor: *"What's blocking convoy [name]?"*
4. If truly stuck: force-close the convoy and re-create it
## Agent Permanently Stuck
**Symptom:** An agent shows as `working` but hasn't produced output for 20+ minutes.
**Common causes:**
- Container process crashed but heartbeat continues
- Agent waiting on an external resource (network, API)
- Infinite loop in agent execution
**Fix:**
1. Ask the Mayor: *"Reset agent [name]"*
2. This clears the hook, resets the agent to idle, and returns the bead to `open`
3. The reconciler will re-dispatch the bead to a fresh agent
## High Failure Rate
**Symptom:** Many beads ending in `failed` status.
**Common causes:**
- Task descriptions are too vague (agents can't figure out what to do)
- The codebase has issues that prevent agents from working (broken build, missing dependencies)
- Model quality is too low for the complexity of the work
**Fix:**
1. Review failed bead descriptions — make them more specific
2. Ensure the repo builds cleanly (agents struggle with pre-existing broken builds)
3. Consider upgrading the model (Auto Balanced → Auto Frontier for complex work)
4. Add custom instructions to guide agents: test commands, build steps, conventions
## Getting Help
If you can't resolve an issue:
1. **Ask the Mayor** — it can diagnose most problems
2. **Check the event timeline** — see exactly what happened and when
3. **Contact support** — reach out at [kilo.ai/discord](https://kilo.ai/discord) with your town ID
@@ -598,7 +598,7 @@ Options:
--path directory path to generate the agent file [string]
--description what the agent should do [string]
--mode agent mode [string] [choices: "all", "primary", "subagent"]
--permissions, --tools comma-separated list of permissions to allow (default: all). Available: "bash, read, edit, glob, grep, webfetch, task, todowrite, websearch, codesearch, lsp, skill" [string]
--permissions, --tools comma-separated list of permissions to allow (default: all). Available: "bash, read, edit, glob, grep, webfetch, task, todowrite, websearch, lsp, skill" [string]
-m, --model model to use in the format of provider/model [string]
```
@@ -1,5 +1,5 @@
---
title: "Kilo CLI"
title: "Kilo Code CLI: Run the AI Coding Agent from Your Terminal"
description: "Using Kilo Code from the command line"
platform: new
---
@@ -8,7 +8,7 @@ platform: new
This documentation applies only to Kilo version 1.0 and later. Users running versions below 1.0 should upgrade before proceeding.
{% /callout %}
# Kilo CLI
# Kilo Code CLI: AI Coding Agent in Your Terminal
Orchestrate agents from your terminal. Plan, debug, and code fast with keyboard-first navigation on the command line.
@@ -346,6 +346,43 @@ Both keys also accept object configuration for specific tools or language server
The TUI gives `Ctrl+Z` to input undo on Windows because native Windows terminals do not support POSIX terminal suspend. On Windows, `input_undo` defaults to `ctrl+z,ctrl+-,super+z` and `terminal_suspend` is disabled. On macOS and Linux, `terminal_suspend` defaults to `ctrl+z`.
#### Enabling Shift+Enter in Windows Terminal
Some terminals don't send modifier keys with Enter by default. Windows Terminal requires a one-time configuration to forward `Shift+Enter` as an escape sequence that Kilo can read.
Open your `settings.json` at:
```
%LOCALAPPDATA%\Packages\Microsoft.WindowsTerminal_8wekyb3d8bbwe\LocalState\settings.json
```
Add this entry to the root-level `actions` array:
```json
"actions": [
{
"command": {
"action": "sendInput",
"input": "\u001b[13;2u"
},
"id": "User.sendInput.ShiftEnterCustom"
}
]
```
Add this entry to the root-level `keybindings` array:
```json
"keybindings": [
{
"keys": "shift+enter",
"id": "User.sendInput.ShiftEnterCustom"
}
]
```
Save the file and restart Windows Terminal or open a new tab. `Shift+Enter` will now insert a newline in the Kilo prompt instead of submitting the message.
### OpenTelemetry Export
Kilo telemetry is enabled by default and can be disabled with `experimental.openTelemetry = false`:
@@ -1,9 +1,9 @@
---
title: "JetBrains Extension"
title: "Kilo Code for JetBrains: Free Open-Source AI Coding Plugin"
description: "Using Kilo Code in JetBrains IDEs"
---
# JetBrains Extension
# Kilo Code for JetBrains: Free AI Coding Plugin
## Installation
@@ -1,9 +1,9 @@
---
title: "VS Code Extension"
title: "Kilo Code for VS Code: Free Open-Source AI Coding Extension"
description: "Using Kilo Code in Visual Studio Code"
---
# VS Code Extension
# Kilo Code for VS Code
Kilo Code is available as two VS Code extensions: the **VSCode (Legacy)** extension and the current **VSCode** version built on Kilo's shared agent runtime.
@@ -24,7 +24,7 @@ Add a thumbs-up / thumbs-down widget next to the existing copy button on every a
| Surface | Approach |
|---|---|
| VS Code extension | Thumbs buttons inline next to the copy button |
| TUI | Keybinds (`<leader>+` / `<leader>-`) on the last assistant message |
| TUI | Keybinds (`<leader>=` / `<leader>-`) on the last assistant message |
### Telemetry Payload
+4
View File
@@ -1,4 +1,5 @@
import React, { useState } from "react"
import Head from "next/head"
import Link from "next/link"
// Terminal content for each tab
@@ -236,6 +237,9 @@ export default function HomePage() {
return (
<div className="homepage">
<Head>
<title>Kilo Code Docs: Setup, Models, MCP, Custom Modes & CLI</title>
</Head>
{/* Dotted background pattern */}
<div className="dot-pattern" />
Binary file not shown.

After

Width:  |  Height:  |  Size: 784 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 625 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 611 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 571 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 347 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 322 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 675 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 687 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 609 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 571 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 650 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 652 KiB

@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:85c08e55bbd0b2c4bd77ed868fde4d6dac03ffcbb41c6103a8a48fd724875e79
size 3251
@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:200794b3b5539cd5ef4977f315214644d4e038766b48f5fdf0a86f735a276bcd
size 3076
@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:fd92f927ee977559db9f2a9cafd19f7f8ec88542c2d5e46ce5ecb2bd11ab6ad4
size 1682
@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:f4f037c58f94e856a1093e914380efe29b5f1f59aebbc8247df4a434575bd4ab
size 1714
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:ee8d8e04b09b58973a1ff66d5a3d8648ba590f88312cf1f37412c39ccdac0480
size 3973
oid sha256:483e73147bed34050ee35e60607c33c04d7e726b8f746921ad27e582cb5b19ee
size 2886
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:f0e9ea1be8fbb44f05bed7960d1ba13f0d4c5e904e4025e4ab4743c4991e93c8
size 4696
oid sha256:3b4687ed34fe06adb7e72797798b1f3602c225e9a36223842ea1c66eebea4601
size 4794
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:66a7ecedac685e17c442bb036d7392320e0642ef43da77ed8dc23be29bc7988e
size 2935
oid sha256:980c9baf0edafa113956157a34eb00495aff55293b10fb4cbb86a8cb5b28e8c0
size 2865
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:0d29bb3a9356cf8bec6bb0a9d6e4d0b59d147c1fa8adee7140c81a061eea407d
size 6930
oid sha256:067a5c757dfa32d27955dd634b8dea52edef2141111a5eecee0161df36c0ed8b
size 7061
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:483c04840175e7678f0cfd1eba377ec199239404acd567fd7778367f4d2cb46c
size 21219
oid sha256:a1ca383c2a5fa9d6f477430d7c63c7330e13e2667bf717906d3c29c215f163c5
size 21564
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:f2130db1ea14a5e8fbf879e96529198592a785b8460d29e12746175e102d50d5
size 53617
oid sha256:a67058f86869ec7457058feec03f880b7a3ac31c00b87ae6db5a55d977295856
size 57421
@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:bdcac95e7969d021ebce19001f37f86b8ab670bd79e39086385c9419617294df
size 27177
+1 -1
View File
@@ -1,7 +1,7 @@
{
"$schema": "https://json.schemastore.org/package.json",
"name": "@kilocode/kilo-gateway",
"version": "7.2.42",
"version": "7.2.48",
"type": "module",
"license": "MIT",
"description": "Unified Kilo Gateway package for OpenCode - authentication, provider, and API integration",
@@ -12,6 +12,24 @@ export const DEFAULT_KILO_API_URL = "https://api.kilo.ai"
/** Base URL for Kilo API - can be overridden by KILO_API_URL env var */
export const KILO_API_BASE = process.env[ENV_KILO_API_URL] || DEFAULT_KILO_API_URL
/** Environment variable for custom Kilo Chat URL */
export const KILO_CHAT_URL_ENV = "KILO_CHAT_URL"
/** Default Kilo Chat URL (REST endpoint for messages, conversations, etc.) */
export const KILO_DEFAULT_CHAT_URL = "https://chat.kiloapps.io"
/** Base URL for Kilo Chat - can be overridden by KILO_CHAT_URL env var */
export const KILO_CHAT_URL = process.env[KILO_CHAT_URL_ENV] || KILO_DEFAULT_CHAT_URL
/** Environment variable for custom Event Service URL */
export const KILO_EVENT_SERVICE_URL_ENV = "EVENT_SERVICE_URL"
/** Default Event Service URL (WebSocket endpoint for kilo-chat events) */
export const KILO_DEFAULT_EVENT_SERVICE_URL = "wss://events.kiloapps.io"
/** Base URL for Event Service - can be overridden by EVENT_SERVICE_URL env var */
export const KILO_EVENT_SERVICE_URL = process.env[KILO_EVENT_SERVICE_URL_ENV] || KILO_DEFAULT_EVENT_SERVICE_URL
/** Default base URL for OpenRouter-compatible endpoint */
export const KILO_OPENROUTER_BASE = `${KILO_API_BASE}/api/openrouter`
@@ -0,0 +1,63 @@
import { z } from "zod"
import { resolveKiloGatewayBaseUrl } from "./url.js"
export type KiloEmbeddingModel = {
id: string
name: string
dimension: number
scoreThreshold: number
note?: string
}
export type KiloEmbeddingModelCatalog = {
defaultModel: string
models: KiloEmbeddingModel[]
aliases: Record<string, string>
}
export const EMPTY_KILO_EMBEDDING_MODEL_CATALOG: KiloEmbeddingModelCatalog = {
defaultModel: "",
models: [],
aliases: {},
}
const model = z.object({
id: z.string(),
name: z.string(),
dimension: z.number().int().positive(),
scoreThreshold: z.number(),
note: z.string().optional(),
})
const catalog = z.object({
defaultModel: z.string(),
models: z.array(model),
aliases: z.record(z.string(), z.string()),
})
type Options = {
baseURL?: string
token?: string
signal?: AbortSignal
}
export async function fetchKiloEmbeddingModelCatalog(options: Options = {}): Promise<KiloEmbeddingModelCatalog> {
const url = new URL("embedding-models", resolveKiloGatewayBaseUrl({ baseURL: options.baseURL, token: options.token }))
try {
const response = await fetch(url, { signal: options.signal })
if (!response.ok) {
console.warn(`[Kilo Gateway] Failed to fetch embedding model catalog: ${response.status}`)
return EMPTY_KILO_EMBEDDING_MODEL_CATALOG
}
const parsed = catalog.safeParse(await response.json())
if (!parsed.success) {
console.warn("[Kilo Gateway] Embedding model catalog response validation failed:", parsed.error.format())
return EMPTY_KILO_EMBEDDING_MODEL_CATALOG
}
return parsed.data
} catch (err) {
console.warn("[Kilo Gateway] Error fetching embedding model catalog:", err)
return EMPTY_KILO_EMBEDDING_MODEL_CATALOG
}
}
+59 -48
View File
@@ -3,6 +3,11 @@ import { getKiloUrlFromToken } from "../auth/token.js"
import { getDefaultHeaders, buildKiloHeaders } from "../headers.js"
import { KILO_API_BASE, KILO_OPENROUTER_BASE, MODELS_FETCH_TIMEOUT_MS, PROMPTS, AI_SDK_PROVIDERS } from "./constants.js"
export type KiloModelsResult = {
models: Record<string, any>
error?: { kind: "unauthorized" | "network" | "schema" | "http"; status?: number }
}
/**
* OpenRouter model schema
*/
@@ -63,13 +68,13 @@ function parseApiPrice(price: string | null | undefined): number | undefined {
* Fetch models from Kilo API (OpenRouter-compatible endpoint)
*
* @param options - Configuration options
* @returns Record of models in ModelsDev.Model format
* @returns Typed result with models and optional error info
*/
export async function fetchKiloModels(options?: {
kilocodeToken?: string
kilocodeOrganizationId?: string
baseURL?: string
}): Promise<Record<string, any>> {
}): Promise<KiloModelsResult> {
const token = options?.kilocodeToken
const organizationId = options?.kilocodeOrganizationId
@@ -84,54 +89,60 @@ export async function fetchKiloModels(options?: {
// Construct models endpoint
const modelsURL = `${finalBaseURL}/models`
try {
// Fetch models with timeout
const response = await fetch(modelsURL, {
headers: {
...getDefaultHeaders(),
...buildKiloHeaders(undefined, { kilocodeOrganizationId: organizationId }),
...(token ? { Authorization: `Bearer ${token}` } : {}),
},
signal: AbortSignal.timeout(MODELS_FETCH_TIMEOUT_MS),
})
const response = await fetch(modelsURL, {
headers: {
...getDefaultHeaders(),
...buildKiloHeaders(undefined, { kilocodeOrganizationId: organizationId }),
...(token ? { Authorization: `Bearer ${token}` } : {}),
},
signal: AbortSignal.timeout(MODELS_FETCH_TIMEOUT_MS),
}).catch((err: unknown) => err as Error)
if (!response.ok) {
throw new Error(`Failed to fetch models: ${response.status} ${response.statusText}`)
}
const json = await response.json()
// Validate response schema
const result = openRouterModelsResponseSchema.safeParse(json)
if (!result.success) {
console.error("Kilo models response validation failed:", result.error.format())
return {}
}
// Transform models to ModelsDev.Model format
const models: Record<string, any> = {}
for (const model of result.data.data) {
// Skip image generation models
if (model.architecture?.output_modalities?.includes("image")) {
continue
}
// Skip models that don't support tools — Kilo requires tool calling
if (!model.supported_parameters?.includes("tools")) {
continue
}
const transformedModel = transformToModelDevFormat(model)
models[model.id] = transformedModel
}
return models
} catch (error) {
console.error("Error fetching Kilo models:", error)
return {}
if (response instanceof Error) {
return { models: {}, error: { kind: "network" } }
}
if (!response.ok) {
// 401 with auth credentials: fall back to unauthenticated public endpoint
if (response.status === 401 && (token || organizationId)) {
return fetchKiloModels({})
}
const kind = response.status === 401 || response.status === 403 ? "unauthorized" : "http"
return { models: {}, error: { kind, status: response.status } }
}
const json = await response.json().catch(() => null)
if (json === null) {
return { models: {}, error: { kind: "schema" } }
}
// Validate response schema
const result = openRouterModelsResponseSchema.safeParse(json)
if (!result.success) {
return { models: {}, error: { kind: "schema" } }
}
// Transform models to ModelsDev.Model format
const models: Record<string, any> = {}
for (const model of result.data.data) {
// Skip image generation models
if (model.architecture?.output_modalities?.includes("image")) {
continue
}
// Skip models that don't support tools — Kilo requires tool calling
if (!model.supported_parameters?.includes("tools")) {
continue
}
const transformedModel = transformToModelDevFormat(model)
models[model.id] = transformedModel
}
return { models }
}
/**
-2
View File
@@ -77,7 +77,6 @@ export async function fetchOrganizationModes(token: string, organizationId: stri
})
if (!response.ok) {
console.warn(`[Kilo Gateway] Failed to fetch organization modes: ${response.status}`)
return []
}
@@ -85,7 +84,6 @@ export async function fetchOrganizationModes(token: string, organizationId: stri
const parsed = ResponseSchema.safeParse(json)
if (!parsed.success) {
console.warn("[Kilo Gateway] Organization modes response validation failed:", parsed.error.format())
return []
}
+30
View File
@@ -0,0 +1,30 @@
import { KILO_API_BASE } from "./constants.js"
import { getKiloUrlFromToken } from "../auth/token.js"
type UrlOptions = {
baseURL?: string
token?: string
}
function route(raw: string, name: "gateway" | "openrouter"): string {
const url = new URL(raw)
const parts = url.pathname.replace(/\/+$/, "").split("/").filter(Boolean)
const api = parts.lastIndexOf("api")
const prefix = api >= 0 ? parts.slice(0, api) : parts
url.pathname = `/${[...prefix, "api", name].join("/")}/`
url.search = ""
url.hash = ""
return url.toString()
}
function base(options: UrlOptions): string {
return getKiloUrlFromToken(options.baseURL ?? KILO_API_BASE, options.token ?? "")
}
export function resolveKiloGatewayBaseUrl(options: UrlOptions = {}): string {
return route(base(options), "gateway")
}
export function resolveKiloOpenRouterBaseUrl(options: UrlOptions = {}): string {
return route(base(options), "openrouter")
}
+7 -11
View File
@@ -4,20 +4,16 @@
*/
export function getKiloUrlFromToken(defaultUrl: string, token: string): string {
// If token contains URL information, extract it
// This is a simplified version - adjust based on actual token format
if (!token) return defaultUrl
try {
// Check if token has URL prefix (format: "url:base64token")
const parts = token.split(":")
if (parts.length > 1 && parts[0].startsWith("http")) {
return parts[0]
}
} catch (e) {
// If parsing fails, return default
}
const match = token.match(/^(https?:\/\/[^:]+(?::\d+)?(?:\/[^:]*)?):/)
if (!match) return defaultUrl
return defaultUrl
try {
return new URL(match[1]).toString().replace(/\/+$/, "")
} catch {
return defaultUrl
}
}
/**
+8 -1
View File
@@ -33,7 +33,14 @@ export {
getKiloDefaultModel,
promptOrganizationSelection,
} from "./api/profile.js"
export { fetchKiloModels } from "./api/models.js"
export { fetchKiloModels, type KiloModelsResult } from "./api/models.js"
export {
EMPTY_KILO_EMBEDDING_MODEL_CATALOG,
fetchKiloEmbeddingModelCatalog,
type KiloEmbeddingModel,
type KiloEmbeddingModelCatalog,
} from "./api/embedding-models.js"
export { resolveKiloGatewayBaseUrl, resolveKiloOpenRouterBaseUrl } from "./api/url.js"
export {
fetchOrganizationModes,
clearModesCache,
+4 -12
View File
@@ -1,9 +1,10 @@
import { createOpenRouter } from "@openrouter/ai-sdk-provider"
import type { Provider as SDK } from "ai"
import type { KiloProviderOptions } from "./types.js"
import { getKiloUrlFromToken, getApiKey } from "./auth/token.js"
import { getApiKey } from "./auth/token.js"
import { buildKiloHeaders, getDefaultHeaders } from "./headers.js"
import { KILO_API_BASE, ANONYMOUS_API_KEY } from "./api/constants.js"
import { ANONYMOUS_API_KEY } from "./api/constants.js"
import { resolveKiloOpenRouterBaseUrl } from "./api/url.js"
/**
* Debug version of createKilo with extensive logging
@@ -18,16 +19,7 @@ export function createKiloDebug(options: KiloProviderOptions = {}): SDK {
console.log(" - Source:", options.kilocodeToken ? "kilocodeToken" : options.apiKey ? "apiKey" : "none")
console.log(" - Value:", apiKey ? `${apiKey.substring(0, 8)}...${apiKey.substring(apiKey.length - 8)}` : "MISSING!")
// Determine base URL
const baseApiUrl = getKiloUrlFromToken(options.baseURL ?? KILO_API_BASE, apiKey ?? "")
console.log("🌐 [KILO DEBUG] Base URL resolved:", baseApiUrl)
// Build OpenRouter URL - only append /openrouter/ if not already present
const openRouterUrl = baseApiUrl.includes("/openrouter")
? baseApiUrl
: baseApiUrl.endsWith("/")
? `${baseApiUrl}openrouter/`
: `${baseApiUrl}/openrouter/`
const openRouterUrl = resolveKiloOpenRouterBaseUrl({ baseURL: options.baseURL, token: apiKey })
console.log("🔗 [KILO DEBUG] OpenRouter URL:", openRouterUrl)
// Merge custom headers with defaults
+4 -11
View File
@@ -4,9 +4,10 @@ import { createAnthropic } from "@ai-sdk/anthropic"
import { createOpenAI } from "@ai-sdk/openai"
import { createOpenAICompatible } from "@ai-sdk/openai-compatible"
import type { KiloProvider, KiloProviderOptions } from "./types.js"
import { getKiloUrlFromToken, getApiKey } from "./auth/token.js"
import { getApiKey } from "./auth/token.js"
import { buildKiloHeaders, getDefaultHeaders } from "./headers.js"
import { KILO_API_BASE, ANONYMOUS_API_KEY } from "./api/constants.js"
import { ANONYMOUS_API_KEY } from "./api/constants.js"
import { resolveKiloOpenRouterBaseUrl } from "./api/url.js"
/**
* Create a KiloCode provider instance
@@ -28,15 +29,7 @@ export function createKilo(options: KiloProviderOptions = {}): KiloProvider {
// Get API key from options or environment
const apiKey = getApiKey(options)
// Determine base URL
const baseApiUrl = getKiloUrlFromToken(options.baseURL ?? KILO_API_BASE, apiKey ?? "")
// Build OpenRouter URL - only append /openrouter/ if not already present
const openRouterUrl = baseApiUrl.includes("/openrouter")
? baseApiUrl
: baseApiUrl.endsWith("/")
? `${baseApiUrl}openrouter/`
: `${baseApiUrl}/openrouter/`
const openRouterUrl = resolveKiloOpenRouterBaseUrl({ baseURL: options.baseURL, token: apiKey })
// Merge custom headers with defaults
const customHeaders = {
+55 -36
View File
@@ -8,7 +8,13 @@
import { fetchProfile, fetchBalance } from "../api/profile.js"
import { fetchKilocodeNotifications, KilocodeNotificationSchema } from "../api/notifications.js"
import { fetchOrganizationModes, clearModesCache } from "../api/modes.js"
import { KILO_API_BASE, HEADER_FEATURE, HEADER_ORGANIZATIONID } from "../api/constants.js"
import {
KILO_API_BASE,
KILO_CHAT_URL,
KILO_EVENT_SERVICE_URL,
HEADER_FEATURE,
HEADER_ORGANIZATIONID,
} from "../api/constants.js"
import { buildKiloHeaders } from "../headers.js"
import type { ImportDeps, DrizzleDb } from "../cloud-sessions.js"
import { fetchCloudSession, fetchCloudSessionForImport, importSessionToDb } from "../cloud-sessions.js"
@@ -32,7 +38,7 @@ interface KiloRoutesDeps extends ImportDeps {
Auth: Auth
ModelCache: ModelCache
z: Z
Instance: ImportDeps["Instance"] & { disposeAll(): Promise<void> }
InstanceStore: { disposeAllInstances(): Promise<void> }
}
const FIM_TIMEOUT_MS = 30_000
@@ -79,6 +85,7 @@ export function createKiloRoutes(deps: KiloRoutesDeps) {
SessionCreatedEvent,
Identifier,
ModelCache,
InstanceStore,
} = deps
const Organization = z.object({
@@ -210,7 +217,7 @@ export function createKiloRoutes(deps: KiloRoutesDeps) {
ModelCache.clear("kilo")
clearModesCache()
await Instance.disposeAll()
await InstanceStore.disposeAllInstances()
return c.json(true)
},
@@ -524,8 +531,24 @@ export function createKiloRoutes(deps: KiloRoutesDeps) {
"application/json": {
schema: resolver(
z.object({
// `recovering` and `restoring` are transitional states the
// worker reports while it brings an instance back online
// after an unexpected stop or a snapshot restore — see
// cloud `services/kiloclaw/src/index.ts` and the
// `PlatformStatusResponse` type in
// cloud/apps/web/src/lib/kiloclaw/types.ts. Keeping them in
// the enum so the SDK types stay accurate.
status: z
.enum(["provisioned", "starting", "restarting", "running", "stopped", "destroying"])
.enum([
"provisioned",
"starting",
"restarting",
"recovering",
"running",
"stopped",
"destroying",
"restoring",
])
.nullable(),
sandboxId: z.string().optional(),
flyRegion: z.string().optional(),
@@ -536,6 +559,7 @@ export function createKiloRoutes(deps: KiloRoutesDeps) {
channelCount: z.number().optional(),
secretCount: z.number().optional(),
userId: z.string().optional(),
botName: z.string().nullable().optional(),
}),
),
},
@@ -578,57 +602,52 @@ export function createKiloRoutes(deps: KiloRoutesDeps) {
"/claw/chat-credentials",
describeRoute({
summary: "Get KiloClaw chat credentials",
description: "Fetch Stream Chat credentials for the user's KiloClaw instance",
description:
"Returns the bearer token and endpoint URLs the client uses to talk to the Kilo Chat worker " +
"and the Event Service. The bearer is the user's existing long-lived Kilo JWT — kilo-chat and " +
"event-service both verify it directly with NEXTAUTH_SECRET, so no separate token mint is needed.",
operationId: "kilo.claw.chatCredentials",
responses: {
200: {
description: "Stream Chat credentials or null",
description: "Kilo Chat credentials or null",
content: {
"application/json": {
schema: resolver(
z
.object({
apiKey: z.string(),
userId: z.string(),
userToken: z.string(),
channelId: z.string(),
token: z.string(),
expiresAt: z.string(),
kiloChatUrl: z.string(),
eventServiceUrl: z.string(),
})
.nullable(),
),
},
},
},
...errors(401, 502),
...errors(401),
},
}),
async (c: any) => {
try {
const auth = await Auth.get("kilo")
if (!auth) return c.json({ error: "Not authenticated with Kilo Gateway" }, 401)
const token = auth.type === "api" ? auth.key : auth.type === "oauth" ? auth.access : undefined
if (!token) return c.json({ error: "No valid token found" }, 401)
const auth = await Auth.get("kilo")
if (!auth) return c.json({ error: "Not authenticated with Kilo Gateway" }, 401)
const token = auth.type === "api" ? auth.key : auth.type === "oauth" ? auth.access : undefined
if (!token) return c.json({ error: "No valid token found" }, 401)
const organizationId = auth.type === "oauth" ? auth.accountId : undefined
const headers: Record<string, string> = {
Authorization: `Bearer ${token}`,
"Content-Type": "application/json",
}
if (organizationId) {
headers[HEADER_ORGANIZATIONID] = organizationId
}
// For OAuth, expires is a millisecond epoch we already track. For
// API tokens we don't have a verified expiry locally — the JWT is
// signed by the cloud and validated by kilo-chat/event-service on
// every request. Use a far-future placeholder so the client cache
// doesn't refetch unnecessarily; on 401 the client clears the
// cache and prompts re-auth.
const expiresAtMs = auth.type === "oauth" ? auth.expires : Date.now() + 365 * 24 * 60 * 60 * 1000
const response = await fetch(`${KILO_API_BASE}/api/kiloclaw/chat-credentials`, { headers })
if (!response.ok) {
const text = await response.text()
return c.json({ error: `KiloClaw request failed: ${response.status} ${text}` }, response.status as any)
}
return c.json(await response.json())
} catch (err: any) {
console.error("[Kilo Gateway] claw/chat-credentials: error", err?.message ?? err)
return c.json({ error: "Failed to reach KiloClaw" }, 502)
}
return c.json({
token,
expiresAt: new Date(expiresAtMs).toISOString(),
kiloChatUrl: KILO_CHAT_URL,
eventServiceUrl: KILO_EVENT_SERVICE_URL,
})
},
)
.get(
@@ -0,0 +1,52 @@
import { describe, expect, mock, test } from "bun:test"
import { EMPTY_KILO_EMBEDDING_MODEL_CATALOG, fetchKiloEmbeddingModelCatalog } from "../../src/api/embedding-models"
describe("fetchKiloEmbeddingModelCatalog", () => {
test("fetches catalog from Kilo Gateway", async () => {
const prev = global.fetch
const fn = mock(() =>
Promise.resolve(
new Response(
JSON.stringify({
defaultModel: "provider/model",
models: [{ id: "provider/model", name: "Provider Model", dimension: 1024, scoreThreshold: 0.4 }],
aliases: { model: "provider/model" },
}),
),
),
) as unknown as typeof fetch
global.fetch = fn
try {
const catalog = await fetchKiloEmbeddingModelCatalog({ baseURL: "https://example.test" })
expect(catalog.defaultModel).toBe("provider/model")
expect((fn as unknown as { mock: { calls: Array<[URL]> } }).mock.calls[0]?.[0].toString()).toBe(
"https://example.test/api/gateway/embedding-models",
)
} finally {
global.fetch = prev
}
})
test("falls back when the request fails", async () => {
const prev = global.fetch
global.fetch = mock(() => Promise.resolve(new Response("nope", { status: 500 }))) as unknown as typeof fetch
try {
await expect(fetchKiloEmbeddingModelCatalog({ baseURL: "https://example.test" })).resolves.toEqual(
EMPTY_KILO_EMBEDDING_MODEL_CATALOG,
)
} finally {
global.fetch = prev
}
})
test("fallback catalog is empty so Cloud owns model metadata", () => {
expect(EMPTY_KILO_EMBEDDING_MODEL_CATALOG).toEqual({
defaultModel: "",
models: [],
aliases: {},
})
})
})
@@ -0,0 +1,124 @@
// Verifies fetchKiloModels typed result and 401 fallback behaviour.
import { test, expect } from "bun:test"
import { fetchKiloModels } from "../../src/api/models.js"
const VALID_RESPONSE = JSON.stringify({
data: [
{
id: "test/model-a",
name: "Test Model A",
context_length: 128000,
max_completion_tokens: 16384,
architecture: {
input_modalities: ["text"],
output_modalities: ["text"],
},
supported_parameters: ["tools", "temperature"],
},
],
})
function stubFetch(fn: (input: string | URL | Request, init?: RequestInit) => Promise<Response>) {
;(globalThis as any).fetch = fn
}
test("returns empty models and error when both auth and public requests return 401", async () => {
const orig = globalThis.fetch
stubFetch(async () => new Response("Unauthorized", { status: 401, statusText: "Unauthorized" }))
const result = await fetchKiloModels({ kilocodeToken: "bad-token" })
;(globalThis as any).fetch = orig
expect(result.models).toEqual({})
expect(result.error).toBeDefined()
})
test("falls back to public endpoint on 401 and returns models", async () => {
const orig = globalThis.fetch
let callCount = 0
stubFetch(async () => {
callCount++
if (callCount === 1) {
return new Response("Unauthorized", { status: 401, statusText: "Unauthorized" })
}
return new Response(VALID_RESPONSE, {
status: 200,
headers: { "content-type": "application/json" },
})
})
const result = await fetchKiloModels({
kilocodeToken: "expired-token",
kilocodeOrganizationId: "org-123",
})
;(globalThis as any).fetch = orig
expect(callCount).toBe(2)
expect(result.error).toBeUndefined()
expect(Object.keys(result.models).length).toBeGreaterThan(0)
})
test("returns error with kind=network on fetch exception", async () => {
const orig = globalThis.fetch
stubFetch(async () => {
throw new Error("network error")
})
const result = await fetchKiloModels({})
;(globalThis as any).fetch = orig
expect(result.models).toEqual({})
expect(result.error?.kind).toBe("network")
})
test("returns error with kind=http on non-auth HTTP error (e.g. 500)", async () => {
const orig = globalThis.fetch
stubFetch(async () => new Response("Server Error", { status: 500, statusText: "Internal Server Error" }))
const result = await fetchKiloModels({})
;(globalThis as any).fetch = orig
expect(result.models).toEqual({})
expect(result.error?.kind).toBe("http")
expect(result.error?.status).toBe(500)
})
test("returns models without error on success", async () => {
const orig = globalThis.fetch
stubFetch(async () =>
new Response(VALID_RESPONSE, {
status: 200,
headers: { "content-type": "application/json" },
}),
)
const result = await fetchKiloModels({})
;(globalThis as any).fetch = orig
expect(result.error).toBeUndefined()
expect(Object.keys(result.models).length).toBeGreaterThan(0)
})
test("returns error with kind=schema when response body is invalid JSON", async () => {
const orig = globalThis.fetch
stubFetch(async () =>
new Response("not valid json{{{{", {
status: 200,
headers: { "content-type": "application/json" },
}),
)
const result = await fetchKiloModels({})
;(globalThis as any).fetch = orig
expect(result.models).toEqual({})
expect(result.error?.kind).toBe("schema")
})
@@ -0,0 +1,52 @@
import { describe, expect, test } from "bun:test"
import { resolveKiloGatewayBaseUrl, resolveKiloOpenRouterBaseUrl } from "../../src/api/url"
describe("Kilo API URL resolvers", () => {
test("resolves production route bases", () => {
expect(resolveKiloGatewayBaseUrl()).toBe("https://api.kilo.ai/api/gateway/")
expect(resolveKiloOpenRouterBaseUrl()).toBe("https://api.kilo.ai/api/openrouter/")
})
test("normalizes root API base overrides", () => {
expect(resolveKiloGatewayBaseUrl({ baseURL: "https://example.test" })).toBe("https://example.test/api/gateway/")
expect(resolveKiloOpenRouterBaseUrl({ baseURL: "https://example.test/" })).toBe(
"https://example.test/api/openrouter/",
)
})
test("replaces existing Kilo API route paths", () => {
expect(resolveKiloGatewayBaseUrl({ baseURL: "https://example.test/api/openrouter/" })).toBe(
"https://example.test/api/gateway/",
)
expect(resolveKiloOpenRouterBaseUrl({ baseURL: "https://example.test/api/gateway/" })).toBe(
"https://example.test/api/openrouter/",
)
})
test("preserves path prefixes before api", () => {
expect(resolveKiloGatewayBaseUrl({ baseURL: "https://example.test/dev/api/openrouter/" })).toBe(
"https://example.test/dev/api/gateway/",
)
expect(resolveKiloOpenRouterBaseUrl({ baseURL: "https://example.test/dev" })).toBe(
"https://example.test/dev/api/openrouter/",
)
})
test("strips search and hash components", () => {
expect(resolveKiloGatewayBaseUrl({ baseURL: "https://example.test/api/openrouter/?x=1#frag" })).toBe(
"https://example.test/api/gateway/",
)
})
test("prefers token-derived URL when token contains one", () => {
expect(resolveKiloGatewayBaseUrl({ baseURL: "https://fallback.test", token: "https://token.test:opaque" })).toBe(
"https://token.test/api/gateway/",
)
})
test("resolves child endpoint URLs", () => {
expect(new URL("embedding-models", resolveKiloGatewayBaseUrl({ baseURL: "https://example.test" })).toString()).toBe(
"https://example.test/api/gateway/embedding-models",
)
})
})
+1 -1
View File
@@ -1,7 +1,7 @@
{
"$schema": "https://json.schemastore.org/package.json",
"name": "@kilocode/kilo-i18n",
"version": "7.2.42",
"version": "7.2.48",
"type": "module",
"license": "MIT",
"description": "Kilo-specific i18n translations and overrides",

Some files were not shown because too many files have changed in this diff Show More