Skip to content

Commit 50e118a

Browse files
waleedlatif1claude
andauthored
fix(serializer): apply tools.config.params before validating required tool params (#4391)
* fix(serializer): apply tools.config.params before validating required tool params * fix(serializer): guard array results and drop redundant fallback in tool param validation * fix(blocks): align canonicalParamId with tool param name for file inputs Renames the canonical id from `document` to `file` on firecrawl, reducto v2, pulse v2, and extend v2 so pre-execution validation resolves the value under the same key the tool expects, eliminating false "missing required fields: file" errors at submit time. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * chore(blocks): drop extraneous comments from canonical file-input renames Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * refactor(serializer): drop tools.config.params invocation; enforce canonical-id contract via audit The serializer's pre-execution validator no longer runs the block's `tools.config.params` mapper to discover renamed tool param ids. Instead it relies on the contract that every required+user-only tool param is backed by a subBlock whose `id` or `canonicalParamId` equals the tool param id, and a new audit (`bun run check:block-canonical`) enforces this. Migrates posthog (`personalApiKey` → canonical `apiKey`) so the audit passes cleanly. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * refactor(scripts): consolidate block registry CI checks into one script Folds the canonical-id contract audit into the existing subblock ID stability script and renames it to `check-block-registry.ts`. Both checks share the same `getAllBlocks()` import and registry-invariant purpose, so a single CI gate now catches both regression classes. The early-exit path on the stability check no longer short-circuits the canonical-id check. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * chore(scripts): unify check-block-registry result reporting Each check returns a discriminated `CheckResult` (pass | skip | fail) so the runner prints one definitive line per check instead of mixing a "skipping" message with a redundant "passed" line. Failure messages include a per-check header explaining the runtime impact. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
1 parent 9655e8e commit 50e118a

9 files changed

Lines changed: 317 additions & 205 deletions

File tree

.github/workflows/test-build.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -90,15 +90,15 @@ jobs:
9090
9191
echo "✅ All feature flags are properly configured"
9292
93-
- name: Check subblock ID stability
93+
- name: Check block registry invariants
9494
run: |
9595
if [ "${{ github.event_name }}" = "pull_request" ]; then
9696
BASE_REF="origin/${{ github.base_ref }}"
9797
git fetch --depth=1 origin "${{ github.base_ref }}" 2>/dev/null || true
9898
else
9999
BASE_REF="HEAD~1"
100100
fi
101-
bun run apps/sim/scripts/check-subblock-id-stability.ts "$BASE_REF"
101+
bun run apps/sim/scripts/check-block-registry.ts "$BASE_REF"
102102
103103
- name: Lint code
104104
run: bun run lint:check

apps/sim/blocks/blocks/extend.ts

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -130,19 +130,25 @@ export const ExtendBlock: BlockConfig<ExtendParserOutput> = {
130130
},
131131
}
132132

