From 2ffbfa0348c50f4adfdf378d1be1aed040af726c Mon Sep 17 00:00:00 2001 From: marius-kilocode Date: Mon, 29 Jun 2026 11:51:17 +0200 Subject: [PATCH] fix(agent-manager): scope branch naming to sessions --- .../server/httpapi/groups/branch-name.ts | 8 ++++-- .../server/httpapi/handlers/branch-name.ts | 10 +++++-- .../src/kilocode/server/httpapi/public.ts | 5 ++++ .../test/kilocode/branch-name.test.ts | 6 ++++ .../server/httpapi-exercise-scenarios.ts | 8 ++++++ .../kilocode/server/httpapi-public.test.ts | 11 ++++++++ packages/sdk/js/src/v2/gen/sdk.gen.ts | 8 +++--- packages/sdk/js/src/v2/gen/types.gen.ts | 9 +++--- packages/sdk/openapi.json | 28 +++++++++++++------ 9 files changed, 71 insertions(+), 22 deletions(-) diff --git a/packages/opencode/src/kilocode/server/httpapi/groups/branch-name.ts b/packages/opencode/src/kilocode/server/httpapi/groups/branch-name.ts index 68c84cf9db4..13eaa9bca90 100644 --- a/packages/opencode/src/kilocode/server/httpapi/groups/branch-name.ts +++ b/packages/opencode/src/kilocode/server/httpapi/groups/branch-name.ts @@ -10,10 +10,11 @@ import { described } from "@/server/routes/instance/httpapi/groups/metadata" import { Schema } from "effect" import { HttpApi, HttpApiEndpoint, HttpApiGroup, OpenApi } from "effect/unstable/httpapi" -const root = "/branch-name" +export const BranchNamePaths = { + generate: "/session/:sessionID/branch-name", +} as const export const BranchNamePayload = Schema.Struct({ - sessionID: SessionID, prompt: Schema.String, providerID: Schema.optional(ProviderID), modelID: Schema.optional(ModelID), @@ -27,7 +28,8 @@ export const BranchNameApi = HttpApi.make("branch-name") .add( HttpApiGroup.make("branch-name") .add( - HttpApiEndpoint.post("generate", root, { + HttpApiEndpoint.post("generate", BranchNamePaths.generate, { + params: { sessionID: SessionID }, query: WorkspaceRoutingQuery, payload: BranchNamePayload, success: described(BranchNameResponse, "Generated branch name or null when the task is not clear yet"), diff --git a/packages/opencode/src/kilocode/server/httpapi/handlers/branch-name.ts b/packages/opencode/src/kilocode/server/httpapi/handlers/branch-name.ts index 70970828041..4ca15f8a20e 100644 --- a/packages/opencode/src/kilocode/server/httpapi/handlers/branch-name.ts +++ b/packages/opencode/src/kilocode/server/httpapi/handlers/branch-name.ts @@ -1,6 +1,7 @@ import * as Log from "@opencode-ai/core/util/log" import { generate, messages } from "@/kilocode/branch-name" import { Session } from "@/session/session" +import { SessionID } from "@/session/schema" import { InstanceHttpApi } from "@/server/routes/instance/httpapi/api" import { Cause, Effect, Option } from "effect" import { HttpApiBuilder } from "effect/unstable/httpapi" @@ -12,11 +13,14 @@ export const branchNameHandlers = HttpApiBuilder.group(InstanceHttpApi, "branch- Effect.gen(function* () { const session = yield* Session.Service - const handle = Effect.fn("BranchNameHttpApi.generate")(function* (ctx: { payload: typeof BranchNamePayload.Type }) { + const handle = Effect.fn("BranchNameHttpApi.generate")(function* (ctx: { + params: { sessionID: SessionID } + payload: typeof BranchNamePayload.Type + }) { const branch = yield* Effect.gen(function* () { - const history = yield* session.messages({ sessionID: ctx.payload.sessionID }) + const history = yield* session.messages({ sessionID: ctx.params.sessionID }) return yield* generate({ - sessionID: ctx.payload.sessionID, + sessionID: ctx.params.sessionID, messages: messages(history, ctx.payload.prompt), providerID: ctx.payload.providerID, modelID: ctx.payload.modelID, diff --git a/packages/opencode/src/kilocode/server/httpapi/public.ts b/packages/opencode/src/kilocode/server/httpapi/public.ts index 2f2340aa970..9d0ba57ef16 100644 --- a/packages/opencode/src/kilocode/server/httpapi/public.ts +++ b/packages/opencode/src/kilocode/server/httpapi/public.ts @@ -79,6 +79,11 @@ export function matchLegacyKiloOpenApi(input: Record) { const pty = spec.components?.schemas?.Pty?.properties if (pty?.sessionID) pty.sessionID = nullable(pty.sessionID) + const out = spec.paths?.["/session/{sessionID}/branch-name"]?.post?.responses?.["200"]?.content?.[ + "application/json" + ]?.schema?.properties + if (out?.branch) out.branch = nullable(out.branch) + const update = spec.paths?.["/pty/{ptyID}"]?.put?.requestBody?.content?.["application/json"]?.schema const name = update?.$ref?.replace("#/components/schemas/", "") const fields = name ? spec.components?.schemas?.[name]?.properties : update?.properties diff --git a/packages/opencode/test/kilocode/branch-name.test.ts b/packages/opencode/test/kilocode/branch-name.test.ts index f1415362b87..d0d09a59b1c 100644 --- a/packages/opencode/test/kilocode/branch-name.test.ts +++ b/packages/opencode/test/kilocode/branch-name.test.ts @@ -64,4 +64,10 @@ describe("branch name generation helpers", () => { const history = [user("one"), user("two"), user("three"), user("four"), user("five")] expect(messages(history, "six")).toEqual(["three", "four", "five", "six"]) }) + + test("truncates large messages before generation", () => { + const big = "x".repeat(2_000) + + expect(messages([user(big)], big)).toEqual(["x".repeat(1_000)]) + }) }) diff --git a/packages/opencode/test/kilocode/server/httpapi-exercise-scenarios.ts b/packages/opencode/test/kilocode/server/httpapi-exercise-scenarios.ts index 35ea6faf154..4029bda2a0b 100644 --- a/packages/opencode/test/kilocode/server/httpapi-exercise-scenarios.ts +++ b/packages/opencode/test/kilocode/server/httpapi-exercise-scenarios.ts @@ -257,6 +257,14 @@ export const kiloScenarios: Scenario[] = [ .post("/commit-message", "commitMessage.generate") .at((ctx) => ({ path: "/commit-message", headers: ctx.headers(), body: {} })) .status(400), + http.protected + .post("/session/{sessionID}/branch-name", "branchName.generate") + .at((ctx) => ({ + path: route("/session/{sessionID}/branch-name", { sessionID: "ses_httpapi_missing" }), + headers: ctx.headers(), + body: {}, + })) + .status(400), http.protected .post("/enhance-prompt", "enhancePrompt.enhance") .at((ctx) => ({ path: "/enhance-prompt", headers: ctx.headers(), body: { text: "" } })) diff --git a/packages/opencode/test/kilocode/server/httpapi-public.test.ts b/packages/opencode/test/kilocode/server/httpapi-public.test.ts index ca260197ec8..b0436ffe77a 100644 --- a/packages/opencode/test/kilocode/server/httpapi-public.test.ts +++ b/packages/opencode/test/kilocode/server/httpapi-public.test.ts @@ -3,6 +3,7 @@ import { Result, Schema as EffectSchema } from "effect" import { OpenApi } from "effect/unstable/httpapi" import { AgentBuilderPaths } from "../../../src/kilocode/server/httpapi/groups/agent-builder" import { BackgroundProcessPaths } from "../../../src/kilocode/server/httpapi/groups/background-process" +import { BranchNamePaths } from "../../../src/kilocode/server/httpapi/groups/branch-name" import { ConfigConsolePaths } from "../../../src/kilocode/server/httpapi/groups/config-console" import { IndexingPaths, KiloEmbeddingModel } from "../../../src/kilocode/server/httpapi/groups/indexing" import { KiloGatewayPaths } from "../../../src/kilocode/server/httpapi/groups/kilo-gateway" @@ -138,6 +139,7 @@ describe("Kilo PublicApi OpenAPI contract", () => { { method: "get", path: ConfigConsolePaths.tuiKeybinds }, { method: "patch", path: ConfigConsolePaths.tuiConfig }, { method: "get", path: KilocodePaths.sessionModelUsage }, + { method: "post", path: BranchNamePaths.generate }, ] satisfies Array<{ method: Method; path: string }> for (const route of routes) { @@ -157,6 +159,15 @@ describe("Kilo PublicApi OpenAPI contract", () => { expect(props?.organizationId).toEqual({ anyOf: [{ type: "string" }, { type: "null" }] }) }) + test("keeps branch-name responses nullable", () => { + const spec = OpenApi.fromApi(PublicApi) + const path = BranchNamePaths.generate.replace(/:([A-Za-z0-9_]+)/g, "{$1}") + const body = spec.paths[path]?.post?.responses?.["200"] as Body | undefined + const branch = body?.content?.["application/json"]?.schema?.properties?.branch + + expect(branch).toEqual({ anyOf: [{ type: "string" }, { type: "null" }] }) + }) + test("keeps Kilo gateway responses nullable", () => { const spec = OpenApi.fromApi(PublicApi) const response = (path: string) => { diff --git a/packages/sdk/js/src/v2/gen/sdk.gen.ts b/packages/sdk/js/src/v2/gen/sdk.gen.ts index 5d43a5ec200..8398a49d165 100644 --- a/packages/sdk/js/src/v2/gen/sdk.gen.ts +++ b/packages/sdk/js/src/v2/gen/sdk.gen.ts @@ -6244,10 +6244,10 @@ export class BranchName extends HeyApiClient { * Generate a task-focused branch name from the current conversation. */ public generate( - parameters?: { + parameters: { + sessionID: string directory?: string workspace?: string - sessionID?: string prompt?: string providerID?: string modelID?: string @@ -6259,9 +6259,9 @@ export class BranchName extends HeyApiClient { [ { args: [ + { in: "path", key: "sessionID" }, { in: "query", key: "directory" }, { in: "query", key: "workspace" }, - { in: "body", key: "sessionID" }, { in: "body", key: "prompt" }, { in: "body", key: "providerID" }, { in: "body", key: "modelID" }, @@ -6270,7 +6270,7 @@ export class BranchName extends HeyApiClient { ], ) return (options?.client ?? this.client).post({ - url: "/branch-name", + url: "/session/{sessionID}/branch-name", ...options, ...params, headers: { diff --git a/packages/sdk/js/src/v2/gen/types.gen.ts b/packages/sdk/js/src/v2/gen/types.gen.ts index da0db21ede5..50b63231bc1 100644 --- a/packages/sdk/js/src/v2/gen/types.gen.ts +++ b/packages/sdk/js/src/v2/gen/types.gen.ts @@ -9632,17 +9632,18 @@ export type BackgroundProcessStopSessionResponse = export type BranchNameGenerateData = { body?: { - sessionID: string prompt: string providerID?: string modelID?: string } - path?: never + path: { + sessionID: string + } query?: { directory?: string workspace?: string } - url: "/branch-name" + url: "/session/{sessionID}/branch-name" } export type BranchNameGenerateErrors = { @@ -9659,7 +9660,7 @@ export type BranchNameGenerateResponses = { * Generated branch name or null when the task is not clear yet */ 200: { - branch: string + branch: string | null } } diff --git a/packages/sdk/openapi.json b/packages/sdk/openapi.json index d30053d7864..01b21577540 100644 --- a/packages/sdk/openapi.json +++ b/packages/sdk/openapi.json @@ -11800,11 +11800,20 @@ ] } }, - "/branch-name": { + "/session/{sessionID}/branch-name": { "post": { "tags": ["branch-name"], "operationId": "branchName.generate", "parameters": [ + { + "name": "sessionID", + "in": "path", + "schema": { + "type": "string", + "pattern": "^ses.*" + }, + "required": true + }, { "name": "directory", "in": "query", @@ -11831,7 +11840,14 @@ "type": "object", "properties": { "branch": { - "type": "string" + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] } }, "required": ["branch"], @@ -11860,10 +11876,6 @@ "schema": { "type": "object", "properties": { - "sessionID": { - "type": "string", - "pattern": "^ses" - }, "prompt": { "type": "string" }, @@ -11874,7 +11886,7 @@ "type": "string" } }, - "required": ["sessionID", "prompt"], + "required": ["prompt"], "additionalProperties": false } } @@ -11883,7 +11895,7 @@ "x-codeSamples": [ { "lang": "js", - "source": "import { createKiloClient } from \"@kilocode/sdk\n\nconst client = createKiloClient()\nawait client.branchName.generate({\n ...\n})" + "source": "import { createKiloClient } from \"@kilocode/sdk\"\n\nconst client = createKiloClient()\nawait client.branchName.generate({\n ...\n})" } ] }