Skip to content

Commit c67c08b

Browse files
j15zclaude
andcommitted
fix(workflow): render agent tool chip names from static sources, not stored title
Tool entries in an agent block's inputs carry a mutable `title` in workflow state, which copilot edits (edit_workflow) could rewrite to change what the UI displays. Derive chip names from static/canonical sources instead: the block registry name for integration tools (raw type id if unregistered), the custom-tool record over the stored snapshot, the live MCP tool name, and a static literal for workflow-as-tool. The stored `title` still persists in state; the UI just no longer reads it for registry-backed tools. Covers both render paths: the expanded tool-input chips and the collapsed canvas summary (resolveToolsLabel, also used by workflow preview). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
1 parent 8b6c2df commit c67c08b

3 files changed

Lines changed: 54 additions & 17 deletions

File tree

apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/tool-input/tool-input.tsx

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1747,6 +1747,17 @@ export const ToolInput = memo(function ToolInput({
17471747
)
17481748
: []
17491749

1750+
// Display name comes from static sources (registry / canonical records),
1751+
// never from the workflow JSON's mutable `title`, so edits to the stored
1752+
// state cannot change what the UI shows.
1753+
const toolDisplayName = isCustomTool
1754+
? resolvedCustomTool?.title || customToolTitle
1755+
: isMcpTool
1756+
? mcpTool?.name || tool.title
1757+
: isWorkflowTool
1758+
? 'Workflow'
1759+
: toolBlock?.name || tool.type
1760+
17501761
const useSubBlocks = !isCustomTool && !isMcpTool && subBlocksResult?.subBlocks?.length
17511762
const displayParams: ToolParameterConfig[] = isCustomTool
17521763
? customToolParams
@@ -1854,7 +1865,7 @@ export const ToolInput = memo(function ToolInput({
18541865
)}
18551866
</div>
18561867
<span className='truncate font-medium text-[var(--text-primary)] text-small'>
1857-
{formatDisplayText((isCustomTool ? customToolTitle : tool.title) ?? '', {
1868+
{formatDisplayText(toolDisplayName ?? '', {
18581869
workflowSearchHighlight: getToolTitleSearchHighlight(toolIndex),
18591870
})}
18601871
</span>

apps/sim/lib/workflows/subblocks/display.test.ts

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,30 @@ describe('resolveToolsLabel', () => {
113113
null
114114
)
115115
})
116+
117+
it('ignores the stored title on registry-backed tools so state edits cannot rename them', () => {
118+
const slackName = resolveToolsLabel(toolInput, [{ type: 'slack' }], [])
119+
expect(slackName).not.toBe(null)
120+
expect(resolveToolsLabel(toolInput, [{ type: 'slack', title: 'Renamed By Copilot' }], [])).toBe(
121+
slackName
122+
)
123+
})
124+
125+
it('falls back to the raw type id for unregistered block-backed tools', () => {
126+
expect(
127+
resolveToolsLabel(toolInput, [{ type: 'not_a_real_block', title: 'Sneaky Title' }], [])
128+
).toBe('not_a_real_block')
129+
})
130+
131+
it('prefers the custom tool record over the stored title', () => {
132+
expect(
133+
resolveToolsLabel(
134+
toolInput,
135+
[{ type: 'custom-tool', customToolId: 'ct-1', title: 'Renamed By Copilot' }],
136+
[{ id: 'ct-1', title: 'My Tool' }]
137+
)
138+
).toBe('My Tool')
139+
})
116140
})
117141

118142
describe('resolveSkillsLabel', () => {

apps/sim/lib/workflows/subblocks/display.ts

Lines changed: 18 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -427,10 +427,11 @@ export function resolveVariablesLabel(
427427
}
428428

429429
/**
430-
* Resolves a tool-input value to a tool-name summary. Stored tool entries
431-
* come in several historical shapes, checked in priority order: explicit
432-
* title, custom tool referenced by id, inline schema name, OpenAI function
433-
* name, then the block registry.
430+
* Resolves a tool-input value to a tool-name summary. Names come from static
431+
* sources first (block registry, custom-tool records) so that edits to the
432+
* stored entry's `title` cannot change what the UI shows; the stored title
433+
* and inline schema names are only fallbacks for shapes with no canonical
434+
* source (MCP snapshots, legacy entries).
434435
*/
435436
export function resolveToolsLabel(
436437
subBlock: SubBlockConfig | undefined,
@@ -445,31 +446,32 @@ export function resolveToolsLabel(
445446
if (!tool || typeof tool !== 'object') return null
446447
const t = tool as Record<string, unknown>
447448

448-
if (typeof t.title === 'string' && t.title) return t.title
449+
if (
450+
typeof t.type === 'string' &&
451+
t.type !== 'custom-tool' &&
452+
t.type !== 'mcp' &&
453+
t.type !== 'workflow' &&
454+
t.type !== 'workflow_input'
455+
) {
456+
const blockConfig = getBlock(t.type)
457+
if (blockConfig?.name) return blockConfig.name
458+
return t.type
459+
}
449460

450461
if (t.type === 'custom-tool' && typeof t.customToolId === 'string') {
451462
const customTool = customTools.find((candidate) => candidate.id === t.customToolId)
452463
if (customTool?.title) return customTool.title
453464
if (customTool?.schema?.function?.name) return customTool.schema.function.name
454465
}
455466

467+
if (typeof t.title === 'string' && t.title) return t.title
468+
456469
const schema = t.schema as { function?: { name?: string } } | undefined
457470
if (schema?.function?.name) return schema.function.name
458471

459472
const fn = t.function as { name?: string } | undefined
460473
if (fn?.name) return fn.name
461474

462-
if (
463-
typeof t.type === 'string' &&
464-
t.type !== 'custom-tool' &&
465-
t.type !== 'mcp' &&
466-
t.type !== 'workflow' &&
467-
t.type !== 'workflow_input'
468-
) {
469-
const blockConfig = getBlock(t.type)
470-
if (blockConfig?.name) return blockConfig.name
471-
}
472-
473475
return null
474476
})
475477
.filter((name): name is string => !!name)

0 commit comments

Comments
 (0)