Skip to content

Commit 2f31981

Browse files
improvement(blocks): two-tier sunset schema (legacy amber / deprecated red) (#5793)
* improvement(blocks): two-tier sunset schema (legacy amber / deprecated red) * fix(blocks): require replacedBy on legacy sunset blocks in registry check * improvement(blocks): reword legacy badge tooltip
1 parent 644afd2 commit 2f31981

37 files changed

Lines changed: 153 additions & 123 deletions

apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/workflow-block/workflow-block.tsx

Lines changed: 43 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -81,39 +81,60 @@ const EMPTY_SUBBLOCK_VALUES = {} as Record<string, any>
8181
/** Stable empty map for rows that never resolve MCP tool names */
8282
const EMPTY_MCP_TOOL_NAMES: ReadonlyMap<string, string> = new Map()
8383

84-
interface BlockDeprecation {
84+
interface BlockSunset {
85+
status: 'legacy' | 'deprecated'
8586
kind: 'block' | 'model'
8687
tooltip: string
8788
prompt: string
8889
}
8990

91+
/** Instruction for the agent to migrate a block instance to its successor. */
92+
function migrationPrompt(name: string, target: BlockConfig): string {
93+
return `Migrate the "${name}" block to the current ${target.name} block: change the block type, then set the new block's required inputs as a separate edit (inputs are validated against the old type when sent in the same edit), or delete it and re-add ${target.name} and rewire the connections.`
94+
}
95+
9096
/**
91-
* Deprecation state for a placed block: the block type itself (via
92-
* `config.deprecated.replacedBy`) or its selected model. `null` when neither
93-
* applies or in diff mode. Drives the canvas badge + click-to-fix prompt.
97+
* Sunset state for a placed block: the block type itself (via `config.sunset`)
98+
* or its selected model. `legacy` (amber) is superseded-but-supported and needs
99+
* a resolvable successor; `deprecated` (red) is no longer supported and badges
100+
* with or without one. `null` when neither applies or in diff mode.
94101
*/
95-
function getBlockDeprecation(
102+
function getBlockSunset(
96103
config: BlockConfig,
97104
name: string,
98105
model: unknown,
99106
isDiffMode: boolean
100-
): BlockDeprecation | null {
107+
): BlockSunset | null {
101108
if (isDiffMode) return null
102109

103-
const replacedBy = config.deprecated?.replacedBy
104-
if (replacedBy) {
105-
const target = getBlock(replacedBy)
106-
if (!target) return null
107-
const hasModel = config.subBlocks?.some((sub) => sub.id === 'model')
110+
const sunset = config.sunset
111+
if (sunset) {
112+
const target = sunset.replacedBy ? getBlock(sunset.replacedBy) : undefined
113+
114+
if (sunset.status === 'legacy') {
115+
if (!target) return null
116+
const hasModel = config.subBlocks?.some((sub) => sub.id === 'model')
117+
return {
118+
status: 'legacy',
119+
kind: 'block',
120+
tooltip: 'This is a legacy block. Click to upgrade',
121+
prompt: `The "${name}" block is legacy. ${migrationPrompt(name, target)}${hasModel ? ' Also pick a current, non-deprecated model.' : ''}`,
122+
}
123+
}
124+
108125
return {
126+
status: 'deprecated',
109127
kind: 'block',
110-
tooltip: 'This block is deprecated. Click to upgrade',
111-
prompt: `The "${name}" block is deprecated. Migrate it to the current ${target.name} block: change the block type, then set the new block's required inputs as a separate edit (inputs are validated against the old type when sent in the same edit), or delete it and re-add ${target.name} and rewire the connections.${hasModel ? ' Also pick a current, non-deprecated model.' : ''}`,
128+
tooltip: 'This block is no longer supported. Click to replace',
129+
prompt: target
130+
? `The "${name}" block is no longer supported. ${migrationPrompt(name, target)}`
131+
: `The "${name}" block is no longer supported and has no direct successor. Replace it with current blocks that achieve the same result and rewire the connections.`,
112132
}
113133
}
114134

115135
if (typeof model === 'string' && isModelDeprecated(model)) {
116136
return {
137+
status: 'legacy',
117138
kind: 'model',
118139
tooltip: `${model} is deprecated. Click to upgrade`,
119140
prompt: `The "${name}" block uses the deprecated model "${model}". Switch it to the latest equivalent model.`,
@@ -533,21 +554,16 @@ export const WorkflowBlock = memo(function WorkflowBlock({
533554

534555
const posthog = usePostHog()
535556

536-
const deprecation = getBlockDeprecation(
537-
config,
538-
name,
539-
blockSubBlockValues.model,
540-
currentWorkflow.isDiffMode
541-
)
557+
const sunset = getBlockSunset(config, name, blockSubBlockValues.model, currentWorkflow.isDiffMode)
542558

543-
const onFixDeprecation = () => {
544-
if (!deprecation) return
559+
const onFixSunset = () => {
560+
if (!sunset) return
545561
captureEvent(posthog, 'deprecated_block_fix_clicked', {
546562
block_type: type,
547563
workflow_id: currentWorkflowId,
548-
kind: deprecation.kind,
564+
kind: sunset.kind,
549565
})
550-
sendMothershipMessage(deprecation.prompt, [
566+
sendMothershipMessage(sunset.prompt, [
551567
{ kind: 'workflow_block', workflowId: currentWorkflowId, blockId: id, label: name },
552568
])
553569
}
@@ -871,9 +887,10 @@ export const WorkflowBlock = memo(function WorkflowBlock({
871887
deployChildWorkflow({ workflowId: childWorkflowId })
872888
}
873889
}}
874-
deprecationTooltip={deprecation?.tooltip}
875-
canFixDeprecation={canEditWorkflow}
876-
onFixDeprecation={onFixDeprecation}
890+
sunsetStatus={sunset?.status}
891+
sunsetTooltip={sunset?.tooltip}
892+
canFixSunset={canEditWorkflow}
893+
onFixSunset={onFixSunset}
877894
shouldShowScheduleBadge={shouldShowScheduleBadge}
878895
scheduleIsDisabled={Boolean(scheduleInfo?.isDisabled)}
879896
onReactivateSchedule={() => {

apps/sim/blocks/blocks/api_trigger.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ export const ApiTriggerBlock: BlockConfig = {
1515
`,
1616
category: 'triggers',
1717
hideFromToolbar: true,
18-
deprecated: { replacedBy: 'start_trigger' },
18+
sunset: { status: 'legacy', replacedBy: 'start_trigger' },
1919
bgColor: '#2F55FF',
2020
icon: ApiIcon,
2121
subBlocks: [

apps/sim/blocks/blocks/chat_trigger.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ export const ChatTriggerBlock: BlockConfig = {
1616
`,
1717
category: 'triggers',
1818
hideFromToolbar: true,
19-
deprecated: { replacedBy: 'start_trigger' },
19+
sunset: { status: 'legacy', replacedBy: 'start_trigger' },
2020
bgColor: '#6F3DFA',
2121
icon: ChatTriggerIcon,
2222
subBlocks: [],

apps/sim/blocks/blocks/confluence.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ export const ConfluenceBlock: BlockConfig<ConfluenceResponse> = {
1212
name: 'Confluence (Legacy)',
1313
description: 'Interact with Confluence',
1414
hideFromToolbar: true,
15-
deprecated: { replacedBy: 'confluence_v2' },
15+
sunset: { status: 'legacy', replacedBy: 'confluence_v2' },
1616
authMode: AuthMode.OAuth,
1717
longDescription:
1818
'Integrate Confluence into the workflow. Can read, create, update, delete pages, manage comments, attachments, labels, and search content.',
@@ -361,7 +361,7 @@ export const ConfluenceBlock: BlockConfig<ConfluenceResponse> = {
361361

362362
export const ConfluenceV2Block: BlockConfig<ConfluenceResponse> = {
363363
...ConfluenceBlock,
364-
deprecated: undefined,
364+
sunset: undefined,
365365
type: 'confluence_v2',
366366
name: 'Confluence',
367367
hideFromToolbar: false,

apps/sim/blocks/blocks/cursor.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ export const CursorBlock: BlockConfig<CursorResponse> = {
1717
icon: CursorIcon,
1818
authMode: AuthMode.ApiKey,
1919
hideFromToolbar: true,
20-
deprecated: { replacedBy: 'cursor_v2' },
20+
sunset: { status: 'legacy', replacedBy: 'cursor_v2' },
2121
subBlocks: [
2222
{
2323
id: 'operation',
@@ -224,7 +224,7 @@ export const CursorBlock: BlockConfig<CursorResponse> = {
224224

225225
export const CursorV2Block: BlockConfig<CursorResponse> = {
226226
...CursorBlock,
227-
deprecated: undefined,
227+
sunset: undefined,
228228
type: 'cursor_v2',
229229
name: 'Cursor',
230230
description: 'Launch and manage Cursor cloud agents to work on GitHub repositories',

apps/sim/blocks/blocks/extend.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ export const ExtendBlock: BlockConfig<ExtendParserOutput> = {
1414
name: 'Extend',
1515
description: 'Parse and extract content from documents',
1616
hideFromToolbar: true,
17-
deprecated: { replacedBy: 'extend_v2' },
17+
sunset: { status: 'legacy', replacedBy: 'extend_v2' },
1818
authMode: AuthMode.ApiKey,
1919
longDescription:
2020
'Integrate Extend AI into the workflow. Parse and extract structured content from documents including PDFs, images, and Office files.',
@@ -166,7 +166,7 @@ const extendV2SubBlocks = (ExtendBlock.subBlocks || []).flatMap((subBlock) => {
166166

167167
export const ExtendV2Block: BlockConfig<ExtendParserOutput> = {
168168
...ExtendBlock,
169-
deprecated: undefined,
169+
sunset: undefined,
170170
type: 'extend_v2',
171171
name: 'Extend',
172172
hideFromToolbar: false,

apps/sim/blocks/blocks/file.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ export const FileBlock: BlockConfig<FileParserOutput> = {
7878
bgColor: '#40916C',
7979
icon: DocumentIcon,
8080
hideFromToolbar: true,
81-
deprecated: { replacedBy: 'file_v5' },
81+
sunset: { status: 'legacy', replacedBy: 'file_v5' },
8282
subBlocks: [
8383
{
8484
id: 'inputMethod',
@@ -186,7 +186,7 @@ export const FileV2Block: BlockConfig<FileParserOutput> = {
186186
name: 'File (Legacy)',
187187
description: 'Read and parse multiple files',
188188
hideFromToolbar: true,
189-
deprecated: { replacedBy: 'file_v5' },
189+
sunset: { status: 'legacy', replacedBy: 'file_v5' },
190190
subBlocks: [
191191
{
192192
id: 'file',
@@ -280,7 +280,7 @@ export const FileV3Block: BlockConfig<FileParserV3Output> = {
280280
bgColor: '#40916C',
281281
icon: DocumentIcon,
282282
hideFromToolbar: true,
283-
deprecated: { replacedBy: 'file_v5' },
283+
sunset: { status: 'legacy', replacedBy: 'file_v5' },
284284
subBlocks: [
285285
{
286286
id: 'operation',
@@ -571,7 +571,7 @@ export const FileV4Block: BlockConfig<FileParserV3Output> = {
571571
longDescription:
572572
'Read workspace files by picker or canonical ID, fetch and parse files from URLs with optional headers, write new workspace files, or append content to existing files.',
573573
hideFromToolbar: true,
574-
deprecated: { replacedBy: 'file_v5' },
574+
sunset: { status: 'legacy', replacedBy: 'file_v5' },
575575
bestPractices: `
576576
- Use Read when you need an existing workspace file object by picker selection or canonical file ID.
577577
- Use Fetch for external file URLs. Add headers for authenticated downloads, for example Slack private file URLs require an Authorization Bearer token.
@@ -824,7 +824,7 @@ export const FileV4Block: BlockConfig<FileParserV3Output> = {
824824

825825
export const FileV5Block: BlockConfig<FileParserV3Output> = {
826826
...FileV4Block,
827-
deprecated: undefined,
827+
sunset: undefined,
828828
type: 'file_v5',
829829
name: 'File',
830830
description:

apps/sim/blocks/blocks/fireflies.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ export const FirefliesBlock: BlockConfig<FirefliesResponse> = {
1111
name: 'Fireflies (Legacy)',
1212
description: 'Interact with Fireflies.ai meeting transcripts and recordings',
1313
hideFromToolbar: true,
14-
deprecated: { replacedBy: 'fireflies_v2' },
14+
sunset: { status: 'legacy', replacedBy: 'fireflies_v2' },
1515
authMode: AuthMode.ApiKey,
1616
triggerAllowed: true,
1717
longDescription:
@@ -647,7 +647,7 @@ const firefliesV2Inputs = FirefliesBlock.inputs
647647

648648
export const FirefliesV2Block: BlockConfig<FirefliesResponse> = {
649649
...FirefliesBlock,
650-
deprecated: undefined,
650+
sunset: undefined,
651651
type: 'fireflies_v2',
652652
name: 'Fireflies',
653653
description: 'Interact with Fireflies.ai meeting transcripts and recordings',

apps/sim/blocks/blocks/github.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ export const GitHubBlock: BlockConfig<GitHubResponse> = {
2020
icon: GithubIcon,
2121
triggerAllowed: true,
2222
hideFromToolbar: true,
23-
deprecated: { replacedBy: 'github_v2' },
23+
sunset: { status: 'legacy', replacedBy: 'github_v2' },
2424
subBlocks: [
2525
{
2626
id: 'operation',
@@ -2103,7 +2103,7 @@ Return ONLY the timestamp string - no explanations, no quotes, no extra text.`,
21032103

21042104
export const GitHubV2Block: BlockConfig<GitHubResponse> = {
21052105
...GitHubBlock,
2106-
deprecated: undefined,
2106+
sunset: undefined,
21072107
type: 'github_v2',
21082108
name: 'GitHub',
21092109
hideFromToolbar: false,

apps/sim/blocks/blocks/gmail.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ export const GmailBlock: BlockConfig<GmailToolResponse> = {
5757
bgColor: '#FFFFFF',
5858
icon: GmailIcon,
5959
hideFromToolbar: true,
60-
deprecated: { replacedBy: 'gmail_v2' },
60+
sunset: { status: 'legacy', replacedBy: 'gmail_v2' },
6161
triggerAllowed: true,
6262
subBlocks: [
6363
// Operation selector
@@ -576,7 +576,7 @@ Return ONLY the search query - no explanations, no extra text.`,
576576

577577
export const GmailV2Block: BlockConfig<GmailToolResponse> = {
578578
...GmailBlock,
579-
deprecated: undefined,
579+
sunset: undefined,
580580
type: 'gmail_v2',
581581
name: 'Gmail',
582582
hideFromToolbar: false,

0 commit comments

Comments
 (0)