Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
'use client'

import { useCallback, useEffect, useRef } from 'react'
import type { CanonicalModeOverrides } from '@/lib/workflows/subblocks/visibility'
import {
buildToolSubBlockId,
resolveToolParamSync,
Expand All @@ -21,6 +22,7 @@ interface ToolSubBlockRendererProps {
/** The tool's block type (e.g. `gmail`), so its params' selectors resolve dependencies. */
toolType: string
toolParams: Record<string, string> | undefined
canonicalModeOverrides?: CanonicalModeOverrides
onParamChange: (toolIndex: number, paramId: string, value: string) => void
disabled: boolean
canonicalToggle?: {
Expand Down Expand Up @@ -59,6 +61,7 @@ export function ToolSubBlockRenderer({
effectiveParamId,
toolType,
toolParams,
canonicalModeOverrides,
onParamChange,
disabled,
canonicalToggle,
Expand Down Expand Up @@ -132,7 +135,7 @@ export function ToolSubBlockRenderer({
}

return (
<DependencyBlockTypeProvider value={toolType}>
<DependencyBlockTypeProvider value={{ blockType: toolType, canonicalModeOverrides }}>
<SubBlock
blockId={blockId}
config={config}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,32 @@ import {
isMcpToolAlreadySelected,
isWorkflowAlreadySelected,
} from '@/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/tool-input/utils'
import { getDependencyCanonicalModeOverrides } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/hooks/use-dependency-block-type'

describe('nested tool dependency modes', () => {
it.each([
{
name: 'uses the nested tool modes instead of the host modes',
context: { blockType: 'table', canonicalModeOverrides: { tableId: 'advanced' as const } },
host: { '0:tableId': 'basic' as const },
expected: { tableId: 'advanced' },
},
{
name: 'keeps missing nested modes missing instead of inheriting host modes',
context: { blockType: 'table', canonicalModeOverrides: undefined },
host: { '0:tableId': 'advanced' as const },
expected: undefined,
},
{
name: 'uses host modes outside a nested tool',
context: null,
host: { tableId: 'basic' as const },
expected: { tableId: 'basic' },
},
] as const)('$name', ({ context, host, expected }) =>
expect(getDependencyCanonicalModeOverrides(context, host)).toEqual(expected)
)
})

describe('isMcpToolAlreadySelected', () => {
describe('basic functionality', () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,7 @@ import {
type CanonicalIndex,
type CanonicalModeOverrides,
evaluateSubBlockCondition,
getCanonicalSubBlocksForSurface,
isCanonicalPair,
reindexToolCanonicalModes,
resolveCanonicalMode,
Expand Down Expand Up @@ -526,13 +527,14 @@ export const ToolInput = memo(function ToolInput({
for (const [toolIndex, tool] of selectedTools.entries()) {
const blockConfig = allBlocks.find((b: { type: string }) => b.type === tool.type)
if (!blockConfig?.subBlocks) continue
const toolCanonical = buildCanonicalIndex(blockConfig.subBlocks)
const actionSubBlocks = getCanonicalSubBlocksForSurface(blockConfig.subBlocks, false)
const toolCanonical = buildCanonicalIndex(actionSubBlocks)
const scopedOverrides = scopeCanonicalModesForTool(
canonicalModeOverrides,
toolIndex,
tool.type
)
const reactiveSubBlock = blockConfig.subBlocks.find(
const reactiveSubBlock = actionSubBlocks.find(
(sb: { reactiveCondition?: unknown }) => sb.reactiveCondition
)
const reactiveCond = reactiveSubBlock?.reactiveCondition as
Expand Down Expand Up @@ -1744,14 +1746,17 @@ export const ToolInput = memo(function ToolInput({
)
: null

const toolCanonicalIndex: CanonicalIndex | null = toolBlock?.subBlocks
? buildCanonicalIndex(toolBlock.subBlocks)
const toolActionSubBlocks = toolBlock?.subBlocks
? getCanonicalSubBlocksForSurface(toolBlock.subBlocks, false)
: null
const toolCanonicalIndex: CanonicalIndex | null = toolActionSubBlocks
? buildCanonicalIndex(toolActionSubBlocks)
: null

const toolContextValues = toolCanonicalIndex
? buildPreviewContextValues(tool.params || {}, {
blockType: tool.type,
subBlocks: toolBlock!.subBlocks,
subBlocks: toolActionSubBlocks!,
canonicalIndex: toolCanonicalIndex,
values: { operation: tool.operation, ...tool.params },
overrides: toolScopedOverrides,
Expand Down Expand Up @@ -2149,6 +2154,7 @@ export const ToolInput = memo(function ToolInput({
effectiveParamId={effectiveParamId}
toolType={tool.type}
toolParams={tool.params}
canonicalModeOverrides={toolScopedOverrides}
onParamChange={handleParamChange}
disabled={disabled}
canonicalToggle={canonicalToggleProp}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,12 @@
import { useCallback, useMemo } from 'react'
import { isEqual } from 'es-toolkit'
import { useStoreWithEqualityFn } from 'zustand/traditional'
import { buildCanonicalIndex, resolveDependencyValue } from '@/lib/workflows/subblocks/visibility'
import {
buildCanonicalIndex,
getCanonicalSubBlocksForSurface,
isPureTriggerBlockConfig,
resolveDependencyValue,
} from '@/lib/workflows/subblocks/visibility'
import { getBlock } from '@/blocks/registry'
import { useWorkflowRegistry } from '@/stores/workflows/registry/store'
import { useSubBlockStore } from '@/stores/workflows/subblock/store'
Expand All @@ -22,10 +27,15 @@ export function useCanonicalSubBlockValue<T = unknown>(
const activeWorkflowId = useWorkflowRegistry((s) => s.activeWorkflowId)
const blockState = useWorkflowStore((state) => state.blocks[blockId])
const blockConfig = blockState?.type ? getBlock(blockState.type) : null
const canonicalIndex = useMemo(
() => buildCanonicalIndex(blockConfig?.subBlocks || []),
[blockConfig?.subBlocks]
)
const canonicalIndex = useMemo(() => {
const subBlocks = blockConfig?.subBlocks || []
return buildCanonicalIndex(
getCanonicalSubBlocksForSurface(
subBlocks,
Boolean(blockState?.triggerMode) || isPureTriggerBlockConfig(blockConfig ?? undefined)
)
)
}, [blockConfig?.subBlocks, blockState?.triggerMode])
const canonicalModeOverrides = blockState?.data?.canonicalModes

return useStoreWithEqualityFn(
Expand Down
Original file line number Diff line number Diff line change
@@ -1,19 +1,24 @@
'use client'

import { createContext, useContext } from 'react'
import type { CanonicalModeOverrides } from '@/lib/workflows/subblocks/visibility'

const DependencyBlockTypeContext = createContext<string | null>(null)
export interface DependencyBlockContextValue {
blockType: string
canonicalModeOverrides: CanonicalModeOverrides | undefined
}

/**
* Provider set by tool-input param rendering (value = the tool's block type, e.g. `gmail`).
*/
const DependencyBlockTypeContext = createContext<DependencyBlockContextValue | null>(null)

/** Provides a nested tool's block type and already-scoped canonical modes. */
export const DependencyBlockTypeProvider = DependencyBlockTypeContext.Provider

/**
* The block type whose config should drive dependency (`dependsOn`) canonical resolution
* for the current subblock. Null for normal blocks (resolve against the host block). Set
* to the tool's type for tool-input params, so a nested tool's selector resolves its
* parents against the TOOL's config (e.g. a Gmail tool's `credential` -> `oauthCredential`,
* which the host Agent block's subblocks don't define) and can fetch its options.
*/
export const useDependencyBlockType = () => useContext(DependencyBlockTypeContext)
export const useDependencyBlockContext = () => useContext(DependencyBlockTypeContext)

export function getDependencyCanonicalModeOverrides(
context: DependencyBlockContextValue | null,
hostOverrides: CanonicalModeOverrides | undefined
): CanonicalModeOverrides | undefined {
// A nested tool with no scoped mode must use legacy inference, not another tool's host keys.
return context ? context.canonicalModeOverrides : hostOverrides
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@ import { isEqual } from 'es-toolkit'
import { useStoreWithEqualityFn } from 'zustand/traditional'
import {
buildCanonicalIndex,
getCanonicalSubBlocksForSurface,
isNonEmptyValue,
isPureTriggerBlockConfig,
normalizeDependencyValue,
parseDependsOn,
resolveDependencyValue,
Expand All @@ -15,7 +17,10 @@ import type { SubBlockConfig } from '@/blocks/types'
import { useWorkflowRegistry } from '@/stores/workflows/registry/store'
import { useSubBlockStore } from '@/stores/workflows/subblock/store'
import { useWorkflowStore } from '@/stores/workflows/workflow/store'
import { useDependencyBlockType } from './use-dependency-block-type'
import {
getDependencyCanonicalModeOverrides,
useDependencyBlockContext,
} from './use-dependency-block-type'

/**
* Centralized dependsOn gating for sub-block components.
Expand All @@ -35,17 +40,26 @@ export function useDependsOnGate(
const activeWorkflowId = useWorkflowRegistry((s) => s.activeWorkflowId)
const blockState = useWorkflowStore((state) => state.blocks[blockId])

const dependencyBlockType = useDependencyBlockType()
const dependencyBlockContext = useDependencyBlockContext()
const dependencyBlockType = dependencyBlockContext?.blockType
const blockConfig = dependencyBlockType
? getBlock(dependencyBlockType)
: blockState?.type
? getBlock(blockState.type)
: null
const canonicalIndex = useMemo(
() => buildCanonicalIndex(blockConfig?.subBlocks || []),
[blockConfig?.subBlocks]
const canonicalIndex = useMemo(() => {
const subBlocks = blockConfig?.subBlocks || []
return buildCanonicalIndex(
getCanonicalSubBlocksForSurface(
subBlocks,
Boolean(blockState?.triggerMode) || isPureTriggerBlockConfig(blockConfig ?? undefined)
)
)
}, [blockConfig?.subBlocks, blockState?.triggerMode])
Comment thread
BillLeoutsakosvl346 marked this conversation as resolved.
const canonicalModeOverrides = getDependencyCanonicalModeOverrides(
dependencyBlockContext,
blockState?.data?.canonicalModes
)
const canonicalModeOverrides = blockState?.data?.canonicalModes

// Parse dependsOn config to get all/any field lists
const { allFields, anyFields, allDependsOnFields } = useMemo(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,12 @@ import { useCallback, useEffect, useMemo, useRef, useState } from 'react'
import { getErrorMessage } from '@sim/utils/errors'
import { isEqual } from 'es-toolkit'
import { useStoreWithEqualityFn } from 'zustand/traditional'
import { buildCanonicalIndex, resolveDependencyValue } from '@/lib/workflows/subblocks/visibility'
import {
buildCanonicalIndex,
getCanonicalSubBlocksForSurface,
isPureTriggerBlockConfig,
resolveDependencyValue,
} from '@/lib/workflows/subblocks/visibility'
import { getBlock } from '@/blocks/registry'
import { useWorkflowRegistry } from '@/stores/workflows/registry/store'
import { useSubBlockStore } from '@/stores/workflows/subblock/store'
Expand Down Expand Up @@ -73,10 +78,15 @@ export function useFetchedOptions({
const blockState = useWorkflowStore((state) => state.blocks[blockId])
const blockConfig = blockState?.type ? getBlock(blockState.type) : null
const canonicalModeOverrides = blockState?.data?.canonicalModes
const canonicalIndex = useMemo(
() => buildCanonicalIndex(blockConfig?.subBlocks || []),
[blockConfig?.subBlocks]
)
const canonicalIndex = useMemo(() => {
const subBlocks = blockConfig?.subBlocks || []
return buildCanonicalIndex(
getCanonicalSubBlocksForSurface(
subBlocks,
Boolean(blockState?.triggerMode) || isPureTriggerBlockConfig(blockConfig ?? undefined)
)
)
}, [blockConfig?.subBlocks, blockState?.triggerMode])

const dependencyValues = useStoreWithEqualityFn(
useSubBlockStore,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,12 @@ import { isRetryEligibleBlock } from '@/lib/workflows/blocks/retry-eligibility'
import {
buildCanonicalIndex,
evaluateSubBlockCondition,
getCanonicalSubBlocksForSurface,
hasAdvancedValues,
isCanonicalPair,
isPureTriggerBlockConfig,
isStandaloneAdvancedMode,
resolveCanonicalMode,
shouldUseSubBlockForTriggerModeCanonicalIndex,
} from '@/lib/workflows/subblocks/visibility'
import { useUserPermissionsContext } from '@/app/workspace/[workspaceId]/providers/workspace-permissions-provider'
import {
Expand Down Expand Up @@ -159,9 +160,11 @@ export function Editor() {

const subBlocksForCanonical = useMemo(() => {
const subBlocks = blockConfig?.subBlocks || []
if (!triggerMode) return subBlocks
return subBlocks.filter(shouldUseSubBlockForTriggerModeCanonicalIndex)
}, [blockConfig?.subBlocks, triggerMode])
return getCanonicalSubBlocksForSurface(
subBlocks,
triggerMode || isPureTriggerBlockConfig(blockConfig ?? undefined)
)
}, [blockConfig, triggerMode])

const canonicalIndex = useMemo(
() => buildCanonicalIndex(subBlocksForCanonical),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,13 @@ import { useCallback, useMemo } from 'react'
import {
buildCanonicalIndex,
evaluateSubBlockCondition,
getCanonicalSubBlocksForSurface,
isPureTriggerBlockConfig,
isSubBlockFeatureEnabled,
isSubBlockHidden,
isSubBlockVisibleForMode,
isSubBlockVisibleForTriggerMode,
isToolInputOnlySubBlock,
shouldUseSubBlockForTriggerModeCanonicalIndex,
} from '@/lib/workflows/subblocks/visibility'
import type { BlockConfig, SubBlockConfig } from '@/blocks/types'
import { usePermissionConfig } from '@/hooks/use-permission-config'
Expand Down Expand Up @@ -43,9 +44,17 @@ export function useEditorSubblockLayout(
)
const { config: permissionConfig } = usePermissionConfig()

const canonicalSubBlocks = useMemo(() => {
const subBlocks = config?.subBlocks || []
return getCanonicalSubBlocksForSurface(
subBlocks,
displayTriggerMode || isPureTriggerBlockConfig(config)
)
}, [config?.subBlocks, displayTriggerMode])

// Evaluate reactive conditions (hooks-based, must be called before useMemo)
const hiddenByReactiveCondition = useReactiveConditions(
config?.subBlocks || [],
canonicalSubBlocks,
blockId,
activeWorkflowId,
blockDataFromStore?.canonicalModes
Expand Down Expand Up @@ -102,10 +111,7 @@ export function useEditorSubblockLayout(
{}
)

const subBlocksForCanonical = displayTriggerMode
? (config.subBlocks || []).filter(shouldUseSubBlockForTriggerModeCanonicalIndex)
: config.subBlocks || []
const canonicalIndex = buildCanonicalIndex(subBlocksForCanonical)
const canonicalIndex = buildCanonicalIndex(canonicalSubBlocks)
const effectiveAdvanced = displayAdvancedMode
const canonicalModeOverrides = blockData?.canonicalModes

Expand Down Expand Up @@ -169,5 +175,6 @@ export function useEditorSubblockLayout(
blockDataFromStore,
hiddenByReactiveCondition,
permissionConfig.disableSkills,
canonicalSubBlocks,
])
}
Loading
Loading