Merge branch 'main' into fix/synthetic-prompts-as-user

This commit is contained in:
Bruno Agatão
2026-10-12 13:45:00 +02:00
committed by GitHub
1002 changed files with 41519 additions and 7758 deletions
@@ -0,0 +1,5 @@
---
"kilo-code": minor
---
Reorder projects in the Agent Manager sidebar with drag and drop. The order is kept after a restart.
@@ -1,5 +0,0 @@
---
"kilo-code": patch
---
Run the Agent Manager worktree setup script for a new worktree even when the setup script of another worktree is still running in the VS Code terminal, and show an error when the setup task cannot start.
@@ -0,0 +1,5 @@
---
"@kilocode/cli": patch
---
Tune the `agent_manager` and `task` tool instructions so agents only start new Agent Manager sessions or worktrees when the user explicitly asks, or after confirming with the user first.
@@ -0,0 +1,5 @@
---
"kilo-code": patch
---
Align the loading spinner with the subagent identicon. The spinner now uses the same 5x5 dot grid as subagent avatars, so loading and subagent indicators share one cell layout. The four corner cells stay empty and the same twinkle animation is kept.
+5
View File
@@ -0,0 +1,5 @@
---
"@kilocode/cli": patch
---
Keep Amazon Bedrock GPT-6 and GPT-5.6 sessions working after a tool returns an image.
+5
View File
@@ -0,0 +1,5 @@
---
"kilo-code": patch
---
Move the agent board button next to the background agents above the prompt. It shows the latest posts and marks new ones, and stays available after agents are cleared or the window reloads.
+5
View File
@@ -0,0 +1,5 @@
---
"kilo-code": patch
---
Match the Integrated Browser color scheme to the current IDE theme so pages that support `prefers-color-scheme` render in dark or light to match the editor.
@@ -0,0 +1,5 @@
---
"kilo-code": minor
---
Open chat links in the system browser with Shift-click when the Integrated Browser handles link clicks
+5
View File
@@ -0,0 +1,5 @@
---
"@kilocode/cli": patch
---
Cloud sessions: the bash tool now advertises a session-scoped temp dir covered by the injected permission rules instead of the denied shared temp dir.
@@ -0,0 +1,5 @@
---
"@kilocode/cli": patch
---
Stop reporting a false frontmatter parse error when saving command markdown files
@@ -0,0 +1,5 @@
---
"@kilocode/cli": patch
---
Report an error instead of silently keeping the old value when a config change is overridden by a higher-priority file such as `opencode.json`. Config validation and write failures now return the file and issue details instead of an opaque server error.
+5
View File
@@ -0,0 +1,5 @@
---
"kilo-code": patch
---
Retry provider connection resets (`Connection reset by server`) through the normal retry path with backoff instead of pausing the session waiting for network reconnection. Connection resets are transient server-side failures when the error is marked retryable; only genuine network disconnections still trigger the offline wait.
+5
View File
@@ -0,0 +1,5 @@
---
"@kilocode/cli": patch
---
Avoid enabling thinking automatically for custom providers whose names contain zai or zhipuai.
@@ -0,0 +1,5 @@
---
"@kilocode/cli": patch
---
Report empty summaries and unrecoverable context overflow during compaction instead of silently stopping. Preserve conversation history and explain when to retry compaction or start a new session.
@@ -0,0 +1,5 @@
---
"kilo-code": patch
---
Fix permission prompts from subagents failing with "Failed to update permissions" and getting stuck
@@ -0,0 +1,5 @@
---
"kilo-code": patch
---
Fix backend startup when a Windows drive root is opened as a workspace.
+5
View File
@@ -0,0 +1,5 @@
---
"@kilocode/cli": patch
---
Improve missing binary error message on Windows ARM64 to provide actionable workarounds (x64 emulation or WSL2).
+5
View File
@@ -0,0 +1,5 @@
---
"@kilocode/cli": patch
---
Expose a custom or plugin command named `goal` as `/goal:command` instead of dropping it. `/goal` still starts a session goal, and the configuration warning now names the alias so the command stays reachable.
@@ -0,0 +1,5 @@
---
"@kilocode/kilo-jetbrains": minor
---
Sign in to remote MCP servers from JetBrains. MCP Settings now highlights servers that need authentication, only shows actions valid for the current connection state, keeps selected-row actions visible when focus moves elsewhere, shows the real sign-in error, lets you cancel an in-progress sign-in without losing credentials you already had, and offers a copyable authorization URL when a browser cannot be opened automatically. Sign-in opens the browser on your own machine, so it also works in remote development. Installing an MCP server from the Marketplace that needs sign-in now offers to start it right away, and the prompt shows a session-issues menu with a sign-in action when a configured MCP server needs authentication. Removing either half of a Marketplace MCP and companion-skill bundle from MCP or Skills Settings now confirms and removes the whole bundle. Remote MCP servers that require a pre-registered OAuth client can now be configured directly from the edit dialog.
+5
View File
@@ -0,0 +1,5 @@
---
"@kilocode/kilo-jetbrains": minor
---
Add a prompt navigator to the chat transcript for jumping between prompts.
@@ -0,0 +1,5 @@
---
"@kilocode/kilo-jetbrains": patch
---
Restore clicking session timeline bars to navigate to the matching transcript content in JetBrains
@@ -0,0 +1,5 @@
---
"@kilocode/kilo-jetbrains": patch
---
Show a worktree's pull request badge as soon as the agent that opened the pull request stops, instead of up to ten minutes later
@@ -0,0 +1,5 @@
---
"@kilocode/cli": patch
---
Log structural diagnostics (message part sequences, tool-call/result pairing, schema issue paths) when the AI SDK rejects an assembled request, instead of failing with a bare `ModelMessage[] schema` error.
+5
View File
@@ -0,0 +1,5 @@
---
"@kilocode/cli": patch
---
Stop MCP servers from piling up when a request is cancelled while they are still starting. Servers that had already started are now shut down, and the next request starts MCP again instead of failing until the backend restarts.
+5
View File
@@ -0,0 +1,5 @@
---
"kilo-code": patch
---
Show page cursor feedback in the Integrated Browser, including text fields, resize handles, and drag controls, without changing page scrolling or compositing.
+5
View File
@@ -0,0 +1,5 @@
---
"kilo-code": minor
---
Support opening the current Integrated Browser page in the external default browser
@@ -0,0 +1,5 @@
---
"@kilocode/kilo-indexing": patch
---
Support OpenAI-compatible indexing endpoints that reject the optional `dimensions` parameter when their native vectors match the configured size. Report a clear error when the endpoint returns a different vector size.
@@ -1,5 +0,0 @@
---
"kilo-code": minor
---
Add an experimental "Per-Conversation Prompt History" setting that keeps prompt input history separate for each conversation
+5
View File
@@ -0,0 +1,5 @@
---
"kilo-code": patch
---
Use the standard open-in-browser icon for the Agent Manager PR panel's external link button, so it matches the open-in-browser affordance used elsewhere in the app.
@@ -0,0 +1,5 @@
---
"@kilocode/cli": patch
---
Keep slash commands working when a plugin or your config defines a command named `goal`. Such a command clashes with Kilo's own `/goal` and used to hide every slash command, including `/init`, `/review`, and your own, leaving autocomplete empty. The clashing command is now skipped and reported as a configuration warning, so the CLI, VS Code, and JetBrains all tell you which command was ignored and why, while the rest keep working. A command list that fails to load also shows a warning in the CLI instead of silently showing no commands.
+5
View File
@@ -0,0 +1,5 @@
---
"kilo-code": minor
---
Show the agent, model, and reasoning selectors as one split control in the prompt toolbar and the Agent Manager New Worktree dialog. While one picker is open, move the pointer to another part to switch to it, or use the Left and Right arrow keys. The reasoning level shows only when it is set and there is room. Otherwise it collapses into the trailing arrow, and a changed level shows for a moment. On narrow sidebars, less used actions move into a "More actions" menu one at a time, while voice input and send stay visible. The toolbar always stays one row, so a long model name truncates and does not hide actions.
+5
View File
@@ -0,0 +1,5 @@
---
"@kilocode/cli": patch
---
Force `kilo serve` to exit after five seconds if graceful shutdown stalls, so editor shutdown cannot leave the backend running indefinitely.
+5
View File
@@ -0,0 +1,5 @@
---
"@kilocode/cli": patch
---
Reduce the work done on every agent step in long sessions once the request passes the payload pruning limit. The session transcript is no longer reloaded and re-serialized after pruning, and pruning no longer reads the whole session history again. Plan mode also keeps its plan file instructions on those steps.
+5
View File
@@ -0,0 +1,5 @@
---
"kilo-code": patch
---
Fix a thin line of diff content showing above the sticky file header in the Agent Manager diff panel and review when VS Code is zoomed.
+5
View File
@@ -0,0 +1,5 @@
---
"kilo-code": patch
---
Add a tooltip to the subagent open button in the chat transcript. It shows whether the subagent opens in an editor tab (sidebar) or in the Agent Manager panel.
+6
View File
@@ -0,0 +1,6 @@
---
"@kilocode/cli": patch
"@kilocode/kilo-jetbrains": patch
---
Fix SVG and other non-raster image attachments failing the whole prompt instead of being read as source. Fix a rejected or failed send losing the typed message with no way to recover it, and add a Dismiss action to standalone error cards that have no transcript tail to retry.
+5
View File
@@ -0,0 +1,5 @@
---
"@kilocode/cli": patch
---
Task tool: a bare model name in `subagent_model` or an agent `model` now resolves to the matching provider model instead of failing with a "Model not found" error or being ignored. The parent session's provider is preferred when several providers offer a model with the same name.
+4
View File
@@ -0,0 +1,4 @@
---
"@kilocode/cli": patch
---
Fix permission and question prompts disappearing when switching sessions in the TUI. Approval prompts for running subagents and sessions now persist across session switches and are restored when switching back, so sessions are never left blocked on an unanswered prompt.
+20
View File
@@ -2,6 +2,26 @@ name: Bug report
description: Report an issue that should be fixed
labels: ["bug"]
body:
# kilocode_change start
- type: dropdown
id: component
attributes:
label: Component
description: Which part of Kilo is this about? It helps us route your report.
options:
- VS Code extension
- CLI / TUI
- Agent Manager
- JetBrains plugin
- Desktop or web app
- Documentation
- Kilo Gateway or account
- SDK
- Other / not sure
validations:
required: true
# kilocode_change end
- type: textarea
id: description
attributes:
@@ -18,3 +18,23 @@ body:
description: What do you want to change or add? What are the benefits of implementing this? Try to be detailed so we can understand your request better :)
validations:
required: true
# kilocode_change start
- type: dropdown
id: component
attributes:
label: Component
description: Which part of Kilo is this about? It helps us route your request.
options:
- VS Code extension
- CLI / TUI
- Agent Manager
- JetBrains plugin
- Desktop or web app
- Documentation
- Kilo Gateway or account
- SDK
- Other / not sure
validations:
required: true
# kilocode_change end
+6
View File
@@ -10,3 +10,9 @@ query-filters:
- path-alert
tags contain: security
security-severity: /^(9(\.[0-9])?|10(\.0)?)$/
# CodeQL classifies extensionless files with a `bun`/`node` shebang as plain
# JavaScript, so TypeScript syntax in these dev-only scripts (e.g. `as const`)
# is reported as "Could not process some files due to syntax errors".
paths-ignore:
- packages/opencode/script/run-workspace-server
+91 -12
View File
@@ -1,27 +1,35 @@
# kilocode_change - new file
version: 2
updates:
# Bun workspace root (packages/*). Known overlap: packages/kilo-docs also
# has its own pnpm block below, since this scan can't see its separate
# pnpm-lock.yaml. Both may target the same package.json line; whichever PR
# merges first makes the other stale (caught by stale-bot-pr-notify.yml).
# Bun workspace root (packages/*). Version updates are OFF (limit 0): this
# scan covers ~17 upstream-owned packages that share one bun.lock, and
# Dependabot cannot scope a workspace scan by path. Nearly every PR it opened
# touched upstream files and was closed by hand (dependabot-auto-merge.yml
# skips them by design). Those dependencies come in with upstream merges.
# A limit of 0 does not disable security updates: alerts still open PRs, and
# security-findings-notify.yml reports them. Those PRs mostly edit upstream
# manifests, and a root `overrides` or catalog pin can make them a no-op
# (check the resolved version in bun.lock before merging). packages/kilo-docs
# has its own block below, since this scan can't see its separate
# pnpm-lock.yaml.
- package-ecosystem: "bun"
directory: "/"
schedule:
interval: "weekly"
cooldown:
default-days: 7
open-pull-requests-limit: 10
open-pull-requests-limit: 0
labels:
- "dependencies"
commit-message:
prefix: "chore"
include: "scope"
# One PR for all security fixes in this scan, instead of one per alert.
# Deliberately not named "*-minor-patch": these PRs are never auto-merged,
# and this group has no update-types filter because a fix may need a major.
groups:
bun-minor-patch:
update-types:
- "minor"
- "patch"
security-updates:
applies-to: security-updates
patterns:
- "*"
# packages/kilo-docs deploys separately via pnpm; see note above.
- package-ecosystem: "npm"
@@ -35,7 +43,33 @@ updates:
- "dependencies"
commit-message:
prefix: "chore(kilo-docs)"
# Majors are never proposed. They can change peer specs or type targets
# (for example the @types/node major, which no longer matched the Node
# versions we target). Bump them by hand, on purpose.
ignore:
- dependency-name: "*"
update-types:
- "version-update:semver-major"
# Related packages move together. Group names must end in "-minor-patch"
# so dependabot-auto-merge.yml recognises them. A dependency joins the
# first group that matches it.
groups:
react-minor-patch:
patterns:
- "react"
- "react-dom"
- "@types/react"
- "@types/react-dom"
update-types:
- "minor"
- "patch"
vitest-minor-patch:
patterns:
- "vitest"
- "@vitest/*"
update-types:
- "minor"
- "patch"
kilo-docs-minor-patch:
update-types:
- "minor"
@@ -47,12 +81,57 @@ updates:
interval: "weekly"
cooldown:
default-days: 7
open-pull-requests-limit: 5
# One slot per group below (6), so no group waits for another to merge.
open-pull-requests-limit: 6
labels:
- "dependencies"
commit-message:
prefix: "chore(jetbrains)"
# Majors are never proposed. They can change the build tooling or test
# APIs (for example okhttp 4 -> 5 deprecates mockwebserver, and a Gradle
# wrapper or Kotlin plugin major changes the build itself). Bump them by
# hand, on purpose.
ignore:
- dependency-name: "*"
update-types:
- "version-update:semver-major"
# One bad update must not block the rest, and coupled artifacts must move
# together (same "-minor-patch" suffix and first-match rules as above).
groups:
intellij-minor-patch:
patterns:
- "org.jetbrains.intellij.platform*"
- "rpc"
update-types:
- "minor"
- "patch"
kotlin-minor-patch:
patterns:
- "org.jetbrains.kotlin*"
- "org.jetbrains.kotlinx*"
update-types:
- "minor"
- "patch"
junit-minor-patch:
patterns:
- "junit*"
- "org.junit*"
update-types:
- "minor"
- "patch"
okhttp-minor-patch:
patterns:
- "com.squareup.okhttp3*"
update-types:
- "minor"
- "patch"
markdown-minor-patch:
patterns:
- "org.commonmark*"
- "org.nibor.autolink*"
update-types:
- "minor"
- "patch"
jetbrains-minor-patch:
update-types:
- "minor"
+2
View File
@@ -1,3 +1,5 @@
<!-- Community PRs are triaged twice a week. If a maintainer asks for changes and we hear nothing for 14 days, the PR is closed. See docs/community-triage.md. -->
## Issue
<!-- Reference an existing issue with `Fixes #123`, `Closes #123`, or equivalent linked issue wording. -->
+36 -7
View File
@@ -6,39 +6,68 @@ Workflows that automate dependency remediation and give the team Slack visibilit
|---|---|---|
| `dependabot-auto-merge.yml` | `schedule`: hourly, plus `workflow_dispatch` | Actions tab → "Dependabot auto-merge" → Run workflow, or `gh workflow run dependabot-auto-merge.yml` |
| `security-findings-notify.yml` | `schedule`: every 6 hours, plus `workflow_dispatch` | Actions tab → "Security findings notify" → Run workflow, or `gh workflow run security-findings-notify.yml` |
| `outdated-kilo-deps.yml` | `schedule`: monthly on the 1st at 09:23 UTC, plus `workflow_dispatch` | Actions tab → "Outdated Kilo dependencies" → Run workflow, or `gh workflow run outdated-kilo-deps.yml` |
| `stale-bot-pr-notify.yml` | `schedule`: daily at 13:00 UTC, plus `workflow_dispatch` | Actions tab → "Stale bot PR notify" → Run workflow, or `gh workflow run stale-bot-pr-notify.yml` |
| `stale-alerts-report.yml` | `schedule`: weekly on Mondays at 08:41 UTC, plus `workflow_dispatch` | Actions tab → "Stale alerts report" → Run workflow, or `gh workflow run stale-alerts-report.yml` |
| `../dependabot.yml` | Not a workflow — read directly by GitHub's Dependabot service | No manual run; check **Insights → Dependency graph → Dependabot** |
## Setup
- Add repo secret `SECURITY_ALERTS_SLACK_WEBHOOK` (a Slack incoming webhook URL) — required by `security-findings-notify.yml` and `stale-bot-pr-notify.yml`.
- Optional repo variables to override defaults: `SECURITY_SLA_CRITICAL_DAYS` (15), `SECURITY_SLA_HIGH_DAYS` (30), `STALE_BOT_PR_DAYS` (3).
- Add repo secret `SECURITY_ALERTS_SLACK_WEBHOOK` (a Slack incoming webhook URL) — required by `security-findings-notify.yml`, `stale-bot-pr-notify.yml`, `outdated-kilo-deps.yml` and `stale-alerts-report.yml`.
- Optional repo secret `SECURITY_SCAN_TOKEN`: a token with the `secret_scanning_alerts` scope. Without it, `security-findings-notify.yml` tries the default token and skips secret scanning with a warning if that fails.
- Optional repo variables to override defaults: `SECURITY_SLA_CRITICAL_DAYS` (15), `SECURITY_SLA_HIGH_DAYS` (30), `SECURITY_SLA_MEDIUM_DAYS` (90), `SECURITY_SLA_LOW_DAYS` (180), `SECURITY_MIN_SEVERITY` (`high`; set to `medium` or `low` to report more), `STALE_BOT_PR_DAYS` (3).
## How it works
### PRs are opened by Dependabot's own service, not by any workflow here
`dependabot.yml` is not a workflow — it's a config file that GitHub's Dependabot backend reads directly. None of the 3 workflow files in this directory ever open a PR; they only act on PRs that already exist.
`dependabot.yml` is not a workflow — it's a config file that GitHub's Dependabot backend reads directly. None of the workflow files in this directory ever open a PR; they only act on PRs or alerts that already exist, or report on dependencies.
Dependabot re-scans on the `schedule: interval` set per ecosystem block, and once immediately whenever `dependabot.yml` itself changes on the default branch. Per ecosystem (`bun`, `npm` for kilo-docs, `gradle` for kilo-jetbrains), it checks each dependency for updates, classifies each as `major`, `minor`, or `patch`, bundles everything matching a `groups: *-minor-patch` rule into one combined PR, and opens every major bump as its own individual PR. These are normal PRs from that point on — required CI and branch protection apply like any other PR.
Dependabot re-scans on the `schedule: interval` set per ecosystem block, and once immediately whenever `dependabot.yml` itself changes on the default branch. Per ecosystem (`npm` for kilo-docs, `gradle` for kilo-jetbrains), it checks each dependency for updates, bundles minor and patch bumps into `groups: *-minor-patch` PRs (related packages share a group, so they move together), and ignores every major bump. Majors are bumped by hand. The `bun` block has `open-pull-requests-limit: 0`, so it opens no version-update PRs, only security-alert PRs, which a `security-updates` group bundles into one PR. These are normal PRs from that point on — required CI and branch protection apply like any other PR.
### The 3 workflows each poll independently on their own schedule
### The 5 workflows each poll independently on their own schedule
They don't call each other or trigger off PR creation. Each one wakes up on its own cron and reads whatever state currently exists on GitHub:
| Workflow | Wakes up | Reads | Does |
|---|---|---|---|
| `dependabot-auto-merge.yml` | Hourly | Open `app/dependabot` PRs with a grouped `*-minor-patch` title | Enables GitHub's native auto-merge flag if every changed file besides shared `bun.lock` is under a `kilo`-named path; otherwise leaves it and logs a warning. |
| `security-findings-notify.yml` | Every 6h | GitHub's Dependabot **Alerts** (the vulnerability list, separate from the PR list above) | Posts new/at-risk/breached critical & high alerts to Slack, independent of whether a fix PR exists. |
| `stale-bot-pr-notify.yml` | Daily, 13:00 UTC | All open bot-authored PRs (not limited to Dependabot) | Flags any that are conflicting or long-unreviewed to Slack. |
| `security-findings-notify.yml` | Every 6h | GitHub's Dependabot **Alerts** (the vulnerability list, separate from the PR list above), **code scanning** (CodeQL) alerts and **secret scanning** alerts | Posts new/at-risk/breached alerts at or above `SECURITY_MIN_SEVERITY` (default high) to Slack, independent of whether a fix PR exists. A leaked secret always counts as critical. |
| `outdated-kilo-deps.yml` | Monthly, 1st at 09:23 UTC | `bun outdated` for `packages/kilo-*` (not kilo-docs; it has its own Dependabot block) | Posts the count of outdated major/minor/patch dependencies to Slack, with the major and minor ones listed. Fills the gap left by turning off the root `bun` version updates. |
| `stale-bot-pr-notify.yml` | Daily, 13:00 UTC | All open bot-authored PRs (not limited to Dependabot) | Flags any that are conflicting, failing a required check or long-unreviewed to Slack. Several PRs that upgrade the same package collapse into one "likely duplicates" line. |
| `stale-alerts-report.yml` | Weekly, Mondays 08:41 UTC | Open Dependabot alerts for npm packages, compared with `bun.lock` and `pnpm-lock.yaml` in the checkout | Posts alerts that look stale to Slack: the manifest is gone, the package left the lockfile, or the lockfile resolves only versions outside the vulnerable range. It dismisses nothing. |
### Where the sources come from
| Source | Used by | Notes |
|---|---|---|
| Dependabot alerts | `security-findings-notify.yml`, `stale-alerts-report.yml` | Needs the `vulnerability-alerts: read` permission. |
| Code scanning alerts | `security-findings-notify.yml` | Needs `security-events: read`. Alerts without a security severity are quality findings and are skipped. If the API returns an error, the run logs a warning and continues. |
| Secret scanning alerts | `security-findings-notify.yml` | The default `GITHUB_TOKEN` cannot read these on every plan. If the API returns an error, the run logs a warning and continues. To enable this source, add the repo secret `SECURITY_SCAN_TOKEN` (a token with the `secret_scanning_alerts` scope). The workflow uses it for this one call only. |
| Findings of the Kilo Security Agent (the "Unreachable" and "Exploitable" labels) | None | They live in the private `Kilo-Org/cloud` stack. A repo workflow cannot read them. |
The helper scripts are `script/kilocode/bot-prs.ts` (duplicate grouping) and `script/kilocode/stale-alerts.ts` (stale alert check). Both have tests next to them. Run `bun test bot-prs.test.ts stale-alerts.test.ts` from `script/kilocode/`.
### Enabling auto-merge isn't the same as merging
`dependabot-auto-merge.yml` only flips a flag on the PR. GitHub itself completes the merge later, automatically, once both the required human approval and all required status checks pass. If either never happens, the PR just sits with auto-merge armed but unfulfilled — nothing forces it through.
## Slack posts
Four of the five workflows post to Slack. All of them use the `SECURITY_ALERTS_SLACK_WEBHOOK` secret, so every post lands in the one channel that webhook was created for. A run that has nothing to report posts nothing.
| Workflow | Posts? | What a post contains | When it posts |
|---|---|---|---|
| `dependabot-auto-merge.yml` | No | Nothing. Its results are only in the Actions log, including a warning when it skips a PR that touches shared code. | Never. |
| `security-findings-notify.yml` | Yes | One message per alert (critical and high by default): a label (`New finding`, `SLA at risk` or `SLA breached`), the source (`Dependabot`, `code scanning` or `secret scanning`), the severity, days open, a link to the alert with its GHSA ID or rule ID, and the summary. A critical alert that breaches its SLA starts with `@here`. | Checked every 6 hours. Each alert posts once per status (new, then at risk, then breached). A breached alert repeats once a day while it stays open. |
| `stale-bot-pr-notify.yml` | Yes | One combined message, "Bot PRs needing attention". Each line has the PR number and title as a link, the author, the age in days, and a reason: `conflicting`, `CI failing` (a required check failed) or `unreviewed`. PRs that upgrade the same package are one line that lists every PR and says to keep one. | Checked daily at 13:00 UTC. Posts only if at least one PR is flagged. A PR repeats every day until it is fixed. |
| `stale-alerts-report.yml` | Yes | One message, "Open Dependabot alerts that look stale: N". Each line has the alert link, the package, the manifest and the reason. The full list is in the job summary. | Checked weekly. Posts only if at least one alert looks stale. |
| `outdated-kilo-deps.yml` | Yes | One short summary, "Outdated Kilo-owned dependencies: N major, N minor, N patch". It lists the major and minor updates with current and latest versions. Patch updates are only counted. A link to the run has the full list. | Checked monthly, on the 1st at 09:23 UTC. Posts only if something is outdated. |
## Fork-safety notes
kilocode is a fork of opencode sharing one `bun.lock` with upstream-owned `@opencode-ai/*` packages. Two things exist specifically to avoid friction with upstream syncs:
- `dependabot-auto-merge.yml` only auto-merges a PR if every changed file (besides the shared `bun.lock`) lives under a `kilo`-named path. Anything touching shared/upstream code is left for a human.
- The root `bun` block has version updates turned off. It scans ~17 upstream-owned packages and cannot be scoped by path, so its PRs almost always touched upstream files. Those dependencies come in with upstream merges.
- `dependabot.yml` deliberately does **not** cover `packages/opencode/Dockerfile` or `.github/workflows/**`: both are shared/upstream paths that the auto-merge guard above would never approve anyway, and `.github/workflows/**` changes routinely fail this repo's `kilocode_change` annotation check unless they land inside an existing marker block. Those paths are covered by `security-findings-notify.yml` instead, which only reads GitHub's alerts, it never proposes a PR.
@@ -11,6 +11,12 @@ on:
- "packages/*/src/kilo-*/**"
- "packages/plugin-atomic-chat/**"
- "packages/opencode/**"
- "packages/core/**"
- "packages/llm/**"
- "packages/schema/**"
- "packages/protocol/**"
- "packages/server/**"
- "packages/tui/**"
- "packages/script/**"
- "packages/shared/**"
- "packages/storybook/**"
+16
View File
@@ -58,6 +58,9 @@ jobs:
languages: java-kotlin
build-mode: manual
config-file: ./.github/codeql/codeql-config.yml
# Leave headroom for the runner agent and OS. CodeQL otherwise claims ~14.6 GB of the
# 16 GB runner, and the query phase OOM-kills the whole VM.
ram: 12288
- name: Build Java/Kotlin
shell: bash
@@ -66,6 +69,19 @@ jobs:
env:
GITHUB_TOKEN: ${{ github.token }}
# The Gradle and Kotlin daemons stay resident for the rest of the job -- setup-gradle only
# stops them in its post step, which runs after CodeQL. Their heap competes with query
# evaluation and takes the runner down with it.
- name: Stop Gradle and Kotlin daemons
shell: bash
working-directory: packages/kilo-jetbrains
run: |
./gradlew --stop || echo "gradlew --stop failed; continuing"
pkill -f KotlinCompileDaemon || echo "no Kotlin compile daemon to stop"
sleep 5
free -m
ps -eo pid,rss,comm --sort=-rss | sed -n '1,15p'
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v4
with:
+47
View File
@@ -0,0 +1,47 @@
# kilocode_change - new file
name: Community label
# Labels new PRs and issues so maintainers can tell community work from team
# work and see which part of the repo it touches. See docs/community-triage.md.
#
# pull_request_target runs with write access, even for fork PRs. That is safe
# here because the job only checks out the base branch and never runs code from
# the PR. It reads PR metadata (title, author, file names) as data.
on:
pull_request_target:
types: [opened, reopened, ready_for_review, edited]
issues:
types: [opened, edited]
concurrency:
group: community-label-${{ github.event.pull_request.number || github.event.issue.number }}
cancel-in-progress: true
permissions:
contents: read
issues: write
pull-requests: write
jobs:
label:
if: github.repository == 'Kilo-Org/kilocode'
runs-on: ubuntu-latest
timeout-minutes: 5
steps:
- uses: actions/checkout@v6
with:
persist-credentials: false
- uses: oven-sh/setup-bun@v2
with:
bun-version-file: package.json
- name: Label
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
REPO: ${{ github.repository }}
# Optional. Lets the job see private org members. See the docs.
MEMBER_TOKEN: ${{ secrets.COMMUNITY_TRIAGE_MEMBER_TOKEN }}
MAINTAINERS: ${{ vars.COMMUNITY_MAINTAINERS }}
run: bun run script/kilocode/community-triage.ts label
+67
View File
@@ -0,0 +1,67 @@
# kilocode_change - new file
name: Community sweep
# Twice a week, the evening before the maintainer triage slots (Friday and
# Tuesday). Works out whose turn it is on each community PR, keeps the status
# labels in sync, closes PRs that got no reply for 14 days and posts the queue.
# See docs/community-triage.md.
on:
schedule:
- cron: "0 17 * * 1,4" # Monday and Thursday, 17:00 UTC
workflow_dispatch:
inputs:
close:
description: "Close PRs with no reply for 14 days. Off means log only."
type: boolean
default: false
concurrency:
group: community-sweep
cancel-in-progress: false
permissions:
contents: read
issues: write
pull-requests: write
jobs:
sweep:
if: github.repository == 'Kilo-Org/kilocode'
runs-on: ubuntu-latest
timeout-minutes: 30
steps:
- uses: actions/checkout@v6
with:
persist-credentials: false
- uses: oven-sh/setup-bun@v2
with:
bun-version-file: package.json
- name: Sweep
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
REPO: ${{ github.repository }}
MEMBER_TOKEN: ${{ secrets.COMMUNITY_TRIAGE_MEMBER_TOKEN }}
MAINTAINERS: ${{ vars.COMMUNITY_MAINTAINERS }}
# Closing stays off until the repo variable is set to "true".
CLOSE_ENABLED: ${{ github.event_name == 'workflow_dispatch' && format('{0}', inputs.close) || vars.COMMUNITY_AUTO_CLOSE_ENABLED }}
SLACK_PAYLOAD: ${{ runner.temp }}/slack-payload.json
run: bun run script/kilocode/community-triage.ts sweep
# A step cannot read its own env in `if`, so a small step exposes the
# secret to itself only and passes on whether it is set.
- name: Check Slack webhook
id: slack
env:
SLACK_WEBHOOK: ${{ secrets.COMMUNITY_TRIAGE_SLACK_WEBHOOK }}
run: echo "enabled=${{ env.SLACK_WEBHOOK != '' }}" >> "$GITHUB_OUTPUT"
- name: Notify Slack
if: steps.slack.outputs.enabled == 'true'
uses: slackapi/slack-github-action@v2.0.0
with:
webhook: ${{ secrets.COMMUNITY_TRIAGE_SLACK_WEBHOOK }}
webhook-type: incoming-webhook
payload-file-path: ${{ runner.temp }}/slack-payload.json
@@ -0,0 +1,36 @@
# kilocode_change - new file
name: Community triage test
# Runs the unit tests for the community triage script. Only runs when the
# script, its tests or the workflows that use it change.
on:
pull_request:
paths:
- script/kilocode/community-triage*.ts
- .github/workflows/community-*.yml
workflow_dispatch:
concurrency:
group: community-triage-test-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true
permissions:
contents: read
jobs:
test:
runs-on: ubuntu-latest
timeout-minutes: 5
steps:
- uses: actions/checkout@v6
with:
persist-credentials: false
- uses: oven-sh/setup-bun@v2
with:
bun-version-file: package.json
- name: Test
working-directory: script/kilocode
run: bun test community-triage.test.ts
+53
View File
@@ -0,0 +1,53 @@
# kilocode_change - new file
name: Outdated Kilo dependencies
# Dependabot's root `bun` scan has version updates turned off (it mostly
# touched upstream files), so nothing else says when a Kilo-owned package falls
# behind. Once a month, list what `bun outdated` finds for packages/kilo-*
# (kilo-docs and kilo-jetbrains keep their own Dependabot blocks) and post a
# short summary to Slack. Read-only: it never opens a PR.
on:
schedule:
- cron: "23 9 1 * *" # 1st of the month, offset to avoid the top of the hour
workflow_dispatch:
concurrency:
group: outdated-kilo-deps
cancel-in-progress: false
permissions:
contents: read
jobs:
report:
if: github.repository == 'Kilo-Org/kilocode'
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- uses: actions/checkout@v6
with:
persist-credentials: false
- uses: oven-sh/setup-bun@v2
with:
bun-version-file: package.json
- id: report
env:
RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
run: |
mkdir -p .cache
bun run script/kilocode/outdated.ts > .cache/report.json
echo "count=$(jq -r .count .cache/report.json)" >> "$GITHUB_OUTPUT"
jq -r '.full' .cache/report.json >> "$GITHUB_STEP_SUMMARY"
# Build real JSON via jq instead of splicing text into a YAML payload.
jq '{text}' .cache/report.json > .cache/slack-payload.json
- if: steps.report.outputs.count != '0'
name: Notify Slack
uses: slackapi/slack-github-action@v2.0.0
with:
webhook: ${{ secrets.SECURITY_ALERTS_SLACK_WEBHOOK }}
webhook-type: incoming-webhook
payload-file-path: .cache/slack-payload.json
+13 -1
View File
@@ -472,6 +472,10 @@ jobs:
# build-cli already uploaded unsigned Windows zips to the draft release;
# replace them with signed copies so release assets match what npm/VSIX ship.
# One asset per invocation with retries, for the same reason build-cli uploads
# that way: a dropped connection can commit the asset anyway, and the retry
# inside gh then dies on "ReleaseAsset.name already exists". --clobber clears
# that asset at the start of the next attempt.
- name: Re-upload signed Windows release zips
shell: pwsh
run: |
@@ -481,7 +485,15 @@ jobs:
Compress-Archive -Path (Join-Path $dir.FullName "bin\*") -DestinationPath $archive -Force
$archives += $archive
}
gh release upload "v${{ needs.build-cli.outputs.version }}" $archives --clobber
foreach ($archive in $archives) {
foreach ($attempt in 1..4) {
gh release upload "v${{ needs.build-cli.outputs.version }}" $archive --clobber
if ($LASTEXITCODE -eq 0) { break }
if ($attempt -eq 4) { throw "Could not upload $archive after 4 attempts" }
Write-Host "upload: $archive failed (attempt $attempt/4), retrying in 10s"
Start-Sleep -Seconds 10
}
}
env:
GH_TOKEN: ${{ github.token }}
GH_REPO: ${{ github.repository }}
+82 -26
View File
@@ -1,9 +1,12 @@
# kilocode_change - new file
name: Security findings notify
# Posts open Dependabot security alerts (critical/high) to Slack, and pings
# the channel when a critical finding breaches its resolution SLA. Read-only;
# follows the cron pattern from watch-opencode-releases.yml.
# Posts open security alerts to Slack, and pings the channel when a critical
# finding breaches its resolution SLA. Read-only; follows the cron pattern from
# watch-opencode-releases.yml. Three sources: Dependabot alerts, code scanning
# (CodeQL) alerts and secret scanning alerts. Dependabot and code scanning
# alerts below SECURITY_MIN_SEVERITY (default: high) are skipped. A leaked
# secret always counts as critical.
on:
schedule:
@@ -17,6 +20,7 @@ concurrency:
permissions:
contents: read
vulnerability-alerts: read
security-events: read
jobs:
list-findings:
@@ -29,32 +33,72 @@ jobs:
- id: build
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
# Optional. The default token cannot read secret scanning alerts on
# every plan. A token with the secret_scanning_alerts scope is used
# for that one call only. Without it, the call uses GH_TOKEN.
SECRET_SCAN_TOKEN: ${{ secrets.SECURITY_SCAN_TOKEN }}
REPO: ${{ github.repository }}
# 15 matches an observed real critical deadline; 30 is a guess.
CRIT_DAYS: ${{ vars.SECURITY_SLA_CRITICAL_DAYS || '15' }}
HIGH_DAYS: ${{ vars.SECURITY_SLA_HIGH_DAYS || '30' }}
# Only used when SECURITY_MIN_SEVERITY lets medium or low through.
MEDIUM_DAYS: ${{ vars.SECURITY_SLA_MEDIUM_DAYS || '90' }}
LOW_DAYS: ${{ vars.SECURITY_SLA_LOW_DAYS || '180' }}
# critical, high, medium or low. Anything below it is not reported.
MIN_SEVERITY: ${{ vars.SECURITY_MIN_SEVERITY || 'high' }}
run: |
# --method GET is required: gh api auto-switches to POST whenever
# -f is passed, which would 404 against this GET-only endpoint.
alerts=$(gh api --paginate --method GET -f state=open -f per_page=100 "repos/$REPO/dependabot/alerts")
# --paginate prints one array per page, so merge them into one.
set -o pipefail
fetch() {
gh api --paginate --method GET -f state=open -f per_page=100 "repos/$REPO/$1" | jq -cs 'add // []'
}
dependabot=$(fetch dependabot/alerts)
# Code scanning returns 404 when it is not enabled, and secret
# scanning is not readable with the default GITHUB_TOKEN on every
# plan (set the SECURITY_SCAN_TOKEN secret to fix that). A source that cannot be read is skipped with a warning, so it
# never hides the Dependabot findings.
if ! code=$(fetch code-scanning/alerts 2>/dev/null); then
echo "::warning::could not read code scanning alerts, skipping them"
code='[]'
fi
if ! secrets=$(GH_TOKEN="${SECRET_SCAN_TOKEN:-$GH_TOKEN}" fetch secret-scanning/alerts 2>/dev/null); then
echo "::warning::could not read secret scanning alerts, skipping them"
secrets='[]'
fi
now=$(date -u +%s)
matrix=$(echo "$alerts" | jq -c --argjson now "$now" --argjson critDays "$CRIT_DAYS" --argjson highDays "$HIGH_DAYS" '
map(select(.security_advisory.severity == "critical" or .security_advisory.severity == "high"))
| map(
. as $a
| $a.security_advisory.severity as $sev
| (if $sev == "critical" then $critDays else $highDays end) as $threshold
| (($now - ($a.created_at | fromdateiso8601)) / 86400) as $age
| {
number: $a.number,
severity: $sev,
ghsa_id: $a.security_advisory.ghsa_id,
summary: $a.security_advisory.summary,
html_url: $a.html_url,
age_days: ($age | floor),
status: (if $age >= $threshold then "breach" elif $age >= ($threshold * 0.7) then "warning" else "new" end)
}
)
matrix=$(jq -cn --argjson now "$now" \
--argjson critDays "$CRIT_DAYS" --argjson highDays "$HIGH_DAYS" \
--argjson medDays "$MEDIUM_DAYS" --argjson lowDays "$LOW_DAYS" \
--arg min "$MIN_SEVERITY" \
--argjson dependabot "$dependabot" --argjson code "$code" --argjson secrets "$secrets" '
def rank: {critical: 4, high: 3, medium: 2, low: 1}[.] // 0;
def days: if . == "critical" then $critDays elif . == "high" then $highDays elif . == "medium" then $medDays else $lowDays end;
def item($kind; $number; $sev; $id; $summary; $url; $created):
(($now - ($created | fromdateiso8601)) / 86400) as $age
| ($sev | days) as $threshold
| {
kind: $kind,
number: $number,
severity: $sev,
ghsa_id: $id,
summary: $summary,
html_url: $url,
age_days: ($age | floor),
status: (if $age >= $threshold then "breach" elif $age >= ($threshold * 0.7) then "warning" else "new" end)
};
(
($dependabot | map(item("dependabot"; .number; .security_advisory.severity; .security_advisory.ghsa_id; .security_advisory.summary; .html_url; .created_at)))
# Code scanning alerts without a security severity are quality
# findings, not vulnerabilities, so they are left out.
+ ($code | map(select(.rule.security_severity_level != null)
| item("code"; .number; .rule.security_severity_level; .rule.id; (.rule.description + " in " + (.most_recent_instance.location.path // "unknown file")); .html_url; .created_at)))
+ ($secrets | map(item("secret"; .number; "critical"; (.secret_type_display_name // .secret_type); "Exposed secret, rotate it now"; .html_url; .created_at)))
)
| map(select((.severity | rank) >= ($min | rank)))
')
echo "matrix=$matrix" >> "$GITHUB_OUTPUT"
@@ -73,15 +117,21 @@ jobs:
# injection from free-text advisory summaries.
- id: key
env:
KIND: ${{ matrix.kind }}
NUMBER: ${{ matrix.number }}
STATUS: ${{ matrix.status }}
run: |
# Alert numbers repeat across sources, so non-Dependabot keys carry
# the source. Dependabot keeps the original "sec-alert" prefix, so
# alerts already notified are not announced again.
prefix="sec-alert"
[ "$KIND" != "dependabot" ] && prefix="sec-${KIND}"
if [ "$STATUS" = "breach" ]; then
# Re-notify once per day while a critical/high stays breached,
# Re-notify once per day while an alert stays breached,
# instead of once ever, so an unresolved breach keeps nagging.
echo "value=sec-alert-${NUMBER}-breach-$(date -u +%Y-%m-%d)" >> "$GITHUB_OUTPUT"
echo "value=${prefix}-${NUMBER}-breach-$(date -u +%Y-%m-%d)" >> "$GITHUB_OUTPUT"
else
echo "value=sec-alert-${NUMBER}-${STATUS}" >> "$GITHUB_OUTPUT"
echo "value=${prefix}-${NUMBER}-${STATUS}" >> "$GITHUB_OUTPUT"
fi
- id: cache
@@ -94,6 +144,7 @@ jobs:
name: Build Slack payload
id: message
env:
KIND: ${{ matrix.kind }}
SEVERITY: ${{ matrix.severity }}
STATUS: ${{ matrix.status }}
AGE_DAYS: ${{ matrix.age_days }}
@@ -110,8 +161,13 @@ jobs:
warning) label=":hourglass_flowing_sand: SLA at risk" ;;
*) label=":mag: New finding" ;;
esac
text=$(printf '%s%s (%s severity, open %sd): <%s|%s> %s' \
"$mention" "$label" "$SEVERITY" "$AGE_DAYS" "$HTML_URL" "$GHSA_ID" "$SUMMARY")
case "$KIND" in
code) source="code scanning" ;;
secret) source="secret scanning" ;;
*) source="Dependabot" ;;
esac
text=$(printf '%s%s [%s] (%s severity, open %sd): <%s|%s> %s' \
"$mention" "$label" "$source" "$SEVERITY" "$AGE_DAYS" "$HTML_URL" "$GHSA_ID" "$SUMMARY")
mkdir -p .cache
jq -n --arg text "$text" '{text: $text}' > .cache/slack-payload.json
+62
View File
@@ -0,0 +1,62 @@
# kilocode_change - new file
name: Stale alerts report
# Dependabot normally closes an alert when the fix lands. Some stay open after
# the manifest is deleted or the lockfile moves past the vulnerable range, and
# keep showing in the security views and the SLA reports. Once a week, compare
# each open npm alert with the lockfiles in this checkout and post the ones
# that look stale to Slack, so a person can dismiss them. Read-only: it never
# dismisses an alert.
on:
schedule:
- cron: "41 8 * * 1" # Mondays, offset to avoid the top of the hour
workflow_dispatch:
concurrency:
group: stale-alerts-report
cancel-in-progress: false
permissions:
contents: read
vulnerability-alerts: read
jobs:
report:
if: github.repository == 'Kilo-Org/kilocode'
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- uses: actions/checkout@v6
with:
persist-credentials: false
- uses: oven-sh/setup-bun@v2
with:
bun-version-file: package.json
- id: report
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
REPO: ${{ github.repository }}
RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
run: |
set -o pipefail
mkdir -p .cache
# --method GET is required: gh api auto-switches to POST whenever
# -f is passed. --paginate prints one array per page, so merge them.
gh api --paginate --method GET -f state=open -f per_page=100 "repos/$REPO/dependabot/alerts" \
| jq -cs 'add // []' > .cache/alerts.json
bun run script/kilocode/stale-alerts.ts .cache/alerts.json > .cache/report.json
echo "count=$(jq -r .count .cache/report.json)" >> "$GITHUB_OUTPUT"
jq -r '.full' .cache/report.json >> "$GITHUB_STEP_SUMMARY"
# Build real JSON via jq instead of splicing text into a YAML payload.
jq '{text}' .cache/report.json > .cache/slack-payload.json
- if: steps.report.outputs.count != '0'
name: Notify Slack
uses: slackapi/slack-github-action@v2.0.0
with:
webhook: ${{ secrets.SECURITY_ALERTS_SLACK_WEBHOOK }}
webhook-type: incoming-webhook
payload-file-path: .cache/slack-payload.json
+19 -11
View File
@@ -7,7 +7,8 @@ name: Stale bot PR notify
# failing -- dependabot-auto-merge.yml only arms auto-merge once and never
# checks back, so nothing else would ever catch that case. Once a day, flag
# any open bot PR that's conflicting, has a failing required check, or has
# sat unreviewed too long.
# sat unreviewed too long. Bots can open several PRs for one fix, so PRs that
# upgrade the same package are reported as one line (script/kilocode/bot-prs.ts).
on:
schedule:
@@ -28,6 +29,14 @@ jobs:
runs-on: ubuntu-latest
timeout-minutes: 5
steps:
- uses: actions/checkout@v6
with:
persist-credentials: false
- uses: oven-sh/setup-bun@v2
with:
bun-version-file: package.json
- id: build
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
@@ -90,16 +99,15 @@ jobs:
flagged=$(jq -c -s '.' .cache/flagged.ndjson)
echo "flagged=$flagged" >> "$GITHUB_OUTPUT"
# Build real JSON via jq instead of splicing PR titles (free text)
# into a YAML payload through `${{ }}`.
echo "$flagged" | jq '
if length == 0 then {text: ""} else
{text: (["*Bot PRs needing attention:*"] + map(
"- <" + .url + "|#" + (.number | tostring) + " " + .title + "> ("
+ .author.login + ", " + (.age_days | tostring) + "d old, " + .reason + ")"
)) | join("\n")}
end
' > .cache/slack-payload.json
# Duplicate fix PRs (several PRs upgrading one package) are found
# across all open bot PRs, so keep the full bot list for the script.
echo "$prs" | jq -c 'map(select(.author.is_bot == true))' > .cache/bots.json
echo "$flagged" > .cache/flagged.json
# bot-prs.ts builds the Slack JSON, so PR titles (free text) never
# reach the payload through Actions expressions. It collapses PRs
# that touch the same package into one line.
bun run script/kilocode/bot-prs.ts .cache/bots.json .cache/flagged.json > .cache/slack-payload.json
- if: steps.build.outputs.flagged != '[]'
name: Notify Slack
+114
View File
@@ -0,0 +1,114 @@
# kilocode_change - new file
# Reproduce the release-only compiled TUI PTY smoke on any branch, without cutting a release.
#
# publish.yml owns the `validate-cli-unix` / `validate-cli-windows` jobs that run
# `kilo --pure __pty-smoke` against the compiled binary, but publish.yml is
# workflow_dispatch-only and goes on to publish, so that gate never runs on a branch
# or PR. test.yml only covers the pty-smoke *unit* tests (synthetic renderers from
# source), not the standalone binary.
#
# This builds the CLI for the host platform only (--single) and runs the same smoke
# step, covering the four targets that failed in runs 37371543591 / 37336767041:
# darwin-arm64, darwin-x64, windows-x64, linux-x64. No signing, no release, no
# publish, no ~1 GB artifact hand-off.
#
# Bubblewrap bundling is skipped so the Linux build does not need the Zig toolchain
# that publish.yml installs; the PTY smoke does not exercise the sandbox helper.
name: validate-cli-smoke
on:
workflow_dispatch:
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
permissions:
contents: read
jobs:
smoke:
name: Compiled PTY smoke (${{ matrix.target }})
runs-on: ${{ matrix.runner }}
timeout-minutes: 30
strategy:
fail-fast: false
matrix:
include:
- target: darwin-arm64
runner: macos-15
package: "@kilocode/cli-darwin-arm64"
- target: darwin-x64
runner: macos-15-intel
package: "@kilocode/cli-darwin-x64"
- target: linux-x64
runner: ubuntu-24.04
package: "@kilocode/cli-linux-x64"
- target: windows-x64
runner: windows-2025
package: "@kilocode/cli-windows-x64"
steps:
- uses: actions/checkout@v6
- uses: ./.github/actions/setup-bun
- name: Build CLI for this host
working-directory: packages/opencode
run: bun run script/build.ts --single --skip-install
env:
KILO_VERSION: 0.0.0-smoke
KILO_SKIP_BUNDLED_BWRAP: "1"
- name: Run compiled PTY smoke
if: runner.os != 'Windows'
working-directory: packages/opencode
run: |
binary="./dist/${{ matrix.package }}/bin/kilo"
"$binary" --version
root="$(mktemp -d)"
trap 'rm -rf "$root"' EXIT
(
unset KILO_MODELS_PATH KILO_MODELS_URL KILO_CONFIG KILO_CONFIG_DIR
export XDG_DATA_HOME="$root/data"
export XDG_CACHE_HOME="$root/cache"
export XDG_CONFIG_HOME="$root/config"
export XDG_STATE_HOME="$root/state"
export KILO_PTY_SMOKE=1
export KILO_DISABLE_MODELS_FETCH=1
export KILO_DISABLE_PROJECT_CONFIG=1
export KILO_CONFIG_CONTENT='{"enabled_providers":["anthropic"]}'
export ANTHROPIC_API_KEY=dummy
"$binary" --pure __pty-smoke
)
- name: Run compiled PTY smoke (Windows)
if: runner.os == 'Windows'
working-directory: packages/opencode
shell: pwsh
run: |
$package = "${{ matrix.package }}".Replace("/", "\")
$binary = ".\dist\$package\bin\kilo.exe"
& $binary --version
if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }
$root = Join-Path $env:RUNNER_TEMP ([guid]::NewGuid().ToString())
New-Item -ItemType Directory -Force -Path $root | Out-Null
try {
foreach ($name in "KILO_MODELS_PATH", "KILO_MODELS_URL", "KILO_CONFIG", "KILO_CONFIG_DIR") {
Remove-Item "Env:$name" -ErrorAction SilentlyContinue
}
$env:XDG_DATA_HOME = Join-Path $root "data"
$env:XDG_CACHE_HOME = Join-Path $root "cache"
$env:XDG_CONFIG_HOME = Join-Path $root "config"
$env:XDG_STATE_HOME = Join-Path $root "state"
$env:KILO_PTY_SMOKE = "1"
$env:KILO_DISABLE_MODELS_FETCH = "1"
$env:KILO_DISABLE_PROJECT_CONFIG = "1"
$env:KILO_CONFIG_CONTENT = '{"enabled_providers":["anthropic"]}'
$env:ANTHROPIC_API_KEY = "dummy"
& $binary --pure __pty-smoke
if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }
} finally {
Remove-Item -Recurse -Force $root -ErrorAction SilentlyContinue
}
+6
View File
@@ -40,6 +40,12 @@ while read -r local_ref local_oid remote_ref remote_oid; do
fi
done
# In a linked worktree git exports GIT_DIR to hooks without GIT_WORK_TREE (githooks(5)). Turbo runs
# git status inside each package, and with GIT_DIR set that directory becomes the work tree root, so
# every tracked file looks deleted and each status call writes tens of MB. Let git find the repository
# from the working directory instead.
unset GIT_DIR GIT_WORK_TREE GIT_INDEX_FILE
bun typecheck --filter='!@kilocode/kilo-jetbrains'
if [ "$jetbrains" -eq 1 ]; then
+2
View File
@@ -132,6 +132,8 @@ Default to a single-word name for variables, parameters, and helper functions. R
You MUST avoid using `mocks` as much as possible.
Tests MUST test actual implementation, do not duplicate logic into a test.
For VS Code UI changes, the optional shared self-test harness is at https://github.com/Kilo-Org/vscode-extension-self-test (the `vscode-self-test` skill). Offer it and let the user decide whether to install or use it.
## Markdown Tables
Do not pad markdown table cells for column alignment. Use the compact form with single-space-padded content cells and a minimal separator row:
+4 -2
View File
@@ -12,11 +12,13 @@ There are lots of ways to contribute to the project:
- **Feature Requests:** Suggest new features or improvements
- **Community Support:** Help other users in the community
Community pull requests are triaged twice a week. See [Community triage](docs/community-triage.md) for what to expect and how long we wait for replies.
The Kilo Community is [on Discord](https://kilo.ai/discord).
## Prerequisites
- **Bun 1.3.14+** — required for all packages.
- **Bun 1.4.2+** — required for all packages.
- **Java 21** — required by the JetBrains plugin. The root `bun turbo typecheck` and `bun turbo test:ci` commands include `@kilocode/kilo-jetbrains` and will fail without Java 21.
The preferred way to install Java is via [SDKMAN](https://sdkman.io/install):
@@ -41,7 +43,7 @@ The Kilo Community is [on Discord](https://kilo.ai/discord).
## Developing Kilo CLI
- **Requirements:** Bun 1.3.14+, Java 21 (see [Prerequisites](#prerequisites) above)
- **Requirements:** Bun 1.4.2+, Java 21 (see [Prerequisites](#prerequisites) above)
- Install dependencies and start the CLI from the repo root:
```bash
+1 -1
View File
@@ -21,5 +21,5 @@
"@types/react-dom": "^19.2.3",
"typescript": "^5.8.2"
},
"version": "7.8.3"
"version": "7.8.8"
}
+147 -354
View File
File diff suppressed because it is too large Load Diff
+1 -1
View File
@@ -2,7 +2,7 @@
exact = true
# Keep Kilo's longer supply-chain quarantine while allowing packages that must track coordinated releases.
minimumReleaseAge = 410520 # seconds (~4.75 days / ~114 hours)
minimumReleaseAgeExcludes = ["mermaid", "@mermaid-js/parser", "@ai-sdk/amazon-bedrock", "@ai-sdk/anthropic", "@opentui/core", "@opentui/core-darwin-arm64", "@opentui/core-darwin-x64", "@opentui/core-linux-arm64", "@opentui/core-linux-arm64-musl", "@opentui/core-linux-x64", "@opentui/core-linux-x64-musl", "@opentui/core-win32-arm64", "@opentui/core-win32-x64", "@opentui/keymap", "@opentui/solid", "opentui-spinner", "gitlab-ai-provider", "opencode-gitlab-auth", "@ff-labs/fff-node", "@ff-labs/fff-bun", "@ff-labs/fff-bin-darwin-arm64", "@ff-labs/fff-bin-darwin-x64", "@ff-labs/fff-bin-linux-arm64-gnu", "@ff-labs/fff-bin-linux-arm64-musl", "@ff-labs/fff-bin-linux-x64-gnu", "@ff-labs/fff-bin-linux-x64-musl", "@ff-labs/fff-bin-win32-arm64", "@ff-labs/fff-bin-win32-x64", "@pierre/diffs", "@pierre/theming", "app-builder-lib", "dmg-builder", "electron-builder", "electron-publish"]
minimumReleaseAgeExcludes = ["@types/bun", "bun-types", "mermaid", "@mermaid-js/parser", "@ai-sdk/amazon-bedrock", "@ai-sdk/anthropic", "@opentui/core", "@opentui/core-darwin-arm64", "@opentui/core-darwin-x64", "@opentui/core-linux-arm64", "@opentui/core-linux-arm64-musl", "@opentui/core-linux-x64", "@opentui/core-linux-x64-musl", "@opentui/core-win32-arm64", "@opentui/core-win32-x64", "@opentui/keymap", "@opentui/solid", "opentui-spinner", "gitlab-ai-provider", "opencode-gitlab-auth", "@ff-labs/fff-node", "@ff-labs/fff-bun", "@ff-labs/fff-bin-darwin-arm64", "@ff-labs/fff-bin-darwin-x64", "@ff-labs/fff-bin-linux-arm64-gnu", "@ff-labs/fff-bin-linux-arm64-musl", "@ff-labs/fff-bin-linux-x64-gnu", "@ff-labs/fff-bin-linux-x64-musl", "@ff-labs/fff-bin-win32-arm64", "@ff-labs/fff-bin-win32-x64", "@pierre/diffs", "@pierre/theming", "app-builder-lib", "dmg-builder", "electron-builder", "electron-publish"]
[test]
root = "./do-not-run-tests-from-root"
+159
View File
@@ -0,0 +1,159 @@
# Community triage
How we handle pull requests and issues from people outside the Kilo team.
The goal is simple. Maintainers see the community work that matters, and contributors know what happens next.
Background: [Slack discussion](https://anaconda.slack.com/archives/C0BJ2GQ40HM/p1791377022461359).
## Overview
```mermaid
flowchart LR
A[PR or issue opened] --> B[community-label.yml]
B --> C[Labels: community, needs-triage, area:*]
C --> D[community-sweep.yml<br/>Mon + Thu 17:00 UTC]
D --> E[Status labels synced]
D --> F[Queue posted to job summary and Slack]
F --> G[Tuesday: Bruno]
F --> H[Friday: Andrea]
G --> I[Review, label, comment]
H --> I
I --> D
```
Two workflows do the work. Both use `script/kilocode/community-triage.ts`.
| Workflow | Runs | Does |
|---|---|---|
| `community-label.yml` | When a PR or issue opens or is edited | Adds `community`, `needs-triage` (new items only, not reopened ones) and `area:*` labels |
| `community-sweep.yml` | Monday and Thursday, 17:00 UTC | Syncs status labels, closes stale PRs, posts the queue |
The sweep runs the evening before each maintainer slot. Andrea starts Friday and Bruno starts Tuesday with a fresh queue.
## Who is a maintainer
An author or commenter is a maintainer when one of these is true:
- GitHub shows them as `OWNER`, `MEMBER` or `COLLABORATOR`.
- Their login is in the repo variable `COMMUNITY_MAINTAINERS` (comma separated).
- The optional secret `COMMUNITY_TRIAGE_MEMBER_TOKEN` (`read:org`) shows they are in the org.
The default token cannot see private org members. They look like normal contributors. Set the variable or the secret so their PRs are not labeled `community`.
Bots are ignored.
## Labels
Set by the workflows:
| Label | Meaning |
|---|---|
| `community` | Author is not a maintainer |
| `area:vscode`, `area:cli`, `area:agent-manager`, `area:jetbrains`, `area:desktop`, `area:docs`, `area:gateway`, `area:sdk`, `area:ui`, `area:i18n` | Part of the repo. Comes from the title scope, the changed paths, or the issue "Component" dropdown |
| `needs-triage` | No maintainer has reacted yet |
| `needs-review` | The contributor replied or pushed. A maintainer should look again |
| `awaiting-contributor` | A maintainer reacted last. The 14 day clock runs |
| `ci-failing` | Checks on the last commit failed |
| `needs-reimplementation` | `high-value` PR with no reply for 14 days. The team takes it over |
Set by hand:
| Label | Meaning |
|---|---|
| `high-value` | We want this change. The sweep never closes it |
| `low-value` | Triaged. Not a priority |
| `keep-open` | The sweep never closes this PR |
The sweep owns the status labels. It adds and removes them on its own. Do not manage them by hand.
The sweep also removes `community` and the status labels from open PRs whose author is a maintainer. This fixes labels set before the maintainer list was known. It only handles PRs.
`area:*` labels are only added, never removed. Fix a wrong one by hand.
## Whose turn is it
The sweep reads the comments, reviews, review replies, commits and reopen events of each open community PR. Drafts are skipped. A reopen by the author counts as a reply. The sweep ignores commit dates more than 5 minutes in the future.
The sweep reads only the latest events. If it finds no maintainer reaction on a PR with more events than it can read, it labels the PR `needs-review` so a person decides.
```mermaid
stateDiagram-v2
[*] --> triage: opened, no maintainer reaction
triage --> awaiting: maintainer comments or requests changes
triage --> approved: maintainer approves
awaiting --> review: contributor comments or pushes
review --> awaiting: maintainer comments or requests changes
review --> approved: maintainer approves
approved --> review: contributor pushes again
awaiting --> closed: 14 days, not high-value
awaiting --> adopted: 14 days, high-value
adopted --> [*]: team re-implements or fixes it
closed --> [*]
```
| State | Label | Rule |
|---|---|---|
| triage | `needs-triage` | No maintainer reaction yet, and no `high-value` or `low-value` label |
| review | `needs-review` | The contributor acted after the last maintainer reaction, or a `high-value` or `low-value` label exists |
| approved | none | The last maintainer reaction is an approval |
| awaiting | `awaiting-contributor` | The last maintainer reaction asked for something and the contributor has not answered |
The 14 day clock starts at the time of the last maintainer reaction.
## The 14 day rule
When a PR has been `awaiting-contributor` for 14 days:
| PR | Result |
|---|---|
| Has `high-value` | Gets `needs-reimplementation` and shows in the digest under "Adopt". Add it to the standup doc and pick an owner |
| Has `keep-open` | Nothing happens |
| Anything else | Closed with a comment |
Before the sweep closes or adopts a PR, it checks the full history for a contributor reply. This catches replies on long PRs.
Closing is **off by default**. Until it is on, the sweep only logs what it would close. To turn it on, set the repo variable `COMMUNITY_AUTO_CLOSE_ENABLED` to `true`. To try it by hand, run the sweep from the Actions tab and tick "close".
When a PR first gets `awaiting-contributor`, the sweep also posts one comment that gives the due date. That comment is also off until closing is on. The sweep posts at most 20 comments per run (`MAX_COMMENTS`).
`kilo-auto-close.yml` is separate. It does not read these labels. It still closes any PR with no activity for 30 days, including `high-value` and `keep-open` PRs.
## The maintainer routine
Open the latest "Community sweep" run. The job summary has the queue. A copy goes to Slack when `COMMUNITY_TRIAGE_SLACK_WEBHOOK` is set.
1. **Adopt**: pick an owner and add the PR to the standup doc.
2. **Needs triage**: decide. Add `high-value` or `low-value`, or comment on it. Not sure? Put it in the standup doc.
3. **Needs review**: the contributor replied. Review again.
4. **Approved**: merge.
5. **Waiting for contributor**: nothing to do.
Friday and Tuesday are three to four days apart. This gives contributors time to reply between reviews.
Useful searches:
- All community PRs: `is:pr is:open label:community`
- Needs a maintainer: `is:pr is:open label:needs-triage,needs-review`
- One area: `is:pr is:open label:community label:area:vscode`
## Issues
New issues get the `community` label, `needs-triage` and an `area:*` label. The area comes from the required "Component" dropdown in the bug and feature templates. The answer "Other / not sure" adds no `area:*` label. The sweep only handles PRs.
## Setup
| Name | Kind | Needed | Purpose |
|---|---|---|---|
| `COMMUNITY_AUTO_CLOSE_ENABLED` | Variable | No | `true` turns on closing and comments |
| `COMMUNITY_MAINTAINERS` | Variable | No | Logins treated as maintainers |
| `COMMUNITY_TRIAGE_MEMBER_TOKEN` | Secret | No | Token with `read:org`. Finds private org members |
| `COMMUNITY_TRIAGE_SLACK_WEBHOOK` | Secret | No | Posts the queue to Slack |
Labels are created by the workflows. Run "Community sweep" once by hand after merge to create them and label the open PRs.
## Safety
- `community-label.yml` uses `pull_request_target`. It checks out the base branch only. It never runs PR code.
- The sweep never closes drafts, `high-value` PRs or `keep-open` PRs.
- Closed PRs can be reopened by the contributor.
+4 -4
View File
@@ -14,19 +14,19 @@ let
sources = {
"aarch64-linux" = {
name = "bun-linux-aarch64";
hash = "sha256-on/7Y6gxA3WDbg1vZorhf6jY0YuIw3yCHGUzGXOhmjs=";
hash = "sha256-VDKLvC2cjgyfiSxUTWbFeoO4QTnjSQnl7oF1jxrI/ac=";
};
"x86_64-linux" = {
name = "bun-linux-x64";
hash = "sha256-lR7iruhV8IWVruxiJSJqKY0/6oOj3NZGXAnLzN9+hI8=";
hash = "sha256-NjaPrvdSeHXV/6UuU81IAhdB8qg+tiCKjdZAaNQiqRM=";
};
"aarch64-darwin" = {
name = "bun-darwin-aarch64";
hash = "sha256-2LliIYKK1vl6x6wKt+lYcjQa92MAHogD6CZ2UsJlJiA=";
hash = "sha256-kJh6OhbX21VtiGrD1VHnttPt8KHPQ6yu1iLoZ2vh0S8=";
};
"x86_64-darwin" = {
name = "bun-darwin-x64";
hash = "sha256-QYPfM3RiPlurMVxUfPoJdFM81FfYa3O2OfeoeXTNZjM=";
hash = "sha256-gFINfhdSYwjJGF0mFnmsbSd5jTgDoOn3/5Ehq4r/sBI=";
};
};
source =
+4 -4
View File
@@ -1,8 +1,8 @@
{
"nodeModules": {
"x86_64-linux": "sha256-GPvjJ+tR9oaGS/M6gcmMmRlI3D65nZHsY5CBhfakn1c=",
"aarch64-linux": "sha256-Krg1Gjnu8byyMf+3VbSSZ95xLwzv/4+dCmLW4NijqIc=",
"aarch64-darwin": "sha256-BSSXijWYB7W5MHedZJhmCknWVM4Dwz09UM4Fky2Xnvg=",
"x86_64-darwin": "sha256-Dbw8wNVbUE3a4+uInDE9mEDxG5S5cZhT+RVL/OcEhi0="
"x86_64-linux": "sha256-yy/c43V7291kA8K9/10N0wUEpteFb9yJK0WeY3OYqQw=",
"aarch64-linux": "sha256-T2PFStBWDJLTGytuUSLf9qK0wl+98SIyvO2HSPZH/1c=",
"aarch64-darwin": "sha256-OZt/MMm+SliHd+W5keq1EX59BSsaSxPzEG1PsuR/174=",
"x86_64-darwin": "sha256-WO8Duy0t2EbV8aWpqvOc0HTLscNVxDRdf7Vr5FQcCeY="
}
}
+7 -6
View File
@@ -4,7 +4,7 @@
"description": "AI-powered development tool",
"private": true,
"type": "module",
"packageManager": "bun@1.3.14",
"packageManager": "bun@1.4.2",
"scripts": {
"dev": "KILO_CLIENT=cli bun run --cwd packages/opencode --conditions=node src/index.ts",
"dev:stats": "bun sst shell --stage=production -- bun run --cwd packages/stats/app dev",
@@ -36,7 +36,7 @@
"@effect/platform-node": "4.0.0-beta.83",
"@anthropic-ai/sandbox-runtime": "0.0.63",
"@npmcli/arborist": "9.4.0",
"@types/bun": "1.3.14",
"@types/bun": "1.4.2",
"@types/cross-spawn": "6.0.6",
"@octokit/rest": "22.0.0",
"@opentui/core": "0.5.11",
@@ -54,7 +54,7 @@
"@solid-primitives/storage": "4.3.3",
"@tailwindcss/vite": "4.1.11",
"diff": "8.0.4",
"dompurify": "3.4.13",
"dompurify": "3.4.16",
"drizzle-kit": "1.0.0-rc.2",
"drizzle-orm": "1.0.0-rc.2",
"effect": "4.0.0-beta.83",
@@ -159,7 +159,8 @@
"fastify": ">=5.8.3",
"vite": "7.3.5",
"diff": "8.0.4",
"dompurify": "3.4.13",
"dompurify": "3.4.16",
"katex": "0.19.0",
"happy-dom": ">=20.8.9",
"@opentui/core": "catalog:",
"@opentui/solid": "catalog:",
@@ -176,7 +177,7 @@
"gcp-metadata@8.1.2": "patches/gcp-metadata@8.1.2.patch",
"@ai-sdk/google@3.0.73": "patches/@ai-sdk%2Fgoogle@3.0.73.patch",
"@pierre/trees@1.0.0-beta.4": "patches/@pierre%2Ftrees@1.0.0-beta.4.patch",
"@modelcontextprotocol/sdk@1.29.0": "patches/@modelcontextprotocol%2Fsdk@1.29.0.patch",
"@modelcontextprotocol/sdk@1.31.0": "patches/@modelcontextprotocol%2Fsdk@1.31.0.patch",
"effect@4.0.0-beta.83": "patches/effect@4.0.0-beta.83.patch",
"@tanstack/virtual-core@3.17.3": "patches/@tanstack%2Fvirtual-core@3.17.3.patch",
"@ai-sdk/openai-compatible@2.0.41": "patches/@ai-sdk%2Fopenai-compatible@2.0.41.patch",
@@ -191,6 +192,6 @@
"@ai-sdk/openai-compatible@2.0.48": "patches/@ai-sdk%2Fopenai-compatible@2.0.48.patch",
"bun-pty@0.4.8": "patches/bun-pty@0.4.8.patch"
},
"version": "7.8.3",
"version": "7.8.8",
"peerDependencies": {}
}
+1 -1
View File
@@ -38,5 +38,5 @@
"@typescript/native-preview": "catalog:",
"effect": "catalog:"
},
"version": "7.8.3"
"version": "7.8.8"
}
+1 -1
View File
@@ -1,7 +1,7 @@
{
"$schema": "https://json.schemastore.org/package.json",
"name": "@opencode-ai/codemode",
"version": "7.8.3",
"version": "7.8.8",
"description": "Effect-native confined code execution over schema-described tools",
"private": true,
"type": "module",
+1 -1
View File
@@ -6,7 +6,7 @@ FROM ${REGISTRY}/build/base:24.04
SHELL ["/bin/bash", "-lc"]
ARG NODE_VERSION=24.4.0
ARG BUN_VERSION=1.3.14
ARG BUN_VERSION=1.4.2
ENV BUN_INSTALL=/opt/bun
ENV PATH=/opt/bun/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
+1 -1
View File
@@ -1,6 +1,6 @@
{
"$schema": "https://json.schemastore.org/package.json",
"version": "7.8.3",
"version": "7.8.8",
"name": "@opencode-ai/core",
"type": "module",
"license": "MIT",
+16 -2
View File
@@ -69,6 +69,8 @@ export interface GlobInput {
readonly follow?: boolean
readonly signal?: AbortSignal
readonly validate?: Effect.Effect<void, unknown> // kilocode_change - bind approved searches at spawn
readonly noRequireGit?: boolean // kilocode_change - honor ignore files outside a git repository
readonly exclude?: readonly string[] // kilocode_change - additional exclusion globs
}
export interface GrepInput extends KiloGrep.Options {
@@ -93,6 +95,7 @@ export interface SearchResult<A> {
readonly items: readonly A[]
readonly truncated: boolean
readonly partial: boolean
readonly invalidPattern?: boolean // kilocode_change - distinguish malformed globs from transient errors
}
// kilocode_change end
@@ -100,8 +103,12 @@ export class Service extends Context.Service<Service, Interface>()("@opencode/v2
const failure = (message: string, cause?: unknown) => new Error({ message, cause })
// kilocode_change start - also classify invalid globs for the marketplace scan
const isInvalidPattern = (stderr: string) =>
stderr.includes("regex parse error") || stderr.includes("error parsing regex")
stderr.includes("regex parse error") ||
stderr.includes("error parsing regex") ||
stderr.includes("error parsing glob")
// kilocode_change end
const layer = Layer.effect(
Service,
@@ -206,6 +213,7 @@ const layer = Layer.effect(
cwd: input.cwd,
limit: input.limit,
signal: input.signal,
pattern: input.pattern, // kilocode_change - surface malformed globs instead of a bare partial result
timeout: 2 * 60 * 1000, // kilocode_change
validate: input.validate, // kilocode_change - preserve spawn-bound target validation
args: [
@@ -213,7 +221,9 @@ const layer = Layer.effect(
"--files",
...(input.hidden ? ["--hidden"] : []),
...(input.follow ? ["--follow"] : []),
...(input.noRequireGit ? ["--no-require-git"] : []), // kilocode_change
`--glob=${input.pattern}`,
...(input.exclude ?? []).map((glob) => `--glob=!${glob}`), // kilocode_change
"--glob=!**/.git/**",
".",
],
@@ -236,7 +246,11 @@ const layer = Layer.effect(
),
})),
// kilocode_change end
Effect.catchTag("Ripgrep.InvalidPatternError", (cause) => Effect.fail(failure(cause.message, cause))),
// kilocode_change start - a malformed glob narrows the result, not the whole request
Effect.catchTag("Ripgrep.InvalidPatternError", () =>
Effect.succeed({ items: [], truncated: false, partial: true, invalidPattern: true }),
),
// kilocode_change end
),
find: (input) =>
run<Entry>({
+4
View File
@@ -203,6 +203,10 @@ export const Info = Schema.Struct({
description:
"Model-specific variant overrides for task-tool subagents, keyed by provider/model. Valid overrides take precedence over saved, agent-specific, and inherited variants.",
}),
memory_model: Schema.optional(Schema.NullOr(Schema.String)).annotate({
description:
"Model for automatic project memory saves in the format of provider/model. If unset or unavailable, memory uses the session model.",
}),
default_agent: Schema.optional(Schema.NullOr(Schema.String)).annotate({
description:
"Default agent to use when none is specified. Must be a primary agent. Falls back to 'code' if not set or if the specified agent is invalid.",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"$schema": "https://json.schemastore.org/package.json",
"version": "7.8.3",
"version": "7.8.8",
"name": "@opencode-ai/effect-drizzle-sqlite",
"type": "module",
"license": "MIT",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"$schema": "https://json.schemastore.org/package.json",
"version": "7.8.3",
"version": "7.8.8",
"name": "@opencode-ai/effect-sqlite-node",
"type": "module",
"license": "MIT",
+6 -6
View File
@@ -1,7 +1,7 @@
id = "kilo"
name = "Kilo"
description = "The open source coding agent."
version = "7.8.3"
version = "7.8.8"
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.8.3/opencode-darwin-arm64.zip"
archive = "https://github.com/Kilo-Org/kilocode/releases/download/v7.8.8/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.8.3/opencode-darwin-x64.zip"
archive = "https://github.com/Kilo-Org/kilocode/releases/download/v7.8.8/opencode-darwin-x64.zip"
cmd = "./opencode"
args = ["acp"]
[agent_servers.opencode.targets.linux-aarch64]
archive = "https://github.com/Kilo-Org/kilocode/releases/download/v7.8.3/opencode-linux-arm64.tar.gz"
archive = "https://github.com/Kilo-Org/kilocode/releases/download/v7.8.8/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.8.3/opencode-linux-x64.tar.gz"
archive = "https://github.com/Kilo-Org/kilocode/releases/download/v7.8.8/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.8.3/opencode-windows-x64.zip"
archive = "https://github.com/Kilo-Org/kilocode/releases/download/v7.8.8/opencode-windows-x64.zip"
cmd = "./opencode.exe"
args = ["acp"]
+1 -1
View File
@@ -1,6 +1,6 @@
{
"$schema": "https://json.schemastore.org/package.json",
"version": "7.8.3",
"version": "7.8.8",
"name": "@opencode-ai/http-recorder",
"description": "Record and replay Effect HTTP client traffic with deterministic cassettes",
"type": "module",
+1 -1
View File
@@ -20,5 +20,5 @@
"@types/bun": "catalog:",
"@typescript/native-preview": "catalog:"
},
"version": "7.8.3"
"version": "7.8.8"
}
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@kilocode/kilo-console",
"version": "7.8.3",
"version": "7.8.8",
"private": true,
"type": "module",
"scripts": {
@@ -16,6 +16,7 @@ const sectionNavItems: SectionNav = {
contributing: Nav.ContributingNav,
"ai-providers": Nav.AiProvidersNav,
gateway: Nav.GatewayNav,
desktop: Nav.DesktopNav,
}
// Main nav items with their section keys
@@ -28,6 +29,7 @@ const mainNavItems = [
{ label: "Automate", href: "/automate", sectionKey: "automate" },
{ label: "Deploy & Secure", href: "/deploy-secure", sectionKey: "deploy-secure" },
{ label: "AI Gateway", href: "/gateway", sectionKey: "gateway" },
{ label: "Kilo Desktop", href: "/desktop", sectionKey: "desktop" },
{ label: "Contributing", href: "/contributing", sectionKey: "contributing" },
]
+31
View File
@@ -0,0 +1,31 @@
interface SvgIconProps {
src: string
size?: string
label?: string
}
/**
* Renders a monochrome SVG from the public folder as a CSS mask filled with
* `currentColor`, so the glyph inherits the surrounding text color and adapts to
* light/dark — unlike the `<img>`-based {% icon %} tag. Use for small line icons
* (e.g., Lucide). Renders on the server, so there is no fetch or flash on load.
*/
export function SvgIcon({ src, size = "1em", label }: SvgIconProps) {
const mask = `url(${JSON.stringify(src)}) center / contain no-repeat`
return (
<span
role={label ? "img" : undefined}
aria-label={label || undefined}
aria-hidden={label ? undefined : true}
style={{
display: "inline-block",
width: size,
height: size,
verticalAlign: "middle",
backgroundColor: "currentColor",
mask,
WebkitMask: mask,
}}
/>
)
}
+22 -2
View File
@@ -7,6 +7,7 @@ import { ThemeToggle } from "./ThemeToggle"
interface NavItem {
label: string
href: string
badge?: string
}
interface DropdownItem {
@@ -31,6 +32,7 @@ const mainNavItems: NavItem[] = [
{ label: "Automate", href: "/automate" },
{ label: "Deploy & Secure", href: "/deploy-secure" },
{ label: "Kilo Gateway", href: "/gateway" },
{ label: "Kilo Desktop", href: "/desktop", badge: "Beta" },
{ label: "Contributing", href: "/contributing" },
]
@@ -198,12 +200,30 @@ function NavTab({ item, isActive }: { item: NavItem; isActive: boolean }) {
<Link
href={item.href}
className={`relative px-1 py-3 text-sm font-medium transition-colors ${
isActive ? "text-indigo-600 dark:text-[#F8F675]" : "hover:text-[var(--text-color)]"
isActive ? "text-indigo-600 dark:text-[var(--kilo-yellow)]" : "hover:text-[var(--text-color)]"
}`}
style={{ color: isActive ? "var(--text-brand)" : "var(--text-secondary)" }}
>
{item.label}
{isActive && <span className="absolute bottom-0 left-0 right-0 h-0.5 bg-indigo-600 dark:bg-[#F8F675]" />}
{item.badge ? (
<span
className="bg-blue-100 text-blue-700 dark:bg-blue-400/10 dark:text-blue-300"
style={{
marginLeft: "0.375rem",
fontSize: "0.625rem",
fontWeight: 600,
textTransform: "uppercase",
letterSpacing: "0.03em",
padding: "0.0625rem 0.3125rem",
borderRadius: "0.1875rem",
lineHeight: 1.4,
verticalAlign: "middle",
}}
>
{item.badge}
</span>
) : null}
{isActive && <span className="absolute bottom-0 left-0 right-0 h-0.5 bg-indigo-600 dark:bg-[var(--kilo-yellow)]" />}
</Link>
)
}
+1
View File
@@ -5,6 +5,7 @@ export * from "./CopyPageButton"
export * from "./CopyLine"
export * from "./Heading"
export * from "./Icon"
export * from "./SvgIcon"
export * from "./Image"
export * from "./ImageGallery"
export * from "./KiloCodeIcon"
+37
View File
@@ -0,0 +1,37 @@
import { NavSection } from "../types"
export const DesktopNav: NavSection[] = [
{
title: "Introduction",
links: [
{ href: "/desktop/overview", children: "Overview" },
{ href: "/desktop/installation", children: "Installation" },
{ href: "/desktop/quickstart", children: "Quickstart" },
{ href: "/desktop/what-you-can-ask", children: "What you can ask" },
],
},
{
title: "Features",
links: [
{ href: "/desktop/features", children: "Overview" },
{ href: "/desktop/features/notebooks", children: "Notebooks" },
{ href: "/desktop/features/browser", children: "In-app browser" },
{ href: "/desktop/features/terminal", children: "Terminal" },
{ href: "/desktop/features/git", children: "Git integration" },
{ href: "/desktop/features/local-inference", children: "Local inference" },
{ href: "/desktop/features/environments", children: "Conda environments" },
{ href: "/desktop/features/files", children: "Files" },
],
},
{
title: "Settings",
links: [
{ href: "/desktop/settings/general", children: "General settings" },
{ href: "/desktop/settings/ai", children: "AI settings" },
],
},
{
title: "Help",
links: [{ href: "/desktop/troubleshooting", children: "Troubleshooting" }],
},
]
+2
View File
@@ -5,6 +5,7 @@ import { CollaborateNav } from "./collaborate"
import { ContributingNav } from "./contributing"
import { CustomizeNav } from "./customize"
import { DeploySecureNav } from "./deploy-secure"
import { DesktopNav } from "./desktop"
import { GatewayNav } from "./gateway"
import { GettingStartedNav } from "./getting-started"
import { ToolsNav } from "./tools"
@@ -19,5 +20,6 @@ export const Nav = {
ContributingNav,
AiProvidersNav,
GatewayNav,
DesktopNav,
ToolsNav,
}
+1
View File
@@ -4,6 +4,7 @@ export * from "./codicon.markdoc"
export * from "./copy-line.markdoc"
export * from "./tabs.markdoc"
export * from "./icon.markdoc"
export * from "./svg-icon.markdoc"
export * from "./image.markdoc"
export * from "./image-gallery.markdoc"
export * from "./kilo-code-icon.markdoc"
@@ -0,0 +1,22 @@
import { SvgIcon } from "../../components"
export const svgIcon = {
render: SvgIcon,
selfClosing: true,
attributes: {
src: {
type: String,
required: true,
description: "Path to the inline SVG (e.g., '/docs/img/desktop/code.svg')",
},
size: {
type: String,
default: "1em",
description: "Icon size (CSS width/height value)",
},
label: {
type: String,
description: "Accessible label; omit for decorative icons that sit next to their text",
},
},
}
+5
View File
@@ -20,6 +20,11 @@ module.exports = withMarkdoc(/* config: https://markdoc.io/docs/nextjs#options *
basePath: false,
permanent: true,
},
{
source: "/desktop",
destination: "/desktop/overview",
permanent: true,
},
...previousDocsRedirects,
]
},
+2 -2
View File
@@ -1,6 +1,6 @@
{
"name": "@kilocode/kilo-docs",
"version": "7.8.3",
"version": "7.8.8",
"private": true,
"scripts": {
"dev": "next dev --webpack --port 3002",
@@ -20,7 +20,7 @@
"@xyflow/react": "12.10.2",
"js-yaml": "^4.3.2",
"mermaid": "11.17.2",
"next": "^16.3.6",
"next": "^16.3.8",
"posthog-js": "^1.335.3",
"prismjs": "^1.30.0",
"react": "^19.2.4",
+3 -3
View File
@@ -146,7 +146,7 @@ export default function Custom404() {
}
.prompt {
color: #f8f674;
color: var(--kilo-yellow);
}
.command {
@@ -171,7 +171,7 @@ export default function Custom404() {
.error-bar {
width: 4px;
height: 80px;
background: #f8f674;
background: var(--kilo-yellow);
margin-right: 1.5rem;
border-radius: 2px;
}
@@ -180,7 +180,7 @@ export default function Custom404() {
font-family: "JetBrains Mono", monospace;
font-size: 4.5rem;
font-weight: 700;
color: #f8f674;
color: var(--kilo-yellow);
line-height: 1;
}
+1
View File
@@ -125,6 +125,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
{ title: "Contributing", nav: Nav.ContributingNav },
{ title: "AI Providers", nav: Nav.AiProvidersNav },
{ title: "Gateway", nav: Nav.GatewayNav },
{ title: "Desktop", nav: Nav.DesktopNav },
{ title: "Tools", nav: Nav.ToolsNav },
]
@@ -5,18 +5,10 @@ description: "Manage Agent Manager sessions across multiple Git repositories"
# Multi-project Agent Manager
Multi-project Agent Manager lets you manage sessions and worktrees from multiple Git repositories in one Agent Manager panel. The feature is experimental and disabled by default. When it is disabled, Agent Manager keeps its existing single-project behavior.
Agent Manager lets you manage sessions and worktrees from multiple Git repositories in one panel. Multi-project support is always available, with no experimental setting required.
One Agent Manager project represents one canonical Git repository. A project is not a collection of repositories, and the VS Code workspace folder does not define a project.
## Enable multi-project mode
1. Open [Kilo Code Settings](/docs/getting-started/settings#experimental-features).
2. Open the **Experimental** tab.
3. Enable **Multi-Project Agent Manager**.
The setting is also available as `kilo-code.new.experimental.multiProject`. It is an application-scoped VS Code setting and defaults to `false`.
## Default project
The first folder in your VS Code workspace is always the **default project**. Agent Manager derives it from the current workspace folder at run time, so it is not an entry in the saved project list and you cannot remove it. It appears first in the **PROJECTS** list, and it has its own Local context, worktrees, sections, sessions, scripts, and settings like any other project.
@@ -32,7 +24,7 @@ The project footer below the list provides these actions:
- **Open local folder...** opens a folder picker. Agent Manager resolves the selected folder to its canonical Git repository root and adds that repository. A folder inside an existing repository attaches that repository, and a linked worktree attaches its primary checkout.
- **Clone repository...** clones a repository with the VS Code Git extension into a parent folder that you choose, then adds it.
Multi-project mode must be enabled and the VS Code window must be trusted before you can add, create, or clone a repository. Adding a repository does not add it to `vscode.workspaceFolders`. Agent Manager identifies a project by its canonical Git root, so adding the same repository again opens the existing project instead of creating a duplicate.
The VS Code window must be trusted before you can add, create, or clone a repository. Adding a repository does not add it to `vscode.workspaceFolders`. Agent Manager identifies a project by its canonical Git root, so adding the same repository again opens the existing project instead of creating a duplicate.
If you select a folder that is not a Git repository, **Open local folder...** asks whether to initialize Git and create an empty first commit before it adds the project.
@@ -541,7 +541,17 @@ Use **Select element** to attach an element reference to your next message. It i
The agent can open the application with `browser_open`. Its automation browser accepts only HTTP URLs on `localhost` or `127.0.0.1` and blocks other origins, including external assets and APIs on separate ports. Serve the resources the agent needs from the same loopback origin. These restrictions apply to the automation browser, not the independent visible preview iframe.
Google Chrome must be installed for the default runtime. To use an already-installed compatible Playwright Chromium browser instead, turn off **Use System Chrome** under **Settings > Web Tools**.
Kilo uses system Google Chrome by default. In WSL, if Chrome is missing and you have not explicitly set a browser preference, Kilo tries an installed compatible Playwright Chromium browser. Set **Use System Chrome** under **Settings > Experimental** to choose Chrome (on) or Chromium (off). Chromium must match the extension's bundled `playwright-core` version.
The Integrated Browser does not install browsers or operating system packages. To install Chromium manually, run the extension's bundled Playwright CLI with VS Code Server's Node inside WSL:
```sh
"<VS Code Server Node path>" "<Kilo extension path>/dist/node_modules/playwright-core/cli.js" install chromium
```
Use the paths for your active VS Code Server and Kilo extension. Linux system libraries may also need manual installation. Keep browser sandbox protection enabled.
In a VS Code WSL window, the browser runs in the WSL extension host. Chrome installed on Windows is not Linux Chrome. Install Chrome inside WSL and keep **Use System Chrome** on, or use matching Chromium with the setting off. This setting is application-scoped, so configure it in Windows-side user settings, not WSL remote settings. `localhost` refers to the extension host, so keep your application's server running inside WSL.
## Terminals
@@ -363,7 +363,7 @@ Every hook is optional. Return only the ones you care about.
| Hook | Description |
|---|---|
| `config` | Receives the fully-resolved config at startup. Read-only — useful for inspection. |
| `config` | Receives the fully-resolved config at startup. Mutate it in place to add or change agents, commands, providers, and other config — this is how a plugin contributes its own slash commands. A command named `goal` is reserved for [session goals](/docs/code-with-ai/agents/goals): Kilo ignores it under that name, warns, and exposes it as `/goal:command` instead. |
| `event` | Called for **every** event on the internal bus (see [Events](#events)). |
### Tools
@@ -81,6 +81,17 @@ Double-click the current session title at the top of the chat to edit it inline.
You can also rename local sessions from **History** using the edit button or the session's context menu.
**Switching session tabs:**
When more than one session is open, the sidebar and Kilo editor tabs show a session tab strip. Use these shortcuts while the chat has focus:
| Shortcut (macOS) | Shortcut (Windows/Linux) | Action |
|---|---|---|
| `Cmd+Alt+Left` / `Right` | `Ctrl+Alt+Left` / `Right` | Previous / next session tab |
| `Cmd+Shift+Left` / `Right` | `Ctrl+Shift+Left` / `Right` | Move the focused session tab left / right |
Hover a session tab next to the active tab to see its shortcut. When the prompt is empty, its placeholder also shows the shortcut. To hide these prompt hints, turn off **Show shortcut hints** in **Settings > Display**. To change the keys, search for **Previous Session Tab** or **Next Session Tab** in **Keyboard Shortcuts**. These are the same keys that switch tabs in the Agent Manager, and each window acts on the surface that has focus.
{% /tab %}
{% tab label="CLI" %}
@@ -103,7 +103,7 @@ Goal-composer mode accepts multiline objectives and file or image attachments. I
## Limits and integration
- A custom command or an MCP prompt named `goal` is reserved. Kilo rejects it and reports an error; rename it.
- A custom command named `goal` (from your config or a plugin) is reserved for this feature. Kilo ignores it, reports a configuration warning, and makes it available as `/goal:command` instead of running it as `/goal`.
- The goal is stored under the `kilo.goal` session metadata key.
- Headless mode supports only status and controls through `kilo run --command goal`, which accepts no argument, `pause`, or `clear`. The agent can still start or resume a goal itself with the `goal` tool.
@@ -23,6 +23,8 @@ You can configure models separately for different tasks in the VS Code extension
- **Subagent model** — the default model for subagents launched by the `task` tool. Configured with the `subagent_model` key in `kilo.jsonc`, or the **Subagent Model** field on the **Settings → Models** tab.
- If left unset, inherits whichever model the parent agent session is currently using.
- **Autocomplete model** — the model used for inline code completions as you type. See [Autocomplete: Provider and Model](/docs/code-with-ai/features/autocomplete#provider-and-model) for how to configure it.
- **Memory model** — the model used for automatic [project memory](/docs/customize/context/memory#use-a-different-model-for-auto-save) saves. Configured with the `memory_model` key in `kilo.jsonc`.
- If left unset, uses whichever model the session is currently using.
- **Compaction model** - the model used to summarize context. Set `agent.compaction.model` in `kilo.jsonc`, or choose **Compaction model** under **Settings → Models**. If unset, compaction uses the current session's model. See [Context Condensing](/docs/customize/context/context-condensing#use-a-different-model-for-compaction).
### Configuring Local Usage
@@ -57,8 +59,8 @@ While the specifics change constantly, some principles stay consistent:
- When the selected model supports variants, type `/variant` to open the reasoning effort selector.
- Press `Shift+Tab` in the prompt input to cycle to the next reasoning effort variant, wrapping after the last one. This works in the sidebar chat, the Agent Manager prompt, and the New Worktree dialog, and the variant selector tooltip shows the shortcut on hover. To keep `Shift+Tab` for keyboard focus navigation instead, disable the `kilo-code.new.chat.shiftTabCyclesVariant` setting (also available under **Settings → Display**).
- Set per-agent defaults and a global default in the **Settings** panel (Models tab), or directly in the `kilo.jsonc` config file.
- **Model precedence:** Session override → Last picked per agent → Per-agent config → Global config → [Auto Free](/docs/code-with-ai/agents/auto-model#tiers) (note: Auto Free may route to providers that log prompts — see the Auto Model page for details).
- The model selector remembers the last model you picked for each agent, so switching agents restores your previous choice. A manual pick always beats config settings.
- **Model precedence:** Pick for the current session or draft and agent → Per-agent config → Global config → Organization recommendation (then the first available Kilo model), or the most recently used available model for individual users → [Auto Free](/docs/code-with-ai/agents/auto-model#tiers) (note: Auto Free may route to providers that log prompts — see the Auto Model page for details).
- The model selector remembers the model you picked for each agent within a session or draft, so switching agents and back restores your choice. New sessions start from config or the default; existing sessions recover each agent's model and reasoning effort from its latest user message. A manual pick beats config only within the session or draft and agent where it was made.
{% /tab %}
{% tab label="CLI" %}
@@ -129,6 +131,8 @@ When an agent delegates work to a subagent (via the `task` tool), the subagent *
This sets the `explore` subagent to always use Haiku regardless of the parent's model. Any subagent without a `model` override uses whatever model the invoking agent is running.
A bare model name without a provider prefix also works — for example `"model": "codestral (latest)"`. Kilo resolves the name against your configured providers and prefers the provider of the parent session when several providers offer a model with the same name. This applies to `agent.<name>.model` and `subagent_model`.
{% /tab %}
{% tab label="VSCode" %}
@@ -50,12 +50,12 @@ Kilo updates the list when:
## Where todo lists appear
**1. Task Header Summary**
Quick progress overview with your next important item
**1. Session Dock**
A progress chip above the prompt shows how many items are done and the item Kilo works on now
{% image src="/docs/img/screenshot-tests/kilo-vscode/visual-regression/chat/task-header-with-todos-chromium-linux.png" alt="Task header summary showing todo list progress" width="420" /%}
{% image src="/docs/img/screenshot-tests/kilo-vscode/visual-regression/chat/chat-view-session-dock-todo-states-chromium-linux.png" alt="Todo progress chip in the session dock, next to the working indicator and the session actions" width="420" /%}
Click the task header summary to expand the full list inline and jump to the current item.
While Kilo works, the chip sits next to the working indicator. When the session is idle, it sits next to the session actions. Click the chip to see the full list.
**2. Interactive Tool Block**
Full todo interface in chat where you can:
@@ -88,6 +88,14 @@ For Kilo Pass pricing, billing, and account management details, use the [Kilo Pa
On Android, you can buy, restore, and change Kilo Pass tiers through Google Play. Tier changes take effect at the next renewal; your current tier and credits stay in place until then. Google manages cancellation and payment methods for passes purchased through Google Play.
### Buying credits in the app
The personal credits section shows an **Add credits** entry at any balance, which opens a **Buy credits** screen with your current balance and four preset packs: $10, $50, $100, and $500. The stores sell fixed products, not arbitrary amounts, so the app offers these preset packs rather than a custom amount. Each row shows the credit amount and the store's localized price; a pack the store cannot price is disabled and reads **Price unavailable**.
Selecting a pack opens the store's own purchase sheet — StoreKit on iOS, Google Play Billing on Android. The app never grants credits itself: credits are added only after the backend validates the purchase with the store. A successful purchase shows a confirmation and refreshes your balance, and a cancelled purchase shows no error. If a purchase fails, the screen shows one inline message with a retry action; if the store is unreachable, it shows one banner naming that store while keeping the four packs visible. A purchase the backend has not yet credited is recovered the next time the app connects, and a refunded pack has its credits reversed.
For other ways to add credits, see [Adding Credits](/docs/getting-started/adding-credits).
{% imageGallery columns="3" width="220px" %}
{% image src="/docs/img/mobile-apps/home.webp" alt="Kilo Code mobile home screen showing active agent sessions" caption="Start coding tasks and resume active sessions from the mobile home screen." /%}
@@ -16,7 +16,7 @@ This document will help you set up your development environment and understand h
Before you begin, make sure you have the following installed:
1. **Git** - For version control
2. **Bun 1.3.14+** - Required for installing dependencies and running scripts
2. **Bun 1.4.2+** - Required for installing dependencies and running scripts
3. **Visual Studio Code** - Our recommended IDE for development
4. **Java 21** - Required only when running JetBrains plugin checks or repo-level checks that include `@kilocode/kilo-jetbrains`
@@ -64,6 +64,18 @@ When memory is enabled and auto-save is on, Kilo reviews completed turns and sav
Explicit saves are not throttled.
### Use a different model for auto-save
Auto-save can use a different model than your main session. By default it runs on the model your session is using. To customize it, set `memory_model` in `kilo.jsonc`:
```jsonc
{
"memory_model": "kilo/kilo-auto/small"
}
```
A smaller or faster model is usually enough for this background work, so you can keep a more capable model for the session itself. If `memory_model` is unset, malformed, or unavailable, auto-save uses the session model. The setting does not affect explicit saves or recall, which run in your session.
## Explicit memory
Use explicit commands when you want to control what is stored:
@@ -175,7 +175,7 @@ Pin a specific model using the `provider/model` format:
model: anthropic/claude-sonnet-4-20250514
```
The model selector also **remembers the last model you picked for each agent** across sessions. A config-pinned `model` acts as the default when no manual pick exists. To reset a pick and let the config take over, use the **reset button** in the model selector (visible when your active model differs from what the config specifies).
The model selector remembers the model you picked for each agent within a session or draft. A config-pinned `model` is the default for new sessions; a manual pick beats config only for the agent and session or draft where it was made. Switching to another agent uses that agent's pick or configured default, and switching back restores your choice. Existing sessions recover each agent's model from its latest user message rather than a shared model preference.
### `steps`

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