Skip to content
Closed
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
40 changes: 40 additions & 0 deletions apps/sim/serializer/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -991,6 +991,46 @@ describe('Serializer', () => {
expect(slackBlock?.config.params.unknownField).toBeUndefined()
})

it.concurrent(
'should include conditional fields when condition field has default value but null stored value',
() => {
const serializer = new Serializer()

// This simulates the cursor block scenario where:
// - operation has a default value function: () => 'cursor_launch_agent'
// - branchName has condition: { field: 'operation', value: 'cursor_launch_agent' }
// - But operation's stored value is null (user never changed the dropdown)
// We'll use slack block which has similar conditional field behavior
const slackBlockWithNullChannel: any = {
id: 'slack-1',
type: 'slack',
name: 'Slack',
position: { x: 0, y: 0 },
advancedMode: false,
subBlocks: {
channel: { value: 'general' }, // This simulates the 'operation' dropdown
text: { value: 'Hello world' }, // This has a condition on channel
// manualChannel would be null as it's in advanced mode
},
outputs: {},
enabled: true,
}

const serialized = serializer.serializeWorkflow(
{ 'slack-1': slackBlockWithNullChannel },
[],
{}
)

const slackBlock = serialized.blocks.find((b) => b.id === 'slack-1')
expect(slackBlock).toBeDefined()

// Both fields should be included since channel (the condition field) has a value
expect(slackBlock?.config.params.channel).toBe('general')
expect(slackBlock?.config.params.text).toBe('Hello world')
}
)

it.concurrent(
'should preserve legacy agent fields (systemPrompt, userPrompt, memories) for backward compatibility',
() => {
Expand Down
18 changes: 18 additions & 0 deletions apps/sim/serializer/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -400,6 +400,24 @@ export class Serializer {
allValues[id] = subBlock.value
})

// Apply default values to allValues before condition evaluation
// This ensures that fields with default value functions (like dropdowns)
// have their defaults applied when evaluating conditions for other fields
blockConfig.subBlocks.forEach((subBlockConfig) => {
const id = subBlockConfig.id
if (
(allValues[id] === null || allValues[id] === undefined) &&
subBlockConfig.value &&
shouldIncludeField(subBlockConfig, isAdvancedMode)
) {
try {
allValues[id] = subBlockConfig.value(allValues)
} catch {
// If the default value function fails, keep the original value
}
}
})

// Second pass: filter by mode and conditions
Object.entries(block.subBlocks).forEach(([id, subBlock]) => {
const matchingConfigs = blockConfig.subBlocks.filter((config) => config.id === id)
Expand Down
Loading