diff --git a/app/canvas/[id]/CanvasEditorLegacyPageClient.tsx b/app/canvas/[id]/CanvasEditorLegacyPageClient.tsx index d164c1d50..ea9631f03 100644 --- a/app/canvas/[id]/CanvasEditorLegacyPageClient.tsx +++ b/app/canvas/[id]/CanvasEditorLegacyPageClient.tsx @@ -8215,11 +8215,20 @@ export default function CanvasEditorLegacyPage({ params }: PageProps) { String(normalizedNextSelections[stepKey] || "") ); }); + const changedStepKeys = new Set( + orderedStepKeys.filter( + (stepKey) => + String(normalizedCurrentSelections[stepKey] || "") !== + String(normalizedNextSelections[stepKey] || "") + ) + ); let clearedSelections = { ...normalizedNextSelections }; if (earliestChangedIndex >= 0) { orderedStepKeys.slice(earliestChangedIndex + 1).forEach((stepKey) => { - delete clearedSelections[stepKey]; + if (!changedStepKeys.has(stepKey)) { + delete clearedSelections[stepKey]; + } }); } diff --git a/components/agents/AgentWorkflowPanelDarkV2.tsx b/components/agents/AgentWorkflowPanelDarkV2.tsx index 9d466e44a..310464cfb 100644 --- a/components/agents/AgentWorkflowPanelDarkV2.tsx +++ b/components/agents/AgentWorkflowPanelDarkV2.tsx @@ -26,6 +26,7 @@ import { getRenderableAgentWorkflowOptionTree, getRenderableAgentWorkflowStepTree, isAgentWorkflowStepSelectionComplete, + sanitizeAgentWorkflowSelectionsForBlockedOptions, serializeAgentWorkflowSelectionPath, } from "@/lib/agents/workflow-ui-config"; import { AgentCanvasTopNavDark } from "@/components/agents/AgentCanvasTopNavDark"; @@ -894,14 +895,20 @@ export function AgentWorkflowPanelDarkV2({ const prefix = parentPath.slice(0, depth - 1); const nextKeys = currentSelectedKey === option.key ? prefix : [...prefix, option.key]; - setPendingAutoAdvanceStepKey(step.key); - onSelectionsChange({ + const nextSelections = { ...selections, [step.key]: nextKeys.length > 0 ? serializeAgentWorkflowSelectionPath(nextKeys) : undefined, - }); + }; + setPendingAutoAdvanceStepKey(step.key); + onSelectionsChange( + sanitizeAgentWorkflowSelectionsForBlockedOptions({ + config: resolvedConfig, + selections: nextSelections, + }) + ); }} index={optionIndex} /> @@ -1214,15 +1221,21 @@ export function AgentWorkflowPanelDarkV2({ currentSelectedKey === option.key ? selectedPathKeys : [...selectedPathKeys, option.key]; - - setPendingAutoAdvanceStepKey(activeEmbeddedStepEntry.step.key); - onSelectionsChange({ + const nextSelections = { ...selections, [activeEmbeddedStepEntry.step.key]: nextKeys.length > 0 ? serializeAgentWorkflowSelectionPath(nextKeys) : undefined, - }); + }; + + setPendingAutoAdvanceStepKey(activeEmbeddedStepEntry.step.key); + onSelectionsChange( + sanitizeAgentWorkflowSelectionsForBlockedOptions({ + config: resolvedConfig, + selections: nextSelections, + }) + ); if (currentSelectedKey !== option.key && nextChildren.length > 0) { setActiveEmbeddedOptionDepth( diff --git a/lib/agents/workflow-ui-config.ts b/lib/agents/workflow-ui-config.ts index c13b09ec3..a1b6efebe 100644 --- a/lib/agents/workflow-ui-config.ts +++ b/lib/agents/workflow-ui-config.ts @@ -1612,6 +1612,67 @@ export function getAgentWorkflowBlockedOptionKeysForSelections(params: { ); } +export function sanitizeAgentWorkflowSelectionsForBlockedOptions(params: { + config?: AgentWorkflowUiConfig | null; + selections: Partial>; +}) { + const config = params.config || defaultAgentWorkflowUiConfig; + const enabledSteps = flattenAgentWorkflowSteps( + getEnabledAgentWorkflowStepTree(Array.isArray(config.steps) ? config.steps : []) + ).map(({ step }) => step); + let sanitizedSelections = sanitizeAgentWorkflowSelections({ + config, + selections: params.selections, + }); + + for (let iteration = 0; iteration < enabledSteps.length; iteration += 1) { + const blockedOptionKeys = new Set( + getAgentWorkflowBlockedOptionKeysForSelections({ + config, + selections: sanitizedSelections, + }) + ); + let changed = false; + const nextSelections = { ...sanitizedSelections }; + + for (const step of enabledSteps) { + const selectedOptions = getAgentWorkflowSelectedOptionPath({ + step, + selectionValue: sanitizedSelections[step.key], + }); + const firstBlockedIndex = selectedOptions.findIndex((option) => + blockedOptionKeys.has(option.key) + ); + + if (firstBlockedIndex < 0) { + continue; + } + + const keptOptionKeys = selectedOptions + .slice(0, firstBlockedIndex) + .map((option) => option.key); + const serialized = serializeAgentWorkflowSelectionPath(keptOptionKeys); + if (serialized) { + nextSelections[step.key] = serialized; + } else { + delete nextSelections[step.key]; + } + changed = true; + } + + if (!changed) { + return sanitizedSelections; + } + + sanitizedSelections = sanitizeAgentWorkflowSelections({ + config, + selections: nextSelections, + }); + } + + return sanitizedSelections; +} + export function isAgentWorkflowStepSelectionComplete(params: { step: AgentWorkflowStepUiConfig; selectionValue?: string | null;