Skip to content

feat(sdk) Variable enhancement - #76

Open
szymon-t-sc wants to merge 28 commits into
mainfrom
feat/WB-139-variables
Open

feat(sdk) Variable enhancement #76
szymon-t-sc wants to merge 28 commits into
mainfrom
feat/WB-139-variables

Conversation

@szymon-t-sc

Copy link
Copy Markdown
Member

Workflow Builder introduced variable support some time ago, allowing sidebar controls to use variables from previous nodes or global variables.

This PR enhances the feature with the missing logic to make variable usage more robust and flexible.

Main changes

Variables provided by nodes to downstream nodes can now depend on:

  • selected type in the sidebar - for example, selecting a time-based trigger returns schedule information, while selecting an event-based trigger returns information about the event type
  • sourceHandle - if a node has success and error source handles, it can provide the actual node output to the success branch and the error output to the error-handling branch
Nagranie.z.ekranu.2026-08-14.o.18.32.10.mov

Node variables store:

Previously, variables from previous nodes were calculated when the control was mounted. We traversed the graph and calculated their values at the same time - now we keep nodes variables in the store and only collect them:

  • The list of variables further down the flow is calculated faster, as we only collect the variables that are available
  • It's now easier to check which variables are available from a node, and we can easily add a plugin to display them
obraz

New single variable control (previously used in conditions)

Nagranie.z.ekranu.2026-08-14.o.18.28.48.mov

It shows a date picker with {} when date variables are available.

Fixes

  • When an edge was added or removed, we sometimes had incorrect highlighting of values in controls. This is now fixed thanks to totalVariables
  • Global variables cannot be added in read-only mode

It's worth noting that the majority of the changes are encapsulated in packages/sdk/src/features/variables/. So while reviewing, we can identify what is used externally as a useful public API and note the core functions that could be valuable to expose.

Comment thread apps/demo/src/app/data/nodes/action/action.ts Outdated
@szymon-t-sc
szymon-t-sc marked this pull request as draft August 27, 2026 09:46
@szymon-t-sc
szymon-t-sc marked this pull request as ready for review August 28, 2026 10:09
@szymon-t-sc szymon-t-sc added the enhancement New feature or request label Aug 28, 2026
});

return {
response: {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It injects a synthetic response variable into every schemaOutput**

Every node using the new schemaOutput ends up advertising {{nodes.<id>.response}} in the suggestions index, even when its schema declares no such property (e.g. the demo trigger only outputs eventType / timestamp). That reference is a promise the execution side doesn't keep: response is an ai-studio convention (its worker really does store output under that key), not a WB-wide contract. A generic engine won't have it, and resolve-template is intentionally strict, so a user who picks "Response" from the picker gets a run that fails on a variable the editor itself suggested.

Why nothing caught it: the injected entry is typed object, and all stock controls filter objects out (text controls, VariableDynamic, conditions), so it's invisible in the demo UI. And there's currently no place in the repo that both uses schemaOutput and executes (demo isn't executed; ai-studio's ai-agent is back on the deprecated outputSchema, which doesn't inject). It will surface through the newly public getNodeVariablesSuggestions, the planned variables plugin, and any future object-accepting control or schema-based validation.

Question: is there a concrete intent behind this (a "reference the whole output" feature)? If yes, let's make it an explicit, documented opt-in once the runtime actually guarantees the key. If not, my recommendation is to drop the injection: a node that exposes its whole result under response can declare it in its own schema, like ai-studio's ai-agent does today.

Side notes if it stays: the label is hardcoded English (bypasses i18n), and a schema that genuinely declares response wins over the injected one via spread order, so removal is backward-safe for those nodes.

Comment thread packages/sdk/src/index.ts
EDGE_OFFSET,
SELF_CONNECTING_EDGE_LABEL_OFFSET,
} from './features/diagram/edges/edge.consts';
export { VARIABLE_NODES_KEY } from './features/variables/constants';

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

VARIABLE_NODES_KEY silently dropped from the public API**

This export existed in 2.3.0 and is listed in the typedoc coverage decision log, so removing it in a minor is a semver break, even if a tiny one. Either re-add it with @deprecated, or keep the removal and call it out explicitly in a changeset. Trimming the rest of the surface (raw store etc.) was the right call since those were never released.

@@ -0,0 +1,5 @@
---
'@workflowbuilder/sdk': patch

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bump says patch, content says feature

Per-handle variables + JSON Schema outputs is a new capability, so this should be minor. No practical effect on the released version (the other changesets already force a minor), but the CHANGELOG entry will land in the wrong section.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

VariableDynamic is missing from the controls reference

The control has a changeset and demo usage, but no section here. Needs the usual table: variableType, placeholder, value contract (plain value vs a single {{...}} reference), and a pointer to the variable picker guide.

// Variables can’t change while the modal containing them is in use, so we only need to refresh them when they change.
// .length is critical here for performance.
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [nodeId, edges.length, nodes.length]);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The nodes.length / edges.length deps (and the key={totalVariables} remount in VariableDynamic) are a documented perf trade-off, and the sidebar remount masks most cases. Remaining gaps: rewiring an edge to a different source (same counts) and the 100 ms indexing race after addNode. A cheap fix later: subscribe to the suggestions-store state identity (one new object per refresh) instead of counts.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants