mirror of
https://github.com/simstudioai/sim.git
synced 2026-01-09 23:17:59 -05:00
feat(debug): create debugger (#1174)
* Updates * Updates * Updates * Checkpoint * Checkpoint * Checkpoitn * Var improvements * Fixes * Execution status * UI improvements * Ui updates * Fix * Fix scoping * Fix workflow vars * Fix env vars * Remove number styling * Variable highlighting * Updates * Update * Fix resume * Stuff * Breakpoint ui * Ui * Ui updates * Loops and parallels * HIde env vars * Checkpoint * Stuff * Panel toggle * Lint
This commit is contained in:
committed by
GitHub
parent
bb5f40a027
commit
7c73f5ffe0
@@ -8,8 +8,6 @@ import {
|
||||
Layers,
|
||||
Play,
|
||||
RefreshCw,
|
||||
SkipForward,
|
||||
StepForward,
|
||||
Store,
|
||||
Trash2,
|
||||
WifiOff,
|
||||
@@ -44,6 +42,7 @@ import {
|
||||
getKeyboardShortcutText,
|
||||
useKeyboardShortcuts,
|
||||
} from '@/app/workspace/[workspaceId]/w/hooks/use-keyboard-shortcuts'
|
||||
import { useExecutionStore } from '@/stores/execution/store'
|
||||
import { useFolderStore } from '@/stores/folders/store'
|
||||
import { usePanelStore } from '@/stores/panel/store'
|
||||
import { useGeneralStore } from '@/stores/settings/general/store'
|
||||
@@ -111,6 +110,9 @@ export function ControlBar({ hasValidationErrors = false }: ControlBarProps) {
|
||||
const [isExpanded, setIsExpanded] = useState(false)
|
||||
const [isTemplateModalOpen, setIsTemplateModalOpen] = useState(false)
|
||||
const [isAutoLayouting, setIsAutoLayouting] = useState(false)
|
||||
// Remove chat modal state
|
||||
// const [isChatPromptOpen, setIsChatPromptOpen] = useState(false)
|
||||
// const [chatPrompt, setChatPrompt] = useState('')
|
||||
|
||||
// Delete workflow state - grouped for better organization
|
||||
const [deleteState, setDeleteState] = useState({
|
||||
@@ -146,6 +148,13 @@ export function ControlBar({ hasValidationErrors = false }: ControlBarProps) {
|
||||
}
|
||||
}, [setActiveTab, isOpen, togglePanel])
|
||||
|
||||
const openDebugPanel = useCallback(() => {
|
||||
setActiveTab('debug')
|
||||
if (!isOpen) {
|
||||
togglePanel()
|
||||
}
|
||||
}, [setActiveTab, isOpen, togglePanel])
|
||||
|
||||
// Shared condition for keyboard shortcut and button disabled state
|
||||
const isWorkflowBlocked = isExecuting || hasValidationErrors
|
||||
|
||||
@@ -819,15 +828,29 @@ export function ControlBar({ hasValidationErrors = false }: ControlBarProps) {
|
||||
return // Do nothing if no executable blocks
|
||||
}
|
||||
|
||||
// Start debugging
|
||||
// Determine starter id for focus
|
||||
const starter = Object.values(blocks).find((b) => b.type === 'starter') as any
|
||||
const starterId = starter?.id as string | undefined
|
||||
|
||||
// Enable debug UI but do NOT start execution
|
||||
if (!isDebugModeEnabled) {
|
||||
toggleDebugMode()
|
||||
}
|
||||
if (usageExceeded) {
|
||||
openSubscriptionSettings()
|
||||
} else {
|
||||
openConsolePanel()
|
||||
handleRunWorkflow(undefined, true) // Start in debug mode
|
||||
// Activate debug session state so the panel is active
|
||||
const execStore = useExecutionStore.getState()
|
||||
execStore.setIsExecuting(false)
|
||||
execStore.setIsDebugging(true)
|
||||
// Set the Start block as pending - it will execute on first Step
|
||||
execStore.setPendingBlocks(starterId ? [starterId] : [])
|
||||
|
||||
// Show Debug tab and mark starter as the current block to execute
|
||||
openDebugPanel()
|
||||
if (starterId) {
|
||||
execStore.setActiveBlocks(new Set([starterId]))
|
||||
}
|
||||
}
|
||||
}
|
||||
}, [
|
||||
@@ -838,8 +861,7 @@ export function ControlBar({ hasValidationErrors = false }: ControlBarProps) {
|
||||
blocks,
|
||||
handleCancelDebug,
|
||||
toggleDebugMode,
|
||||
handleRunWorkflow,
|
||||
openConsolePanel,
|
||||
openDebugPanel,
|
||||
])
|
||||
|
||||
/**
|
||||
@@ -859,40 +881,7 @@ export function ControlBar({ hasValidationErrors = false }: ControlBarProps) {
|
||||
|
||||
return (
|
||||
<div className='flex items-center gap-1'>
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<Button
|
||||
onClick={() => {
|
||||
openConsolePanel()
|
||||
handleStepDebug()
|
||||
}}
|
||||
className={debugButtonClass}
|
||||
disabled={isControlDisabled}
|
||||
>
|
||||
<StepForward className='h-5 w-5' />
|
||||
<span className='sr-only'>Step Forward</span>
|
||||
</Button>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent>Step Forward</TooltipContent>
|
||||
</Tooltip>
|
||||
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<Button
|
||||
onClick={() => {
|
||||
openConsolePanel()
|
||||
handleResumeDebug()
|
||||
}}
|
||||
className={debugButtonClass}
|
||||
disabled={isControlDisabled}
|
||||
>
|
||||
<SkipForward className='h-5 w-5' />
|
||||
<span className='sr-only'>Resume Until End</span>
|
||||
</Button>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent>Resume Until End</TooltipContent>
|
||||
</Tooltip>
|
||||
|
||||
{/* Keep only cancel (X) here; step/resume moved to panel */}
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<Button
|
||||
@@ -1214,7 +1203,7 @@ export function ControlBar({ hasValidationErrors = false }: ControlBarProps) {
|
||||
{isExpanded && renderPublishButton()}
|
||||
{renderDeleteButton()}
|
||||
{renderDuplicateButton()}
|
||||
{!isDebugging && renderDebugModeToggle()}
|
||||
{renderDebugModeToggle()}
|
||||
{renderDeployButton()}
|
||||
{isDebugging ? renderDebugControlsBar() : renderRunButton()}
|
||||
|
||||
@@ -1226,6 +1215,8 @@ export function ControlBar({ hasValidationErrors = false }: ControlBarProps) {
|
||||
workflowId={activeWorkflowId}
|
||||
/>
|
||||
)}
|
||||
|
||||
{/* Removed chat prompt dialog; chat input now lives in DebugPanel */}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -10,6 +10,7 @@ import {
|
||||
import { ScrollArea } from '@/components/ui/scroll-area'
|
||||
import { Tooltip, TooltipContent, TooltipTrigger } from '@/components/ui/tooltip'
|
||||
import { useCopilotStore } from '@/stores/copilot/store'
|
||||
import { useExecutionStore } from '@/stores/execution/store'
|
||||
import { useChatStore } from '@/stores/panel/chat/store'
|
||||
import { useConsoleStore } from '@/stores/panel/console/store'
|
||||
import { usePanelStore } from '@/stores/panel/store'
|
||||
@@ -17,6 +18,7 @@ import { useWorkflowRegistry } from '@/stores/workflows/registry/store'
|
||||
import { Chat } from './components/chat/chat'
|
||||
import { Console } from './components/console/console'
|
||||
import { Copilot } from './components/copilot/copilot'
|
||||
import { DebugPanel } from './components/debug/debug'
|
||||
import { Variables } from './components/variables/variables'
|
||||
|
||||
export function Panel() {
|
||||
@@ -44,6 +46,9 @@ export function Panel() {
|
||||
const exportChatCSV = useChatStore((state) => state.exportChatCSV)
|
||||
const { activeWorkflowId } = useWorkflowRegistry()
|
||||
|
||||
// Get debug state
|
||||
const isDebugging = useExecutionStore((state) => state.isDebugging)
|
||||
|
||||
// Copilot store for chat management
|
||||
const {
|
||||
chats,
|
||||
@@ -216,7 +221,11 @@ export function Panel() {
|
||||
)
|
||||
|
||||
// Handle tab clicks - no loading, just switch tabs
|
||||
const handleTabClick = async (tab: 'chat' | 'console' | 'variables' | 'copilot') => {
|
||||
const handleTabClick = async (tab: 'chat' | 'console' | 'variables' | 'copilot' | 'debug') => {
|
||||
// Don't allow clicking debug tab if not debugging
|
||||
if (tab === 'debug' && !isDebugging) {
|
||||
return
|
||||
}
|
||||
setActiveTab(tab)
|
||||
if (!isOpen) {
|
||||
togglePanel()
|
||||
@@ -284,10 +293,30 @@ export function Panel() {
|
||||
}
|
||||
}, [activeWorkflowId, copilotWorkflowId, ensureCopilotDataLoaded])
|
||||
|
||||
// When debug mode ends, switch to a different tab if debug was active
|
||||
useEffect(() => {
|
||||
if (!isDebugging && activeTab === 'debug') {
|
||||
setActiveTab('console')
|
||||
}
|
||||
}, [isDebugging, activeTab, setActiveTab])
|
||||
|
||||
// When debug mode starts, automatically open the debug panel
|
||||
useEffect(() => {
|
||||
if (isDebugging) {
|
||||
setActiveTab('debug')
|
||||
if (!isOpen) {
|
||||
togglePanel()
|
||||
}
|
||||
}
|
||||
}, [isDebugging, setActiveTab, isOpen, togglePanel])
|
||||
|
||||
return (
|
||||
<>
|
||||
{/* Tab Selector - Always visible */}
|
||||
<div className='fixed top-[76px] right-4 z-20 flex h-9 w-[308px] items-center gap-1 rounded-[14px] border bg-card px-[2.5px] py-1 shadow-xs'>
|
||||
<div
|
||||
className='fixed top-[76px] right-4 z-20 flex h-9 items-center gap-1 rounded-[14px] border bg-card px-[2.5px] py-1 shadow-xs'
|
||||
style={{ width: isDebugging ? '380px' : '308px' }}
|
||||
>
|
||||
<button
|
||||
onClick={() => handleTabClick('chat')}
|
||||
className={`panel-tab-base inline-flex flex-1 cursor-pointer items-center justify-center rounded-[10px] border border-transparent py-1 font-[450] text-sm outline-none transition-colors duration-200 ${
|
||||
@@ -320,6 +349,16 @@ export function Panel() {
|
||||
>
|
||||
Variables
|
||||
</button>
|
||||
{isDebugging && (
|
||||
<button
|
||||
onClick={() => handleTabClick('debug')}
|
||||
className={`panel-tab-base inline-flex flex-1 cursor-pointer items-center justify-center rounded-[10px] border border-transparent py-1 font-[450] text-sm outline-none transition-colors duration-200 ${
|
||||
isOpen && activeTab === 'debug' ? 'panel-tab-active' : 'panel-tab-inactive'
|
||||
}`}
|
||||
>
|
||||
Debug
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Panel Content - Only visible when isOpen is true */}
|
||||
@@ -512,6 +551,9 @@ export function Panel() {
|
||||
<div style={{ display: activeTab === 'variables' ? 'block' : 'none', height: '100%' }}>
|
||||
<Variables />
|
||||
</div>
|
||||
<div style={{ display: activeTab === 'debug' ? 'block' : 'none', height: '100%' }}>
|
||||
<DebugPanel />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
@@ -13,6 +13,8 @@ import { useUserPermissionsContext } from '@/app/workspace/[workspaceId]/provide
|
||||
import type { BlockConfig, SubBlockConfig, SubBlockType } from '@/blocks/types'
|
||||
import { useCollaborativeWorkflow } from '@/hooks/use-collaborative-workflow'
|
||||
import { useExecutionStore } from '@/stores/execution/store'
|
||||
import { usePanelStore } from '@/stores/panel/store'
|
||||
import { useGeneralStore } from '@/stores/settings/general/store'
|
||||
import { useWorkflowDiffStore } from '@/stores/workflow-diff'
|
||||
import { useWorkflowRegistry } from '@/stores/workflows/registry/store'
|
||||
import { useSubBlockStore } from '@/stores/workflows/subblock/store'
|
||||
@@ -580,6 +582,71 @@ export function WorkflowBlock({ id, data }: NodeProps<WorkflowBlockProps>) {
|
||||
type === 'schedule' && !isLoadingScheduleInfo && scheduleInfo !== null
|
||||
const userPermissions = useUserPermissionsContext()
|
||||
|
||||
// Debug mode and active selection
|
||||
const isDebugModeEnabled = useGeneralStore((s) => s.isDebugModeEnabled)
|
||||
const activeBlockIds = useExecutionStore((s) => s.activeBlockIds)
|
||||
const panelFocusedBlockId = useExecutionStore((s) => s.panelFocusedBlockId)
|
||||
const setPanelFocusedBlockId = useExecutionStore((s) => s.setPanelFocusedBlockId)
|
||||
const executingBlockIds = useExecutionStore((s) => s.executingBlockIds)
|
||||
const setActiveBlocks = useExecutionStore((s) => s.setActiveBlocks)
|
||||
const setActiveTab = usePanelStore((s) => s.setActiveTab)
|
||||
const breakpointId = useExecutionStore((s) => s.breakpointId)
|
||||
const debugContext = useExecutionStore((s) => s.debugContext)
|
||||
|
||||
const handleDebugOpen = (e: React.MouseEvent) => {
|
||||
if (!isDebugModeEnabled) return
|
||||
e.stopPropagation()
|
||||
setActiveBlocks(new Set([id]))
|
||||
setActiveTab('debug')
|
||||
// Always select this block for the debug panel focus
|
||||
setPanelFocusedBlockId(id)
|
||||
}
|
||||
|
||||
// In debug mode, use executingBlockIds to detect actual executing blocks (not selection);
|
||||
// outside debug, fall back to activeBlockIds driven by the executor
|
||||
const isExecutingNow = isDebugModeEnabled ? executingBlockIds.has(id) : activeBlockIds.has(id)
|
||||
const isCurrentBlock = isDebugModeEnabled && isPending
|
||||
const isPanelFocused = isDebugModeEnabled && panelFocusedBlockId === id
|
||||
|
||||
// Check if block has errored during debug execution
|
||||
const hasError =
|
||||
isDebugModeEnabled && debugContext
|
||||
? (() => {
|
||||
// Check direct block state for error
|
||||
const directState = debugContext.blockStates?.get(id)
|
||||
if (
|
||||
directState?.output &&
|
||||
typeof directState.output === 'object' &&
|
||||
'error' in directState.output
|
||||
) {
|
||||
return true
|
||||
}
|
||||
// Check virtual executions for errors (for blocks inside parallels)
|
||||
for (const [key, state] of debugContext.blockStates?.entries() || []) {
|
||||
// Check if this is a virtual ID for our block
|
||||
if (typeof key === 'string' && key.startsWith(`${id}_parallel_`)) {
|
||||
if (state?.output && typeof state.output === 'object' && 'error' in state.output) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
// Also check block logs for this block
|
||||
const hasErrorLog = debugContext.blockLogs?.some((log: any) => {
|
||||
if (log.blockId === id && !log.success) return true
|
||||
// Check if log is for a virtual version of this block
|
||||
if (
|
||||
typeof log.blockId === 'string' &&
|
||||
log.blockId.startsWith(`${id}_parallel_`) &&
|
||||
!log.success
|
||||
) {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
})
|
||||
return hasErrorLog || false
|
||||
})()
|
||||
: false
|
||||
|
||||
return (
|
||||
<div className='group relative'>
|
||||
<Card
|
||||
@@ -589,20 +656,45 @@ export function WorkflowBlock({ id, data }: NodeProps<WorkflowBlockProps>) {
|
||||
'transition-block-bg transition-ring',
|
||||
displayIsWide ? 'w-[480px]' : 'w-[320px]',
|
||||
!isEnabled && 'shadow-sm',
|
||||
isActive && 'animate-pulse-ring ring-2 ring-blue-500',
|
||||
isPending && 'ring-2 ring-amber-500',
|
||||
// Diff highlighting
|
||||
diffStatus === 'new' && 'bg-green-50/50 ring-2 ring-green-500 dark:bg-green-900/10',
|
||||
diffStatus === 'edited' && 'bg-orange-50/50 ring-2 ring-orange-500 dark:bg-orange-900/10',
|
||||
// Error state - highest priority (only border, no background)
|
||||
hasError && 'ring-2 ring-red-500',
|
||||
// Panel-focused block highlight (unless errored)
|
||||
!hasError && isPanelFocused && 'bg-blue-50/60 dark:bg-blue-900/5',
|
||||
// Executing blocks match staging: pulsing blue ring
|
||||
!hasError && isExecutingNow && 'animate-pulse-ring ring-2 ring-blue-500',
|
||||
// Pending blocks show blue border when not executing
|
||||
!hasError && !isExecutingNow && isCurrentBlock && 'ring-2 ring-blue-500',
|
||||
// Diff highlighting (only if not in debug error state)
|
||||
!hasError &&
|
||||
diffStatus === 'new' &&
|
||||
'bg-green-50/50 ring-2 ring-green-500 dark:bg-green-900/10',
|
||||
!hasError &&
|
||||
diffStatus === 'edited' &&
|
||||
'bg-orange-50/50 ring-2 ring-orange-500 dark:bg-orange-900/10',
|
||||
// Deleted block highlighting (in original workflow)
|
||||
isDeletedBlock && 'bg-red-50/50 ring-2 ring-red-500 dark:bg-red-900/10',
|
||||
'z-[20]'
|
||||
)}
|
||||
onClick={handleDebugOpen}
|
||||
>
|
||||
{/* Show debug indicator for pending blocks */}
|
||||
{isPending && (
|
||||
<div className='-top-6 -translate-x-1/2 absolute left-1/2 z-10 transform rounded-t-md bg-amber-500 px-2 py-0.5 text-white text-xs'>
|
||||
Next Step
|
||||
{/* Show error indicator for errored blocks */}
|
||||
{hasError && (
|
||||
<div className='-top-6 -translate-x-1/2 absolute left-1/2 z-10 transform rounded-t-md bg-red-500 px-2 py-0.5 text-white text-xs'>
|
||||
Error
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Show debug indicator for current blocks in debug mode (pending or executing) - but not if errored */}
|
||||
{!hasError && isDebugModeEnabled && (isPending || executingBlockIds.has(id)) && (
|
||||
<div className='-top-6 -translate-x-1/2 absolute left-1/2 z-10 transform rounded-t-md bg-blue-500 px-2 py-0.5 text-white text-xs'>
|
||||
Current
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Show breakpoint indicator */}
|
||||
{isDebugModeEnabled && breakpointId === id && (
|
||||
<div className='-bottom-6 -translate-x-1/2 absolute left-1/2 z-10 transform rounded-b-md bg-red-500 px-2 py-0.5 text-white text-xs'>
|
||||
Breakpoint
|
||||
</div>
|
||||
)}
|
||||
|
||||
|
||||
@@ -62,6 +62,7 @@ export function useWorkflowExecution() {
|
||||
setExecutor,
|
||||
setDebugContext,
|
||||
setActiveBlocks,
|
||||
setExecutingBlockIds,
|
||||
} = useExecutionStore()
|
||||
const [executionResult, setExecutionResult] = useState<ExecutionResult | null>(null)
|
||||
|
||||
@@ -93,6 +94,7 @@ export function useWorkflowExecution() {
|
||||
setExecutor(null)
|
||||
setPendingBlocks([])
|
||||
setActiveBlocks(new Set())
|
||||
setExecutingBlockIds(new Set())
|
||||
|
||||
// Reset debug mode setting if it was enabled
|
||||
if (isDebugModeEnabled) {
|
||||
@@ -105,6 +107,7 @@ export function useWorkflowExecution() {
|
||||
setExecutor,
|
||||
setPendingBlocks,
|
||||
setActiveBlocks,
|
||||
setExecutingBlockIds,
|
||||
isDebugModeEnabled,
|
||||
])
|
||||
|
||||
@@ -120,7 +123,7 @@ export function useWorkflowExecution() {
|
||||
}, [])
|
||||
|
||||
/**
|
||||
* Handles debug session completion
|
||||
* Handles debug session completion - keep debug session open for inspection
|
||||
*/
|
||||
const handleDebugSessionComplete = useCallback(
|
||||
async (result: ExecutionResult) => {
|
||||
@@ -130,10 +133,14 @@ export function useWorkflowExecution() {
|
||||
// Persist logs
|
||||
await persistLogs(uuidv4(), result)
|
||||
|
||||
// Reset debug state
|
||||
resetDebugState()
|
||||
// Keep debug mode open for inspection: stop executing, clear pending
|
||||
setIsExecuting(false)
|
||||
setPendingBlocks([])
|
||||
setExecutingBlockIds(new Set())
|
||||
// Keep debugContext and executor so the panel can inspect state
|
||||
// Do not reset isDebugging
|
||||
},
|
||||
[activeWorkflowId, resetDebugState]
|
||||
[activeWorkflowId, setIsExecuting, setPendingBlocks, setExecutingBlockIds]
|
||||
)
|
||||
|
||||
/**
|
||||
@@ -157,7 +164,7 @@ export function useWorkflowExecution() {
|
||||
)
|
||||
|
||||
/**
|
||||
* Handles debug execution errors
|
||||
* Handles debug execution errors - keep debug open for inspection
|
||||
*/
|
||||
const handleDebugExecutionError = useCallback(
|
||||
async (error: any, operation: string) => {
|
||||
@@ -176,10 +183,13 @@ export function useWorkflowExecution() {
|
||||
// Persist logs
|
||||
await persistLogs(uuidv4(), errorResult)
|
||||
|
||||
// Reset debug state
|
||||
resetDebugState()
|
||||
// Keep debug session open for inspection
|
||||
setIsExecuting(false)
|
||||
setPendingBlocks([])
|
||||
setExecutingBlockIds(new Set())
|
||||
// Keep isDebugging, debugContext, and executor intact
|
||||
},
|
||||
[debugContext, activeWorkflowId, resetDebugState]
|
||||
[debugContext, activeWorkflowId, setIsExecuting, setPendingBlocks, setExecutingBlockIds]
|
||||
)
|
||||
|
||||
const persistLogs = async (
|
||||
@@ -268,8 +278,8 @@ export function useWorkflowExecution() {
|
||||
const isChatExecution =
|
||||
workflowInput && typeof workflowInput === 'object' && 'input' in workflowInput
|
||||
|
||||
// For chat executions, we'll use a streaming approach
|
||||
if (isChatExecution) {
|
||||
// For chat executions, use streaming only when NOT debugging
|
||||
if (isChatExecution && !enableDebug) {
|
||||
const stream = new ReadableStream({
|
||||
async start(controller) {
|
||||
const encoder = new TextEncoder()
|
||||
@@ -448,7 +458,6 @@ export function useWorkflowExecution() {
|
||||
} catch (error: any) {
|
||||
controller.error(error)
|
||||
} finally {
|
||||
controller.close()
|
||||
setIsExecuting(false)
|
||||
setIsDebugging(false)
|
||||
setActiveBlocks(new Set())
|
||||
@@ -458,7 +467,7 @@ export function useWorkflowExecution() {
|
||||
return { success: true, stream }
|
||||
}
|
||||
|
||||
// For manual (non-chat) execution
|
||||
// For manual (non-streaming) execution including debug and non-chat
|
||||
const executionId = uuidv4()
|
||||
try {
|
||||
const result = await executeWorkflow(workflowInput, undefined, executionId)
|
||||
@@ -748,14 +757,19 @@ export function useWorkflowExecution() {
|
||||
// Validate debug state
|
||||
const validation = validateDebugState()
|
||||
if (!validation.isValid) {
|
||||
resetDebugState()
|
||||
// Keep session open for inspection; simply stop executing
|
||||
setIsExecuting(false)
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
logger.info('Executing debug step with blocks:', pendingBlocks)
|
||||
// Mark current pending blocks as executing for UI pulse
|
||||
setExecutingBlockIds(new Set(pendingBlocks))
|
||||
const result = await executor!.continueExecution(pendingBlocks, debugContext!)
|
||||
logger.info('Debug step execution result:', result)
|
||||
// Clear executing state after step returns
|
||||
setExecutingBlockIds(new Set())
|
||||
|
||||
if (isDebugSessionComplete(result)) {
|
||||
await handleDebugSessionComplete(result)
|
||||
@@ -763,6 +777,7 @@ export function useWorkflowExecution() {
|
||||
handleDebugSessionContinuation(result)
|
||||
}
|
||||
} catch (error: any) {
|
||||
setExecutingBlockIds(new Set())
|
||||
await handleDebugExecutionError(error, 'step')
|
||||
}
|
||||
}, [
|
||||
@@ -771,7 +786,8 @@ export function useWorkflowExecution() {
|
||||
pendingBlocks,
|
||||
activeWorkflowId,
|
||||
validateDebugState,
|
||||
resetDebugState,
|
||||
setIsExecuting,
|
||||
setExecutingBlockIds,
|
||||
isDebugSessionComplete,
|
||||
handleDebugSessionComplete,
|
||||
handleDebugSessionContinuation,
|
||||
@@ -791,7 +807,8 @@ export function useWorkflowExecution() {
|
||||
// Validate debug state
|
||||
const validation = validateDebugState()
|
||||
if (!validation.isValid) {
|
||||
resetDebugState()
|
||||
// Keep session open for inspection; simply stop executing
|
||||
setIsExecuting(false)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -819,7 +836,9 @@ export function useWorkflowExecution() {
|
||||
`Resume iteration ${iterationCount + 1}, executing ${currentPendingBlocks.length} blocks`
|
||||
)
|
||||
|
||||
setExecutingBlockIds(new Set(currentPendingBlocks))
|
||||
currentResult = await executor!.continueExecution(currentPendingBlocks, currentContext)
|
||||
setExecutingBlockIds(new Set())
|
||||
|
||||
logger.info('Resume iteration result:', {
|
||||
success: currentResult.success,
|
||||
@@ -864,6 +883,7 @@ export function useWorkflowExecution() {
|
||||
// Handle completion
|
||||
await handleDebugSessionComplete(currentResult)
|
||||
} catch (error: any) {
|
||||
setExecutingBlockIds(new Set())
|
||||
await handleDebugExecutionError(error, 'resume')
|
||||
}
|
||||
}, [
|
||||
@@ -872,7 +892,8 @@ export function useWorkflowExecution() {
|
||||
pendingBlocks,
|
||||
activeWorkflowId,
|
||||
validateDebugState,
|
||||
resetDebugState,
|
||||
setIsExecuting,
|
||||
setExecutingBlockIds,
|
||||
handleDebugSessionComplete,
|
||||
handleDebugExecutionError,
|
||||
])
|
||||
|
||||
@@ -746,7 +746,7 @@ export class Executor {
|
||||
Object.entries(this.initialBlockStates).forEach(([blockId, output]) => {
|
||||
context.blockStates.set(blockId, {
|
||||
output: output as NormalizedBlockOutput,
|
||||
executed: true,
|
||||
executed: false,
|
||||
executionTime: 0,
|
||||
})
|
||||
})
|
||||
|
||||
@@ -61,5 +61,8 @@ export const useExecutionStore = create<ExecutionState & ExecutionActions>()((se
|
||||
setExecutor: (executor) => set({ executor }),
|
||||
setDebugContext: (debugContext) => set({ debugContext }),
|
||||
setAutoPanDisabled: (disabled) => set({ autoPanDisabled: disabled }),
|
||||
setPanelFocusedBlockId: (id) => set({ panelFocusedBlockId: id }),
|
||||
setExecutingBlockIds: (ids) => set({ executingBlockIds: new Set(ids) }),
|
||||
setBreakpointId: (id) => set({ breakpointId: id }),
|
||||
reset: () => set(initialState),
|
||||
}))
|
||||
|
||||
@@ -9,6 +9,9 @@ export interface ExecutionState {
|
||||
executor: Executor | null
|
||||
debugContext: ExecutionContext | null
|
||||
autoPanDisabled: boolean
|
||||
panelFocusedBlockId?: string | null
|
||||
executingBlockIds: Set<string>
|
||||
breakpointId: string | null
|
||||
}
|
||||
|
||||
export interface ExecutionActions {
|
||||
@@ -19,6 +22,9 @@ export interface ExecutionActions {
|
||||
setExecutor: (executor: Executor | null) => void
|
||||
setDebugContext: (context: ExecutionContext | null) => void
|
||||
setAutoPanDisabled: (disabled: boolean) => void
|
||||
setPanelFocusedBlockId: (id: string | null) => void
|
||||
setExecutingBlockIds: (ids: Set<string>) => void
|
||||
setBreakpointId: (id: string | null) => void
|
||||
reset: () => void
|
||||
}
|
||||
|
||||
@@ -30,6 +36,9 @@ export const initialState: ExecutionState = {
|
||||
executor: null,
|
||||
debugContext: null,
|
||||
autoPanDisabled: false,
|
||||
panelFocusedBlockId: null,
|
||||
executingBlockIds: new Set(),
|
||||
breakpointId: null,
|
||||
}
|
||||
|
||||
// Types for panning functionality
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
export type PanelTab = 'console' | 'variables' | 'chat' | 'copilot'
|
||||
export type PanelTab = 'console' | 'variables' | 'chat' | 'copilot' | 'debug'
|
||||
|
||||
export interface PanelStore {
|
||||
isOpen: boolean
|
||||
|
||||
Reference in New Issue
Block a user