Skip to content
Merged
Show file tree
Hide file tree
Changes from 43 commits
Commits
Show all changes
52 commits
Select commit Hold shift + click to select a range
bbf67d0
fix(tui): render all non-synthetic text parts of a user message (#24009)
jlongster Apr 23, 2026
0517ab4
refactor(session): migrate session domain to Effect Schema (#24005)
kitlangton Apr 23, 2026
2cd89d6
chore: generate
opencode-agent[bot] Apr 23, 2026
eb7555d
sync release versions for v1.14.22
Apr 23, 2026
9df7c78
fix(npm): respect npmrc for version lookups (#24016)
nexxeln Apr 23, 2026
353532b
chore: generate
opencode-agent[bot] Apr 23, 2026
c50d65b
refactor(sync): make session events schema-first (#24019)
kitlangton Apr 23, 2026
aed0307
chore: generate
opencode-agent[bot] Apr 23, 2026
8b2f835
docs(schema): mark sync/index.ts migrated with compat-bridge note (#2…
kitlangton Apr 23, 2026
1e439b8
sync
fwang Apr 23, 2026
93940a1
refactor(provider): migrate provider domain to Effect Schema (#24027)
kitlangton Apr 23, 2026
0590452
refactor(schema): use Schema.Int and consolidate PositiveInt/NonNegat…
kitlangton Apr 23, 2026
cd93533
refactor(bus): migrate BusEvent to Effect Schema (#24040)
kitlangton Apr 23, 2026
2489255
chore: generate
opencode-agent[bot] Apr 23, 2026
3910a6e
refactor(tool): migrate tool framework + all 18 built-in tools to Eff…
kitlangton Apr 23, 2026
3f8c659
chore: generate
opencode-agent[bot] Apr 23, 2026
98ea5b6
feat(tui): support builtin protocol for handling context from editors…
jlongster Apr 23, 2026
4c3e65c
chore: generate
opencode-agent[bot] Apr 23, 2026
814e83f
docs: update effect schema migration tracker (#24054)
kitlangton Apr 23, 2026
31d01d4
refactor(control-plane): migrate workspace DTO schemas (#24056)
kitlangton Apr 23, 2026
a771859
chore: generate
opencode-agent[bot] Apr 23, 2026
8732194
chore: update copilot readme to symlink to an agents md to prevent du…
rekram1-node Apr 23, 2026
334ab47
fix: account for additional openai retry case (#24063)
rekram1-node Apr 23, 2026
e50a688
feat(httpapi): bridge workspace read endpoints (#24062)
kitlangton Apr 23, 2026
f8c6ddd
feat(truncate): allow configuring tool output truncation limits (#23770)
1rgs Apr 23, 2026
5c5069b
chore: generate
opencode-agent[bot] Apr 23, 2026
3bfe6a1
ci: add platform-specific bun install flags (#23822)
Brendonovich Apr 24, 2026
2e156b8
fix(desktop): avoid relaunching without installing updates (#23806)
Brendonovich Apr 24, 2026
6c1268f
chore: generate
opencode-agent[bot] Apr 24, 2026
4712f0f
feat(prompt): add shell mode UI with cancel button, custom icon, and …
Brendonovich Apr 24, 2026
f4616c8
sync
fwang Apr 24, 2026
a4bd88a
zen: deepseek v4 pro
fwang Apr 24, 2026
f033d2d
fix(app): conditionally show model variant selector (#24115)
Brendonovich Apr 24, 2026
2cda629
test(prompt): align shell placeholder expectation (#24147)
simonklee Apr 24, 2026
a882e95
fix: deepseek variants (#24157)
rekram1-node Apr 24, 2026
923af96
fix: preserve empty reasoning_content for DeepSeek V4 thinking mode (…
heimoshuiyu Apr 24, 2026
f8e939d
fix: support `max` for deepseek (#24163)
rekram1-node Apr 24, 2026
3d31ae2
release: v1.14.23
Apr 24, 2026
108dfe3
refactor: kilo compat for v1.14.23
imanolmzd-svg Apr 27, 2026
fd8818e
resolve merge conflicts
imanolmzd-svg Apr 27, 2026
24d1f69
refactor(opencode): migrate zod schemas to effect Schema across kilo …
imanolmzd-svg Apr 27, 2026
d4e44b6
fix(kilo-sessions): apply type-erasure workaround for Bus.subscribe s…
imanolmzd-svg Apr 27, 2026
85eb4fa
chore: regenerate SDK
imanolmzd-svg Apr 27, 2026
27a4db5
Merge branch 'main' into imanolmaiztegui/kilo-opencode-v1.14.23
imanolmzd-svg Apr 27, 2026
bb7ee00
refactor(kilo-indexing): migrate BusEvent and SemanticSearchTool sche…
imanolmzd-svg Apr 27, 2026
0573557
test(opencode): update tool parameter snapshots and assertions for sc…
imanolmzd-svg Apr 27, 2026
2c81961
fix(opencode): align test expectations with kilocode package rename a…
imanolmzd-svg Apr 27, 2026
5ac8a7c
build(opencode): replace models-snapshot inline data with stub and ig…
imanolmzd-svg Apr 27, 2026
aef45c4
Merge remote-tracking branch 'origin/main' into imanolmaiztegui/kilo-…
markijbema Apr 28, 2026
b037746
docs: keep markdown tables minimal
markijbema Apr 28, 2026
8671a57
Fix semantic merge with indexing code
markijbema Apr 28, 2026
e902880
ran ./script/generate.ts
markijbema Apr 28, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 7 additions & 2 deletions .github/actions/setup-bun/action.yml
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
name: "Setup Bun"
description: "Setup Bun with caching and install dependencies"
inputs:
install-flags:
description: "Additional flags to pass to 'bun install'"
required: false
default: ""
runs:
using: "composite"
steps:
Expand Down Expand Up @@ -46,8 +51,8 @@ runs:
# e.g. ./patches/ for standard-openapi
# https://github.com/oven-sh/bun/issues/28147
if [ "$RUNNER_OS" = "Windows" ]; then
bun install --linker hoisted
bun install --linker hoisted ${{ inputs.install-flags }}
else
bun install
bun install ${{ inputs.install-flags }}
fi
shell: bash
5 changes: 2 additions & 3 deletions .github/workflows/watch-opencode-releases.yml
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
on:
schedule:
- cron: "7,37 * * * *" # every 30 min at :07 and :37
- cron: "7,37 * * * *" # every 30 min at :07 and :37
workflow_dispatch:

concurrency:
group: opencode-release-watch
cancel-in-progress: false

permissions:
permissions:
contents: read

jobs:
Expand Down Expand Up @@ -66,4 +66,3 @@ jobs:
with:
path: .cache/last-seen
key: last-seen-${{ steps.fetch.outputs.tag }}

44 changes: 21 additions & 23 deletions TESTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,11 +32,11 @@ For in-process tests (no socket, fastest loop) see `packages/opencode/test/kiloc

## 2. `kilo serve` vs `bun dev serve` — important

| Command | What it runs |
|---|---|
| `kilo serve` | The npm-installed production CLI on `$PATH`. **Not the code in this repo.** |
| `bun dev serve …` (repo root) | The local main-branch backend from this worktree. **This is what you want.** |
| `bun run --cwd packages/opencode --conditions=browser src/index.ts serve …` | Same as `bun dev serve`, fully expanded. |
| Command | What it runs |
| --------------------------------------------------------------------------- | ---------------------------------------------------------------------------- |
| `kilo serve` | The npm-installed production CLI on `$PATH`. **Not the code in this repo.** |
| `bun dev serve …` (repo root) | The local main-branch backend from this worktree. **This is what you want.** |
| `bun run --cwd packages/opencode --conditions=browser src/index.ts serve …` | Same as `bun dev serve`, fully expanded. |

Root `package.json` defines `"dev"` as the full `bun run --cwd packages/opencode --conditions=browser src/index.ts` invocation, so `bun dev <args>` forwards `<args>` to the local CLI entry point (`packages/opencode/src/index.ts`) without touching the installed binary.

Expand Down Expand Up @@ -82,13 +82,13 @@ BASE="http://127.0.0.1:$PORT"

### Flags (`packages/opencode/src/cli/network.ts`)

| Flag | Default | Notes |
|---|---|---|
| `--port` | `0` (OS-assigned) | Must be passed literally when overriding `opencode.json`'s `server.port`. |
| `--hostname` | `127.0.0.1` | Becomes `0.0.0.0` when `--mdns` is set without an override. |
| `--mdns` | `false` | Publishes an mDNS SRV record. |
| `--mdns-domain` | `kilo.local` | |
| `--cors` | `[]` | Extra allowed origins. |
| Flag | Default | Notes |
| --------------- | ----------------- | ------------------------------------------------------------------------- |
| `--port` | `0` (OS-assigned) | Must be passed literally when overriding `opencode.json`'s `server.port`. |
| `--hostname` | `127.0.0.1` | Becomes `0.0.0.0` when `--mdns` is set without an override. |
| `--mdns` | `false` | Publishes an mDNS SRV record. |
| `--mdns-domain` | `kilo.local` | |
| `--cors` | `[]` | Extra allowed origins. |

## 4. The two mandatory request knobs

Expand Down Expand Up @@ -190,14 +190,14 @@ rm -f /tmp/kilo-serve.pid /tmp/kilo-serve.log

## 7. Useful environment variables

| Var | Why you'd set it |
|---|---|
| `KILO_SERVER_PASSWORD` | Enable Basic auth. Omit for auth-bypassed local testing. |
| `KILO_DB=":memory:"` | Skip on-disk SQLite — hermetic runs. |
| `KILO_DISABLE_DEFAULT_PLUGINS=true` | Don't auto-load bundled plugins. |
| `KILO_WORKSPACE_ID=<id>` | Single-workspace mode; disables control-plane routes. |
| `KILO_TELEMETRY_LEVEL=off` | Disable PostHog during tests. |
| `KILO_CONFIG_CONTENT='{…}'` | Inline JSON config without writing a file. |
| Var | Why you'd set it |
| ----------------------------------- | -------------------------------------------------------- |
| `KILO_SERVER_PASSWORD` | Enable Basic auth. Omit for auth-bypassed local testing. |
| `KILO_DB=":memory:"` | Skip on-disk SQLite — hermetic runs. |
| `KILO_DISABLE_DEFAULT_PLUGINS=true` | Don't auto-load bundled plugins. |
| `KILO_WORKSPACE_ID=<id>` | Single-workspace mode; disables control-plane routes. |
| `KILO_TELEMETRY_LEVEL=off` | Disable PostHog during tests. |
| `KILO_CONFIG_CONTENT='{…}'` | Inline JSON config without writing a file. |

## 8. Last resort: typed SDK via a throwaway script

Expand All @@ -209,9 +209,7 @@ import { createKiloClient } from "@kilocode/sdk/v2"

const port = process.env.PORT!
const pass = process.env.KILO_SERVER_PASSWORD
const headers = pass
? { Authorization: "Basic " + Buffer.from("kilo:" + pass).toString("base64") }
: undefined
const headers = pass ? { Authorization: "Basic " + Buffer.from("kilo:" + pass).toString("base64") } : undefined

const client = createKiloClient({
baseUrl: `http://127.0.0.1:${port}`,
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,6 @@
"[email protected]": "patches/[email protected]",
"[email protected]": "patches/[email protected]"
},
"version": "7.2.26",
"version": "7.2.25",
"peerDependencies": {}
}
2 changes: 1 addition & 1 deletion packages/app/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@opencode-ai/app",
"version": "7.2.26",
"version": "7.2.25",
"description": "",
"type": "module",
"exports": {
Expand Down
81 changes: 46 additions & 35 deletions packages/app/src/components/prompt-input.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -270,7 +270,7 @@ export const PromptInput: Component<PromptInputProps> = (props) => {
const buttonsSpring = useSpring(() => (store.mode === "normal" ? 1 : 0), { visualDuration: 0.2, bounce: 0 })
const motion = (value: number) => ({
opacity: value,
transform: `scale(${0.95 + value * 0.05})`,
transform: `scale(${0.98 + value * 0.02})`,
filter: `blur(${(1 - value) * 2}px)`,
"pointer-events": value > 0.5 ? ("auto" as const) : ("none" as const),
})
Expand Down Expand Up @@ -345,7 +345,7 @@ export const PromptInput: Component<PromptInputProps> = (props) => {
promptPlaceholder({
mode: store.mode,
commentCount: commentCount(),
example: suggest() ? language.t(EXAMPLES[store.placeholder]) : "",
example: suggest() ? (store.mode === "shell" ? "git status" : language.t(EXAMPLES[store.placeholder])) : "",
suggest: suggest(),
t: (key, params) => language.t(key as Parameters<typeof language.t>[0], params as never),
}),
Expand Down Expand Up @@ -1404,12 +1404,11 @@ export const PromptInput: Component<PromptInputProps> = (props) => {
<IconButton
data-action="prompt-submit"
type="submit"
disabled={store.mode !== "normal" || (!working() && blank())}
disabled={!working() && blank()}
tabIndex={store.mode === "normal" ? undefined : -1}
icon={stopping() ? "stop" : "arrow-up"}
icon={stopping() ? "stop" : store.mode === "shell" ? "arrow-undo-down" : "arrow-up"}
variant="primary"
class="size-8"
style={buttons()}
aria-label={stopping() ? language.t("prompt.action.stop") : language.t("prompt.action.send")}
/>
</Tooltip>
Expand Down Expand Up @@ -1455,14 +1454,24 @@ export const PromptInput: Component<PromptInputProps> = (props) => {
>
<div class="flex items-center gap-1.5 min-w-0 flex-1 relative">
<div
class="h-7 flex items-center gap-1.5 max-w-[160px] min-w-0 absolute inset-y-0 left-0"
class="h-7 flex items-center gap-1.5 min-w-0 absolute inset-0"
style={{
padding: "0 4px 0 8px",
padding: "0 0px 0 8px",
...shell(),
}}
>
<span class="truncate text-13-medium text-text-strong">{language.t("prompt.mode.shell")}</span>
<div class="size-4 shrink-0" />
<Icon name="console" />
<span class="truncate text-13-medium text-text-base">{language.t("prompt.mode.shell")}</span>
<div class="flex-1" />
<Button
variant="ghost"
class="text-text-base"
onClick={() => {
setStore("mode", "normal")
}}
>
{language.t("common.cancel")}
</Button>
</div>
<div class="flex items-center gap-1.5 min-w-0 flex-1 h-7">
<Show when={!agentsLoading()}>
Expand Down Expand Up @@ -1569,33 +1578,35 @@ export const PromptInput: Component<PromptInputProps> = (props) => {
</TooltipKeybind>
</Show>
</div>
<div
data-component="prompt-variant-control"
style={providersShouldFadeIn() ? { animation: "fade-in 0.3s" } : undefined}
>
<TooltipKeybind
placement="top"
gutter={4}
title={language.t("command.model.variant.cycle")}
keybind={command.keybind("model.variant.cycle")}
<Show when={variants().length > 2}>
<div
data-component="prompt-variant-control"
style={providersShouldFadeIn() ? { animation: "fade-in 0.3s" } : undefined}
>
<Select
size="normal"
options={variants()}
current={local.model.variant.current() ?? "default"}
label={(x) => (x === "default" ? language.t("common.default") : x)}
onSelect={(value) => {
local.model.variant.set(value === "default" ? undefined : value)
restoreFocus()
}}
class="capitalize max-w-[160px] text-text-base"
valueClass="truncate text-13-regular text-text-base"
triggerStyle={control()}
triggerProps={{ "data-action": "prompt-model-variant" }}
variant="ghost"
/>
</TooltipKeybind>
</div>
<TooltipKeybind
placement="top"
gutter={4}
title={language.t("command.model.variant.cycle")}
keybind={command.keybind("model.variant.cycle")}
>
<Select
size="normal"
options={variants()}
current={local.model.variant.current() ?? "default"}
label={(x) => (x === "default" ? language.t("common.default") : x)}
onSelect={(value) => {
local.model.variant.set(value === "default" ? undefined : value)
restoreFocus()
}}
class="capitalize max-w-[160px] text-text-base"
valueClass="truncate text-13-regular text-text-base"
triggerStyle={control()}
triggerProps={{ "data-action": "prompt-model-variant" }}
variant="ghost"
/>
</TooltipKeybind>
</div>
</Show>
</Show>
</Show>
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ describe("promptPlaceholder", () => {
suggest: true,
t,
})
expect(value).toBe("prompt.placeholder.shell")
expect(value).toBe("prompt.placeholder.shell:example")
})

test("returns summarize placeholders for comment context", () => {
Expand Down
2 changes: 1 addition & 1 deletion packages/app/src/components/prompt-input/placeholder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ type PromptPlaceholderInput = {
}

export function promptPlaceholder(input: PromptPlaceholderInput) {
if (input.mode === "shell") return input.t("prompt.placeholder.shell")
if (input.mode === "shell") return input.t("prompt.placeholder.shell", { example: input.example })
if (input.commentCount > 1) return input.t("prompt.placeholder.summarizeComments")
if (input.commentCount === 1) return input.t("prompt.placeholder.summarizeComment")
if (!input.suggest) return input.t("prompt.placeholder.simple")
Expand Down
38 changes: 18 additions & 20 deletions packages/app/src/components/settings-general.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -128,27 +128,25 @@ export const SettingsGeneral: Component = () => {
return
}

const actions =
platform.update && platform.restart
? [
{
label: language.t("toast.update.action.installRestart"),
onClick: async () => {
await platform.update!()
await platform.restart!()
},
const actions = platform.updateAndRestart
? [
{
label: language.t("toast.update.action.installRestart"),
onClick: async () => {
await platform.updateAndRestart!()
},
{
label: language.t("toast.update.action.notYet"),
onClick: "dismiss" as const,
},
]
: [
{
label: language.t("toast.update.action.notYet"),
onClick: "dismiss" as const,
},
]
},
{
label: language.t("toast.update.action.notYet"),
onClick: "dismiss" as const,
},
]
: [
{
label: language.t("toast.update.action.notYet"),
onClick: "dismiss" as const,
},
]

showToast({
persistent: true,
Expand Down
6 changes: 3 additions & 3 deletions packages/app/src/context/platform.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -49,11 +49,11 @@ export type Platform = {
/** Storage mechanism, defaults to localStorage */
storage?: (name?: string) => SyncStorage | AsyncStorage

/** Check for updates (Tauri only) */
/** Check for a downloadable desktop update */
checkUpdate?(): Promise<UpdateInfo>

/** Install updates (Tauri only) */
update?(): Promise<void>
/** Install the downloaded update using the platform restart flow */
updateAndRestart?(): Promise<void>

/** Fetch override */
fetch?: typeof fetch
Expand Down
2 changes: 1 addition & 1 deletion packages/app/src/i18n/ar.ts

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion packages/app/src/i18n/br.ts

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion packages/app/src/i18n/bs.ts

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion packages/app/src/i18n/da.ts

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion packages/app/src/i18n/de.ts

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion packages/app/src/i18n/en.ts
Original file line number Diff line number Diff line change
Expand Up @@ -230,7 +230,7 @@ export const dict = {
"common.default": "Default",
"common.attachment": "attachment",

"prompt.placeholder.shell": "Enter shell command...",
"prompt.placeholder.shell": "Enter shell command... {{example}}",
"prompt.placeholder.normal": 'Ask anything... "{{example}}"',
"prompt.placeholder.simple": "Ask anything...",
"prompt.placeholder.summarizeComments": "Summarize comments…",
Expand Down
2 changes: 1 addition & 1 deletion packages/app/src/i18n/es.ts

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading