diff --git a/.changeset/board-env-flag.md b/.changeset/board-env-flag.md new file mode 100644 index 00000000000..1ffc362c873 --- /dev/null +++ b/.changeset/board-env-flag.md @@ -0,0 +1,5 @@ +--- +"@kilocode/cli": patch +--- + +Enable the experimental shared agent board (Kilo Swarm) with the `KILO_EXPERIMENTAL_SHARED_AGENT_BOARD` environment variable, or the umbrella `KILO_EXPERIMENTAL`, in addition to the `experimental.shared_agent_board` config key. diff --git a/packages/opencode/src/agent/agent.ts b/packages/opencode/src/agent/agent.ts index 367af349b9d..6542066e737 100644 --- a/packages/opencode/src/agent/agent.ts +++ b/packages/opencode/src/agent/agent.ts @@ -158,7 +158,7 @@ const layer = Layer.effect( }) // kilocode_change start - patch defaults with bash allowlist and recall permission - const kilo = KiloAgent.prepare(cfg) + const kilo = KiloAgent.prepare(cfg, flags) const defaults = Permission.merge(baseDefaults, kilo.defaultsPatch) // kilocode_change end @@ -325,7 +325,7 @@ const layer = Layer.effect( } // kilocode_change start - rename build→code, add debug/orchestrator/ask, patch plan/explore - KiloAgent.patchAgents(agents, defaults, user, cfg, kilo, ctx.worktree, whitelistedDirs) + KiloAgent.patchAgents(agents, defaults, user, kilo, ctx.worktree, whitelistedDirs) const agentConfigs = KiloAgent.preprocessConfig(cfg.agent ?? {}) for (const [key, value] of Object.entries(agentConfigs)) { diff --git a/packages/opencode/src/effect/runtime-flags.ts b/packages/opencode/src/effect/runtime-flags.ts index f6836c24c94..ac2b2de8c58 100644 --- a/packages/opencode/src/effect/runtime-flags.ts +++ b/packages/opencode/src/effect/runtime-flags.ts @@ -56,6 +56,7 @@ export class Service extends ConfigService.Service()("@opencode/Runtime experimentalCodeMode: enabledByExperimental("KILO_EXPERIMENTAL_CODE_MODE"), experimentalEventSystem: enabledByExperimental("KILO_EXPERIMENTAL_EVENT_SYSTEM"), experimentalSessionSwitcher: enabledByExperimental("KILO_EXPERIMENTAL_SESSION_SWITCHER"), // kilocode_change + experimentalSharedAgentBoard: enabledByExperimental("KILO_EXPERIMENTAL_SHARED_AGENT_BOARD"), // kilocode_change experimentalWorkspaces: enabledByExperimental("KILO_EXPERIMENTAL_WORKSPACES"), experimentalIconDiscovery: enabledByExperimental("KILO_EXPERIMENTAL_ICON_DISCOVERY"), experimentalMcpApps: enabledByExperimental("KILO_EXPERIMENTAL_MCP_APPS"), // kilocode_change diff --git a/packages/opencode/src/kilocode/agent/index.ts b/packages/opencode/src/kilocode/agent/index.ts index 05920c38143..a438f753a49 100644 --- a/packages/opencode/src/kilocode/agent/index.ts +++ b/packages/opencode/src/kilocode/agent/index.ts @@ -10,6 +10,8 @@ import path from "path" import { Global } from "@opencode-ai/core/global" import { Flag } from "@opencode-ai/core/flag/flag" import { applyEdits, modify, parse as parseJsonc } from "jsonc-parser" +import type { RuntimeFlags } from "@/effect/runtime-flags" +import { BoardEnabled } from "@/kilocode/board/enabled" import { KilocodeConfigSources } from "../config/sources" import PROMPT_DEBUG from "../../agent/prompt/debug.txt" @@ -356,14 +358,19 @@ export function getMcpRules(cfg: Config.Info): Record defaultsPatch: Permission.Ruleset + board: boolean } // Prepare kilo-specific data derived from config. Call once per state initialization. -export function prepare(cfg: Config.Info): KiloData { +export function prepare(cfg: Config.Info, flags: Pick): KiloData { const mcpRules = getMcpRules(cfg) + const enabled = BoardEnabled.resolve({ + config: cfg.experimental?.shared_agent_board, + flag: flags.experimentalSharedAgentBoard, + }) const defaultsPatch = Permission.fromConfig({ bash, - ...board(cfg.experimental?.shared_agent_board === true), + ...board(enabled), recall: "ask", ...(Flag.KILO_CLIENT === "vscode" && cfg.experimental?.native_notebook_tools === true ? { notebook_read: "ask" as const, notebook_edit: "ask" as const, notebook_execute: "ask" as const } @@ -372,7 +379,7 @@ export function prepare(cfg: Config.Info): KiloData { kilo_memory_recall: "ask", kilo_memory_save: "ask", }) - return { mcpRules, defaultsPatch } + return { mcpRules, defaultsPatch, board: enabled } } export function cacheKey(cfg: Config.Info) { @@ -489,12 +496,11 @@ export function patchAgents( >, defaults: Permission.Ruleset, user: Permission.Ruleset, - cfg: Config.Info, kilo: KiloData, worktree: string, whitelistedDirs: string[], ) { - const enabled = cfg.experimental?.shared_agent_board === true + const enabled = kilo.board // Rename "build" → "code" for backward compatibility if (agents.build) { agents.code = { diff --git a/packages/opencode/src/kilocode/board/context.ts b/packages/opencode/src/kilocode/board/context.ts index daa2c06ad57..09cabeae0cc 100644 --- a/packages/opencode/src/kilocode/board/context.ts +++ b/packages/opencode/src/kilocode/board/context.ts @@ -2,11 +2,13 @@ import { Cause, Effect } from "effect" import { Database } from "@opencode-ai/core/database/database" import { Config } from "@/config/config" import { Permission } from "@/permission" +import { RuntimeFlags } from "@/effect/runtime-flags" import { Agent } from "@/agent/agent" import { Session } from "@/session/session" import type { MessageV2 } from "@/session/message-v2" import type { Tool } from "@/tool/tool" import { KiloSessionPrompt } from "@/kilocode/session/prompt" +import { BoardEnabled } from "./enabled" import { BoardStore } from "./store" import { BoardNotice } from "./notice" @@ -48,6 +50,7 @@ export namespace BoardContext { export const notifier = Effect.fn("BoardContext.notifier")(function* (input: Input) { const config = yield* Config.Service + const flags = yield* RuntimeFlags.Service const sessions = yield* Session.Service const agents = yield* Agent.Service const database = yield* Database.Service @@ -56,7 +59,14 @@ export namespace BoardContext { Effect.gen(function* () { if (signal?.aborted) return output const cfg = yield* config.get() - if (cfg.experimental?.shared_agent_board !== true) return output + if ( + !BoardEnabled.resolve({ + config: cfg.experimental?.shared_agent_board, + flag: flags.experimentalSharedAgentBoard, + }) + ) { + return output + } const session = yield* sessions.get(input.session.id) const agent = yield* agents.get(input.agent.name, cfg) if (!agent || !allowed({ session, agent, user: input.user })) return output diff --git a/packages/opencode/src/kilocode/board/enabled.ts b/packages/opencode/src/kilocode/board/enabled.ts new file mode 100644 index 00000000000..e9300509903 --- /dev/null +++ b/packages/opencode/src/kilocode/board/enabled.ts @@ -0,0 +1,13 @@ +export namespace BoardEnabled { + /** + * Resolve the effective shared agent board state. + * + * The `experimental.shared_agent_board` config key is the source of truth for + * an explicit enable. The experimental environment flag is an additional + * enable path, so an explicit config `false` does not turn the board off when + * the flag is set. + */ + export function resolve(input: { config?: boolean; flag?: boolean }) { + return input.config === true || input.flag === true + } +} diff --git a/packages/opencode/src/kilocode/tool/board.ts b/packages/opencode/src/kilocode/tool/board.ts index 8c13a78c417..69f3f4912e8 100644 --- a/packages/opencode/src/kilocode/tool/board.ts +++ b/packages/opencode/src/kilocode/tool/board.ts @@ -4,6 +4,8 @@ import { Config } from "@/config/config" import { BackgroundJob } from "@/background/job" import { SessionStatus } from "@/session/status" import { Tool } from "@/tool/tool" +import { RuntimeFlags } from "@/effect/runtime-flags" +import { BoardEnabled } from "@/kilocode/board/enabled" import { BoardStore } from "@/kilocode/board/store" const Read = Schema.Struct({ @@ -66,12 +68,13 @@ const snapshot = Effect.fn("BoardTools.snapshot")(function* ( export const BoardReadTool = Tool.define< typeof Read, ReadMeta, - Config.Service | Database.Service | BackgroundJob.Service | SessionStatus.Service, + Config.Service | Database.Service | BackgroundJob.Service | SessionStatus.Service | RuntimeFlags.Service, "board_read" >( "board_read", Effect.gen(function* () { const config = yield* Config.Service + const flags = yield* RuntimeFlags.Service const database = yield* Database.Service const jobs = yield* BackgroundJob.Service const status = yield* SessionStatus.Service @@ -91,7 +94,12 @@ export const BoardReadTool = Tool.define< execute: (params, ctx) => Effect.gen(function* () { const cfg = yield* config.get() - if (cfg.experimental?.shared_agent_board !== true) { + if ( + !BoardEnabled.resolve({ + config: cfg.experimental?.shared_agent_board, + flag: flags.experimentalSharedAgentBoard, + }) + ) { return yield* Effect.fail( new Error("The shared agent board is disabled. Enable it in Experimental settings."), ) @@ -123,12 +131,13 @@ export const BoardReadTool = Tool.define< export const BoardPostTool = Tool.define< typeof Post, PostMeta, - Config.Service | Database.Service | BackgroundJob.Service | SessionStatus.Service, + Config.Service | Database.Service | BackgroundJob.Service | SessionStatus.Service | RuntimeFlags.Service, "board_post" >( "board_post", Effect.gen(function* () { const config = yield* Config.Service + const flags = yield* RuntimeFlags.Service const database = yield* Database.Service const jobs = yield* BackgroundJob.Service const status = yield* SessionStatus.Service @@ -154,7 +163,12 @@ export const BoardPostTool = Tool.define< execute: (params, ctx) => Effect.gen(function* () { const cfg = yield* config.get() - if (cfg.experimental?.shared_agent_board !== true) { + if ( + !BoardEnabled.resolve({ + config: cfg.experimental?.shared_agent_board, + flag: flags.experimentalSharedAgentBoard, + }) + ) { return yield* Effect.fail( new Error("The shared agent board is disabled. Enable it in Experimental settings."), ) diff --git a/packages/opencode/src/kilocode/tool/registry.ts b/packages/opencode/src/kilocode/tool/registry.ts index 30904432bc0..37ff5b992ff 100644 --- a/packages/opencode/src/kilocode/tool/registry.ts +++ b/packages/opencode/src/kilocode/tool/registry.ts @@ -22,6 +22,8 @@ import { AgentManager, HostError } from "@/kilocode/agent-manager/service" import { KiloSessions } from "@/kilo-sessions/kilo-sessions" import * as Log from "@opencode-ai/core/util/log" import type { Config } from "@/config/config" +import type { RuntimeFlags } from "@/effect/runtime-flags" +import { BoardEnabled } from "@/kilocode/board/enabled" import { Agent } from "@/agent/agent" import * as Truncate from "@/tool/truncate" import { InstanceState } from "@/effect/instance-state" @@ -275,13 +277,16 @@ export namespace KiloToolRegistry { shared_agent_board?: boolean } }, + flags: Pick, ): Tool.Def[] { + const enabled = BoardEnabled.resolve({ + config: cfg.experimental?.shared_agent_board, + flag: flags.experimentalSharedAgentBoard, + }) return [ ...(tools.goalReport ? [tools.goalReport] : []), ...(cfg.experimental?.image_generation === true ? [tools.image] : []), - ...(cfg.experimental?.shared_agent_board === true && tools.boardRead && tools.boardPost - ? [tools.boardRead, tools.boardPost] - : []), + ...(enabled && tools.boardRead && tools.boardPost ? [tools.boardRead, tools.boardPost] : []), ...(tools.semantic ? [tools.semantic] : []), tools.memory, tools.save, diff --git a/packages/opencode/src/session/prompt.ts b/packages/opencode/src/session/prompt.ts index 7eed50e1429..aafcc4adc1b 100644 --- a/packages/opencode/src/session/prompt.ts +++ b/packages/opencode/src/session/prompt.ts @@ -1770,6 +1770,7 @@ export const layer = Layer.effect( Effect.provideService(Database.Service, database), Effect.provideService(Agent.Service, agents), Effect.provideService(Session.Service, sessions), + Effect.provideService(RuntimeFlags.Service, flags), ) : undefined // kilocode_change end diff --git a/packages/opencode/src/session/tools.ts b/packages/opencode/src/session/tools.ts index 044727e1946..27f9104bfe2 100644 --- a/packages/opencode/src/session/tools.ts +++ b/packages/opencode/src/session/tools.ts @@ -30,6 +30,7 @@ import { ModelV2 } from "@opencode-ai/core/model" import { Config } from "@/config/config" import { PermissionProvenance } from "@/kilocode/permission/provenance" import { McpApps } from "@/kilocode/mcp/apps" +import { BoardEnabled } from "@/kilocode/board/enabled" // kilocode_change end import { isRecord } from "@/util/record" import { RuntimeFlags } from "@/effect/runtime-flags" @@ -74,9 +75,15 @@ export const resolve = Effect.fn("SessionTools.resolve")(function* (input: { const truncate = yield* Truncate.Service // kilocode_change start - permission provenance const config = yield* Config.Service + const flags = yield* RuntimeFlags.Service const cfg = yield* config.get() const permissionOrigins = cfg.permission_origins - const notify = cfg.experimental?.shared_agent_board === true ? input.notify : undefined + const notify = BoardEnabled.resolve({ + config: cfg.experimental?.shared_agent_board, + flag: flags.experimentalSharedAgentBoard, + }) + ? input.notify + : undefined type Output = Parameters[1] const finish = (name: string, output: T, opts: ToolExecutionOptions) => Effect.gen(function* () { @@ -91,7 +98,6 @@ export const resolve = Effect.fn("SessionTools.resolve")(function* (input: { return result }) // kilocode_change end - const flags = yield* RuntimeFlags.Service const restricted = yield* SandboxPolicy.networkRestricted(input.session.id) // kilocode_change const sandboxed = (yield* SandboxPolicy.status(input.session.id)).enabled // kilocode_change const context = (args: Record, options: ToolExecutionOptions): Tool.Context => { diff --git a/packages/opencode/src/tool/registry.ts b/packages/opencode/src/tool/registry.ts index 88887c617f3..bd2c9bf94b1 100644 --- a/packages/opencode/src/tool/registry.ts +++ b/packages/opencode/src/tool/registry.ts @@ -307,7 +307,7 @@ const layer = Layer.effect( tool.patch, tool.plan, ...(["cli", "vscode"].includes(flags.client) ? [tool.suggest] : []), - ...KiloToolRegistry.extra(kilo, cfg), + ...KiloToolRegistry.extra(kilo, cfg, flags), ...(tool.execute ? [tool.execute] : []), ...(flags.experimentalLspTool ? [tool.lsp] : []), ], diff --git a/packages/opencode/test/kilocode/board-context.test.ts b/packages/opencode/test/kilocode/board-context.test.ts index 6abc72df02d..3cb3bbabaeb 100644 --- a/packages/opencode/test/kilocode/board-context.test.ts +++ b/packages/opencode/test/kilocode/board-context.test.ts @@ -8,6 +8,7 @@ import { CrossSpawnSpawner } from "@opencode-ai/core/cross-spawn-spawner" import { ModelV2 } from "@opencode-ai/core/model" import { ProviderV2 } from "@opencode-ai/core/provider" import { Config } from "../../src/config/config" +import { RuntimeFlags } from "../../src/effect/runtime-flags" import { Agent } from "../../src/agent/agent" import { Session } from "../../src/session/session" import { SessionStatus } from "../../src/session/status" @@ -33,6 +34,7 @@ const it = testEffect( BackgroundJob.node, SessionProjector.node, Config.node, + RuntimeFlags.node, Database.node, Agent.node, Truncate.node, diff --git a/packages/opencode/test/kilocode/board-enabled.test.ts b/packages/opencode/test/kilocode/board-enabled.test.ts new file mode 100644 index 00000000000..24119209df7 --- /dev/null +++ b/packages/opencode/test/kilocode/board-enabled.test.ts @@ -0,0 +1,65 @@ +import { describe, expect } from "bun:test" +import { ConfigProvider, Effect, Layer } from "effect" +import { AppNodeBuilder } from "@opencode-ai/core/effect/app-node-builder" +import { RuntimeFlags } from "../../src/effect/runtime-flags" +import { BoardEnabled } from "../../src/kilocode/board/enabled" +import { it } from "../lib/effect" + +const fromEnv = (input: Record) => + AppNodeBuilder.build(RuntimeFlags.node).pipe(Layer.provide(ConfigProvider.layer(ConfigProvider.fromUnknown(input)))) + +const resolve = (config: boolean | undefined, input: Record) => + Effect.gen(function* () { + const flags = yield* RuntimeFlags.Service + return BoardEnabled.resolve({ config, flag: flags.experimentalSharedAgentBoard }) + }).pipe(Effect.provide(fromEnv(input))) + +describe("shared agent board enablement", () => { + it.effect("enables when the config key is true", () => + Effect.gen(function* () { + expect(yield* resolve(true, {})).toBe(true) + }), + ) + + it.effect("stays disabled when config is false and the env flag is unset", () => + Effect.gen(function* () { + expect(yield* resolve(false, {})).toBe(false) + expect(yield* resolve(undefined, {})).toBe(false) + }), + ) + + it.effect("enables when the specific env flag is true", () => + Effect.gen(function* () { + expect(yield* resolve(undefined, { KILO_EXPERIMENTAL_SHARED_AGENT_BOARD: "true" })).toBe(true) + }), + ) + + it.effect("stays disabled when the specific env flag is false", () => + Effect.gen(function* () { + expect(yield* resolve(undefined, { KILO_EXPERIMENTAL_SHARED_AGENT_BOARD: "false" })).toBe(false) + }), + ) + + it.effect("enables when the KILO_EXPERIMENTAL umbrella is true", () => + Effect.gen(function* () { + expect(yield* resolve(undefined, { KILO_EXPERIMENTAL: "true" })).toBe(true) + }), + ) + + it.effect("lets the specific flag override the umbrella", () => + Effect.gen(function* () { + expect( + yield* resolve(undefined, { + KILO_EXPERIMENTAL: "true", + KILO_EXPERIMENTAL_SHARED_AGENT_BOARD: "false", + }), + ).toBe(false) + }), + ) + + it.effect("keeps the env path enabled when config is explicitly false", () => + Effect.gen(function* () { + expect(yield* resolve(false, { KILO_EXPERIMENTAL_SHARED_AGENT_BOARD: "true" })).toBe(true) + }), + ) +}) diff --git a/packages/opencode/test/kilocode/board-tools.test.ts b/packages/opencode/test/kilocode/board-tools.test.ts index 6ef3bbc9b1a..fd90fc9d089 100644 --- a/packages/opencode/test/kilocode/board-tools.test.ts +++ b/packages/opencode/test/kilocode/board-tools.test.ts @@ -16,6 +16,7 @@ import { MessageID, PartID, SessionID } from "../../src/session/schema" import { Permission } from "../../src/permission" import { Tool } from "../../src/tool/tool" import { ToolRegistry } from "../../src/tool/registry" +import { RuntimeFlags } from "../../src/effect/runtime-flags" import { BoardReadTool, BoardPostTool } from "../../src/kilocode/tool/board" import { BoardStore } from "../../src/kilocode/board/store" import { KiloTask } from "../../src/kilocode/tool/task" @@ -34,6 +35,7 @@ const it = testEffect( BackgroundJob.node, Agent.node, Config.node, + RuntimeFlags.node, Database.node, Truncate.node, CrossSpawnSpawner.node, diff --git a/packages/opencode/test/kilocode/chart-tool-gating.test.ts b/packages/opencode/test/kilocode/chart-tool-gating.test.ts index 55b5f30e9ae..7080ee2c8ed 100644 --- a/packages/opencode/test/kilocode/chart-tool-gating.test.ts +++ b/packages/opencode/test/kilocode/chart-tool-gating.test.ts @@ -24,7 +24,7 @@ function ids(client: string) { const prev = process.env.KILO_CLIENT try { process.env.KILO_CLIENT = client - return KiloToolRegistry.extra(tools, {}).map((t) => t.id) + return KiloToolRegistry.extra(tools, {}, { experimentalSharedAgentBoard: false }).map((t) => t.id) } finally { if (prev === undefined) delete process.env.KILO_CLIENT else process.env.KILO_CLIENT = prev diff --git a/packages/opencode/test/kilocode/notebook-tools.test.ts b/packages/opencode/test/kilocode/notebook-tools.test.ts index 4b9cd5b890c..b43326d1d2e 100644 --- a/packages/opencode/test/kilocode/notebook-tools.test.ts +++ b/packages/opencode/test/kilocode/notebook-tools.test.ts @@ -162,10 +162,13 @@ test("uses dedicated VS Code notebook permission defaults only when enabled", () const prev = process.env.KILO_CLIENT try { process.env.KILO_CLIENT = "vscode" - const disabled = KiloAgent.prepare({}).defaultsPatch + const disabled = KiloAgent.prepare({}, { experimentalSharedAgentBoard: false }).defaultsPatch expect(disabled.some((rule) => rule.permission.startsWith("notebook_"))).toBe(false) - const rules = KiloAgent.prepare({ experimental: { native_notebook_tools: true } }).defaultsPatch + const rules = KiloAgent.prepare( + { experimental: { native_notebook_tools: true } }, + { experimentalSharedAgentBoard: false }, + ).defaultsPatch expect(rules.findLast((rule) => rule.permission === "notebook_read")?.action).toBe("ask") expect(rules.findLast((rule) => rule.permission === "notebook_edit")?.action).toBe("ask") expect(rules.findLast((rule) => rule.permission === "notebook_execute")?.action).toBe("ask") diff --git a/packages/opencode/test/kilocode/system-prompt.test.ts b/packages/opencode/test/kilocode/system-prompt.test.ts index b999a72b88f..bf3162e2254 100644 --- a/packages/opencode/test/kilocode/system-prompt.test.ts +++ b/packages/opencode/test/kilocode/system-prompt.test.ts @@ -150,7 +150,7 @@ describe("Ask diagram guidance", () => { if (client === undefined) delete process.env.KILO_CLIENT if (client !== undefined) process.env.KILO_CLIENT = client const agents: Parameters[0] = {} - patchAgents(agents, [], [], {}, { mcpRules: {}, defaultsPatch: [] }, "/repo", []) + patchAgents(agents, [], [], { mcpRules: {}, defaultsPatch: [], board: false }, "/repo", []) const prompt = agents.ask.prompt expect(prompt).toContain("You are in Ask mode") expect(prompt).toContain("You must NOT modify files") diff --git a/packages/opencode/test/kilocode/tool-registry-indexing.test.ts b/packages/opencode/test/kilocode/tool-registry-indexing.test.ts index 22ecd97cfb6..afe8a0a8667 100644 --- a/packages/opencode/test/kilocode/tool-registry-indexing.test.ts +++ b/packages/opencode/test/kilocode/tool-registry-indexing.test.ts @@ -351,10 +351,11 @@ describe("kilocode tool registry indexing", () => { notebookEdit: def("notebook_edit"), notebookExecute: def("notebook_execute"), } + const flags = { experimentalSharedAgentBoard: false } try { process.env["KILO_CLIENT"] = "cli" - expect(KiloToolRegistry.extra(tools, {}).map((tool) => tool.id)).toEqual([ + expect(KiloToolRegistry.extra(tools, {}, flags).map((tool) => tool.id)).toEqual([ "semantic_search", "kilo_memory_recall", "kilo_memory_save", @@ -364,7 +365,7 @@ describe("kilocode tool registry indexing", () => { "send_file", ]) expect( - KiloToolRegistry.extra(tools, { experimental: { image_generation: true } }).map((tool) => tool.id), + KiloToolRegistry.extra(tools, { experimental: { image_generation: true } }, flags).map((tool) => tool.id), ).toEqual([ "generate_image", "semantic_search", @@ -378,18 +379,20 @@ describe("kilocode tool registry indexing", () => { for (const client of ["cli", "run", "acp"]) { process.env["KILO_CLIENT"] = client - const enabled = KiloToolRegistry.extra(tools, { experimental: { task_model_selection: true } }).map( + const enabled = KiloToolRegistry.extra(tools, { experimental: { task_model_selection: true } }, flags).map( (tool) => tool.id, ) expect(enabled).toContain("agent_manager_models") expect(enabled).not.toContain("agent_manager") expect( - KiloToolRegistry.extra(tools, { experimental: { task_model_selection: false } }).map((tool) => tool.id), + KiloToolRegistry.extra(tools, { experimental: { task_model_selection: false } }, flags).map( + (tool) => tool.id, + ), ).not.toContain("agent_manager_models") } process.env["KILO_CLIENT"] = "vscode" - expect(KiloToolRegistry.extra(tools, {}).map((tool) => tool.id)).toEqual([ + expect(KiloToolRegistry.extra(tools, {}, flags).map((tool) => tool.id)).toEqual([ "semantic_search", "kilo_memory_recall", "kilo_memory_save", @@ -403,9 +406,13 @@ describe("kilocode tool registry indexing", () => { "send_file", ]) expect( - KiloToolRegistry.extra(tools, { - experimental: { native_notebook_tools: true }, - }).map((tool) => tool.id), + KiloToolRegistry.extra( + tools, + { + experimental: { native_notebook_tools: true }, + }, + flags, + ).map((tool) => tool.id), ).toEqual([ "semantic_search", "kilo_memory_recall", @@ -422,7 +429,7 @@ describe("kilocode tool registry indexing", () => { "notify_user", "send_file", ]) - expect(KiloToolRegistry.extra({ ...tools, semantic: undefined }, {}).map((tool) => tool.id)).toEqual([ + expect(KiloToolRegistry.extra({ ...tools, semantic: undefined }, {}, flags).map((tool) => tool.id)).toEqual([ "kilo_memory_recall", "kilo_memory_save", "recall", @@ -436,7 +443,7 @@ describe("kilocode tool registry indexing", () => { ]) process.env["KILO_CLIENT"] = "desktop" - expect(KiloToolRegistry.extra(tools, {}).map((tool) => tool.id)).toEqual([ + expect(KiloToolRegistry.extra(tools, {}, flags).map((tool) => tool.id)).toEqual([ "semantic_search", "kilo_memory_recall", "kilo_memory_save", @@ -446,7 +453,7 @@ describe("kilocode tool registry indexing", () => { ]) process.env["KILO_CLIENT"] = "run" - expect(KiloToolRegistry.extra(tools, {}).map((tool) => tool.id)).toEqual([ + expect(KiloToolRegistry.extra(tools, {}, flags).map((tool) => tool.id)).toEqual([ "semantic_search", "kilo_memory_recall", "kilo_memory_save", @@ -456,7 +463,7 @@ describe("kilocode tool registry indexing", () => { ]) process.env["KILO_CLIENT"] = "acp" - expect(KiloToolRegistry.extra(tools, {}).map((tool) => tool.id)).toEqual([ + expect(KiloToolRegistry.extra(tools, {}, flags).map((tool) => tool.id)).toEqual([ "semantic_search", "kilo_memory_recall", "kilo_memory_save", @@ -467,12 +474,16 @@ describe("kilocode tool registry indexing", () => { for (const client of ["cli", "vscode", "jetbrains", "desktop", "run", "acp"]) { process.env["KILO_CLIENT"] = client for (const enabled of [false, true]) { - const ids = KiloToolRegistry.extra(tools, { experimental: { shared_agent_board: enabled } }) + const ids = KiloToolRegistry.extra(tools, { experimental: { shared_agent_board: enabled } }, flags) .map((tool) => tool.id) .filter((id) => id.startsWith("board_")) expect(ids).toEqual(enabled ? ["board_read", "board_post"] : []) } } + const flagged = KiloToolRegistry.extra(tools, {}, { experimentalSharedAgentBoard: true }) + .map((tool) => tool.id) + .filter((id) => id.startsWith("board_")) + expect(flagged).toEqual(["board_read", "board_post"]) } finally { if (prev === undefined) delete process.env["KILO_CLIENT"] if (prev !== undefined) process.env["KILO_CLIENT"] = prev diff --git a/packages/opencode/test/kilocode/tool/memory-save.test.ts b/packages/opencode/test/kilocode/tool/memory-save.test.ts index e23c1d87cb7..5fb2f5a2568 100644 --- a/packages/opencode/test/kilocode/tool/memory-save.test.ts +++ b/packages/opencode/test/kilocode/tool/memory-save.test.ts @@ -110,7 +110,7 @@ describe("kilo_memory_save", () => { }) test("defaults mutating memory tool permission to ask", () => { - const kilo = KiloAgent.prepare({}) + const kilo = KiloAgent.prepare({}, { experimentalSharedAgentBoard: false }) expect(Permission.evaluate("kilo_memory_recall", "typed", kilo.defaultsPatch).action).toBe("ask") expect(Permission.evaluate("kilo_memory_save", "remember", kilo.defaultsPatch).action).toBe("ask") diff --git a/packages/opencode/test/kilocode/tool/send-file.test.ts b/packages/opencode/test/kilocode/tool/send-file.test.ts index c66215dc28b..74e0e0df81e 100644 --- a/packages/opencode/test/kilocode/tool/send-file.test.ts +++ b/packages/opencode/test/kilocode/tool/send-file.test.ts @@ -455,6 +455,7 @@ describe("send_file tool", () => { send: tool, }, {}, + { experimentalSharedAgentBoard: false }, ) const ids = extra.map((t) => t.id)