Background
Extracted from #1365 (Bug 2). The other two bugs from that issue are fixed in #1368.
Problem
When a nested step function captures closure variables, the step bundle hoists the function and replaces the closure references with __private_getClosureVars(). This works correctly when the step is invoked through workflow context (the runtime provides the closure vars), but breaks when the enclosing function is called directly from normal code.
For example:
import { xai as xaiProvider } from '@ai-sdk/xai';
export function xai(...args) {
return async () => {
'use step';
return xaiProvider(...args);
};
}
In the step bundle output, the hoisted step function calls __private_getClosureVars() to retrieve args, but the enclosing xai() function just returns a bare reference to the hoisted function — it never sets up the closure vars. So calling xai('gpt-4') directly (not through a workflow) would fail because __private_getClosureVars() has no values to provide.
Expected behavior
Step functions that capture closure variables should continue to work when their enclosing function is called directly from client/step code, not only when orchestrated by a workflow.
Suggested approach
In the step bundle, the enclosing wrapper function should use AsyncLocalStorage (or equivalent) to provide the closure variable values before returning the step function reference, mimicking the same mechanism the workflow runtime uses. This way the step function works correctly regardless of whether it's invoked from workflow context or directly.
References
Background
Extracted from #1365 (Bug 2). The other two bugs from that issue are fixed in #1368.
Problem
When a nested step function captures closure variables, the step bundle hoists the function and replaces the closure references with
__private_getClosureVars(). This works correctly when the step is invoked through workflow context (the runtime provides the closure vars), but breaks when the enclosing function is called directly from normal code.For example:
In the step bundle output, the hoisted step function calls
__private_getClosureVars()to retrieveargs, but the enclosingxai()function just returns a bare reference to the hoisted function — it never sets up the closure vars. So callingxai('gpt-4')directly (not through a workflow) would fail because__private_getClosureVars()has no values to provide.Expected behavior
Step functions that capture closure variables should continue to work when their enclosing function is called directly from client/step code, not only when orchestrated by a workflow.
Suggested approach
In the step bundle, the enclosing wrapper function should use
AsyncLocalStorage(or equivalent) to provide the closure variable values before returning the step function reference, mimicking the same mechanism the workflow runtime uses. This way the step function works correctly regardless of whether it's invoked from workflow context or directly.References
newexpressions and module-level declarations #1368