Skip to content
Merged
Changes from 1 commit
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
3f84ed9
fix(settings): fix long description on wordpress integration (#2195)
waleedlatif1 Dec 4, 2025
dc5a2b1
fix(envvar): fix envvar dropdown positioning, remove dead code (#2196)
waleedlatif1 Dec 4, 2025
ca3eb5b
fix(subscription): fixed text clipping on subscription panel (#2198)
waleedlatif1 Dec 4, 2025
8e7d8c9
fix(profile-pics): remove sharp dependency for serving profile pics i…
waleedlatif1 Dec 4, 2025
d22b578
fix(enterprise-plan): seats should be taken from metadata (#2200)
icecrasher321 Dec 5, 2025
1642ed7
improvement: modal UI (#2202)
emir-karabeg Dec 5, 2025
dcbdcb4
chore(deps): upgrade to nextjs 16 (#2203)
waleedlatif1 Dec 5, 2025
3b9f0f9
feat(error-notifications): workspace-level configuration of slack, em…
icecrasher321 Dec 5, 2025
414a54c
feat(i18n): update translations (#2204)
waleedlatif1 Dec 5, 2025
1b903f2
fix(images): updated helm charts with branding URL guidance, removed …
waleedlatif1 Dec 5, 2025
ca818a6
feat(admin): added admin APIs for admin management (#2206)
waleedlatif1 Dec 5, 2025
8ef9a45
fix(env-vars): refactor for workspace/personal env vars to work with …
icecrasher321 Dec 5, 2025
58251e2
feat(copilot): superagent (#2201)
Sg312 Dec 5, 2025
7101dc5
improvement: loading, optimistic actions (#2193)
emir-karabeg Dec 5, 2025
7752bea
fix(import): fix array errors on import/export (#2211)
Sg312 Dec 5, 2025
5d6c1f7
feat(tools): added more slack tools (#2212)
waleedlatif1 Dec 5, 2025
002713e
feat(i18n): update translations (#2208)
waleedlatif1 Dec 5, 2025
4fd5f00
fix(copilot): validation (#2215)
Sg312 Dec 5, 2025
fb4c982
fix(custom-bot-slack): dependsOn incorrectly set for bot_token (#2214)
icecrasher321 Dec 5, 2025
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
Prev Previous commit
Next Next commit
fix(copilot): validation (#2215)
* Fix validation error

* Fix lint
  • Loading branch information
Sg312 authored Dec 5, 2025
commit 4fd5f0051f87254d22a0d83c7804c51f7cfe40c9
120 changes: 32 additions & 88 deletions apps/sim/lib/copilot/tools/server/workflow/edit-workflow.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,19 @@ interface SkippedItem {
details?: Record<string, any>
}

/**
* Logs and records a skipped item
*/
function logSkippedItem(skippedItems: SkippedItem[], item: SkippedItem): void {
validationLogger.warn(`Skipped ${item.operationType} operation: ${item.reason}`, {
type: item.type,
operationType: item.operationType,
blockId: item.blockId,
...(item.details && { details: item.details }),
})
skippedItems.push(item)
}

/**
* Result of input validation
*/
Expand All @@ -79,8 +92,7 @@ interface ValidationResult {
function validateInputsForBlock(
blockType: string,
inputs: Record<string, any>,
blockId: string,
existingInputs?: Record<string, any>
blockId: string
): ValidationResult {
const errors: ValidationError[] = []
const blockConfig = getBlock(blockType)
Expand All @@ -99,9 +111,6 @@ function validateInputsForBlock(
subBlockMap.set(subBlock.id, subBlock)
}

// Merge existing inputs with new inputs to evaluate conditions properly
const mergedInputs = { ...existingInputs, ...inputs }

for (const [key, value] of Object.entries(inputs)) {
// Skip runtime subblock IDs
if (TRIGGER_RUNTIME_SUBBLOCK_IDS.includes(key)) {
Expand All @@ -128,17 +137,10 @@ function validateInputsForBlock(
continue
}

// Check if the field's condition is met
if (subBlockConfig.condition && !evaluateCondition(subBlockConfig.condition, mergedInputs)) {
errors.push({
blockId,
blockType,
field: key,
value,
error: `Field "${key}" condition not met - this field is not applicable for the current configuration`,
})
continue
}
// Note: We do NOT check subBlockConfig.condition here.
// Conditions are for UI display logic (show/hide fields in the editor).
// For API/Copilot, any valid field in the block schema should be accepted.
// The runtime will use the relevant fields based on the actual operation.

// Validate value based on subBlock type
const validationResult = validateValueForSubBlockType(
Expand All @@ -158,44 +160,6 @@ function validateInputsForBlock(
return { validInputs: validatedInputs, errors }
}

/**
* Evaluates a condition object against current inputs
*/
function evaluateCondition(
condition: SubBlockConfig['condition'],
inputs: Record<string, any>
): boolean {
if (!condition) return true

// Handle function conditions
const resolvedCondition = typeof condition === 'function' ? condition() : condition

const fieldValue = inputs[resolvedCondition.field]
const expectedValues = Array.isArray(resolvedCondition.value)
? resolvedCondition.value
: [resolvedCondition.value]

let matches = expectedValues.includes(fieldValue)
if (resolvedCondition.not) {
matches = !matches
}

// Handle AND condition
if (matches && resolvedCondition.and) {
const andFieldValue = inputs[resolvedCondition.and.field]
const andExpectedValues = Array.isArray(resolvedCondition.and.value)
? resolvedCondition.and.value
: [resolvedCondition.and.value]
let andMatches = andExpectedValues.includes(andFieldValue)
if (resolvedCondition.and.not) {
andMatches = !andMatches
}
matches = matches && andMatches
}

return matches
}

/**
* Result of validating a single value
*/
Expand Down Expand Up @@ -920,7 +884,7 @@ function applyOperationsToWorkflowState(
switch (operation_type) {
case 'delete': {
if (!modifiedState.blocks[block_id]) {
skippedItems.push({
logSkippedItem(skippedItems, {
type: 'block_not_found',
operationType: 'delete',
blockId: block_id,
Expand Down Expand Up @@ -953,7 +917,7 @@ function applyOperationsToWorkflowState(

case 'edit': {
if (!modifiedState.blocks[block_id]) {
skippedItems.push({
logSkippedItem(skippedItems, {
type: 'block_not_found',
operationType: 'edit',
blockId: block_id,
Expand All @@ -970,7 +934,7 @@ function applyOperationsToWorkflowState(
blockKeys: Object.keys(block),
blockData: JSON.stringify(block),
})
skippedItems.push({
logSkippedItem(skippedItems, {
type: 'block_not_found',
operationType: 'edit',
blockId: block_id,
Expand All @@ -983,19 +947,8 @@ function applyOperationsToWorkflowState(
if (params?.inputs) {
if (!block.subBlocks) block.subBlocks = {}

// Get existing input values for condition evaluation
const existingInputs: Record<string, any> = {}
Object.entries(block.subBlocks).forEach(([key, subBlock]: [string, any]) => {
existingInputs[key] = subBlock?.value
})

// Validate inputs against block configuration
const validationResult = validateInputsForBlock(
block.type,
params.inputs,
block_id,
existingInputs
)
const validationResult = validateInputsForBlock(block.type, params.inputs, block_id)
validationErrors.push(...validationResult.errors)

Object.entries(validationResult.validInputs).forEach(([inputKey, value]) => {
Expand Down Expand Up @@ -1119,7 +1072,7 @@ function applyOperationsToWorkflowState(
// Validate type before setting (skip validation for container types)
const blockConfig = getBlock(params.type)
if (!blockConfig && !isContainerType) {
skippedItems.push({
logSkippedItem(skippedItems, {
type: 'invalid_block_type',
operationType: 'edit',
blockId: block_id,
Expand Down Expand Up @@ -1269,7 +1222,7 @@ function applyOperationsToWorkflowState(
existingBlocks: Object.keys(modifiedState.blocks),
}
)
skippedItems.push({
logSkippedItem(skippedItems, {
type: 'invalid_edge_target',
operationType: 'edit',
blockId: block_id,
Expand Down Expand Up @@ -1322,7 +1275,7 @@ function applyOperationsToWorkflowState(

case 'add': {
if (!params?.type || !params?.name) {
skippedItems.push({
logSkippedItem(skippedItems, {
type: 'missing_required_params',
operationType: 'add',
blockId: block_id,
Expand All @@ -1338,7 +1291,7 @@ function applyOperationsToWorkflowState(
// Validate block type before adding (skip validation for container types)
const addBlockConfig = getBlock(params.type)
if (!addBlockConfig && !isContainerType) {
skippedItems.push({
logSkippedItem(skippedItems, {
type: 'invalid_block_type',
operationType: 'add',
blockId: block_id,
Expand Down Expand Up @@ -1427,7 +1380,7 @@ function applyOperationsToWorkflowState(
case 'insert_into_subflow': {
const subflowId = params?.subflowId
if (!subflowId || !params?.type || !params?.name) {
skippedItems.push({
logSkippedItem(skippedItems, {
type: 'missing_required_params',
operationType: 'insert_into_subflow',
blockId: block_id,
Expand All @@ -1443,7 +1396,7 @@ function applyOperationsToWorkflowState(

const subflowBlock = modifiedState.blocks[subflowId]
if (!subflowBlock) {
skippedItems.push({
logSkippedItem(skippedItems, {
type: 'invalid_subflow_parent',
operationType: 'insert_into_subflow',
blockId: block_id,
Expand Down Expand Up @@ -1478,20 +1431,11 @@ function applyOperationsToWorkflowState(

// Update inputs if provided (with validation)
if (params.inputs) {
// Get existing input values for condition evaluation
const existingInputs: Record<string, any> = {}
Object.entries(existingBlock.subBlocks || {}).forEach(
([key, subBlock]: [string, any]) => {
existingInputs[key] = subBlock?.value
}
)

// Validate inputs against block configuration
const validationResult = validateInputsForBlock(
existingBlock.type,
params.inputs,
block_id,
existingInputs
block_id
)
validationErrors.push(...validationResult.errors)

Expand Down Expand Up @@ -1537,7 +1481,7 @@ function applyOperationsToWorkflowState(
// Validate block type before creating (skip validation for container types)
const insertBlockConfig = getBlock(params.type)
if (!insertBlockConfig && !isContainerType) {
skippedItems.push({
logSkippedItem(skippedItems, {
type: 'invalid_block_type',
operationType: 'insert_into_subflow',
blockId: block_id,
Expand Down Expand Up @@ -1566,7 +1510,7 @@ function applyOperationsToWorkflowState(
case 'extract_from_subflow': {
const subflowId = params?.subflowId
if (!subflowId) {
skippedItems.push({
logSkippedItem(skippedItems, {
type: 'missing_required_params',
operationType: 'extract_from_subflow',
blockId: block_id,
Expand All @@ -1577,7 +1521,7 @@ function applyOperationsToWorkflowState(

const block = modifiedState.blocks[block_id]
if (!block) {
skippedItems.push({
logSkippedItem(skippedItems, {
type: 'block_not_found',
operationType: 'extract_from_subflow',
blockId: block_id,
Expand Down
Loading