Entity Evaluators
All workflow entity types (Event, Prompt, Action, and others) are evaluated by a single UnifiedEntityEvaluator, built on BaseEntityEvaluator. Type-specific configuration (conditions, prompts, branching modes) still varies by entity type, but the execution pipeline is the same for every node.
Base Class
UnifiedEntityEvaluator extends BaseEntityEvaluator (rocketwave-stream-shared/src/execution/evaluators/BaseEntityEvaluator.ts), which owns injection helpers, condition helpers, prompt/model injection, handlebar processing, script execution, and context capture.
Shared Methods
| Method | Description |
|---|---|
injectMessage(context, message) | Inject incoming message fields as top-level globals in the V8 isolate |
injectArguments(context, entity, executionContext) | Inject entity arguments |
injectPromptAndModel(context, entity, executionContext) | Inject prompt/model data |
processPromptHandlebars(context, entity, executionContext) | Substitute {{...}} placeholders in prompt |
runScript(...) | Execute a preScript or postScript body inside an async wrapper |
getResultVariable(context) | Read ___result___ value |
setResultVariable(context, value) | Set ___result___ value |
evaluateVariable(context, variableName) | Read any variable value |
captureContextVariables(context, executionContext) | Capture all context vars |
Argument Injection
Arguments support two modes:
Literal Values:
argumentValue: "Hello World"
// Becomes: var argName = "Hello World";
Handlebar References:
argumentValue: "{{user.id}}"
// Becomes: var argName = user.id;
UnifiedEntityEvaluator
File location
rocketwave-stream-shared/src/execution/evaluators/UnifiedEntityEvaluator.ts (Consumer workers use this shared implementation).
Unified pipeline (all entity types)
Every node runs the same ordered steps:
- Pre-Script — If
entity.preScriptis non-empty, run it in the isolate (failure → exit workflow with error). - Inject arguments — Skipped when there are none; iterable parents may also inject
item/index. - Evaluate condition — If
entity.conditionis missing, result is true; otherwiseevaluateCondition(tree, message)runs and___result___is set. - Prompt + model — If a prompt and/or model is configured: inject prompt/model, handlebars, optional image resolution, then
executePromptWithModel()when a model is present (failures can exit the workflow). - Post-Script — If
entity.postScriptis non-empty, run it (failure → exit workflow with error). - Capture context — Persist eligible globals into
executionContextfor downstream nodes. - Branching —
determineBranch(...)implements Single Path, True/False, Multi (including*wildcard), and Iterable modes using the condition result andtfCondition/logicField/logicconfiguration.
Older deployments used separate EventEvaluator, PromptEvaluator, and ActionEvaluator classes. Those have been replaced by this single evaluator; field names on entities are now preScript and postScript instead of one script field.
Branching modes (after the unified steps)
| Mode | Configuration | Behavior |
|---|---|---|
| Single Path | tfCondition: "Single Path" | Continue if condition true, exit if false |
| True/False | tfCondition: "True/False" | Match child by value: "true" / "false" |
| Multi | tfCondition: "Multi", logicField | Read branch variable from context; match child value, else * wildcard |
| Iterable | tfCondition: "Iterable", logicField | Fan out children per array element |
Multi / wildcard: Same as before — explicit branch values match first; a child with value: "*" is the catch-all.
Triggers
Messages produced by workflow triggers are not auto-passed through the root node. They go through the same pipeline as any other message (pre-script → … → branching).
Return Actions
UnifiedEntityEvaluator returns an action object:
| Action | Meaning | Typical case |
|---|---|---|
exit_workflow | Stop this workflow path (condition failed, branch miss, or error) | After condition or branching, or script/model failure |
process_children | Continue to selected child nodes | Condition passed and branch resolved |
workflow_complete | Workflow finished, stop processing | Orchestrator (automatic) |
Action Object Structure
interface EvaluatorResult {
action: 'exit_workflow' | 'process_children' | 'workflow_complete';
children?: WorkflowNode[]; // For process_children
}
Script Execution Details
Wrapping for Async
Pre and post snippets are wrapped the same way (pseudocode):
async runScript(context, entity, scriptBody, phase /* 'preScript' | 'postScript' */) {
const wrappedScript = `(async () => { ${scriptBody} })()`;
await context.eval(wrappedScript, {
timeout: this.scriptTimeout,
promise: true
});
}
Timeout Handling
await context.eval(wrappedScript, {
timeout: this.scriptTimeout // Default 5000ms
});
// On timeout:
// Error: Script execution timed out
Error Recovery
Failures in preScript / postScript (or timeouts) are logged; the unified evaluator returns exit_workflow with error details instead of continuing when a script phase fails.
Context Variable Capture
After postScript (and prompt/model work), eligible globals are captured:
async captureContextVariables(context, executionContext) {
const varsJson = await context.eval(`JSON.stringify(
Object.keys(global).reduce((acc, key) => {
if (!key.startsWith('__') && typeof global[key] !== 'function') {
try {
acc[key] = global[key];
} catch (e) {}
}
return acc;
}, {})
)`);
const vars = JSON.parse(varsJson);
Object.assign(executionContext, vars);
}
This makes variables set by one entity available to downstream entities.
Related Topics
- Evaluator Overview — Main orchestrator
- Workflow Entities — Entity configuration
- Scripts Reference — Available script functions
- Processed Messages — View workflow results