Skip to content

Conversation

@waleedlatif1
Copy link
Collaborator

Summary

  • Replaced 17+ individual *SyncWrapper components with a single centralized ToolSubBlockRenderer that bridges the subblock store with StoredTool.params via synthetic store keys (~1000 lines reduced)
  • Added basic/advanced mode toggle (ArrowLeftRight) matching standalone SubBlock behavior, using collaborative functions for real-time sync
  • Added dependsOn gating via useDependsOnGate so fields with unmet dependencies show as disabled rather than being hidden
  • Added paramVisibility field to SubBlockConfig for explicit tool-input visibility control
  • Shows (optional) label for non-user-only fields since the LLM can inject them at runtime

Test plan

  • Add Slack tool to agent block, expand, verify channel selector loads and is gated by credential
  • Verify basic/advanced toggle appears and switches between selector and manual ID input
  • Verify Generate (wand) button appears for fields with wandConfig
  • Add two tools of the same type, verify params are isolated between instances
  • Run bun test --filter "tool-input" — 42 tests pass
  • Run bun test --filter "params" — 31 tests pass
  • TypeScript compiles with zero errors

🤖 Generated with Claude Code

…d dependsOn gating

Replace 17+ individual SyncWrapper components with a single centralized
ToolSubBlockRenderer that bridges the subblock store with StoredTool.params
via synthetic store keys. This reduces ~1000 lines of duplicated wrapper
code and ensures tool-input renders subblock components identically to
the standalone SubBlock path.

- Add ToolSubBlockRenderer with bidirectional store sync
- Add basic/advanced mode toggle (ArrowLeftRight) using collaborative functions
- Add dependsOn gating via useDependsOnGate (fields disable instead of hiding)
- Add paramVisibility field to SubBlockConfig for tool-input visibility control
- Pass canonicalModeOverrides through getSubBlocksForToolInput
- Show (optional) label for non-user-only fields (LLM can inject at runtime)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@vercel
Copy link

vercel bot commented Feb 9, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
Project Deployment Actions Updated (UTC)
docs Skipped Skipped Feb 9, 2026 5:20pm

Request Review

@greptile-apps
Copy link
Contributor

greptile-apps bot commented Feb 9, 2026

Greptile Overview

Greptile Summary

This PR refactors tool-input rendering for registry tools to be driven by the block’s subBlocks (via getSubBlocksForToolInput) instead of many per-UI SyncWrapper components. It introduces a centralized ToolSubBlockRenderer that bridges the subblock store with StoredTool.params using synthetic IDs, adds canonical basic/advanced toggling (collaborative-synced), and adds dependsOn-based gating so unmet dependencies disable inputs rather than hiding them.

The change fits into the existing architecture by reusing the same SubBlock definitions/visibility primitives already used elsewhere (conditions, canonical pairs, dependsOn), while keeping a legacy ToolParameterConfig path for custom/MCP tools and registry tools without subblock definitions.

Blocking issues found are around (1) default visibility inference in getSubBlocksForToolInput likely hiding many optional params unintentionally, (2) store initialization behavior for synthetic subblock keys when param values are empty, and (3) required/optional labeling logic that’s currently keyed off visibility rather than the required flag.

Confidence Score: 3/5

  • This PR is close to mergeable but has a few correctness issues that will affect tool-input rendering behavior.
  • The refactor is substantial but reuses existing visibility/canonical logic; however, the new default visibility inference can hide optional params unexpectedly, and the synthetic store sync has an initialization gap for empty values. Fixing these should make the change safe to merge.
  • apps/sim/tools/params.ts; apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/tool-input/components/tool-sub-block-renderer.tsx; apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/tool-input/tool-input.tsx

Important Files Changed

Filename Overview
apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/tool-input/components/tool-sub-block-renderer.tsx Adds ToolSubBlockRenderer to replace many per-component sync wrappers by syncing synthetic subblock store keys with StoredTool.params; issue: synthetic store key isn’t initialized for empty-string param values, which can leave store-driven inputs uninitialized.
apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/tool-input/tool-input.tsx Refactors tool-input rendering to be subblock-first (ToolSubBlockRenderer), adds canonical mode toggle and wand generate UI; issue: optional/required label logic is based on visibility rather than requiredness, causing incorrect “(optional)” labeling.
apps/sim/blocks/types.ts Adds SubBlockConfig.paramVisibility to explicitly control tool-input visibility; change is type-only and low-risk.
apps/sim/tools/params-resolver.ts Re-exports canonical mode helpers/types from subblocks visibility utilities; no functional logic change beyond exports.
apps/sim/tools/params.ts Adds getSubBlocksForToolInput filtering/visibility inference for registry tools and passes wandConfig through; issue: default visibility inference makes optional params default to user-only, likely hiding many optional params from tool-input unexpectedly.

Sequence Diagram

