From 8be33032b906f27c0d883212354f75eeb7044f35 Mon Sep 17 00:00:00 2001 From: Christiaan Arnoldus Date: Mon, 3 Aug 2026 10:18:00 +0200 Subject: [PATCH 1/3] fix(cli): omit persona from generated names --- .changeset/quiet-metadata-generators.md | 5 + .../opencode/src/kilocode/system-prompt.ts | 4 + packages/opencode/src/session/llm/request.ts | 12 +- .../test/kilocode/session-llm-request.test.ts | 127 ++++++++++++++++++ 4 files changed, 143 insertions(+), 5 deletions(-) create mode 100644 .changeset/quiet-metadata-generators.md create mode 100644 packages/opencode/test/kilocode/session-llm-request.test.ts diff --git a/.changeset/quiet-metadata-generators.md b/.changeset/quiet-metadata-generators.md new file mode 100644 index 000000000000..67bb87bcbd97 --- /dev/null +++ b/.changeset/quiet-metadata-generators.md @@ -0,0 +1,5 @@ +--- +"@kilocode/cli": patch +--- + +Keep Kilo's persona out of generated conversation titles and Agent Manager branch names. diff --git a/packages/opencode/src/kilocode/system-prompt.ts b/packages/opencode/src/kilocode/system-prompt.ts index 9e2e393471c5..7733f592ba63 100644 --- a/packages/opencode/src/kilocode/system-prompt.ts +++ b/packages/opencode/src/kilocode/system-prompt.ts @@ -13,6 +13,10 @@ import * as Log from "@opencode-ai/core/util/log" const log = Log.create({ service: "kilocode.system-prompt" }) export namespace KilocodeSystemPrompt { + export function persona(agent: string) { + return agent !== "title" && agent !== "branch-name" + } + export function environment(input: { ctx: InstanceContext; model: Provider.Model; editor?: EditorContext }) { return [ [ diff --git a/packages/opencode/src/session/llm/request.ts b/packages/opencode/src/session/llm/request.ts index 8e8bc2541230..07b77e3e708d 100644 --- a/packages/opencode/src/session/llm/request.ts +++ b/packages/opencode/src/session/llm/request.ts @@ -27,6 +27,7 @@ import { import { Identity } from "@kilocode/kilo-telemetry" import { KiloSession } from "@/kilocode/session" import { stripInternalOptions } from "@/kilocode/agent/options" +import { KilocodeSystemPrompt } from "@/kilocode/system-prompt" // kilocode_change end type PrepareInput = { @@ -67,10 +68,11 @@ const mergeOptions = (target: Record, source: Record | export const prepare = Effect.fn("LLMRequestPrep.prepare")(function* (input: PrepareInput) { const isOpenaiOauth = input.provider.id === "openai" && input.auth?.type === "oauth" + const persona = KilocodeSystemPrompt.persona(input.agent.name) // kilocode_change const system = [ [ // kilocode_change start - soul defines core identity and personality - ...(isOpenaiOauth ? [] : [SystemPrompt.soul()]), + ...(isOpenaiOauth || !persona ? [] : [SystemPrompt.soul()]), // kilocode_change end ...(input.agent.prompt ? [input.agent.prompt] : SystemPrompt.provider(input.model)), ...input.system, @@ -116,10 +118,10 @@ export const prepare = Effect.fn("LLMRequestPrep.prepare")(function* (input: Pre delete options.include } if (isOpenaiOauth) { - // kilocode_change start - prepend soul to instructions - options.instructions = SystemPrompt.soul() + "\n" + system.join("\n") - // kilocode_change end -} + // kilocode_change start - prepend soul to instructions + options.instructions = [...(persona ? [SystemPrompt.soul()] : []), ...system].join("\n") + // kilocode_change end + } const messages = isOpenaiOauth || input.isWorkflow diff --git a/packages/opencode/test/kilocode/session-llm-request.test.ts b/packages/opencode/test/kilocode/session-llm-request.test.ts new file mode 100644 index 000000000000..cef2db3a4e21 --- /dev/null +++ b/packages/opencode/test/kilocode/session-llm-request.test.ts @@ -0,0 +1,127 @@ +import { describe, expect, test } from "bun:test" +import { Effect } from "effect" +import type { ModelMessage } from "ai" +import { ProviderV2 } from "@opencode-ai/core/provider" +import { ModelV2 } from "@opencode-ai/core/model" +import { SessionV1 } from "@opencode-ai/core/v1/session" +import type { Agent } from "@/agent/agent" +import type { Auth } from "@/auth" +import { RuntimeFlags } from "@/effect/runtime-flags" +import type { Plugin } from "@/plugin" +import type { Provider } from "@/provider/provider" +import { LLMRequestPrep } from "@/session/llm/request" +import { MessageID, SessionID } from "@/session/schema" +import { SystemPrompt } from "@/session/system" + +const model: Provider.Model = { + id: ModelV2.ID.make("test-model"), + providerID: ProviderV2.ID.make("test"), + api: { + id: "test-model", + url: "https://example.com/v1", + npm: "@ai-sdk/openai", + }, + name: "Test model", + capabilities: { + temperature: true, + reasoning: false, + attachment: false, + toolcall: true, + input: { text: true, audio: false, image: false, video: false, pdf: false }, + output: { text: true, audio: false, image: false, video: false, pdf: false }, + interleaved: false, + }, + cost: { input: 0, output: 0, cache: { read: 0, write: 0 } }, + limit: { context: 128_000, output: 32_000 }, + status: "active", + options: {}, + headers: {}, + release_date: "2026-01-01", +} + +const plugin: Plugin.Interface = { + init: () => Effect.void, + trigger: (_name, _input, output) => Effect.succeed(output), + list: () => Effect.succeed([]), +} + +function agent(name: string): Agent.Info { + return { + name, + mode: "primary", + options: {}, + permission: [], + prompt: `${name} generation prompt`, + } +} + +function user(name: string): SessionV1.User { + return { + id: MessageID.make("msg_test"), + sessionID: SessionID.make("ses_test"), + role: "user", + time: { created: Date.now() }, + agent: name, + model: { providerID: model.providerID, modelID: model.id }, + system: "request-specific system text", + } +} + +async function prepare(name: string, oauth = false) { + const auth: Auth.Info | undefined = oauth + ? { type: "oauth", refresh: "refresh", access: "access", expires: Date.now() + 60_000 } + : undefined + const provider: Provider.Info = { + id: ProviderV2.ID.make(oauth ? "openai" : "test"), + name: "Test provider", + source: "config", + env: [], + options: {}, + models: {}, + } + const flags = await Effect.runPromise( + RuntimeFlags.Service.pipe(Effect.provide(RuntimeFlags.layer({ client: "test" }))), + ) + return Effect.runPromise( + LLMRequestPrep.prepare({ + user: user(name), + sessionID: "ses_test", + model, + agent: agent(name), + system: [], + messages: [{ role: "user", content: "Generate a name" }] satisfies ModelMessage[], + tools: {}, + provider, + auth, + plugin, + flags, + isWorkflow: false, + }), + ) +} + +describe("Kilo persona in generated metadata requests", () => { + test.each(["title", "branch-name"])("omits the persona for %s generation", async (name) => { + const result = await prepare(name) + + expect(result.system[0]).toContain(`${name} generation prompt`) + expect(result.system[0]).toContain("request-specific system text") + expect(result.system[0]).not.toContain(SystemPrompt.soul()) + }) + + test.each(["title", "branch-name"])("omits the persona from OpenAI OAuth %s generation", async (name) => { + const result = await prepare(name, true) + + expect(result.params.options.instructions).toContain(`${name} generation prompt`) + expect(result.params.options.instructions).toContain("request-specific system text") + expect(result.params.options.instructions).not.toContain(SystemPrompt.soul()) + }) + + test("keeps the persona for ordinary agent requests", async () => { + const result = await prepare("code") + const oauth = await prepare("code", true) + + expect(result.system[0]).toContain(SystemPrompt.soul()) + expect(oauth.params.options.instructions).toContain(SystemPrompt.soul()) + }) +}) From 0cfd301745d9483d6a506206731d2331c0e231af Mon Sep 17 00:00:00 2001 From: Christiaan Arnoldus Date: Mon, 3 Aug 2026 10:31:22 +0200 Subject: [PATCH 2/3] refactor(cli): clarify generated-name persona policy --- packages/opencode/src/agent/agent.ts | 3 ++- packages/opencode/src/kilocode/branch-name.ts | 3 ++- .../opencode/src/kilocode/system-prompt.ts | 9 +++++++-- packages/opencode/src/session/llm/request.ts | 6 +++--- .../test/kilocode/session-llm-request.test.ts | 18 +++++++++++------- 5 files changed, 25 insertions(+), 14 deletions(-) diff --git a/packages/opencode/src/agent/agent.ts b/packages/opencode/src/agent/agent.ts index b3df2c7ae528..20aa52ef5aa8 100644 --- a/packages/opencode/src/agent/agent.ts +++ b/packages/opencode/src/agent/agent.ts @@ -32,6 +32,7 @@ import * as KiloAgent from "@/kilocode/agent" import { RuntimeFlags } from "@/effect/runtime-flags" import * as AgentRequirements from "@/kilocode/agent-requirements" import * as KiloReference from "@/kilocode/reference" +import { KilocodeSystemPrompt } from "@/kilocode/system-prompt" import { MCP } from "@/mcp" // kilocode_change end import { ProviderV2 } from "@opencode-ai/core/provider" @@ -309,7 +310,7 @@ export const layer = Layer.effect( options: {}, }, title: { - name: "title", + name: KilocodeSystemPrompt.agents.title, // kilocode_change mode: "primary", options: {}, native: true, diff --git a/packages/opencode/src/kilocode/branch-name.ts b/packages/opencode/src/kilocode/branch-name.ts index a6bb008bcf33..cd5d2daa4476 100644 --- a/packages/opencode/src/kilocode/branch-name.ts +++ b/packages/opencode/src/kilocode/branch-name.ts @@ -6,6 +6,7 @@ import { Provider } from "@/provider/provider" import { LLM } from "@/session/llm" import { MessageV2 } from "@/session/message-v2" import { MessageID, SessionID } from "@/session/schema" +import { KilocodeSystemPrompt } from "@/kilocode/system-prompt" import { Effect } from "effect" const LIMIT = 4 @@ -85,7 +86,7 @@ export const generate = Effect.fn("BranchName.generate")(function* (input: { const model = (yield* provider.getSmallModel(ref.providerID)) ?? (yield* provider.getModel(ref.providerID, ref.modelID)) const agent: Agent.Info = { - name: "branch-name", + name: KilocodeSystemPrompt.agents.branch, mode: "primary", hidden: true, options: {}, diff --git a/packages/opencode/src/kilocode/system-prompt.ts b/packages/opencode/src/kilocode/system-prompt.ts index 7733f592ba63..a1471116019d 100644 --- a/packages/opencode/src/kilocode/system-prompt.ts +++ b/packages/opencode/src/kilocode/system-prompt.ts @@ -13,8 +13,13 @@ import * as Log from "@opencode-ai/core/util/log" const log = Log.create({ service: "kilocode.system-prompt" }) export namespace KilocodeSystemPrompt { - export function persona(agent: string) { - return agent !== "title" && agent !== "branch-name" + export const agents = { + title: "title", + branch: "branch-name", + } as const + + export function shouldIncludePersona(agent: string) { + return agent !== agents.title && agent !== agents.branch } export function environment(input: { ctx: InstanceContext; model: Provider.Model; editor?: EditorContext }) { diff --git a/packages/opencode/src/session/llm/request.ts b/packages/opencode/src/session/llm/request.ts index 07b77e3e708d..4bb5b81e70eb 100644 --- a/packages/opencode/src/session/llm/request.ts +++ b/packages/opencode/src/session/llm/request.ts @@ -68,11 +68,11 @@ const mergeOptions = (target: Record, source: Record | export const prepare = Effect.fn("LLMRequestPrep.prepare")(function* (input: PrepareInput) { const isOpenaiOauth = input.provider.id === "openai" && input.auth?.type === "oauth" - const persona = KilocodeSystemPrompt.persona(input.agent.name) // kilocode_change + const includePersona = KilocodeSystemPrompt.shouldIncludePersona(input.agent.name) // kilocode_change const system = [ [ // kilocode_change start - soul defines core identity and personality - ...(isOpenaiOauth || !persona ? [] : [SystemPrompt.soul()]), + ...(isOpenaiOauth || !includePersona ? [] : [SystemPrompt.soul()]), // kilocode_change end ...(input.agent.prompt ? [input.agent.prompt] : SystemPrompt.provider(input.model)), ...input.system, @@ -119,7 +119,7 @@ export const prepare = Effect.fn("LLMRequestPrep.prepare")(function* (input: Pre } if (isOpenaiOauth) { // kilocode_change start - prepend soul to instructions - options.instructions = [...(persona ? [SystemPrompt.soul()] : []), ...system].join("\n") + options.instructions = [...(includePersona ? [SystemPrompt.soul()] : []), ...system].join("\n") // kilocode_change end } diff --git a/packages/opencode/test/kilocode/session-llm-request.test.ts b/packages/opencode/test/kilocode/session-llm-request.test.ts index cef2db3a4e21..2919ca14d9f0 100644 --- a/packages/opencode/test/kilocode/session-llm-request.test.ts +++ b/packages/opencode/test/kilocode/session-llm-request.test.ts @@ -9,6 +9,7 @@ import type { Auth } from "@/auth" import { RuntimeFlags } from "@/effect/runtime-flags" import type { Plugin } from "@/plugin" import type { Provider } from "@/provider/provider" +import { KilocodeSystemPrompt } from "@/kilocode/system-prompt" import { LLMRequestPrep } from "@/session/llm/request" import { MessageID, SessionID } from "@/session/schema" import { SystemPrompt } from "@/session/system" @@ -101,7 +102,7 @@ async function prepare(name: string, oauth = false) { } describe("Kilo persona in generated metadata requests", () => { - test.each(["title", "branch-name"])("omits the persona for %s generation", async (name) => { + test.each(Object.values(KilocodeSystemPrompt.agents))("omits the persona for %s generation", async (name) => { const result = await prepare(name) expect(result.system[0]).toContain(`${name} generation prompt`) @@ -109,13 +110,16 @@ describe("Kilo persona in generated metadata requests", () => { expect(result.system[0]).not.toContain(SystemPrompt.soul()) }) - test.each(["title", "branch-name"])("omits the persona from OpenAI OAuth %s generation", async (name) => { - const result = await prepare(name, true) + test.each(Object.values(KilocodeSystemPrompt.agents))( + "omits the persona from OpenAI OAuth %s generation", + async (name) => { + const result = await prepare(name, true) - expect(result.params.options.instructions).toContain(`${name} generation prompt`) - expect(result.params.options.instructions).toContain("request-specific system text") - expect(result.params.options.instructions).not.toContain(SystemPrompt.soul()) - }) + expect(result.params.options.instructions).toContain(`${name} generation prompt`) + expect(result.params.options.instructions).toContain("request-specific system text") + expect(result.params.options.instructions).not.toContain(SystemPrompt.soul()) + }, + ) test("keeps the persona for ordinary agent requests", async () => { const result = await prepare("code") From 323f096e11512549c7a87a570380ee732e053006 Mon Sep 17 00:00:00 2001 From: Christiaan Arnoldus Date: Mon, 3 Aug 2026 10:53:57 +0200 Subject: [PATCH 3/3] refactor(cli): revert shared metadata identifiers --- packages/opencode/src/agent/agent.ts | 3 +-- packages/opencode/src/kilocode/branch-name.ts | 3 +-- .../opencode/src/kilocode/system-prompt.ts | 7 +------ .../test/kilocode/session-llm-request.test.ts | 18 +++++++----------- 4 files changed, 10 insertions(+), 21 deletions(-) diff --git a/packages/opencode/src/agent/agent.ts b/packages/opencode/src/agent/agent.ts index 20aa52ef5aa8..b3df2c7ae528 100644 --- a/packages/opencode/src/agent/agent.ts +++ b/packages/opencode/src/agent/agent.ts @@ -32,7 +32,6 @@ import * as KiloAgent from "@/kilocode/agent" import { RuntimeFlags } from "@/effect/runtime-flags" import * as AgentRequirements from "@/kilocode/agent-requirements" import * as KiloReference from "@/kilocode/reference" -import { KilocodeSystemPrompt } from "@/kilocode/system-prompt" import { MCP } from "@/mcp" // kilocode_change end import { ProviderV2 } from "@opencode-ai/core/provider" @@ -310,7 +309,7 @@ export const layer = Layer.effect( options: {}, }, title: { - name: KilocodeSystemPrompt.agents.title, // kilocode_change + name: "title", mode: "primary", options: {}, native: true, diff --git a/packages/opencode/src/kilocode/branch-name.ts b/packages/opencode/src/kilocode/branch-name.ts index cd5d2daa4476..a6bb008bcf33 100644 --- a/packages/opencode/src/kilocode/branch-name.ts +++ b/packages/opencode/src/kilocode/branch-name.ts @@ -6,7 +6,6 @@ import { Provider } from "@/provider/provider" import { LLM } from "@/session/llm" import { MessageV2 } from "@/session/message-v2" import { MessageID, SessionID } from "@/session/schema" -import { KilocodeSystemPrompt } from "@/kilocode/system-prompt" import { Effect } from "effect" const LIMIT = 4 @@ -86,7 +85,7 @@ export const generate = Effect.fn("BranchName.generate")(function* (input: { const model = (yield* provider.getSmallModel(ref.providerID)) ?? (yield* provider.getModel(ref.providerID, ref.modelID)) const agent: Agent.Info = { - name: KilocodeSystemPrompt.agents.branch, + name: "branch-name", mode: "primary", hidden: true, options: {}, diff --git a/packages/opencode/src/kilocode/system-prompt.ts b/packages/opencode/src/kilocode/system-prompt.ts index a1471116019d..d10d34d5458d 100644 --- a/packages/opencode/src/kilocode/system-prompt.ts +++ b/packages/opencode/src/kilocode/system-prompt.ts @@ -13,13 +13,8 @@ import * as Log from "@opencode-ai/core/util/log" const log = Log.create({ service: "kilocode.system-prompt" }) export namespace KilocodeSystemPrompt { - export const agents = { - title: "title", - branch: "branch-name", - } as const - export function shouldIncludePersona(agent: string) { - return agent !== agents.title && agent !== agents.branch + return agent !== "title" && agent !== "branch-name" } export function environment(input: { ctx: InstanceContext; model: Provider.Model; editor?: EditorContext }) { diff --git a/packages/opencode/test/kilocode/session-llm-request.test.ts b/packages/opencode/test/kilocode/session-llm-request.test.ts index 2919ca14d9f0..cef2db3a4e21 100644 --- a/packages/opencode/test/kilocode/session-llm-request.test.ts +++ b/packages/opencode/test/kilocode/session-llm-request.test.ts @@ -9,7 +9,6 @@ import type { Auth } from "@/auth" import { RuntimeFlags } from "@/effect/runtime-flags" import type { Plugin } from "@/plugin" import type { Provider } from "@/provider/provider" -import { KilocodeSystemPrompt } from "@/kilocode/system-prompt" import { LLMRequestPrep } from "@/session/llm/request" import { MessageID, SessionID } from "@/session/schema" import { SystemPrompt } from "@/session/system" @@ -102,7 +101,7 @@ async function prepare(name: string, oauth = false) { } describe("Kilo persona in generated metadata requests", () => { - test.each(Object.values(KilocodeSystemPrompt.agents))("omits the persona for %s generation", async (name) => { + test.each(["title", "branch-name"])("omits the persona for %s generation", async (name) => { const result = await prepare(name) expect(result.system[0]).toContain(`${name} generation prompt`) @@ -110,16 +109,13 @@ describe("Kilo persona in generated metadata requests", () => { expect(result.system[0]).not.toContain(SystemPrompt.soul()) }) - test.each(Object.values(KilocodeSystemPrompt.agents))( - "omits the persona from OpenAI OAuth %s generation", - async (name) => { - const result = await prepare(name, true) + test.each(["title", "branch-name"])("omits the persona from OpenAI OAuth %s generation", async (name) => { + const result = await prepare(name, true) - expect(result.params.options.instructions).toContain(`${name} generation prompt`) - expect(result.params.options.instructions).toContain("request-specific system text") - expect(result.params.options.instructions).not.toContain(SystemPrompt.soul()) - }, - ) + expect(result.params.options.instructions).toContain(`${name} generation prompt`) + expect(result.params.options.instructions).toContain("request-specific system text") + expect(result.params.options.instructions).not.toContain(SystemPrompt.soul()) + }) test("keeps the persona for ordinary agent requests", async () => { const result = await prepare("code")