Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
14 commits
Select commit Hold shift + click to select a range
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(blocks): move type coercions from tools.config.tool to tools.conf…
…ig.params (#3264)

* fix(blocks): move type coercions from tools.config.tool to tools.config.params

Number() coercions in tools.config.tool ran at serialization time before
variable resolution, destroying dynamic references like <block.result.count>
by converting them to NaN/null. Moved all coercions to tools.config.params
which runs at execution time after variables are resolved.

Fixed in 15 blocks: exa, arxiv, sentry, incidentio, wikipedia, ahrefs,
posthog, elasticsearch, dropbox, hunter, lemlist, spotify, youtube, grafana,
parallel. Also added mode: 'advanced' to optional exa fields.

Closes #3258

* fix(blocks): address PR review — move remaining param mutations from tool() to params()

- Moved field mappings from tool() to params() in grafana, posthog,
  lemlist, spotify, dropbox (same dynamic reference bug)
- Fixed parallel.ts excerpts/full_content boolean logic
- Fixed parallel.ts search_queries empty case (must set undefined)
- Fixed elasticsearch.ts timeout not included when already ends with 's'
- Restored dropbox.ts tool() switch for proper default fallback

* fix(blocks): restore field renames to tool() for serialization-time validation

Field renames (e.g. personalApiKey→apiKey) must be in tool() because
validateRequiredFieldsBeforeExecution calls selectToolId()→tool() then
checks renamed field names on params. Only type coercions (Number(),
boolean) stay in params() to avoid destroying dynamic variable references.
  • Loading branch information
waleedlatif1 authored Feb 20, 2026
commit 9920882dc56131be338736d61ec6f024eaf57271
2 changes: 2 additions & 0 deletions .claude/commands/add-block.md
Original file line number Diff line number Diff line change
Expand Up @@ -454,6 +454,8 @@ Enables AI-assisted field generation.

## Tools Configuration

**Important:** `tools.config.tool` runs during serialization before variable resolution. Put `Number()` and other type coercions in `tools.config.params` instead, which runs at execution time after variables are resolved.

**Preferred:** Use tool names directly as dropdown option IDs to avoid switch cases:
```typescript
// Dropdown options use tool IDs directly
Expand Down
4 changes: 3 additions & 1 deletion CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -238,14 +238,16 @@ export const ServiceBlock: BlockConfig = {
bgColor: '#hexcolor',
icon: ServiceIcon,
subBlocks: [ /* see SubBlock Properties */ ],
tools: { access: ['service_action'], config: { tool: (p) => `service_${p.operation}` } },
tools: { access: ['service_action'], config: { tool: (p) => `service_${p.operation}`, params: (p) => ({ /* type coercions here */ }) } },
inputs: { /* ... */ },
outputs: { /* ... */ },
}
```

Register in `blocks/registry.ts` (alphabetically).

**Important:** `tools.config.tool` runs during serialization (before variable resolution). Never do `Number()` or other type coercions there — dynamic references like `<Block.output>` will be destroyed. Use `tools.config.params` for type coercions (it runs during execution, after variables are resolved).

**SubBlock Properties:**
```typescript
{
Expand Down
14 changes: 6 additions & 8 deletions apps/sim/blocks/blocks/ahrefs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -485,14 +485,6 @@ Return ONLY the date string in YYYY-MM-DD format - no explanations, no quotes, n
],
config: {
tool: (params) => {
// Convert numeric string inputs to numbers
if (params.limit) {
params.limit = Number(params.limit)
}
if (params.offset) {
params.offset = Number(params.offset)
}

switch (params.operation) {
case 'ahrefs_domain_rating':
return 'ahrefs_domain_rating'
Expand All @@ -514,6 +506,12 @@ Return ONLY the date string in YYYY-MM-DD format - no explanations, no quotes, n
return 'ahrefs_domain_rating'
}
},
params: (params) => {
const result: Record<string, unknown> = {}
if (params.limit) result.limit = Number(params.limit)
if (params.offset) result.offset = Number(params.offset)
return result
},
},
},
inputs: {
Expand Down
10 changes: 5 additions & 5 deletions apps/sim/blocks/blocks/arxiv.ts
Original file line number Diff line number Diff line change
Expand Up @@ -110,11 +110,6 @@ export const ArxivBlock: BlockConfig<ArxivResponse> = {
access: ['arxiv_search', 'arxiv_get_paper', 'arxiv_get_author_papers'],
config: {
tool: (params) => {
// Convert maxResults to a number for operations that use it
if (params.maxResults) {
params.maxResults = Number(params.maxResults)
}

switch (params.operation) {
case 'arxiv_search':
return 'arxiv_search'
Expand All @@ -126,6 +121,11 @@ export const ArxivBlock: BlockConfig<ArxivResponse> = {
return 'arxiv_search'
}
},
params: (params) => {
const result: Record<string, unknown> = {}
if (params.maxResults) result.maxResults = Number(params.maxResults)
return result
},
},
},
inputs: {
Expand Down
24 changes: 10 additions & 14 deletions apps/sim/blocks/blocks/dropbox.ts
Original file line number Diff line number Diff line change
Expand Up @@ -309,20 +309,6 @@ Return ONLY the timestamp string - no explanations, no quotes, no extra text.`,
],
config: {
tool: (params) => {
// Convert numeric params
if (params.limit) {
params.limit = Number(params.limit)
}
if (params.maxResults) {
params.maxResults = Number(params.maxResults)
}

// Normalize file input for upload operation - use canonical 'file' param
const normalizedFile = normalizeFileInput(params.file, { single: true })
if (normalizedFile) {
params.file = normalizedFile
}

switch (params.operation) {
case 'dropbox_upload':
return 'dropbox_upload'
Expand All @@ -348,6 +334,16 @@ Return ONLY the timestamp string - no explanations, no quotes, no extra text.`,
return 'dropbox_upload'
}
},
params: (params) => {
const result: Record<string, unknown> = {}
if (params.limit) result.limit = Number(params.limit)
if (params.maxResults) result.maxResults = Number(params.maxResults)
const normalizedFile = normalizeFileInput(params.file, { single: true })
if (normalizedFile) {
result.file = normalizedFile
}
return result
},
},
},
inputs: {
Expand Down
25 changes: 10 additions & 15 deletions apps/sim/blocks/blocks/elasticsearch.ts
Original file line number Diff line number Diff line change
Expand Up @@ -457,24 +457,19 @@ Return ONLY valid JSON - no explanations, no markdown code blocks.`,
],
config: {
tool: (params) => {
// Convert numeric strings to numbers
if (params.size) {
params.size = Number(params.size)
}
if (params.from) {
params.from = Number(params.from)
}
if (params.retryOnConflict) {
params.retryOnConflict = Number(params.retryOnConflict)
}
// Append 's' to timeout for Elasticsearch time format
if (params.timeout && !params.timeout.endsWith('s')) {
params.timeout = `${params.timeout}s`
}

// Return the operation as the tool ID
return params.operation || 'elasticsearch_search'
},
params: (params) => {
const result: Record<string, unknown> = {}
if (params.size) result.size = Number(params.size)
if (params.from) result.from = Number(params.from)
if (params.retryOnConflict) result.retryOnConflict = Number(params.retryOnConflict)
if (params.timeout && typeof params.timeout === 'string') {
result.timeout = params.timeout.endsWith('s') ? params.timeout : `${params.timeout}s`
}
return result
},
},
},

Expand Down
40 changes: 30 additions & 10 deletions apps/sim/blocks/blocks/exa.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ export const ExaBlock: BlockConfig<ExaResponse> = {
title: 'Use Autoprompt',
type: 'switch',
condition: { field: 'operation', value: 'exa_search' },
mode: 'advanced',
},
{
id: 'type',
Expand All @@ -62,20 +63,23 @@ export const ExaBlock: BlockConfig<ExaResponse> = {
],
value: () => 'auto',
condition: { field: 'operation', value: 'exa_search' },
mode: 'advanced',
},
{
id: 'includeDomains',
title: 'Include Domains',
type: 'long-input',
placeholder: 'example.com, another.com (comma-separated)',
condition: { field: 'operation', value: 'exa_search' },
mode: 'advanced',
},
{
id: 'excludeDomains',
title: 'Exclude Domains',
type: 'long-input',
placeholder: 'exclude.com, another.com (comma-separated)',
condition: { field: 'operation', value: 'exa_search' },
mode: 'advanced',
},
{
id: 'category',
Expand All @@ -95,6 +99,7 @@ export const ExaBlock: BlockConfig<ExaResponse> = {
],
value: () => '',
condition: { field: 'operation', value: 'exa_search' },
mode: 'advanced',
},
{
id: 'text',
Expand All @@ -107,12 +112,14 @@ export const ExaBlock: BlockConfig<ExaResponse> = {
title: 'Include Highlights',
type: 'switch',
condition: { field: 'operation', value: 'exa_search' },
mode: 'advanced',
},
{
id: 'summary',
title: 'Include Summary',
type: 'switch',
condition: { field: 'operation', value: 'exa_search' },
mode: 'advanced',
},
{
id: 'livecrawl',
Expand All @@ -125,6 +132,7 @@ export const ExaBlock: BlockConfig<ExaResponse> = {
],
value: () => 'never',
condition: { field: 'operation', value: 'exa_search' },
mode: 'advanced',
},
// Get Contents operation inputs
{
Expand All @@ -147,26 +155,30 @@ export const ExaBlock: BlockConfig<ExaResponse> = {
type: 'long-input',
placeholder: 'Enter a query to guide the summary generation...',
condition: { field: 'operation', value: 'exa_get_contents' },
mode: 'advanced',
},
{
id: 'subpages',
title: 'Number of Subpages',
type: 'short-input',
placeholder: '5',
condition: { field: 'operation', value: 'exa_get_contents' },
mode: 'advanced',
},
{
id: 'subpageTarget',
title: 'Subpage Target Keywords',
type: 'long-input',
placeholder: 'docs, tutorial, about (comma-separated)',
condition: { field: 'operation', value: 'exa_get_contents' },
mode: 'advanced',
},
{
id: 'highlights',
title: 'Include Highlights',
type: 'switch',
condition: { field: 'operation', value: 'exa_get_contents' },
mode: 'advanced',
},
// Find Similar Links operation inputs
{
Expand Down Expand Up @@ -196,19 +208,22 @@ export const ExaBlock: BlockConfig<ExaResponse> = {
type: 'long-input',
placeholder: 'example.com, another.com (comma-separated)',
condition: { field: 'operation', value: 'exa_find_similar_links' },
mode: 'advanced',
},
{
id: 'excludeDomains',
title: 'Exclude Domains',
type: 'long-input',
placeholder: 'exclude.com, another.com (comma-separated)',
condition: { field: 'operation', value: 'exa_find_similar_links' },
mode: 'advanced',
},
{
id: 'excludeSourceDomain',
title: 'Exclude Source Domain',
type: 'switch',
condition: { field: 'operation', value: 'exa_find_similar_links' },
mode: 'advanced',
},
{
id: 'category',
Expand All @@ -228,18 +243,21 @@ export const ExaBlock: BlockConfig<ExaResponse> = {
],
value: () => '',
condition: { field: 'operation', value: 'exa_find_similar_links' },
mode: 'advanced',
},
{
id: 'highlights',
title: 'Include Highlights',
type: 'switch',
condition: { field: 'operation', value: 'exa_find_similar_links' },
mode: 'advanced',
},
{
id: 'summary',
title: 'Include Summary',
type: 'switch',
condition: { field: 'operation', value: 'exa_find_similar_links' },
mode: 'advanced',
},
{
id: 'livecrawl',
Expand All @@ -252,6 +270,7 @@ export const ExaBlock: BlockConfig<ExaResponse> = {
],
value: () => 'never',
condition: { field: 'operation', value: 'exa_find_similar_links' },
mode: 'advanced',
},
// Answer operation inputs
{
Expand All @@ -267,6 +286,7 @@ export const ExaBlock: BlockConfig<ExaResponse> = {
title: 'Include Text',
type: 'switch',
condition: { field: 'operation', value: 'exa_answer' },
mode: 'advanced',
},
// Research operation inputs
{
Expand Down Expand Up @@ -309,16 +329,6 @@ export const ExaBlock: BlockConfig<ExaResponse> = {
],
config: {
tool: (params) => {
// Convert numResults to a number for operations that use it
if (params.numResults) {
params.numResults = Number(params.numResults)
}

// Convert subpages to a number if provided
if (params.subpages) {
params.subpages = Number(params.subpages)
}

switch (params.operation) {
case 'exa_search':
return 'exa_search'
Expand All @@ -334,6 +344,16 @@ export const ExaBlock: BlockConfig<ExaResponse> = {
return 'exa_search'
}
},
params: (params) => {
const result: Record<string, unknown> = {}
if (params.numResults) {
result.numResults = Number(params.numResults)
}
if (params.subpages) {
result.subpages = Number(params.subpages)
}
return result
},
},
},
inputs: {
Expand Down
Loading
Loading