IJPL-248623 Preserve plan-mode launch requests

Prompt launch requests already carry provider option intent from their caller. Re-filtering those ids in AgentSessionLaunchService could drop plan_mode when provider options were hidden or not refreshed yet, so Junie plan prompts were treated as standard prompts.

Preserve explicit launch-request options through the service and let providers resolve capability-dependent handling. The prompt UI still filters selectable options before submit, and the global prompt spec now records that boundary.

GitOrigin-RevId: 386ad157eff4633704fb3a93f8951fc814dafc16
This commit is contained in:
Vladimir Krivosheev
2026-07-09 18:59:27 +00:00
committed by intellij-monorepo-bot
parent eef80ad4e2
commit 2e33fd8109
2 changed files with 7 additions and 30 deletions
@@ -55,14 +55,12 @@ import com.intellij.platform.ai.agent.sessions.core.providers.AgentInitialMessag
import com.intellij.platform.ai.agent.sessions.core.providers.AgentPendingSessionMetadata
import com.intellij.platform.ai.agent.sessions.core.providers.AgentPrestartNewSessionLaunchRequest
import com.intellij.platform.ai.agent.sessions.core.providers.AgentTerminalPromptDispatch
import com.intellij.platform.ai.agent.sessions.core.providers.AgentPromptProviderOptionTarget
import com.intellij.platform.ai.agent.sessions.core.providers.AgentSessionLaunchProfileResolver
import com.intellij.platform.ai.agent.sessions.core.providers.AgentSessionProviderDescriptor
import com.intellij.platform.ai.agent.sessions.core.providers.AgentSessionProviderUiContributors
import com.intellij.platform.ai.agent.sessions.core.providers.AgentSessionProviders
import com.intellij.platform.ai.agent.sessions.core.providers.AgentSessionTerminalLaunchSpec
import com.intellij.platform.ai.agent.sessions.core.providers.isBlockedForExistingThreadPlanMode
import com.intellij.platform.ai.agent.sessions.core.providers.resolveEffectiveProviderOptionIds
import com.intellij.agent.workbench.sessions.statistics.AgentWorkbenchEntryPoint
import com.intellij.agent.workbench.sessions.statistics.AgentWorkbenchTargetKind
import com.intellij.agent.workbench.sessions.statistics.AgentWorkbenchTelemetry
@@ -1290,12 +1288,8 @@ class AgentSessionLaunchService internal constructor(
return NewSessionLaunchPreparationResult.Failed(AgentPromptLaunchError.PROVIDER_UNAVAILABLE)
}
notifyAgentSessionConversationOpened(descriptor)
val staticInitialMessageRequest = initialMessageRequest?.withEffectiveProviderOptions(
descriptor = descriptor,
target = AgentPromptProviderOptionTarget.NEW_TASK,
)
val staticInitialMessagePlan = if (initialMessageRequestBuilder == null) {
staticInitialMessageRequest?.let(descriptor::buildInitialMessagePlan) ?: AgentInitialMessagePlan.EMPTY
initialMessageRequest?.let(descriptor::buildInitialMessagePlan) ?: AgentInitialMessagePlan.EMPTY
}
else {
AgentInitialMessagePlan.EMPTY
@@ -1332,11 +1326,7 @@ class AgentSessionLaunchService internal constructor(
launchProfileId = launchProfileId,
launchTargetId = plannedLaunch.intent.launchTargetId,
)
val effectiveInitialMessageRequest = (initialMessageRequestBuilder?.invoke(preliminaryContext) ?: staticInitialMessageRequest)
?.withEffectiveProviderOptions(
descriptor = descriptor,
target = AgentPromptProviderOptionTarget.NEW_TASK,
)
val effectiveInitialMessageRequest = initialMessageRequestBuilder?.invoke(preliminaryContext) ?: initialMessageRequest
if (updateGeneralProviderPreferences && descriptor.supportsPromptLaunch) {
uiPreferencesState.updateProviderOptionsOnLaunch(provider.value, effectiveInitialMessageRequest)
}
@@ -1498,10 +1488,7 @@ class AgentSessionLaunchService internal constructor(
threadId = targetThreadId,
)
?: return@run reportPromptLaunchResolved(AgentPromptLaunchResult.failure(AgentPromptLaunchError.TARGET_THREAD_NOT_FOUND))
val effectiveInitialMessageRequest = request.initialMessageRequest.withEffectiveProviderOptions(
descriptor = bridge,
target = AgentPromptProviderOptionTarget.EXISTING_TASK,
)
val effectiveInitialMessageRequest = request.initialMessageRequest
val initialMessagePlan = bridge.buildInitialMessagePlan(effectiveInitialMessageRequest)
if (initialMessagePlan.isBlockedForExistingThreadPlanMode(targetThread.activityReport.rowActivity)) {
return@run reportPromptLaunchResolved(AgentPromptLaunchResult.failure(AgentPromptLaunchError.TARGET_THREAD_BUSY_FOR_PLAN_MODE))
@@ -1813,18 +1800,6 @@ private fun resolveLaunchProjectDirectory(
return resolveAgentWorkbenchProjectDirectory(identityPath = path)
}
private fun AgentPromptInitialMessageRequest.withEffectiveProviderOptions(
descriptor: AgentSessionProviderDescriptor,
target: AgentPromptProviderOptionTarget,
): AgentPromptInitialMessageRequest {
val effectiveOptionIds = resolveEffectiveProviderOptionIds(
selectedProvider = descriptor,
selectedOptionIds = providerOptionIds,
target = target,
)
return if (effectiveOptionIds == providerOptionIds) this else copy(providerOptionIds = effectiveOptionIds)
}
private suspend fun resolvePromptInitialMessageDispatchPlan(
normalizedPath: String,
thread: AgentSessionThread,
@@ -24,7 +24,7 @@ targets:
# Global Prompt Entry
Status: Draft
Date: 2026-05-27
Date: 2026-06-30
## Summary
The global prompt opens a project-scoped prompt surface for starting a new task or sending a prompt to an existing loaded task. The surface is normally a popup, but the empty editor state may host a compact inline prompt composer instead of standard empty-state hints. This spec owns popup and inline lifecycle, target mode, validation, keyboard behavior, provider selection, and launcher handoff. Context collection and Add-to-Agent-Context routing are specified separately.
@@ -96,9 +96,11 @@ The global prompt opens a project-scoped prompt surface for starting a new task
[@test] ../../chat/testSrc/AgentChatFileEditorLifecycleTest.kt
[@test] ../../sessions/testSrc/AgentSessionLaunchServiceTest.kt
- Plan mode is available as a compact header icon toggle only when the selected provider exposes the plan-mode option. Its last-used state is stored per provider as provider option preferences, restored on prompt open, sent in the launch payload, and forced off or rejected for busy existing tasks. A typed `/plan` prefix remains prompt text and does not toggle the option.
- Plan mode is available as a compact header icon toggle only when the selected provider exposes the plan-mode option. Its last-used state is stored per provider as provider option preferences, restored on prompt open, sent in the launch payload, and forced off or rejected for busy existing tasks. The prompt UI filters selectable options before submit, but launch service preserves explicit launch-request option ids so providers can resolve capability-dependent behavior. A typed `/plan` prefix remains prompt text and does not toggle the option.
[@test] ../../prompt/ui/testSrc/AgentPromptPlanModeDecisionsTest.kt
[@test] ../../sessions/testSrc/AgentSessionPromptLauncherBridgeTest.kt
[@test] ../../lib-agent/providers/junie/sessions/testSrc/JunieExistingThreadPromptLaunchIntegrationTest.kt
[@test] ../../lib-agent/providers/junie/sessions/testSrc/JunieNewThreadPromptLaunchIntegrationTest.kt
- Codex `NEW_TASK` prompts start through the Codex app-server remote-resume path. Standard prompts start plain app-server turns; Plan prompts do not type `/plan`, and the resumed TUI must visibly enter Plan mode before the prompt starts. An acknowledged no-op `thread/settings/update` without `thread/settings/updated` must not be treated as prompt-send failure.
[@test] ../../lib-agent/providers/codex/sessions/testSrc/CodexPlanPromptRealAppServerIntegrationTest.kt