diff --git a/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/app/KiloAutoApproveService.kt b/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/app/KiloAutoApproveService.kt deleted file mode 100644 index f89aa23a805..00000000000 --- a/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/app/KiloAutoApproveService.kt +++ /dev/null @@ -1,32 +0,0 @@ -package ai.kilocode.client.app - -import com.intellij.openapi.components.Service -import kotlinx.coroutines.flow.MutableStateFlow -import kotlinx.coroutines.flow.StateFlow -import kotlinx.coroutines.flow.asStateFlow - -/** - * Application-level service tracking the runtime auto-approve toggle. - * - * This is a **client-side** toggle only. It does NOT write CLI config or - * create persistent permission rules. While enabled, each permission request - * is automatically replied with `"once"`. - */ -@Service(Service.Level.APP) -class KiloAutoApproveService { - private val state = MutableStateFlow(false) - val enabled: StateFlow = state.asStateFlow() - - fun active(): Boolean = state.value - - fun set(value: Boolean) { - if (state.value == value) return - state.value = value - } - - fun toggle(): Boolean { - val next = !state.value - set(next) - return next - } -} diff --git a/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/session/SessionUi.kt b/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/session/SessionUi.kt index 856156012af..c06995e0ccd 100644 --- a/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/session/SessionUi.kt +++ b/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/session/SessionUi.kt @@ -1,7 +1,6 @@ package ai.kilocode.client.session import ai.kilocode.client.app.KiloAppService -import ai.kilocode.client.app.KiloAutoApproveService import ai.kilocode.client.app.KiloSessionService import ai.kilocode.client.app.Workspace import ai.kilocode.client.session.model.SessionModelEvent @@ -33,7 +32,6 @@ import com.intellij.util.ui.JBUI import ai.kilocode.log.KiloLog import com.intellij.ide.ui.LafManagerListener import com.intellij.openapi.application.ApplicationManager -import com.intellij.openapi.components.service import com.intellij.openapi.editor.colors.EditorColorsListener import com.intellij.openapi.editor.colors.EditorColorsManager import com.intellij.openapi.Disposable @@ -74,7 +72,6 @@ class SessionUi( private val project = project private val app = app - private val auto = service() private var opening = ref != null private var pending = false private var loaded: Boolean? = null @@ -93,7 +90,6 @@ class SessionUi( beforeUpdate = { if (opening) false else scroll.atBottom() }, afterUpdate = { if (!opening) scroll.followBottom(it) }, loaded = ::onSessionLoaded, - auto = auto, openProfileAction = ::openProfileSettings, ) @@ -199,8 +195,6 @@ class SessionUi( project = project, onSend = { text -> sendPrompt(text) }, onAbort = { controller.abort() }, - autoApprove = { auto.active() }, - onAutoApproveToggle = { controller.toggleAutoApprove() }, ) sessionContent.add(header, BorderLayout.NORTH) @@ -300,15 +294,6 @@ class SessionUi( is SessionModelEvent.Cleared -> Unit } } - - prompt.setAutoApprove(auto.active()) - cs.launch { - auto.enabled.collect { value -> - ApplicationManager.getApplication().invokeLater { - if (!Disposer.isDisposed(this@SessionUi)) prompt.setAutoApprove(value) - } - } - } } private fun bindStyle() { diff --git a/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/session/controller/SessionController.kt b/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/session/controller/SessionController.kt index e038a4fe45a..32067597492 100644 --- a/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/session/controller/SessionController.kt +++ b/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/session/controller/SessionController.kt @@ -1,7 +1,6 @@ package ai.kilocode.client.session.controller import ai.kilocode.client.app.KiloAppService -import ai.kilocode.client.app.KiloAutoApproveService import ai.kilocode.client.app.KiloSessionService import ai.kilocode.client.app.Workspace import ai.kilocode.client.plugin.KiloBundle @@ -79,7 +78,6 @@ class SessionController( private val beforeUpdate: () -> Boolean = { false }, private val afterUpdate: (Boolean) -> Unit = {}, private val loaded: (Boolean) -> Unit = {}, - private val auto: KiloAutoApproveService? = null, private val openProfileAction: () -> Unit = {}, ) : Disposable { @@ -378,31 +376,6 @@ class SessionController( } } - fun toggleAutoApprove(): Boolean { - assertEdt() - val next = auto?.toggle() ?: false - if (next) drainPermissions() - return next - } - - private fun drainPermissions() { - val id = sid ?: return - val tracked = childIds.toSet() - cs.launch { - try { - val all = sessions.pendingPermissions(directory) - val ids = setOf(id) + tracked - val pending = all.filter { it.sessionID in ids } - for (req in pending) { - sessions.replyPermission(req.id, directory, PermissionReplyDto("once")) - } - LOG.debug { "${ChatLogSummary.sid(id)} kind=auto-approve drain count=${pending.size}" } - } catch (e: Exception) { - LOG.warn("${ChatLogSummary.sid(id)} kind=auto-approve drain failed message=${e.message}", e) - } - } - } - init { (ref as? SessionRef.Local)?.session?.let { model.setSession(it) } when (val item = ref) { @@ -654,12 +627,6 @@ class SessionController( val permissions = sessions.pendingPermissions(directory).filter { it.sessionID == child } if (permissions.isEmpty()) return LOG.debug { "${ChatLogSummary.sid(sid ?: "pending")} kind=child-recovery child=$child permissions=${permissions.size}" } - if (auto?.active() == true) { - for (req in permissions) { - sessions.replyPermission(req.id, directory, PermissionReplyDto("once")) - } - return - } val last = toPermission(permissions.last()) runEdt { if (disposed) return@runEdt @@ -687,12 +654,6 @@ class SessionController( LOG.debug { "${ChatLogSummary.sid(id)} kind=recovery permissions=${permissions.size} questions=${questions.size} status=${status?.type ?: "none"} branch=$branch" } - if (permissions.isNotEmpty() && auto?.active() == true) { - for (req in permissions) { - sessions.replyPermission(req.id, directory, PermissionReplyDto("once")) - } - return - } runEdt { if (disposed) return@runEdt if (sid != id) return@runEdt @@ -802,10 +763,6 @@ class SessionController( is ChatEventDto.PermissionAsked -> { val perm = toPermission(event.request) - if (auto?.active() == true) { - replyPermission(perm.id, PermissionReplyDto("once")) - return - } model.setState(SessionState.AwaitingPermission(perm)) } diff --git a/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/session/ui/prompt/PromptPanel.kt b/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/session/ui/prompt/PromptPanel.kt index 4600ad811e8..811847628f0 100644 --- a/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/session/ui/prompt/PromptPanel.kt +++ b/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/session/ui/prompt/PromptPanel.kt @@ -62,15 +62,12 @@ class PromptPanel( private val project: Project, private val onSend: (String) -> Unit, private val onAbort: () -> Unit, - private val autoApprove: () -> Boolean = { false }, - private val onAutoApproveToggle: () -> Boolean = { false }, ) : BorderLayoutPanel(), SessionEditorStyleTarget, SendPromptContext { companion object { private val LOG = KiloLog.create(PromptPanel::class.java) private val SEND_ICON: Icon = IconLoader.getIcon("/icons/send.svg", PromptPanel::class.java) private val STOP_ICON: Icon = IconLoader.getIcon("/icons/stop.svg", PromptPanel::class.java) - private val SHIELD_ICON: Icon = IconLoader.getIcon("/icons/shield.svg", PromptPanel::class.java) } val mode = ModePicker() @@ -134,13 +131,6 @@ class PromptPanel( addActionListener { onReset() } } - private val autoIcon = HoverIcon().apply { - icon = SHIELD_ICON - addActionListener { - setAutoApprove(onAutoApproveToggle()) - } - } - @Volatile private var busy = false private var ready = false @@ -163,7 +153,6 @@ class PromptPanel( ) applyStyle(style) - setAutoApprove(autoApprove()) shell.add(editor, BorderLayout.CENTER) val bar = BorderLayoutPanel().apply { @@ -179,8 +168,6 @@ class PromptPanel( bar.add(Box.createHorizontalStrut(JBUI.scale(SessionUiStyle.View.Prompt.CONTROL_GAP))) bar.add(reset) bar.add(Box.createHorizontalGlue()) - bar.add(autoIcon) - bar.add(Box.createHorizontalStrut(JBUI.scale(SessionUiStyle.View.Prompt.CONTROL_GAP))) bar.add(button) shell.add(bar, BorderLayout.SOUTH) add(shell, BorderLayout.CENTER) @@ -224,8 +211,6 @@ class PromptPanel( internal fun buttonForTest(): JButton = button - internal fun autoApproveButtonForTest(): JButton = autoIcon - internal val defaultFocusedComponent: JComponent get() = editor override fun applyStyle(style: SessionEditorStyle) { @@ -249,21 +234,6 @@ class PromptPanel( editor.requestFocusInWindow() } - fun setAutoApprove(value: Boolean) { - autoIcon.putClientProperty("selected", value) - autoIcon.toolTipText = if (value) { - KiloBundle.message("prompt.autoApprove.enabled") - } else { - KiloBundle.message("prompt.autoApprove.disabled") - } - autoIcon.accessibleContext.accessibleName = if (value) { - KiloBundle.message("prompt.autoApprove.disable") - } else { - KiloBundle.message("prompt.autoApprove.enable") - } - autoIcon.repaint() - } - override fun addNotify() { super.addNotify() bindKeymap() diff --git a/packages/kilo-jetbrains/frontend/src/main/resources/icons/shield.svg b/packages/kilo-jetbrains/frontend/src/main/resources/icons/shield.svg deleted file mode 100644 index 822bbc97cf0..00000000000 --- a/packages/kilo-jetbrains/frontend/src/main/resources/icons/shield.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/packages/kilo-jetbrains/frontend/src/main/resources/messages/KiloBundle.properties b/packages/kilo-jetbrains/frontend/src/main/resources/messages/KiloBundle.properties index 631833ae75f..763099b2a0a 100644 --- a/packages/kilo-jetbrains/frontend/src/main/resources/messages/KiloBundle.properties +++ b/packages/kilo-jetbrains/frontend/src/main/resources/messages/KiloBundle.properties @@ -46,10 +46,6 @@ session.permission.tool.todowrite=Update Todo List session.permission.tool.task=Task session.permission.tool.skill=Skill session.permission.tool.lsp=Language Server -prompt.autoApprove.enable=Enable auto-approve -prompt.autoApprove.disable=Disable auto-approve -prompt.autoApprove.enabled=Auto-approve enabled. Permission requests will be approved once automatically. -prompt.autoApprove.disabled=Auto-approve disabled. Click to auto-approve permission requests. session.question.dismiss=Dismiss session.question.submit=Submit session.question.next=Next diff --git a/packages/kilo-jetbrains/frontend/src/test/kotlin/ai/kilocode/client/app/KiloAutoApproveServiceTest.kt b/packages/kilo-jetbrains/frontend/src/test/kotlin/ai/kilocode/client/app/KiloAutoApproveServiceTest.kt deleted file mode 100644 index 7960de9f2dc..00000000000 --- a/packages/kilo-jetbrains/frontend/src/test/kotlin/ai/kilocode/client/app/KiloAutoApproveServiceTest.kt +++ /dev/null @@ -1,26 +0,0 @@ -package ai.kilocode.client.app - -import com.intellij.ide.util.PropertiesComponent -import com.intellij.testFramework.fixtures.BasePlatformTestCase - -@Suppress("UnstableApiUsage") -class KiloAutoApproveServiceTest : BasePlatformTestCase() { - - fun `test auto approve starts disabled even with stale persisted value`() { - PropertiesComponent.getInstance().setValue("kilo.permission.autoApprove.enabled", "true") - - val svc = KiloAutoApproveService() - - assertFalse(svc.active()) - PropertiesComponent.getInstance().unsetValue("kilo.permission.autoApprove.enabled") - } - - fun `test toggle is runtime only`() { - val svc = KiloAutoApproveService() - - assertTrue(svc.toggle()) - assertTrue(svc.active()) - - assertFalse(PropertiesComponent.getInstance().getBoolean("kilo.permission.autoApprove.enabled", false)) - } -} diff --git a/packages/kilo-jetbrains/frontend/src/test/kotlin/ai/kilocode/client/session/controller/PromptLifecycleTest.kt b/packages/kilo-jetbrains/frontend/src/test/kotlin/ai/kilocode/client/session/controller/PromptLifecycleTest.kt index e302c814b3d..aa095e0a136 100644 --- a/packages/kilo-jetbrains/frontend/src/test/kotlin/ai/kilocode/client/session/controller/PromptLifecycleTest.kt +++ b/packages/kilo-jetbrains/frontend/src/test/kotlin/ai/kilocode/client/session/controller/PromptLifecycleTest.kt @@ -1,6 +1,5 @@ package ai.kilocode.client.session.controller -import ai.kilocode.client.app.KiloAutoApproveService import ai.kilocode.client.session.model.PermissionFileDiff import ai.kilocode.client.session.model.PermissionMeta import ai.kilocode.client.session.model.SessionState @@ -213,27 +212,6 @@ class PromptLifecycleTest : SessionControllerTestBase() { assertEquals(1, rpc.permissionReplies.size) } - fun `test auto-approve live event replies once without showing prompt`() { - appRpc.state.value = ai.kilocode.rpc.dto.KiloAppStateDto( - ai.kilocode.rpc.dto.KiloAppStatusDto.READY, - config = ai.kilocode.rpc.dto.ConfigDto(model = "kilo/gpt-5"), - ) - projectRpc.state.value = workspaceReady() - val svc = KiloAutoApproveService() - svc.set(true) - val m = controller(flushMs = Long.MAX_VALUE, auto = svc) - edt { m.prompt("go") } - flush() - - emit(ChatEventDto.PermissionAsked("ses_test", permission("perm_auto"))) - - assertFalse(m.model.state is SessionState.AwaitingPermission) - assertEquals(1, rpc.permissionReplies.size) - assertEquals("once", rpc.permissionReplies[0].third.reply) - - svc.set(false) - } - // ------ Child session (subagent) permission bubbling ------ fun `test task part with child sessionId causes controller to track child`() { @@ -315,28 +293,6 @@ class PromptLifecycleTest : SessionControllerTestBase() { assertTrue("Root state must not be changed by child non-permission events", stateEvents.isEmpty()) } - fun `test child permission with auto-approve replies once without showing prompt`() { - appRpc.state.value = ai.kilocode.rpc.dto.KiloAppStateDto( - ai.kilocode.rpc.dto.KiloAppStatusDto.READY, - config = ai.kilocode.rpc.dto.ConfigDto(model = "kilo/gpt-5"), - ) - projectRpc.state.value = workspaceReady() - val svc = KiloAutoApproveService() - svc.set(true) - val m = controller(flushMs = Long.MAX_VALUE, auto = svc) - edt { m.prompt("go") } - flush() - - emit(taskPart("ses_child"), flush = false) - emit(ChatEventDto.PermissionAsked("ses_child", childPermission("child_auto"))) - - assertFalse(m.model.state is SessionState.AwaitingPermission) - assertEquals(1, rpc.permissionReplies.size) - assertEquals("once", rpc.permissionReplies[0].third.reply) - - svc.set(false) - } - fun `test root permission event is not processed as child permission`() { val (m, _, _) = prompted() diff --git a/packages/kilo-jetbrains/frontend/src/test/kotlin/ai/kilocode/client/session/controller/SessionControllerTestBase.kt b/packages/kilo-jetbrains/frontend/src/test/kotlin/ai/kilocode/client/session/controller/SessionControllerTestBase.kt index 5cc82d85656..51bd2bf2e4e 100644 --- a/packages/kilo-jetbrains/frontend/src/test/kotlin/ai/kilocode/client/session/controller/SessionControllerTestBase.kt +++ b/packages/kilo-jetbrains/frontend/src/test/kotlin/ai/kilocode/client/session/controller/SessionControllerTestBase.kt @@ -1,7 +1,6 @@ package ai.kilocode.client.session.controller import ai.kilocode.client.app.KiloAppService -import ai.kilocode.client.app.KiloAutoApproveService import ai.kilocode.client.app.KiloSessionService import ai.kilocode.client.session.model.SessionModel import ai.kilocode.client.session.model.SessionModelEvent @@ -129,9 +128,8 @@ abstract class SessionControllerTestBase : BasePlatformTestCase() { id: String? = null, flushMs: Long = Long.MAX_VALUE, displayMs: Long = Long.MAX_VALUE, - auto: KiloAutoApproveService? = null, ): SessionController { - return controller(id, flushMs, true, displayMs = displayMs, auto = auto) + return controller(id, flushMs, true, displayMs = displayMs) } protected fun controller( @@ -151,7 +149,6 @@ abstract class SessionControllerTestBase : BasePlatformTestCase() { beforeUpdate: () -> Boolean = { false }, afterUpdate: (Boolean) -> Unit = {}, ref: SessionRef? = if (session != null) SessionRef.Local(session) else SessionRef.from(id), - auto: KiloAutoApproveService? = null, ): SessionController { val root = Root() val m = SessionController( @@ -167,7 +164,6 @@ abstract class SessionControllerTestBase : BasePlatformTestCase() { displayMs, beforeUpdate = beforeUpdate, afterUpdate = afterUpdate, - auto = auto, ) controllers.add(m) roots[m] = root diff --git a/packages/kilo-jetbrains/frontend/src/test/kotlin/ai/kilocode/client/session/controller/SessionRecoveryTest.kt b/packages/kilo-jetbrains/frontend/src/test/kotlin/ai/kilocode/client/session/controller/SessionRecoveryTest.kt index 625931ca2b4..8cb0c1f3507 100644 --- a/packages/kilo-jetbrains/frontend/src/test/kotlin/ai/kilocode/client/session/controller/SessionRecoveryTest.kt +++ b/packages/kilo-jetbrains/frontend/src/test/kotlin/ai/kilocode/client/session/controller/SessionRecoveryTest.kt @@ -1,6 +1,5 @@ package ai.kilocode.client.session.controller -import ai.kilocode.client.app.KiloAutoApproveService import ai.kilocode.client.session.model.SessionState import ai.kilocode.rpc.dto.MessageWithPartsDto import ai.kilocode.rpc.dto.PartDto @@ -236,31 +235,6 @@ class SessionRecoveryTest : SessionControllerTestBase() { ) } - fun `test auto-approve recovery replies once and does not show awaiting permission`() { - rpc.pendingPermissionList.add( - PermissionRequestDto( - id = "perm_auto", - sessionID = "ses_test", - permission = "read", - patterns = listOf("*.json"), - ) - ) - - val svc = KiloAutoApproveService() - svc.set(true) - - appRpc.state.value = ai.kilocode.rpc.dto.KiloAppStateDto(ai.kilocode.rpc.dto.KiloAppStatusDto.READY, config = ai.kilocode.rpc.dto.ConfigDto(model = "kilo/gpt-5")) - projectRpc.state.value = workspaceReady() - val m = controller("ses_test", auto = svc) - flush() - - assertFalse(m.model.state is SessionState.AwaitingPermission) - assertEquals(1, rpc.permissionReplies.size) - assertEquals("once", rpc.permissionReplies[0].third.reply) - - svc.set(false) - } - // ------ Child session permission recovery from history ------ fun `test history with task part and pending child permission recovers to AwaitingPermission`() { @@ -299,46 +273,6 @@ class SessionRecoveryTest : SessionControllerTestBase() { assertEquals("ses_child", perm.sessionId) } - fun `test auto-approve child permission recovery replies once without showing prompt`() { - rpc.history.add( - MessageWithPartsDto( - info = msg("msg1", "ses_test", "assistant"), - parts = listOf( - PartDto( - id = "part_task", - sessionID = "ses_test", - messageID = "msg1", - type = "tool", - tool = "task", - metadata = mapOf("sessionId" to "ses_child"), - ), - ), - ) - ) - rpc.pendingPermissionList.add( - PermissionRequestDto( - id = "child_perm_auto", - sessionID = "ses_child", - permission = "edit", - patterns = listOf("*.kt"), - ) - ) - - val svc = KiloAutoApproveService() - svc.set(true) - - appRpc.state.value = ai.kilocode.rpc.dto.KiloAppStateDto(ai.kilocode.rpc.dto.KiloAppStatusDto.READY) - projectRpc.state.value = workspaceReady() - val m = controller("ses_test", auto = svc) - flush() - - assertFalse(m.model.state is SessionState.AwaitingPermission) - assertEquals(1, rpc.permissionReplies.size) - assertEquals("once", rpc.permissionReplies[0].third.reply) - - svc.set(false) - } - fun `test pending child permission from unrelated session is ignored`() { rpc.pendingPermissionList.add( PermissionRequestDto( diff --git a/packages/kilo-jetbrains/frontend/src/test/kotlin/ai/kilocode/client/session/ui/PromptPanelTest.kt b/packages/kilo-jetbrains/frontend/src/test/kotlin/ai/kilocode/client/session/ui/PromptPanelTest.kt index 596ff29285e..1b210456b57 100644 --- a/packages/kilo-jetbrains/frontend/src/test/kotlin/ai/kilocode/client/session/ui/PromptPanelTest.kt +++ b/packages/kilo-jetbrains/frontend/src/test/kotlin/ai/kilocode/client/session/ui/PromptPanelTest.kt @@ -120,54 +120,6 @@ class PromptPanelTest : BasePlatformTestCase() { assertSame(shell, panel.mode.parent.parent) } - fun `test auto-approve button initializes with disabled tooltip`() { - val panel = PromptPanel(project, {}, {}, autoApprove = { false }, onAutoApproveToggle = { true }) - - assertEquals( - "Auto-approve disabled. Click to auto-approve permission requests.", - panel.autoApproveButtonForTest().toolTipText, - ) - } - - fun `test clicking auto-approve button toggles state`() { - var enabled = false - val panel = PromptPanel( - project, {}, {}, - autoApprove = { enabled }, - onAutoApproveToggle = { enabled = !enabled; enabled }, - ) - - panel.autoApproveButtonForTest().doClick() - - assertEquals( - "Auto-approve enabled. Permission requests will be approved once automatically.", - panel.autoApproveButtonForTest().toolTipText, - ) - } - - fun `test setAutoApprove updates tooltip to enabled`() { - val panel = PromptPanel(project, {}, {}) - - panel.setAutoApprove(true) - - assertEquals( - "Auto-approve enabled. Permission requests will be approved once automatically.", - panel.autoApproveButtonForTest().toolTipText, - ) - } - - fun `test setAutoApprove updates tooltip to disabled`() { - val panel = PromptPanel(project, {}, {}) - panel.setAutoApprove(true) - - panel.setAutoApprove(false) - - assertEquals( - "Auto-approve disabled. Click to auto-approve permission requests.", - panel.autoApproveButtonForTest().toolTipText, - ) - } - private class TestSink : DataSink { var send: Any? = null