@particle-academy/fancy-flow 0.10.1 → 0.12.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (49) hide show
  1. package/dist/{chunk-2DMBJN2O.js → chunk-D6W5FMCT.js} +6 -5
  2. package/dist/chunk-D6W5FMCT.js.map +1 -0
  3. package/dist/{chunk-NCPQDVUE.js → chunk-IK5DS5JP.js} +262 -29
  4. package/dist/chunk-IK5DS5JP.js.map +1 -0
  5. package/dist/{chunk-HNBO4HP3.js → chunk-L4AX73Q6.js} +12 -5
  6. package/dist/chunk-L4AX73Q6.js.map +1 -0
  7. package/dist/{chunk-VEI743ZX.js → chunk-MFMRTRPO.js} +3 -3
  8. package/dist/{chunk-VEI743ZX.js.map → chunk-MFMRTRPO.js.map} +1 -1
  9. package/dist/{chunk-CPSOC27D.js → chunk-U5F22BHV.js} +18 -4
  10. package/dist/chunk-U5F22BHV.js.map +1 -0
  11. package/dist/engine.cjs +20 -3
  12. package/dist/engine.cjs.map +1 -1
  13. package/dist/engine.d.cts +4 -2
  14. package/dist/engine.d.ts +4 -2
  15. package/dist/engine.js +2 -2
  16. package/dist/index.cjs +417 -189
  17. package/dist/index.cjs.map +1 -1
  18. package/dist/index.d.cts +4 -4
  19. package/dist/index.d.ts +4 -4
  20. package/dist/index.js +9 -9
  21. package/dist/registry/index.d.cts +149 -5
  22. package/dist/registry/index.d.ts +149 -5
  23. package/dist/registry.cjs +475 -71
  24. package/dist/registry.cjs.map +1 -1
  25. package/dist/registry.js +3 -2
  26. package/dist/runtime/index.d.cts +1 -1
  27. package/dist/runtime/index.d.ts +1 -1
  28. package/dist/runtime.cjs +20 -3
  29. package/dist/runtime.cjs.map +1 -1
  30. package/dist/runtime.js +3 -3
  31. package/dist/schema/index.d.cts +1 -1
  32. package/dist/schema/index.d.ts +1 -1
  33. package/dist/schema.cjs +11 -3
  34. package/dist/schema.cjs.map +1 -1
  35. package/dist/schema.js +2 -2
  36. package/dist/{types-BS3Gwnkq.d.cts → types-Dg2REAz5.d.cts} +6 -0
  37. package/dist/{types-BS3Gwnkq.d.ts → types-Dg2REAz5.d.ts} +6 -0
  38. package/dist/{types-BocBFh6l.d.ts → types-DnQ4TEHN.d.cts} +16 -2
  39. package/dist/{types-DKqaUjF_.d.cts → types-DtXOwXjA.d.ts} +16 -2
  40. package/dist/ux.cjs +5 -0
  41. package/dist/ux.cjs.map +1 -1
  42. package/dist/ux.d.cts +2 -2
  43. package/dist/ux.d.ts +2 -2
  44. package/dist/ux.js +1 -1
  45. package/package.json +1 -1
  46. package/dist/chunk-2DMBJN2O.js.map +0 -1
  47. package/dist/chunk-CPSOC27D.js.map +0 -1
  48. package/dist/chunk-HNBO4HP3.js.map +0 -1
  49. package/dist/chunk-NCPQDVUE.js.map +0 -1
@@ -1,5 +1,5 @@
1
1
  import { resolveNodePorts } from './chunk-TITD5W4Y.js';
2
- import { getNodeKind, defaultConfigFor, validateConfig } from './chunk-CPSOC27D.js';
2
+ import { getNodeKind, defaultConfigFor, validateConfig } from './chunk-U5F22BHV.js';
3
3
 
4
4
  // src/schema/workflow-schema.ts
5
5
  var WORKFLOW_SCHEMA_VERSION = 1;
@@ -72,12 +72,13 @@ function importWorkflow(schema, options = {}) {
72
72
  issues.push({ level: "warning", nodeId: n.id, message: `${iss.key}: ${iss.message}` });
73
73
  }
74
74
  }