133-
const extendV2Inputs = ExtendBlock.inputs
133+
const extendV2Inputs = {
134+
file: { type: 'json' as const, description: 'Document (file upload or file reference)' },
135+
apiKey: ExtendBlock.inputs?.apiKey,
136+
outputFormat: ExtendBlock.inputs?.outputFormat,
137+
chunking: ExtendBlock.inputs?.chunking,
138+
engine: ExtendBlock.inputs?.engine,
139+
}
134140
const extendV2SubBlocks = (ExtendBlock.subBlocks || []).flatMap((subBlock) => {
135141
if (subBlock.id === 'filePath') {
136142
return []
137143
}
138144
if (subBlock.id === 'fileUpload') {
139145
return [
140-
subBlock,
146+
{ ...subBlock, canonicalParamId: 'file' },
141147
{
142148
id: 'fileReference',
143149
title: 'Document',
144150
type: 'short-input' as SubBlockType,
145-
canonicalParamId: 'document',
151+
canonicalParamId: 'file',
146152
placeholder: 'Connect a file output from another block',
147153
mode: 'advanced' as const,
148154
required: true,
@@ -173,7 +179,7 @@ export const ExtendV2Block: BlockConfig<ExtendParserOutput> = {
173179
apiKey: params.apiKey.trim(),
174180
}
175181

176-
const documentInput = normalizeFileInput(params.document, { single: true })
182+
const documentInput = normalizeFileInput(params.file, { single: true })
177183
if (!documentInput) {
178184
throw new Error('Document file is required')
179185
}

apps/sim/blocks/blocks/firecrawl.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ export const FirecrawlBlock: BlockConfig<FirecrawlResponse> = {
3737
id: 'fileUpload',
3838
title: 'Document',
3939
type: 'file-upload' as SubBlockType,
40-
canonicalParamId: 'document',
40+
canonicalParamId: 'file',
4141
acceptedTypes:
4242
'application/pdf,application/vnd.openxmlformats-officedocument.wordprocessingml.document,application/msword,application/vnd.oasis.opendocument.text,application/rtf,text/rtf,application/vnd.openxmlformats-officedocument.spreadsheetml.sheet,application/vnd.ms-excel,text/html',
4343
placeholder: 'Upload a document (PDF, DOCX, HTML, XLSX, etc.)',
@@ -53,7 +53,7 @@ export const FirecrawlBlock: BlockConfig<FirecrawlResponse> = {
5353
id: 'fileReference',
5454
title: 'File Reference',
5555
type: 'short-input' as SubBlockType,
56-
canonicalParamId: 'document',
56+
canonicalParamId: 'file',
5757
placeholder: 'File reference from previous block',
5858
mode: 'advanced',
5959
condition: {
@@ -487,7 +487,7 @@ Example 2 - Product Data:
487487
break
488488

489489
case 'parse': {
490-
const file = normalizeFileInput(params.document, { single: true })
490+
const file = normalizeFileInput(params.file, { single: true })
491491
if (!file) {
492492
throw new Error('A document file is required for the parse operation')
493493
}
@@ -624,7 +624,7 @@ Example 2 - Product Data:
624624
},
625625
maxCredits: { type: 'number', description: 'Maximum credits to spend' },
626626
strictConstrainToURLs: { type: 'boolean', description: 'Limit agent to provided URLs only' },
627-
document: { type: 'json', description: 'Document input (file upload or file reference)' },
627+
file: { type: 'json', description: 'Document input (file upload or file reference)' },
628628
includeTags: { type: 'json', description: 'HTML tags to include during parsing' },
629629
excludeTags: { type: 'json', description: 'HTML tags to exclude during parsing' },
630630
parsers: { type: 'json', description: 'Parser configuration (e.g., [{"type": "pdf"}])' },

apps/sim/blocks/blocks/posthog.ts

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,7 @@ export const PostHogBlock: BlockConfig<PostHogResponse> = {
156156
id: 'personalApiKey',
157157
title: 'Personal API Key',
158158
type: 'short-input',
159+
canonicalParamId: 'apiKey',
159160
placeholder: 'Enter your PostHog personal API key',
160161
password: true,
161162
condition: {
@@ -1192,9 +1193,6 @@ Return ONLY the timestamp string - no explanations, no quotes, no extra text.`,
11921193
if (params.operation === 'posthog_get_project' && params.projectIdParam) {
11931194
params.projectId = params.projectIdParam
11941195
}
1195-
if (params.personalApiKey) {
1196-
params.apiKey = params.personalApiKey
1197-
}
11981196

11991197
const flagOps = [
12001198
'posthog_get_feature_flag',
@@ -1276,7 +1274,7 @@ Return ONLY the timestamp string - no explanations, no quotes, no extra text.`,
12761274
operation: { type: 'string', description: 'Operation to perform' },
12771275
region: { type: 'string', description: 'PostHog region (us or eu)' },
12781276
projectApiKey: { type: 'string', description: 'Project API key for public endpoints' },
1279-
personalApiKey: { type: 'string', description: 'Personal API key for private endpoints' },
1277+
apiKey: { type: 'string', description: 'Personal API key for private endpoints' },
12801278
projectId: { type: 'string', description: 'PostHog project ID' },
12811279
// Core Data
12821280
event: { type: 'string', description: 'Event name' },

apps/sim/blocks/blocks/pulse.ts

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,6 @@ export const PulseBlock: BlockConfig<PulseParserOutput> = {
7474
apiKey: params.apiKey.trim(),
7575
}
7676

77-
// document is the canonical param from fileUpload (basic) or filePath (advanced)
7877
const documentInput = params.document
7978
if (typeof documentInput === 'object') {
8079
parameters.file = documentInput
@@ -128,21 +127,25 @@ export const PulseBlock: BlockConfig<PulseParserOutput> = {
128127
},
129128
}
130129

131-
// PulseV2Block uses the same canonical param 'document' for both basic and advanced modes
132-
const pulseV2Inputs = PulseBlock.inputs
130+
const pulseV2Inputs = {
131+
file: { type: 'json' as const, description: 'Document (file upload or file reference)' },
132+
apiKey: PulseBlock.inputs?.apiKey,
133+
pages: PulseBlock.inputs?.pages,
134+
chunking: PulseBlock.inputs?.chunking,
135+
chunkSize: PulseBlock.inputs?.chunkSize,
136+
}
133137
const pulseV2SubBlocks = (PulseBlock.subBlocks || []).flatMap((subBlock) => {
134138
if (subBlock.id === 'filePath') {
135-
return [] // Remove the old filePath subblock
139+
return []
136140
}
137141
if (subBlock.id === 'fileUpload') {
138-
// Insert fileReference right after fileUpload
139142
return [
140-
subBlock,
143+
{ ...subBlock, canonicalParamId: 'file' },
141144
{
142145
id: 'fileReference',
143146
title: 'Document',
144147
type: 'short-input' as SubBlockType,
145-
canonicalParamId: 'document',
148+
canonicalParamId: 'file',
146149
placeholder: 'File reference',
147150
mode: 'advanced' as const,
148151
required: true,
@@ -173,8 +176,7 @@ export const PulseV2Block: BlockConfig<PulseParserOutput> = {
173176
apiKey: params.apiKey.trim(),
174177
}
175178

176-
// document is the canonical param from fileUpload (basic) or fileReference (advanced)
177-
const normalizedFile = normalizeFileInput(params.document, { single: true })
179+
const normalizedFile = normalizeFileInput(params.file, { single: true })
178180
if (!normalizedFile) {
179181
throw new Error('Document file is required')
180182
}

apps/sim/blocks/blocks/reducto.ts

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,6 @@ export const ReductoBlock: BlockConfig<ReductoParserOutput> = {
7171
apiKey: params.apiKey.trim(),
7272
}
7373

74-
// document is the canonical param from fileUpload (basic) or filePath (advanced)
7574
const documentInput = params.document
7675

7776
if (typeof documentInput === 'object') {
@@ -135,20 +134,24 @@ export const ReductoBlock: BlockConfig<ReductoParserOutput> = {
135134
},
136135
}
137136

138-
// ReductoV2Block uses the same canonical param 'document' for both basic and advanced modes
139-
const reductoV2Inputs = ReductoBlock.inputs
137+
const reductoV2Inputs = {
138+
file: { type: 'json' as const, description: 'PDF document (file upload or file reference)' },
139+
apiKey: ReductoBlock.inputs?.apiKey,
140+
pages: ReductoBlock.inputs?.pages,
141+
tableOutputFormat: ReductoBlock.inputs?.tableOutputFormat,
142+
}
140143
const reductoV2SubBlocks = (ReductoBlock.subBlocks || []).flatMap((subBlock) => {
141144
if (subBlock.id === 'filePath') {
142145
return []
143146
}
144147
if (subBlock.id === 'fileUpload') {
145148
return [
146-
subBlock,
149+
{ ...subBlock, canonicalParamId: 'file' },
147150
{
148151
id: 'fileReference',
149152
title: 'PDF Document',
150153
type: 'short-input' as SubBlockType,
151-
canonicalParamId: 'document',
154+
canonicalParamId: 'file',
152155
placeholder: 'File reference',
153156
mode: 'advanced' as const,
154157
required: true,
@@ -178,12 +181,11 @@ export const ReductoV2Block: BlockConfig<ReductoParserOutput> = {
178181
apiKey: params.apiKey.trim(),
179182
}
180183

181-
// document is the canonical param from fileUpload (basic) or fileReference (advanced)
182-
const documentInput = normalizeFileInput(params.document, { single: true })
183-
if (!documentInput) {
184+
const fileInput = normalizeFileInput(params.file, { single: true })
185+
if (!fileInput) {
184186
throw new Error('PDF document file is required')
185187
}
186-
parameters.file = documentInput
188+
parameters.file = fileInput
187189

188190
let pagesArray: number[] | undefined
189191
if (params.pages && params.pages.trim() !== '') {

0 commit comments

Comments
 (0)