From 0951c413ad7db8d0b697a9bdf8f5fd47abd093d8 Mon Sep 17 00:00:00 2001 From: noobezlol Date: Fri, 3 Jul 2026 18:38:00 +0530 Subject: [PATCH] fix(cli): deny subagent permission prompts --- .changeset/subagent-permission-asks.md | 5 +++ .../opencode/src/kilocode/session/prompt.ts | 1 + packages/opencode/src/permission/index.ts | 9 ++++- .../session-prompt-permission-refresh.test.ts | 36 +++++++++++++++++++ 4 files changed, 50 insertions(+), 1 deletion(-) create mode 100644 .changeset/subagent-permission-asks.md diff --git a/.changeset/subagent-permission-asks.md b/.changeset/subagent-permission-asks.md new file mode 100644 index 00000000000..f97514ededd --- /dev/null +++ b/.changeset/subagent-permission-asks.md @@ -0,0 +1,5 @@ +--- +"@kilocode/cli": patch +--- + +Fail subagent permission prompts immediately instead of waiting for unavailable user input. \ No newline at end of file diff --git a/packages/opencode/src/kilocode/session/prompt.ts b/packages/opencode/src/kilocode/session/prompt.ts index 0f7946f0d33..c1be00ddda6 100644 --- a/packages/opencode/src/kilocode/session/prompt.ts +++ b/packages/opencode/src/kilocode/session/prompt.ts @@ -176,6 +176,7 @@ export namespace KiloSessionPrompt { ...input.request, ruleset: Permission.merge(agent.permission, guardPermissions({ agent, session })), hardRuleset: hardPermissions({ agent }), + nonInteractive: agent.mode === "subagent", }) }) diff --git a/packages/opencode/src/permission/index.ts b/packages/opencode/src/permission/index.ts index d57f9efff89..faf69f0f914 100644 --- a/packages/opencode/src/permission/index.ts +++ b/packages/opencode/src/permission/index.ts @@ -120,6 +120,7 @@ export const AskInput = Schema.Struct({ id: Schema.optional(PermissionID), ruleset: Ruleset, hardRuleset: Schema.optional(Ruleset), // kilocode_change + nonInteractive: Schema.optional(Schema.Boolean), // kilocode_change }).annotate({ identifier: "PermissionAskInput" }) export type AskInput = Schema.Schema.Type @@ -245,7 +246,7 @@ export const layer = Layer.effect( const ask = Effect.fn("Permission.ask")(function* (input: AskInput) { const { approved, pending } = yield* InstanceState.get(state) // kilocode_change start - const { ruleset, hardRuleset, ...request } = input + const { ruleset, hardRuleset, nonInteractive, ...request } = input const s = yield* InstanceState.get(state) const local = s.session[request.sessionID] ?? [] // kilocode_change end @@ -276,6 +277,12 @@ export const layer = Layer.effect( if (!needsAsk) return + // kilocode_change start - subagents cannot answer permission prompts + if (nonInteractive) { + return yield* new DeniedError({ ruleset: subset(request.permission, ruleset) }) + } + // kilocode_change end + const id = request.id ?? PermissionID.ascending() const info: Request = { id, diff --git a/packages/opencode/test/kilocode/session-prompt-permission-refresh.test.ts b/packages/opencode/test/kilocode/session-prompt-permission-refresh.test.ts index 3f86f491f27..835c5db4be3 100644 --- a/packages/opencode/test/kilocode/session-prompt-permission-refresh.test.ts +++ b/packages/opencode/test/kilocode/session-prompt-permission-refresh.test.ts @@ -12,6 +12,7 @@ import { Bus } from "../../src/bus" import { Command } from "../../src/command" import { Config } from "../../src/config/config" import { RuntimeFlags } from "../../src/effect/runtime-flags" +import { KiloSessionPrompt } from "../../src/kilocode/session/prompt" import { EventV2Bridge } from "../../src/event-v2-bridge" import { Env } from "../../src/env" import { Format } from "../../src/format" @@ -290,3 +291,38 @@ it.live("active tool calls use permissions changed after model streaming starts" }, ), ) + +it.live("subagent permission asks fail instead of waiting for a human reply", () => + provideTmpdirServer( + Effect.fnUntraced(function* () { + const permission = yield* Permission.Service + const sessions = yield* Session.Service + const chat = yield* sessions.create({ title: "Parent" }) + const agent = { + name: "general", + mode: "subagent" as const, + permission: Permission.fromConfig({ bash: "ask" }), + options: {}, + } + + const err = yield* KiloSessionPrompt.askPermission({ + permission, + agents: { get: () => Effect.succeed(agent) }, + sessions, + agent, + session: chat, + request: { + sessionID: chat.id, + permission: "bash", + patterns: ["echo 1"], + always: ["echo 1"], + metadata: {}, + }, + }).pipe(Effect.flip) + + expect(err).toBeInstanceOf(Permission.DeniedError) + expect(yield* permission.list()).toEqual([]) + }), + { git: true }, + ), +)