75
+ const kindId = kind?.name ?? n.kind;
75
76
  return {
76
77
  id: n.id,
77
- type: n.kind,
78
+ type: kindId,
78
79
  position: { x: n.position?.x ?? 0, y: n.position?.y ?? 0 },
79
80
  data: {
80
- kind: n.kind,
81
+ kind: kindId,
81
82
  label: n.label ?? kind?.label ?? n.kind,
82
83
  description: n.description,
83
84
  config,
@@ -115,5 +116,5 @@ function workflowToBlob(schema) {
115
116
  }
116
117
 
117
118
  export { WORKFLOW_SCHEMA_URL, WORKFLOW_SCHEMA_VERSION, exportWorkflow, importWorkflow, workflowToBlob };
118
- //# sourceMappingURL=chunk-2DMBJN2O.js.map
119
- //# sourceMappingURL=chunk-2DMBJN2O.js.map
119
+ //# sourceMappingURL=chunk-D6W5FMCT.js.map
120
+ //# sourceMappingURL=chunk-D6W5FMCT.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/schema/workflow-schema.ts"],"names":[],"mappings":"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","file":"chunk-D6W5FMCT.js","sourcesContent":["import type { FlowEdge, FlowGraph, FlowNode, PortDescriptor } from \"../types\";\nimport { defaultConfigFor, getNodeKind, validateConfig } from \"../registry/registry\";\nimport { resolveNodePorts } from \"../registry/ports\";\n\n/** Schema version. Bump on breaking shape changes; add migrations as needed. */\nexport const WORKFLOW_SCHEMA_VERSION = 1 as const;\nexport const WORKFLOW_SCHEMA_URL = \"https://particle.academy/schemas/workflow/v1.json\";\n\nexport type WorkflowSchema = {\n $schema: typeof WORKFLOW_SCHEMA_URL;\n version: typeof WORKFLOW_SCHEMA_VERSION;\n metadata?: WorkflowMetadata;\n graph: {\n nodes: WorkflowSchemaNode[];\n edges: WorkflowSchemaEdge[];\n };\n view?: {\n viewport?: { x: number; y: number; zoom: number };\n };\n};\n\nexport type WorkflowMetadata = {\n id?: string;\n name?: string;\n description?: string;\n createdAt?: number;\n updatedAt?: number;\n author?: string;\n tags?: string[];\n};\n\nexport type WorkflowSchemaNode = {\n id: string;\n /** Registry kind name (e.g. \"memory_store\"). */\n kind: string;\n position: { x: number; y: number };\n label?: string;\n description?: string;\n config?: Record<string, unknown>;\n /**\n * Resolved ports, written on export.\n *\n * A kind may derive its ports from config (`switch_case` cases,\n * `llm_branch` routes), and that derivation is a JavaScript function — a\n * runtime in another language cannot execute it. Without the resolved ports\n * in the document, the PHP twin sees no declared outputs and falls back to a\n * single `out`, so every branch edge in an exported flow silently stops\n * firing. Serializing them keeps the schema self-describing and preserves\n * the cross-runtime guarantee: same JSON in, same routing out.\n *\n * Optional and additive — a hand-written schema may omit them, and each\n * runtime then falls back to its own kind registry.\n */\n inputs?: PortDescriptor[];\n outputs?: PortDescriptor[];\n};\n\nexport type WorkflowSchemaEdge = {\n id: string;\n source: string;\n target: string;\n sourceHandle?: string;\n targetHandle?: string;\n label?: string;\n};\n\nexport type ImportIssue = {\n level: \"error\" | \"warning\";\n nodeId?: string;\n edgeId?: string;\n message: string;\n};\n\nexport type ImportResult = {\n graph: FlowGraph;\n issues: ImportIssue[];\n /** True when the import produced a usable graph (errors may have been\n * rewritten to warnings via `lenient: true`). */\n ok: boolean;\n};\n\n/** Snapshot the in-memory graph as a portable WorkflowSchema. */\nexport function exportWorkflow(\n graph: FlowGraph,\n metadata?: WorkflowMetadata,\n view?: WorkflowSchema[\"view\"],\n): WorkflowSchema {\n return {\n $schema: WORKFLOW_SCHEMA_URL,\n version: WORKFLOW_SCHEMA_VERSION,\n metadata: metadata ? { ...metadata, updatedAt: Date.now() } : undefined,\n graph: {\n nodes: graph.nodes.map(toSchemaNode),\n edges: graph.edges.map(toSchemaEdge),\n },\n view,\n };\n}\n\nfunction toSchemaNode(n: FlowNode): WorkflowSchemaNode {\n const data: any = n.data ?? {};\n const kindName = data.kind ?? n.type ?? \"custom\";\n // Resolve through the same helper the canvas and runtime use, so a\n // config-driven kind writes its ACTUAL ports into the document instead of\n // leaving another language's runtime to guess at them.\n const ports = resolveNodePorts(n, getNodeKind(kindName) ?? undefined);\n return {\n id: n.id,\n kind: kindName,\n position: { x: n.position.x, y: n.position.y },\n label: data.label,\n description: data.description,\n config: data.config,\n inputs: ports.inputs,\n outputs: ports.outputs,\n };\n}\n\nfunction toSchemaEdge(e: FlowEdge): WorkflowSchemaEdge {\n return {\n id: e.id,\n source: e.source,\n target: e.target,\n sourceHandle: e.sourceHandle ?? undefined,\n targetHandle: e.targetHandle ?? undefined,\n label: typeof e.label === \"string\" ? e.label : undefined,\n };\n}\n\nexport type ImportOptions = {\n /** When true, unknown kinds become warnings + a \"custom\" placeholder\n * instead of errors. Default false. */\n lenient?: boolean;\n};\n\n/**\n * Hydrate a schema into runtime FlowGraph + validate kinds/configs against\n * the registry. Reports issues for unknown kinds, missing required config,\n * and dangling edges.\n */\nexport function importWorkflow(schema: unknown, options: ImportOptions = {}): ImportResult {\n const issues: ImportIssue[] = [];\n const lenient = options.lenient === true;\n\n if (!schema || typeof schema !== \"object\") {\n return { ok: false, graph: { nodes: [], edges: [] }, issues: [{ level: \"error\", message: \"Schema is not an object.\" }] };\n }\n const s = schema as Partial<WorkflowSchema>;\n if (s.version !== WORKFLOW_SCHEMA_VERSION) {\n issues.push({\n level: lenient ? \"warning\" : \"error\",\n message: `Unsupported workflow schema version: ${s.version} (expected ${WORKFLOW_SCHEMA_VERSION})`,\n });\n if (!lenient) return { ok: false, graph: { nodes: [], edges: [] }, issues };\n }\n\n const rawNodes = s.graph?.nodes ?? [];\n const rawEdges = s.graph?.edges ?? [];\n\n const nodes: FlowNode[] = rawNodes.map((n) => {\n const kind = getNodeKind(n.kind);\n if (!kind) {\n issues.push({\n level: lenient ? \"warning\" : \"error\",\n nodeId: n.id,\n message: `Unknown kind \"${n.kind}\" — register it before importing.`,\n });\n }\n const config = n.config ?? (kind ? defaultConfigFor(kind) : {});\n if (kind) {\n for (const iss of validateConfig(kind, config)) {\n issues.push({ level: \"warning\", nodeId: n.id, message: `${iss.key}: ${iss.message}` });\n }\n }\n // Canonicalise on the way in: a document may carry a pre-namespace id, and\n // rewriting it here means the graph converges on the canonical id the next\n // time it is saved, rather than carrying the ambiguous name forever.\n const kindId = kind?.name ?? n.kind;\n return {\n id: n.id,\n type: kindId,\n position: { x: n.position?.x ?? 0, y: n.position?.y ?? 0 },\n data: {\n kind: kindId,\n label: n.label ?? kind?.label ?? n.kind,\n description: n.description,\n config,\n // Carry serialized ports back onto the node so a round-trip is stable\n // and an unknown kind still routes the way the document described.\n ...(n.inputs ? { inputs: n.inputs } : {}),\n ...(n.outputs ? { outputs: n.outputs } : {}),\n } as any,\n };\n });\n\n const nodeIds = new Set(nodes.map((n) => n.id));\n const edges: FlowEdge[] = rawEdges\n .map((e) => {\n if (!nodeIds.has(e.source)) {\n issues.push({ level: \"warning\", edgeId: e.id, message: `Edge source \"${e.source}\" not found.` });\n return null;\n }\n if (!nodeIds.has(e.target)) {\n issues.push({ level: \"warning\", edgeId: e.id, message: `Edge target \"${e.target}\" not found.` });\n return null;\n }\n return {\n id: e.id,\n source: e.source,\n target: e.target,\n sourceHandle: e.sourceHandle,\n targetHandle: e.targetHandle,\n label: e.label,\n } as FlowEdge;\n })\n .filter((e): e is FlowEdge => e !== null);\n\n const ok = issues.every((i) => i.level !== \"error\");\n return { ok, graph: { nodes, edges }, issues };\n}\n\n/** Convenience: serialize a schema as a downloadable JSON Blob. */\nexport function workflowToBlob(schema: WorkflowSchema): Blob {\n return new Blob([JSON.stringify(schema, null, 2)], { type: \"application/json\" });\n}\n"]}
@@ -1,10 +1,162 @@
1
1
  import { Handle, Position } from './chunk-NF6NPY5N.js';
2
+ import { runFlow } from './chunk-L4AX73Q6.js';
2
3
  import { resolveNodePorts, nodeConfig } from './chunk-TITD5W4Y.js';
3
- import { getNodeKind, categoryAccent, registerNodeKind, listNodeKinds } from './chunk-CPSOC27D.js';
4
+ import { getNodeKind, categoryAccent, registerNodeKind, listNodeKinds, kindIds } from './chunk-U5F22BHV.js';
4
5
  import { RichInputPreview } from './chunk-F5RPRB7A.js';
5
6
  import { memo, useMemo, createElement } from 'react';
6
7
  import { jsxs, jsx } from 'react/jsx-runtime';
7
8
 
9
+ // src/registry/capabilities.ts
10
+ var llmClient = null;
11
+ function registerLlmClient(client) {
12
+ llmClient = client;
13
+ return () => {
14
+ if (llmClient === client) llmClient = null;
15
+ };
16
+ }
17
+ function getLlmClient() {
18
+ return llmClient;
19
+ }
20
+ var workflowResolver = null;
21
+ function registerWorkflowResolver(resolver) {
22
+ workflowResolver = resolver;
23
+ return () => {
24
+ if (workflowResolver === resolver) workflowResolver = null;
25
+ };
26
+ }
27
+ function getWorkflowResolver() {
28
+ return workflowResolver;
29
+ }
30
+ function capabilityStatus() {
31
+ let documentReady = false;
32
+ try {
33
+ documentReady = Boolean(globalThis.__fancyFlowDocumentAdapter);
34
+ } catch {
35
+ documentReady = false;
36
+ }
37
+ return {
38
+ llm: llmClient !== null,
39
+ workflow_resolver: workflowResolver !== null,
40
+ document: documentReady
41
+ };
42
+ }
43
+
44
+ // src/registry/subflow.ts
45
+ var DEFAULT_MAX_DEPTH = 8;
46
+ function subflowMode(config) {
47
+ const mode = config.mode;
48
+ return mode === "stream" || mode === "both" ? mode : "output";
49
+ }
50
+ function subflowPorts(config) {
51
+ const mode = subflowMode(config);
52
+ const ports = [{ id: "out", label: "result" }];
53
+ if (mode === "stream" || mode === "both") ports.unshift({ id: "stream", label: "stream" });
54
+ return ports;
55
+ }
56
+ var subflowExecutor = async (ctx) => {
57
+ const config = ctx.node.data?.config ?? {};
58
+ const ref = String(config.workflow ?? "").trim();
59
+ if (!ref) ctx.abort("subflow has no workflow reference configured");
60
+ const resolver = getWorkflowResolver();
61
+ if (!resolver) {
62
+ ctx.abort(
63
+ "No workflow resolver registered. Call registerWorkflowResolver() so subflow can find the workflow it references."
64
+ );
65
+ }
66
+ const maxDepth = Number.isFinite(config.maxDepth) ? Number(config.maxDepth) : DEFAULT_MAX_DEPTH;
67
+ const depth = ctx.depth ?? 0;
68
+ if (depth + 1 > maxDepth) {
69
+ ctx.abort(
70
+ `subflow depth limit reached (${maxDepth}) at "${ref}" \u2014 a workflow is referencing itself, directly or through a chain.`
71
+ );
72
+ }
73
+ const child = await resolver(ref);
74
+ if (!child) ctx.abort(`subflow could not resolve workflow "${ref}"`);
75
+ const mode = subflowMode(config);
76
+ const streaming = mode === "stream" || mode === "both";
77
+ const forward = (event) => {
78
+ if (!streaming) return;
79
+ const detail = event.type === "node-status" ? `${event.nodeId} ${event.status}` : event.type === "run-end" ? `finished (${event.ok ? "ok" : "failed"})` : event.type;
80
+ ctx.emit({
81
+ type: "log",
82
+ nodeId: ctx.node.id,
83
+ level: "info",
84
+ message: `[${ref}] ${detail}`
85
+ });
86
+ };
87
+ const result = await runFlow(
88
+ child,
89
+ config.executors ?? {},
90
+ forward,
91
+ {
92
+ initialInputs: config.inputs ?? {
93
+ // With no explicit mapping, hand the parent's inputs to the child's
94
+ // entry points — the obvious default, and it makes the simple case
95
+ // require no configuration at all.
96
+ __parent: ctx.inputs
97
+ },
98
+ depth: depth + 1
99
+ }
100
+ );
101
+ if (!result.ok) {
102
+ ctx.abort(`subflow "${ref}" failed: ${result.error ?? "unknown error"}`);
103
+ }
104
+ if (mode === "stream") {
105
+ return { __port: "stream", value: result.outputs };
106
+ }
107
+ if (mode === "both") {
108
+ return result.outputs;
109
+ }
110
+ return { __port: "out", value: result.outputs };
111
+ };
112
+
113
+ // src/registry/llm-branch.ts
114
+ function declaredRoutes(config) {
115
+ const raw = config.routes;
116
+ if (!Array.isArray(raw)) return [];
117
+ return raw.map((r) => ({ port: String(r?.port ?? "").trim(), description: r?.description })).filter((r) => r.port !== "");
118
+ }
119
+ function resolveFallbackPort(routes, fallbackEnabled) {
120
+ if (fallbackEnabled) return "fallback";
121
+ return routes[0]?.port ?? "out";
122
+ }
123
+ var llmBranchExecutor = async (ctx) => {
124
+ const config = ctx.node.data?.config ?? {};
125
+ const routes = declaredRoutes(config);
126
+ if (routes.length === 0) {
127
+ ctx.abort("llm_branch has no routes configured");
128
+ }
129
+ const client = getLlmClient();
130
+ if (!client) {
131
+ ctx.abort(
132
+ "No LLM client registered. Call registerLlmClient() with your provider adapter \u2014 fancy-flow ships the routing, not the model call."
133
+ );
134
+ }
135
+ const fallbackEnabled = config.fallback !== false;
136
+ const choice = await client.chooseRoute({
137
+ system: typeof config.system === "string" ? config.system : void 0,
138
+ prompt: String(config.prompt ?? ctx.inputs ?? ""),
139
+ routes,
140
+ provider: typeof config.provider === "string" ? config.provider : void 0,
141
+ model: typeof config.model === "string" ? config.model : void 0,
142
+ credential: typeof config.credential === "string" ? config.credential : void 0
143
+ });
144
+ const offered = new Set(routes.map((r) => r.port));
145
+ let port = choice?.port ?? "";
146
+ let reason = choice?.reason;
147
+ if (!offered.has(port)) {
148
+ const safe = resolveFallbackPort(routes, fallbackEnabled);
149
+ ctx.emit({
150
+ type: "log",
151
+ nodeId: ctx.node.id,
152
+ level: "warn",
153
+ message: `llm_branch: model returned "${port || "(nothing)"}", which is not a declared route. Routing to "${safe}".`
154
+ });
155
+ reason = reason ?? `unrecognised route "${port}"`;
156
+ port = safe;
157
+ }
158
+ return { __port: port, value: { route: port, reason, input: ctx.inputs } };
159
+ };
8
160
  function RegistryNodeInner(props) {
9
161
  const kindName = props.data.kind ?? props.type;
10
162
  const kind = useMemo(() => getNodeKind(kindName), [kindName]);
@@ -163,7 +315,8 @@ var HTTP_METHODS = [
163
315
  var KINDS = [
164
316
  // ───────────── Triggers ─────────────
165
317
  {
166
- name: "manual_trigger",
318
+ name: "@particle-academy/manual_trigger",
319
+ aliases: ["manual_trigger", "@fancy/manual_trigger"],
167
320
  category: "trigger",
168
321
  label: "Manual",
169
322
  description: "Entry point fired when the user clicks Run.",
@@ -172,7 +325,8 @@ var KINDS = [
172
325
  outputs: [{ id: "out" }]
173
326
  },
174
327
  {
175
- name: "webhook_trigger",
328
+ name: "@particle-academy/webhook_trigger",
329
+ aliases: ["webhook_trigger", "@fancy/webhook_trigger"],
176
330
  category: "trigger",
177
331
  label: "Webhook",
178
332
  description: "Triggered by an inbound HTTP request to a host-provided URL.",
@@ -189,7 +343,8 @@ var KINDS = [
189
343
  ]
190
344
  },
191
345
  {
192
- name: "schedule_trigger",
346
+ name: "@particle-academy/schedule_trigger",
347
+ aliases: ["schedule_trigger", "@fancy/schedule_trigger"],
193
348
  category: "trigger",
194
349
  label: "Schedule",
195
350
  description: "Fires on a cron schedule (host-implemented).",
@@ -209,7 +364,8 @@ var KINDS = [
209
364
  ]
210
365
  },
211
366
  {
212
- name: "user_input",
367
+ name: "@particle-academy/user_input",
368
+ aliases: ["user_input", "@fancy/user_input"],
213
369
  category: "human",
214
370
  label: "User Input",
215
371
  description: "Pause the flow until the user submits the configured form.",
@@ -249,7 +405,8 @@ var KINDS = [
249
405
  ]
250
406
  },
251
407
  {
252
- name: "rich_user_input",
408
+ name: "@particle-academy/rich_user_input",
409
+ aliases: ["rich_user_input", "@fancy/rich_user_input"],
253
410
  category: "human",
254
411
  label: "Rich User Input",
255
412
  description: "Pause the flow on a fully authored page \u2014 content, required reading, multi-section forms.",
@@ -272,9 +429,61 @@ var KINDS = [
272
429
  // what the person hitting this step will actually see.
273
430
  renderBody: (ctx) => createElement(RichInputPreview, { config: ctx.config ?? {} })
274
431
  },
432
+ {
433
+ name: "@particle-academy/subflow",
434
+ aliases: ["subflow", "@fancy/subflow"],
435
+ category: "logic",
436
+ label: "SubFlow",
437
+ description: "Run another workflow and bring its result \u2014 or its live progress \u2014 back into this one.",
438
+ icon: "\u29C9",
439
+ inputs: [{ id: "in" }],
440
+ // The stream port only exists when something actually streams.
441
+ outputs: (config) => subflowPorts(config ?? {}),
442
+ // Core, not marketplace: it runs a child graph through this same engine and
443
+ // needs nothing from outside except where workflows live.
444
+ executor: subflowExecutor,
445
+ configSchema: [
446
+ {
447
+ type: "text",
448
+ key: "workflow",
449
+ label: "Workflow",
450
+ required: true,
451
+ placeholder: "onboarding-v2",
452
+ description: "Reference resolved by the host's registerWorkflowResolver()."
453
+ },
454
+ {
455
+ type: "select",
456
+ key: "mode",
457
+ label: "Return",
458
+ default: "output",
459
+ options: [
460
+ { value: "output", label: "Output when it finishes" },
461
+ { value: "stream", label: "Stream progress as it runs" },
462
+ { value: "both", label: "Both \u2014 stream, then output" }
463
+ ],
464
+ description: "Streaming adds a second port so a parent can show progress instead of a spinner."
465
+ },
466
+ {
467
+ type: "json",
468
+ key: "inputs",
469
+ label: "Input mapping",
470
+ description: "Entry-point inputs for the child run. Omit to pass this node's inputs straight through."
471
+ },
472
+ {
473
+ type: "number",
474
+ key: "maxDepth",
475
+ label: "Max nesting depth",
476
+ default: DEFAULT_MAX_DEPTH,
477
+ min: 1,
478
+ max: 32,
479
+ description: "Guards against a workflow referencing itself."
480
+ }
481
+ ]
482
+ },
275
483
  // ───────────── Logic ─────────────
276
484
  {
277
- name: "branch",
485
+ name: "@particle-academy/branch",
486
+ aliases: ["branch", "@fancy/branch"],
278
487
  category: "logic",
279
488
  label: "Branch",
280
489
  description: "Multi-way branch on a condition or value.",
@@ -286,7 +495,8 @@ var KINDS = [
286
495
  ]
287
496
  },
288
497
  {
289
- name: "switch_case",
498
+ name: "@particle-academy/switch_case",
499
+ aliases: ["switch_case", "@fancy/switch_case"],
290
500
  category: "logic",
291
501
  label: "Switch",
292
502
  description: "Route to one of N labelled outputs based on a key.",
@@ -313,7 +523,8 @@ var KINDS = [
313
523
  ]
314
524
  },
315
525
  {
316
- name: "for_each",
526
+ name: "@particle-academy/for_each",
527
+ aliases: ["for_each", "@fancy/for_each"],
317
528
  category: "logic",
318
529
  label: "For Each",
319
530
  description: "Iterate over a list, emitting each item on `item`.",
@@ -326,7 +537,8 @@ var KINDS = [
326
537
  ]
327
538
  },
328
539
  {
329
- name: "merge",
540
+ name: "@particle-academy/merge",
541
+ aliases: ["merge", "@fancy/merge"],
330
542
  category: "logic",
331
543
  label: "Merge",
332
544
  description: "Combine multiple inputs into one object or array.",
@@ -344,7 +556,8 @@ var KINDS = [
344
556
  ]
345
557
  },
346
558
  {
347
- name: "wait",
559
+ name: "@particle-academy/wait",
560
+ aliases: ["wait", "@fancy/wait"],
348
561
  category: "logic",
349
562
  label: "Wait",
350
563
  description: "Sleep or wait for an external event.",
@@ -361,7 +574,8 @@ var KINDS = [
361
574
  ]
362
575
  },
363
576
  {
364
- name: "transform",
577
+ name: "@particle-academy/transform",
578
+ aliases: ["transform", "@fancy/transform"],
365
579
  category: "logic",
366
580
  label: "Transform",
367
581
  description: "Reshape data with an expression.",
@@ -378,7 +592,8 @@ var KINDS = [
378
592
  },
379
593
  // ───────────── Data ─────────────
380
594
  {
381
- name: "memory_store",
595
+ name: "@particle-academy/memory_store",
596
+ aliases: ["memory_store", "@fancy/memory_store"],
382
597
  category: "data",
383
598
  label: "Memory Store",
384
599
  description: "Read or write per-conversation memory.",
@@ -398,7 +613,8 @@ var KINDS = [
398
613
  ]
399
614
  },
400
615
  {
401
- name: "data_store",
616
+ name: "@particle-academy/data_store",
617
+ aliases: ["data_store", "@fancy/data_store"],
402
618
  category: "data",
403
619
  label: "Data Store",
404
620
  description: "Key-value or table read/write against a host store.",
@@ -426,7 +642,8 @@ var KINDS = [
426
642
  ]
427
643
  },
428
644
  {
429
- name: "variable",
645
+ name: "@particle-academy/variable",
646
+ aliases: ["variable", "@fancy/variable"],
430
647
  category: "data",
431
648
  label: "Variable",
432
649
  description: "Workflow-scoped value used by other nodes.",
@@ -438,7 +655,8 @@ var KINDS = [
438
655
  },
439
656
  // ───────────── AI ─────────────
440
657
  {
441
- name: "llm_call",
658
+ name: "@particle-academy/llm_call",
659
+ aliases: ["llm_call", "@fancy/llm_call"],
442
660
  category: "ai",
443
661
  label: "LLM Call",
444
662
  description: "Send a prompt + context to a model and receive a response.",
@@ -465,7 +683,8 @@ var KINDS = [
465
683
  ]
466
684
  },
467
685
  {
468
- name: "llm_branch",
686
+ name: "@particle-academy/llm_branch",
687
+ aliases: ["llm_branch", "@fancy/llm_branch"],
469
688
  category: "ai",
470
689
  label: "LLM Router",
471
690
  description: "Let a model choose which route the flow takes.",
@@ -474,6 +693,10 @@ var KINDS = [
474
693
  // Each declared route is a port. The executor returns `{ __port: id }`
475
694
  // (or `Port.only(id)` on the PHP runtime) to pick one.
476
695
  outputs: (config) => routePorts(config?.routes, config?.fallback),
696
+ // A shuttle, not an engine: it carries the routes out to whatever LLM
697
+ // client the host registered and carries the choice back. No provider SDK
698
+ // reaches core, so this stays a builtin without adding a dependency.
699
+ executor: llmBranchExecutor,
477
700
  configSchema: [
478
701
  {
479
702
  type: "textarea",
@@ -535,7 +758,8 @@ var KINDS = [
535
758
  ]
536
759
  },
537
760
  {
538
- name: "tool_use",
761
+ name: "@particle-academy/tool_use",
762
+ aliases: ["tool_use", "@fancy/tool_use"],
539
763
  category: "ai",
540
764
  label: "Tool Use",
541
765
  description: "Hand control to a host-registered tool by name.",
@@ -546,7 +770,8 @@ var KINDS = [
546
770
  ]
547
771
  },
548
772
  {
549
- name: "embed_search",
773
+ name: "@particle-academy/embed_search",
774
+ aliases: ["embed_search", "@fancy/embed_search"],
550
775
  category: "ai",
551
776
  label: "Embed & Search",
552
777
  description: "Embed a query and search a vector store.",
@@ -559,7 +784,8 @@ var KINDS = [
559
784
  },
560
785
  // ───────────── IO ─────────────
561
786
  {
562
- name: "api_request",
787
+ name: "@particle-academy/api_request",
788
+ aliases: ["api_request", "@fancy/api_request"],
563
789
  category: "io",
564
790
  label: "API Request",
565
791
  description: "HTTP request to any URL.",
@@ -573,7 +799,8 @@ var KINDS = [
573
799
  ]
574
800
  },
575
801
  {
576
- name: "webhook_out",
802
+ name: "@particle-academy/webhook_out",
803
+ aliases: ["webhook_out", "@fancy/webhook_out"],
577
804
  category: "io",
578
805
  label: "Send Webhook",
579
806
  description: "POST a payload to a configured URL.",
@@ -586,7 +813,8 @@ var KINDS = [
586
813
  },
587
814
  // ───────────── Human ─────────────
588
815
  {
589
- name: "human_approval",
816
+ name: "@particle-academy/human_approval",
817
+ aliases: ["human_approval", "@fancy/human_approval"],
590
818
  category: "human",
591
819
  label: "Human Approval",
592
820
  description: "Pause until a human approves or denies.",
@@ -600,7 +828,8 @@ var KINDS = [
600
828
  ]
601
829
  },
602
830
  {
603
- name: "notify",
831
+ name: "@particle-academy/notify",
832
+ aliases: ["notify", "@fancy/notify"],
604
833
  category: "human",
605
834
  label: "Notify",
606
835
  description: "Send a message via Slack / email / SMS / etc.",
@@ -624,7 +853,8 @@ var KINDS = [
624
853
  },
625
854
  // ───────────── Output ─────────────
626
855
  {
627
- name: "output",
856
+ name: "@particle-academy/output",
857
+ aliases: ["output", "@fancy/output"],
628
858
  category: "output",
629
859
  label: "Output",
630
860
  description: "Terminal node \u2014 captures the workflow's result.",
@@ -633,7 +863,8 @@ var KINDS = [
633
863
  outputs: []
634
864
  },
635
865
  {
636
- name: "log",
866
+ name: "@particle-academy/log",
867
+ aliases: ["log", "@fancy/log"],
637
868
  category: "output",
638
869
  label: "Log",
639
870
  description: "Send to the run feed.",
@@ -660,10 +891,12 @@ var BUILTIN_KINDS = KINDS;
660
891
  // src/registry/index.ts
661
892
  function buildNodeTypes() {
662
893
  const map = {};
663
- for (const k of listNodeKinds()) map[k.name] = RegistryNode;
894
+ for (const k of listNodeKinds()) {
895
+ for (const id of kindIds(k)) map[id] = RegistryNode;
896
+ }
664
897
  return map;
665
898
  }
666
899
 
667
- export { BUILTIN_KINDS, RegistryNode, buildNodeTypes, registerBuiltinKinds };
668
- //# sourceMappingURL=chunk-NCPQDVUE.js.map
669
- //# sourceMappingURL=chunk-NCPQDVUE.js.map
900
+ export { BUILTIN_KINDS, DEFAULT_MAX_DEPTH, RegistryNode, buildNodeTypes, capabilityStatus, declaredRoutes, getLlmClient, getWorkflowResolver, llmBranchExecutor, registerBuiltinKinds, registerLlmClient, registerWorkflowResolver, resolveFallbackPort, subflowExecutor, subflowMode, subflowPorts };
901
+ //# sourceMappingURL=chunk-IK5DS5JP.js.map
902
+ //# sourceMappingURL=chunk-IK5DS5JP.js.map