Skip to content

Commit 57d4e2b

Browse files
fix(custom-blocks): share one large-value id list so nested blocks propagate
1 parent dd9748f commit 57d4e2b

2 files changed

Lines changed: 38 additions & 13 deletions

File tree

apps/sim/executor/handlers/workflow/workflow-handler.test.ts

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1130,6 +1130,30 @@ describe('WorkflowBlockHandler', () => {
11301130
expect(ctx.largeValueExecutionIds).toContain(extensions.executionId)
11311131
})
11321132

1133+
it('shares one large-value id list so nested custom blocks propagate upward', async () => {
1134+
const ctx = customBlockContext()
1135+
await handler.execute(ctx, customBlock(), {})
1136+
1137+
const childIds = executorOptions[0].contextExtensions.largeValueExecutionIds
1138+
// Same array instance, not a copy — that is what lets a nested custom
1139+
// block's grandchild id reach the top-level invoker.
1140+
expect(childIds).toBe(ctx.largeValueExecutionIds)
1141+
1142+
// Simulate a nested custom block appending its own child id deeper down.
1143+
childIds.push('grandchild-execution-id')
1144+
expect(ctx.largeValueExecutionIds).toContain('grandchild-execution-id')
1145+
})
1146+
1147+
it('does not duplicate ids across repeated invocations', async () => {
1148+
const ctx = customBlockContext()
1149+
await handler.execute(ctx, customBlock(), {})
1150+
await handler.execute(ctx, customBlock(), {})
1151+
1152+
const ids = ctx.largeValueExecutionIds as string[]
1153+
expect(new Set(ids).size).toBe(ids.length)
1154+
expect(ids.filter((id) => id === 'parent-execution-id')).toHaveLength(1)
1155+
})
1156+
11331157
it('never forwards the consumer SSE callbacks into the source run', async () => {
11341158
const ctx = customBlockContext({
11351159
onBlockStart: vi.fn(),

apps/sim/executor/handlers/workflow/workflow-handler.ts

Lines changed: 14 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -255,6 +255,8 @@ export class WorkflowBlockHandler implements BlockHandler {
255255
let childSessionStarted = false
256256
/** Set once the child's session reached a terminal state, so the catch doesn't re-complete it. */
257257
let childSessionFinalized = false
258+
/** Large-value id list shared with the child (and any nested custom blocks). */
259+
let sharedLargeValueIds: string[] | undefined
258260
let childCancellation: { signal: AbortSignal; dispose: () => void } | undefined
259261
try {
260262
// A custom block runs the source's latest deployment; if the source has been
@@ -467,10 +469,16 @@ export class WorkflowBlockHandler implements BlockHandler {
467469
parentExecutionId: ctx.executionId,
468470
})
469471
// Large values are scoped by execution id, so the parent must be able to
470-
// read a large exposed output the child produced.
471-
ctx.largeValueExecutionIds = Array.from(
472-
new Set([...(ctx.largeValueExecutionIds ?? []), childExecutionId])
473-
)
472+
// read a large exposed output the child produced. ONE array is shared down
473+
// the whole chain rather than copied per hop: a nested custom block pushes
474+
// its own child id into this same list, so a grandchild's large output is
475+
// still materializable at the top level. Copying would strand those ids at
476+
// the depth that created them.
477+
ctx.largeValueExecutionIds ??= []
478+
sharedLargeValueIds = ctx.largeValueExecutionIds
479+
for (const id of [ctx.executionId, childExecutionId]) {
480+
if (id && !sharedLargeValueIds.includes(id)) sharedLargeValueIds.push(id)
481+
}
474482
}
475483

476484
// Trusted run metadata for the child's Start block. Every field describes
@@ -525,15 +533,8 @@ export class WorkflowBlockHandler implements BlockHandler {
525533
executionId: childExecutionId ?? ctx.executionId,
526534
// Large values are cached per execution id, so a child running under its
527535
// own id still needs the invoking run's id to read values in its inputs.
528-
...(childExecutionId
529-
? {
530-
largeValueExecutionIds: Array.from(
531-
new Set([
532-
...(ctx.executionId ? [ctx.executionId] : []),
533-
...(ctx.largeValueExecutionIds ?? []),
534-
])
535-
),
536-
}
536+
...(childExecutionId && sharedLargeValueIds
537+
? { largeValueExecutionIds: sharedLargeValueIds }
537538
: {}),
538539
// Same-workspace children share the parent's frozen payer decision so
539540
// internal tool calls (knowledge, guardrails, MCP, Mothership) can

0 commit comments

Comments
 (0)