[agent-workbench]: EP-driven extension tab auto-selection in prompt palette (IJPL-233558)

IJ-MR-197006

Move auto-selection decision to the EP side: add shouldAutoSelect()
to AgentPromptPaletteExtension so each extension can declare whether
it should be auto-selected given the context. Alt+Cmd+\ queries
matching extensions to autoselect tab.

GitOrigin-RevId: 550b96b2cba505e1b20c665ce47f34e77d90132b
This commit is contained in:
Dmitry Zhuravlev
2026-03-24 13:45:13 +00:00
committed by intellij-monorepo-bot
parent 2e8eb9d191
commit cf6d783545
7 changed files with 85 additions and 5 deletions
@@ -458,6 +458,7 @@ public abstract class KeymapsTestCase extends KeymapsTestCaseBase {
"Git.Rename.Local.Branch", "ShelvedChanges.Rename", "ChangesView.Rename", "Terminal.SearchInCommandHistory"},
{"control U", SECOND_STROKE, "CommanderSwapPanels", "org.intellij.plugins.markdown.ui.actions.styling.InsertImageAction"},
{"control W", "CloseContent", "Terminal.DeletePreviousWord"},
{"ctrl alt BACK_SLASH", "AgentWorkbenchPrompt.OpenGlobalPaletteAutoSelect", "ClassNameCompletion"},
{"control alt DOWN", "MethodDown", "NextOccurence", "Console.TableResult.NextPage"},
{"control alt E", "Console.History.Browse", "ExecuteInPyConsoleAction", "PerforceDirect.Edit", "ToggleFindInSelection"},
{"shift alt U", "ActivateFindToolWindow", "ToggleCamelSnakeCase"},
@@ -24,5 +24,13 @@
<keyboard-shortcut first-keystroke="meta BACK_SLASH" keymap="Mac OS X 10.5+"/>
<add-to-group group-id="ToolsMenu" anchor="last"/>
</action>
<action
id="AgentWorkbenchPrompt.OpenGlobalPaletteAutoSelect"
class="com.intellij.agent.workbench.prompt.actions.AgentWorkbenchGlobalPromptAutoSelectAction">
<keyboard-shortcut first-keystroke="alt control BACK_SLASH" keymap="$default"/>
<keyboard-shortcut first-keystroke="alt meta BACK_SLASH" keymap="Mac OS X 10.5+"/>
<add-to-group group-id="ToolsMenu" anchor="last"/>
</action>
</actions>
</idea-plugin>
@@ -56,4 +56,6 @@ context.paths.title=Paths
popup.preview.toggle.tooltip=Toggle markdown preview
action.AgentWorkbenchPrompt.OpenGlobalPalette.text=Ask Agent with Context
action.AgentWorkbenchPrompt.OpenGlobalPalette.description=Open global prompt palette with context from the current IDE state
action.AgentWorkbenchPrompt.OpenGlobalPaletteAutoSelect.text=Ask Agent Depending on Context
action.AgentWorkbenchPrompt.OpenGlobalPaletteAutoSelect.description=Open global prompt palette and auto-select the extension tab that matches the context
context.tree.selection.title=Tree Selection ({0})
@@ -0,0 +1,44 @@
// Copyright 2000-2026 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.agent.workbench.prompt.actions
// @spec community/plugins/agent-workbench/spec/actions/global-prompt-entry.spec.md
import com.intellij.agent.workbench.prompt.context.AGENT_PROMPT_INVOCATION_ACTION_EVENT_KEY
import com.intellij.agent.workbench.prompt.ui.AgentPromptPalettePopup
import com.intellij.agent.workbench.sessions.core.prompt.AGENT_PROMPT_INVOCATION_DATA_CONTEXT_KEY
import com.intellij.agent.workbench.sessions.core.prompt.AGENT_PROMPT_INVOCATION_PREFER_EXTENSIONS_KEY
import com.intellij.agent.workbench.sessions.core.prompt.AgentPromptInvocationData
import com.intellij.openapi.actionSystem.ActionUpdateThread
import com.intellij.openapi.actionSystem.AnAction
import com.intellij.openapi.actionSystem.AnActionEvent
import com.intellij.openapi.project.DumbAware
internal class AgentWorkbenchGlobalPromptAutoSelectAction : AnAction(), DumbAware {
companion object {
private const val ACTION_ID: String = "AgentWorkbenchPrompt.OpenGlobalPaletteAutoSelect"
}
override fun actionPerformed(e: AnActionEvent) {
val project = e.project ?: return
AgentPromptPalettePopup(
invocationData = AgentPromptInvocationData(
project = project,
actionId = ACTION_ID,
actionText = e.presentation.text,
actionPlace = e.place,
invokedAtMs = System.currentTimeMillis(),
attributes = mapOf(
AGENT_PROMPT_INVOCATION_DATA_CONTEXT_KEY to e.dataContext,
AGENT_PROMPT_INVOCATION_ACTION_EVENT_KEY to e,
AGENT_PROMPT_INVOCATION_PREFER_EXTENSIONS_KEY to true,
),
)
).show()
}
override fun update(e: AnActionEvent) {
e.presentation.isEnabledAndVisible = e.project != null
}
override fun getActionUpdateThread(): ActionUpdateThread = ActionUpdateThread.BGT
}
@@ -17,6 +17,7 @@ import com.intellij.agent.workbench.sessions.core.prompt.AgentPromptContextEnvel
import com.intellij.agent.workbench.sessions.core.prompt.AgentPromptContextItem
import com.intellij.agent.workbench.sessions.core.prompt.AgentPromptInitialMessageRequest
import com.intellij.agent.workbench.sessions.core.prompt.AgentPromptInvocationData
import com.intellij.agent.workbench.sessions.core.prompt.AGENT_PROMPT_INVOCATION_PREFER_EXTENSIONS_KEY
import com.intellij.agent.workbench.sessions.core.prompt.AgentPromptLaunchError
import com.intellij.agent.workbench.sessions.core.prompt.AgentPromptLaunchRequest
import com.intellij.agent.workbench.sessions.core.prompt.AgentPromptLauncherBridge
@@ -185,8 +186,8 @@ internal class AgentPromptPalettePopup(
restoreTaskDrafts(draft)
refreshExtensionTaskDraftsFromContext()
val preferExtensions = invocationData.attributes[AGENT_PROMPT_INVOCATION_PREFER_EXTENSIONS_KEY] == true
if (preferExtensions && activeExtensionTabs.isNotEmpty()) {
selectFirstExtensionTab()
if (preferExtensions) {
selectAutoSelectExtensionTab()
}
val initialText = invocationData.dataContextOrNull()?.getData(AGENT_PROMPT_INITIAL_TEXT_DATA_KEY)
if (!initialText.isNullOrBlank()) {
@@ -412,9 +413,10 @@ internal class AgentPromptPalettePopup(
}
}
private fun selectFirstExtensionTab() {
val firstExtension = activeExtensionTabs.firstOrNull() ?: return
val index = (0 until tabbedPane.tabCount).firstOrNull { tabbedPane.getComponentAt(it) === firstExtension.tabPanel }
private fun selectAutoSelectExtensionTab() {
val items = contextEntries.map { it.item }
val target = activeExtensionTabs.firstOrNull { it.extension.shouldAutoSelect(items) } ?: return
val index = (0 until tabbedPane.tabCount).firstOrNull { tabbedPane.getComponentAt(it) === target.tabPanel }
if (index != null) {
tabbedPane.selectedIndex = index
}
@@ -60,6 +60,13 @@ interface AgentPromptPaletteExtension {
* or `null` to use the default hint.
*/
fun getFooterHint(): @Nls String?
/**
* Returns `true` if this extension's tab should be auto-selected when the popup
* is invoked with the "prefer extensions" shortcut and this extension [matches] the context.
* When multiple extensions return `true`, the first one wins.
*/
fun shouldAutoSelect(contextItems: List<AgentPromptContextItem>): Boolean = matches(contextItems)
}
object AgentPromptPaletteExtensions {
@@ -3,6 +3,7 @@ name: Global Prompt Entry
description: Requirements for global prompt action behavior, target routing, keyboard semantics, validation, and launcher handoff.
targets:
- ../../prompt/src/actions/AgentWorkbenchGlobalPromptAction.kt
- ../../prompt/src/actions/AgentWorkbenchGlobalPromptAutoSelectAction.kt
- ../../prompt/src/ui/AgentPromptPalettePopup.kt
- ../../prompt/src/ui/AgentPromptPaletteView.kt
- ../../prompt/src/ui/AgentPromptPaletteModels.kt
@@ -10,6 +11,7 @@ targets:
- ../../prompt/resources/intellij.agent.workbench.prompt.xml
- ../../prompt/resources/messages/AgentPromptBundle.properties
- ../../sessions-core/src/prompt/AgentPromptLauncherBridge.kt
- ../../sessions-core/src/prompt/AgentPromptPaletteExtension.kt
- ../../sessions/src/service/AgentSessionPromptLauncherBridge.kt
- ../../prompt/testSrc/ui/AgentPromptProviderSelectionDecisionsTest.kt
- ../../prompt/testSrc/ui/AgentPromptSubmitValidationDecisionsTest.kt
@@ -44,6 +46,8 @@ Prompt-context collection and rendering contracts are specified separately in `s
## Requirements
- Global action id `AgentWorkbenchPrompt.OpenGlobalPalette` must be available only when a project is open.
- Global action id `AgentWorkbenchPrompt.OpenGlobalPaletteAutoSelect` must be available only when a project is open. It opens the same popup but with EP-driven extension tab auto-selection (see below).
- Prompt target mode must support exactly:
- `PromptTargetMode.NEW_TASK`,
- `PromptTargetMode.EXISTING_TASK`.
@@ -112,6 +116,18 @@ Prompt-context collection and rendering contracts are specified separately in `s
- Context block soft-cap limit is `12_000` characters. When exceeded, user must explicitly choose send-full, auto-trim, or cancel before launch.
- Extension tab auto-selection (`Alt+Cmd+\` / `Alt+Ctrl+\`):
- When the popup is opened via `AgentWorkbenchPrompt.OpenGlobalPaletteAutoSelect`, the `AGENT_PROMPT_INVOCATION_PREFER_EXTENSIONS_KEY` attribute is set to `true`.
- When `preferExtensions` is true, the popup calls `AgentPromptPaletteExtension.shouldAutoSelect(contextItems)` on each active extension tab in order.
- The first extension that returns `true` from `shouldAutoSelect` has its tab auto-selected.
- If no extension returns `true`, the default "New Task" tab remains selected.
- `shouldAutoSelect` defaults to `false` in the EP interface; each extension opts in independently.
- When the popup is opened via the standard `AgentWorkbenchPrompt.OpenGlobalPalette` (`Cmd+\` / `Ctrl+\`), no auto-selection occurs regardless of extension tab state.
- Extension tab submit flow:
- When an extension tab is active, submit delegates to the action returned by `AgentPromptPaletteExtension.getSubmitActionId()`.
- Provider options, plan mode, and existing-task routing are bypassed for extension tabs.
## User Experience
- Popup opens as a project-scoped launcher for both new and existing task targets.
- Existing-task mode exposes provider-scoped thread list with loading/empty/error states.