sequenceDiagram
  participant UI as ToolInput
  participant Params as getSubBlocksForToolInput
  participant SubStore as SubBlockStore
  participant Renderer as ToolSubBlockRenderer
  participant Gate as useDependsOnGate
  participant Collab as collaborativeSetBlockCanonicalMode

  UI->>Params: getSubBlocksForToolInput(toolId, blockType, {operation, params}, canonicalOverrides)
  Params-->>UI: filtered subBlocks (+ oauthConfig)

  UI->>Renderer: render each SubBlock (syntheticId)
  Renderer->>SubStore: useSubBlockValue(blockId, syntheticId)
  Renderer->>Gate: compute finalDisabled(dependsOn + disabled)

  Note over Renderer,SubStore: init effect syncs tool.params -> store
  Renderer->>SubStore: setStoreValue(toolParamValue)

  Note over Renderer,UI: sync effect propagates store -> tool.params
  Renderer->>UI: onParamChange(toolIndex, effectiveParamId, stringValue)

  UI->>Collab: toggle canonical mode (basic/advanced)
  Collab-->>UI: updates block canonicalModes (real-time)
  UI->>Params: re-filter/re-resolve canonical pair mode
Loading

Copy link
Contributor

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

5 files reviewed, 3 comments

Edit Code Review Agent Settings | Greptile

Comment on lines +910 to +915
const toolParamIds = new Set(Object.keys(toolConfig.params || {}))
const toolParamVisibility: Record<string, ParameterVisibility> = {}
for (const [paramId, param] of Object.entries(toolConfig.params || {})) {
toolParamVisibility[paramId] =
param.visibility ?? (param.required ? 'user-or-llm' : 'user-only')
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wrong default visibility

In getSubBlocksForToolInput, the fallback assigns param.required ? 'user-or-llm' : 'user-only' as the default visibility. That makes optional params default to user-only, which will hide them from the tool-input UI even though they’re intended to be user-editable (previous behavior showed these with an “(optional)” label).

This should match the existing defaulting used elsewhere (optional → user-or-llm, required → user-only/user-or-llm depending on policy), or at least avoid excluding optional params by default. As-is, many optional tool params will stop rendering for registry tools that don’t set paramVisibility explicitly.

Comment on lines +101 to +108
// Initialize the store on first mount
const hasInitializedRef = useRef(false)
useEffect(() => {
if (!hasInitializedRef.current && toolParamValue) {
hasInitializedRef.current = true
setStoreValue(toolParamValue)
}
}, []) // eslint-disable-line react-hooks/exhaustive-deps
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Store never initializes empty

The “Initialize the store on first mount” effect only runs when toolParamValue is truthy. If the current param is an empty string (a common initial state), the synthetic subblock store key is never initialized, so store-driven components may render with stale/undefined state until the user types.

This should initialize even for empty values (e.g., set once on mount regardless), or otherwise ensure useSubBlockValue starts from '' for these synthetic IDs.

Comment on lines +167 to +175
return (
<div key={paramId} className='relative min-w-0 space-y-[6px]'>
<div className='flex items-center justify-between gap-[6px] pl-[2px]'>
<Label className='flex items-center gap-[6px] whitespace-nowrap font-medium text-[13px] text-[var(--text-primary)]'>
{title}
{isRequired && visibility === 'user-only' && <span className='ml-0.5'>*</span>}
{visibility !== 'user-only' && (
<span className='ml-[6px] text-[12px] text-[var(--text-tertiary)]'>(optional)</span>
)}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Optional label logic broken

ParameterWithLabel shows “(optional)” for any visibility !== 'user-only'. That means user-or-llm and hidden/llm-only would be labeled optional, and it ignores isRequired for non-user-only params.

This should key off isRequired (or the param’s required flag) rather than visibility, otherwise required-but-user-or-llm params will be incorrectly labeled optional in the UI.

Copy link

@cursor cursor bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 2 potential issues.

Bugbot Autofix is OFF. To automatically fix reported issues with Cloud Agents, enable Autofix in the Cursor dashboard.

onToggle: () => {
const nextMode =
canonicalMode === 'advanced' ? 'basic' : 'advanced'
collaborativeSetBlockCanonicalMode(blockId, canonicalId, nextMode)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Canonical mode toggle shared across same-type tool instances

Medium Severity

The canonical mode override (basic/advanced toggle) is stored on the agent block via collaborativeSetBlockCanonicalMode(blockId, canonicalId, nextMode), keyed only by canonicalId (e.g., channelId). When two tools of the same type are added, they share the same canonicalId values, so toggling the mode on one instance changes the mode for all instances of that type. The canonicalModeOverrides read from the agent block is also passed to getSubBlocksForToolInput for each tool instance, causing both to render the same mode variant.

Additional Locations (2)

Fix in Cursor Fix in Web

</p>
</Tooltip.Content>
</Tooltip.Root>
)}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wand search UI duplicated across two components

Low Severity

The ParameterWithLabel component in tool-input.tsx duplicates the wand "Generate" search UI (button, input field, submit/cancel handlers, streaming state display, and canonical toggle) that already exists in renderLabel within sub-block.tsx. Both render the same Generate button, search input with identical CSS classes, key handlers, blur logic, and ArrowLeftRight canonical toggle. A shared component or extracted render function would avoid divergence if the wand UI behavior changes.

Fix in Cursor Fix in Web

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant