11import { createLogger } from '@sim/logger'
22import { getErrorMessage } from '@sim/utils/errors'
33import { generateId } from '@sim/utils/id'
4+ import { getPersonalAndWorkspaceEnv } from '@/lib/environment/utils'
45import { buildNextCallChain , validateCallChain } from '@/lib/execution/call-chain'
56import { snapshotService } from '@/lib/logs/execution/snapshot/service'
67import { buildTraceSpans } from '@/lib/logs/execution/trace-spans/trace-spans'
@@ -239,10 +240,32 @@ export class WorkflowBlockHandler implements BlockHandler {
239240 )
240241 }
241242
243+ // A custom block is an invocation boundary: the child runs under the SOURCE
244+ // workflow owner's identity, workspace, and environment — not the consumer's —
245+ // so it resolves credentials/integrations/env exactly as published and the
246+ // consumer needs no access to any of them. (Billing still lands on the
247+ // consumer's org, aggregated onto the block above.) Regular workflow blocks
248+ // keep running in the parent's context.
249+ let childUserId = ctx . userId
250+ let childWorkspaceId = ctx . workspaceId
251+ let childEnvVarValues = ctx . environmentVariables
252+ if ( isCustomBlock ) {
253+ if ( ! loadUserId ) {
254+ throw new Error ( 'Custom block source workflow has no owner' )
255+ }
256+ if ( ! childWorkflow . workspaceId ) {
257+ throw new Error ( 'Custom block source workflow has no workspace' )
258+ }
259+ childUserId = loadUserId
260+ childWorkspaceId = childWorkflow . workspaceId
261+ const ownerEnv = await getPersonalAndWorkspaceEnv ( loadUserId , childWorkflow . workspaceId )
262+ childEnvVarValues = { ...ownerEnv . personalDecrypted , ...ownerEnv . workspaceDecrypted }
263+ }
264+
242265 const subExecutor = new Executor ( {
243266 workflow : childWorkflow . serializedState ,
244267 workflowInput : childWorkflowInput ,
245- envVarValues : ctx . environmentVariables ,
268+ envVarValues : childEnvVarValues ,
246269 workflowVariables : childWorkflow . variables || { } ,
247270 contextExtensions : {
248271 isChildExecution : true ,
@@ -251,8 +274,8 @@ export class WorkflowBlockHandler implements BlockHandler {
251274 // deployed graph as draft. `useDeployed` folds in the custom-block case.
252275 isDeployedContext : useDeployed ,
253276 enforceCredentialAccess : ctx . enforceCredentialAccess ,
254- workspaceId : ctx . workspaceId ,
255- userId : ctx . userId ,
277+ workspaceId : childWorkspaceId ,
278+ userId : childUserId ,
256279 executionId : ctx . executionId ,
257280 abortSignal : ctx . abortSignal ,
258281 // Propagate in-flight block-output redaction into child workflows so
0 commit comments