@uipath/maestro-builder-sdk 6.40.1 → 6.41.1

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.
@@ -0,0 +1,652 @@
1
+ /**
2
+ * The process family's node types — one per published-resource kind this SDK
3
+ * starts as a job: an RPA process, an API workflow, a published function, and an
4
+ * agentic process in each of its three forms. Each type's name carries the
5
+ * resource's key (`uipath.core.function.<key>`), so the type owns every name under
6
+ * its prefix (`typeNamePrefix`) and supplies the definition it built, transcribed
7
+ * from the platform's own synthesized one (per-tenant). Moved from serialize.ts /
8
+ * check.ts / decompile.ts in Phase 4 of docs/NODE_TYPE_ARCHITECTURE_DESIGN.md;
9
+ * serialized output, printed source, codes and messages unchanged.
10
+ */
11
+ import { AGENTIC_PROCESS_FORMS } from '../../../core/agentic-process-forms.js';
12
+ import { definitionHasErrorPort } from '../../../core/error-port.js';
13
+ import { Expr } from '../../../core/expr.js';
14
+ import { ownEntry } from '../../../core/own-entry.js';
15
+ import { resourceNodeSortOrder } from '../../../core/process-resource-sort-order.js';
16
+ import { processResourceBindings, processResourceLocation } from '../../../core/process-resource.js';
17
+ import { checkAgenticProcess, checkAgenticProcessRead, checkApiWorkflow, checkApiWorkflowRead, checkPublishedFunction, checkPublishedFunctionRead, checkRpaWorkflow, rpaWorkflowRead, } from './check.js';
18
+ /** The RPA-workflow node type family. The process's release key is the suffix. */
19
+ const RPA_WORKFLOW_PREFIX = 'uipath.core.rpa-workflow.';
20
+ /**
21
+ * Build the definition for one deployed RPA process.
22
+ *
23
+ * This definition CANNOT be bundled, and that is not an oversight: the node type
24
+ * carries the process's release key, and the contract (which arguments it takes,
25
+ * what it returns) belongs to whatever someone published on a tenant. The
26
+ * platform's own answer is per-tenant too — `uip maestro flow registry get
27
+ * uipath.core.rpa-workflow.<key>` synthesizes it from the deployed process.
28
+ *
29
+ * So the static parts below are transcribed from the deployed corpus rather than
30
+ * invented: 45 `uipath.core.rpa-workflow.*` instances across 47 `.flow`
31
+ * artifacts of `skill-flow-rpa` (2026-05-18 → 07-24), cross-checked against
32
+ * `registry get` for the same process. Every constant here is one those agree
33
+ * on — `category: 'rpa-workflow'`, `sortOrder: 530`, `supportsErrorHandling`,
34
+ * `icon: 'rpa'`, the `bpmn:ServiceTask` / `Orchestrator.StartJob` / `v2` model,
35
+ * `section: 'Published'` (42/45), `debug.runtime: 'bpmnEngine'`, and the
36
+ * `<bindings.name>` / `<bindings.folderPath>` context placeholders.
37
+ * `sortOrder` is not one of those constants any more: flow-core derives it from
38
+ * the category, and renumbered after 07-29, so it comes from
39
+ * `resourceNodeSortOrder` (#1045).
40
+ *
41
+ * Two fields are derived from the author's call rather than from the tenant, and
42
+ * that limit is real:
43
+ *
44
+ * - `inputDefinition` declares exactly the arguments the author passes, typed
45
+ * from the values passed. That is the PR-10 payload-schema pattern — declare
46
+ * what is evidenced — but here the evidence is the authored call, not the
47
+ * deployed process, so it cannot catch an argument the process does not have.
48
+ * Nothing local can: only the robot knows. A wrong argument name faults the
49
+ * job on a live run, which is what the `robot` rung is for.
50
+ * - `outputDefinition.output` carries no schema for the same reason. Its source
51
+ * expressions follow the tenant-current registry (`=this` / `=Error`). Older
52
+ * corpus artifacts mostly used `=result.response` / `=result.Error`, but a
53
+ * 2026-08-04 product debug proved that old spelling now completes the RPA
54
+ * element while publishing a null step output.
55
+ */
56
+ function rpaWorkflowDef(spec) {
57
+ const loc = processResourceLocation(spec);
58
+ const { resourceKey } = loc;
59
+ const properties = {};
60
+ for (const [k, v] of Object.entries(spec.inputs)) {
61
+ properties[k] = { type: typeof v === 'number' ? 'number' : typeof v === 'boolean' ? 'boolean' : 'string' };
62
+ }
63
+ return {
64
+ nodeType: `${RPA_WORKFLOW_PREFIX}${spec.key}`,
65
+ version: '1.0.0',
66
+ category: 'rpa-workflow',
67
+ runtimeConstraints: { exclude: ['api-function'] },
68
+ description: loc.description,
69
+ tags: loc.tags,
70
+ sortOrder: resourceNodeSortOrder('rpa-workflow'),
71
+ supportsErrorHandling: true,
72
+ display: { label: spec.name, icon: 'rpa' },
73
+ handleConfiguration: [
74
+ { position: 'left', handles: [{ id: 'input', type: 'target', handleType: 'input' }] },
75
+ { position: 'right', handles: [{ id: 'output', type: 'source', handleType: 'output' }] },
76
+ ],
77
+ ...loc.definitionExtras,
78
+ model: {
79
+ type: 'bpmn:ServiceTask',
80
+ serviceType: 'Orchestrator.StartJob',
81
+ version: 'v2',
82
+ ...(loc.inSolution ? {} : { section: 'Published' }),
83
+ bindings: {
84
+ resource: 'process',
85
+ resourceSubType: 'Process',
86
+ resourceKey,
87
+ orchestratorType: 'process',
88
+ values: [
89
+ { name: 'name', propertyAttribute: 'name', default: spec.name },
90
+ { name: 'folderPath', propertyAttribute: 'folderPath', default: loc.folderPath },
91
+ ],
92
+ },
93
+ ...loc.modelExtras,
94
+ context: [
95
+ { name: 'name', type: 'string', value: '<bindings.name>' },
96
+ { name: 'folderPath', type: 'string', value: '<bindings.folderPath>' },
97
+ { name: '_label', type: 'string', value: spec.name },
98
+ ],
99
+ },
100
+ inputDefinition: Object.keys(properties).length ? { type: 'object', properties } : {},
101
+ inputDefaults: {},
102
+ outputDefinition: {
103
+ output: {
104
+ type: 'object',
105
+ description: 'The return value of the RPA process',
106
+ source: '=this',
107
+ var: 'output',
108
+ // The process's output arguments, as the author declared them. The
109
+ // platform's own definition carries this when it can read the deployed
110
+ // process (`registry get` returns exactly this shape); offline the author
111
+ // is the only source, and without it a downstream read is unresolvable ("reads
112
+ // from an undefined object") — rightly, since nothing could tell a real
113
+ // field from a typo.
114
+ ...(spec.returns && Object.keys(spec.returns).length
115
+ ? {
116
+ schema: {
117
+ type: 'object',
118
+ properties: Object.fromEntries(Object.entries(spec.returns).map(([k, t]) => [k, { type: t }])),
119
+ },
120
+ }
121
+ : {}),
122
+ },
123
+ error: {
124
+ type: 'object',
125
+ description: 'Error information if the RPA process fails',
126
+ source: '=Error',
127
+ var: 'error',
128
+ },
129
+ },
130
+ debug: { runtime: 'bpmnEngine' },
131
+ };
132
+ }
133
+ /** The API-workflow node type family. The published workflow's key is the suffix. */
134
+ const API_WORKFLOW_PREFIX = 'uipath.core.api-workflow.';
135
+ /**
136
+ * Build the definition for one deployed API workflow.
137
+ *
138
+ * Like rpa's, this definition CANNOT be bundled: the node type carries the
139
+ * workflow's key and the platform synthesizes the contract from whatever someone
140
+ * published on a tenant (`uip maestro flow registry get
141
+ * uipath.core.api-workflow.<key>` answers per-tenant). Unlike rpa's, the
142
+ * platform's copy declares a real OUTPUT SCHEMA as well as an input one — for
143
+ * `Shared.NameToAgeFixed` it is `{EstimatedAge: integer}` — which is still not
144
+ * knowable offline, so `returns` is what supplies it.
145
+ *
146
+ * The constants are transcribed from what the deployed corpus and the registry
147
+ * AGREE on, not invented: 42 `uipath.core.api-workflow.*` instances across 42
148
+ * dated `skill-flow-api-workflow` artifacts (2026-05-18 → 07-29), cross-checked
149
+ * against `registry get` for today's workflow. Every one is unanimous —
150
+ * `category: 'api-workflow'`, `sortOrder: 510` (now 640, see `resourceNodeSortOrder`, #1045), `supportsErrorHandling`,
151
+ * `icon: 'api'`, the `bpmn:ServiceTask` / `Orchestrator.ExecuteApiWorkflowAsync` /
152
+ * `v2` model, `section: 'Published'` (37/42; the 5 without are the in-solution
153
+ * shape, a bare-GUID resourceKey — rpa's 3/45 split again), `resourceSubType:
154
+ * 'Api'`, `orchestratorType: 'api'`, `debug.runtime: 'bpmnEngine'`, and the
155
+ * `<bindings.name>` / `<bindings.folderPath>` context placeholders.
156
+ *
157
+ * `version` is `1.0.0` in 42 of 42 and the registry agrees: one version, no
158
+ * migrations, so unlike queue's there is no era to be a version behind on. What
159
+ * there IS an era on is the tenant RESOURCE — the corpus splits 31/4 between two
160
+ * different workflows with two different output field names (`estimatedAge` vs
161
+ * `EstimatedAge`) — which is why the author passes the three identifiers rather than
162
+ * anything here defaulting them.
163
+ *
164
+ * (This comment used to say those 31 "name a workflow that no longer exists". That
165
+ * was backwards: re-dated 2026-07-29 with `--all-folders --limit 300`, 36 of the 42
166
+ * still resolve, the 31 among them, and the 6 that do not are early shapes with a
167
+ * blank or stale folder. The claim rested on the 50-row truncation PR 14 found. See
168
+ * `typescript/tests/api-workflow.test.ts`'s header for the recount.)
169
+ *
170
+ * `outputs.output.source` is `=this`, which is what the definition itself
171
+ * declares. The corpus is split (`=result.response` 33/42, `=this` 6/42, and two
172
+ * passing runs a day apart disagree), so the platform accepts either; we emit the
173
+ * one the platform's own definition writes.
174
+ */
175
+ function apiWorkflowDef(spec) {
176
+ const resourceKey = `${spec.folderPath}.${spec.name}`;
177
+ const properties = {};
178
+ for (const [k, v] of Object.entries(spec.inputs)) {
179
+ properties[k] = { type: typeof v === 'number' ? 'number' : typeof v === 'boolean' ? 'boolean' : 'string' };
180
+ }
181
+ return {
182
+ nodeType: `${API_WORKFLOW_PREFIX}${spec.key}`,
183
+ version: '1.0.0',
184
+ category: 'api-workflow',
185
+ runtimeConstraints: { exclude: ['api-function'] },
186
+ description: `(${spec.folderPath})`,
187
+ tags: [],
188
+ sortOrder: resourceNodeSortOrder('api-workflow'),
189
+ supportsErrorHandling: true,
190
+ display: { label: spec.name, icon: 'api' },
191
+ handleConfiguration: [
192
+ { position: 'left', handles: [{ id: 'input', type: 'target', handleType: 'input' }] },
193
+ { position: 'right', handles: [{ id: 'output', type: 'source', handleType: 'output' }] },
194
+ ],
195
+ model: {
196
+ type: 'bpmn:ServiceTask',
197
+ serviceType: 'Orchestrator.ExecuteApiWorkflowAsync',
198
+ version: 'v2',
199
+ section: 'Published',
200
+ bindings: {
201
+ resource: 'process',
202
+ resourceSubType: 'Api',
203
+ resourceKey,
204
+ orchestratorType: 'api',
205
+ values: [
206
+ { name: 'name', propertyAttribute: 'name', default: spec.name },
207
+ { name: 'folderPath', propertyAttribute: 'folderPath', default: spec.folderPath },
208
+ ],
209
+ },
210
+ context: [
211
+ { name: 'name', type: 'string', value: '<bindings.name>' },
212
+ { name: 'folderPath', type: 'string', value: '<bindings.folderPath>' },
213
+ { name: '_label', type: 'string', value: spec.name },
214
+ ],
215
+ },
216
+ inputDefinition: Object.keys(properties).length ? { type: 'object', properties } : {},
217
+ inputDefaults: {},
218
+ outputDefinition: {
219
+ output: {
220
+ type: 'object',
221
+ source: '=this',
222
+ var: 'output',
223
+ // The workflow's output arguments, as the author declared them — the same
224
+ // shape `registry get` returns when it can reach the deployed workflow.
225
+ // Without it a downstream read is unresolvable, rightly: nothing offline could
226
+ // tell a real field from a typo.
227
+ ...(spec.returns && Object.keys(spec.returns).length
228
+ ? {
229
+ schema: {
230
+ type: 'object',
231
+ properties: Object.fromEntries(Object.entries(spec.returns).map(([k, t]) => [k, { type: t }])),
232
+ },
233
+ }
234
+ : {}),
235
+ },
236
+ error: {
237
+ type: 'object',
238
+ description: 'Error information if the node fails',
239
+ source: '=Error',
240
+ var: 'error',
241
+ },
242
+ },
243
+ debug: { runtime: 'bpmnEngine' },
244
+ };
245
+ }
246
+ /** The agentic-process node type family. The published process's key is the suffix. */
247
+ const FUNCTION_PREFIX = 'uipath.core.function.';
248
+ /**
249
+ * The definition for one published Orchestrator FUNCTION, transcribed from the
250
+ * platform's own synthesized manifest (`uip maestro flow registry get
251
+ * uipath.core.function.<key>`, read 2026-08-19): the api-workflow shape with
252
+ * this family's service type (`Orchestrator.ExecuteFunctionAsync`), binding
253
+ * subtype (`Function`), `orchestratorType: 'function'`, and its own
254
+ * `listingVersion` — plus the `error` source handle the registry declares
255
+ * alongside `output` (gated on `errorHandlingEnabled`, as everywhere else).
256
+ */
257
+ function publishedFunctionDef(spec) {
258
+ const resourceKey = `${spec.folderPath}.${spec.name}`;
259
+ const properties = {};
260
+ for (const [k, v] of Object.entries(spec.inputs)) {
261
+ properties[k] = { type: typeof v === 'number' ? 'number' : typeof v === 'boolean' ? 'boolean' : 'string' };
262
+ }
263
+ return {
264
+ nodeType: `${FUNCTION_PREFIX}${spec.key}`,
265
+ version: '1.0.0',
266
+ category: 'function',
267
+ description: `(${spec.folderPath})`,
268
+ tags: [],
269
+ sortOrder: resourceNodeSortOrder('function'),
270
+ supportsErrorHandling: true,
271
+ display: { label: spec.name, icon: 'function' },
272
+ handleConfiguration: [
273
+ { position: 'left', handles: [{ id: 'input', type: 'target', handleType: 'input' }] },
274
+ {
275
+ position: 'right',
276
+ handles: [
277
+ { id: 'output', type: 'source', handleType: 'output' },
278
+ {
279
+ id: 'error', label: 'Error', type: 'source', handleType: 'output',
280
+ visible: '{inputs.errorHandlingEnabled}',
281
+ constraints: { maxConnections: 1 },
282
+ },
283
+ ],
284
+ },
285
+ ],
286
+ model: {
287
+ type: 'bpmn:ServiceTask',
288
+ serviceType: 'Orchestrator.ExecuteFunctionAsync',
289
+ version: 'v2',
290
+ section: 'Published',
291
+ listingVersion: '1.0.0',
292
+ bindings: {
293
+ resource: 'process',
294
+ resourceSubType: 'Function',
295
+ resourceKey,
296
+ orchestratorType: 'function',
297
+ values: [
298
+ { name: 'name', propertyAttribute: 'name', default: spec.name },
299
+ { name: 'folderPath', propertyAttribute: 'folderPath', default: spec.folderPath },
300
+ ],
301
+ },
302
+ context: [
303
+ { name: 'name', type: 'string', value: '<bindings.name>' },
304
+ { name: 'folderPath', type: 'string', value: '<bindings.folderPath>' },
305
+ { name: '_label', type: 'string', value: spec.name },
306
+ ],
307
+ },
308
+ inputDefinition: Object.keys(properties).length ? { type: 'object', properties } : {},
309
+ inputDefaults: {},
310
+ outputDefinition: {
311
+ output: {
312
+ type: 'object',
313
+ source: '=this',
314
+ var: 'output',
315
+ // The function's output arguments as the author declared them — same
316
+ // rule and same reason as the api-workflow family's.
317
+ ...(spec.returns && Object.keys(spec.returns).length
318
+ ? {
319
+ schema: {
320
+ type: 'object',
321
+ properties: Object.fromEntries(Object.entries(spec.returns).map(([k, t]) => [k, { type: t }])),
322
+ },
323
+ }
324
+ : {}),
325
+ },
326
+ error: {
327
+ type: 'object',
328
+ description: 'Error information if the node fails',
329
+ source: '=Error',
330
+ var: 'error',
331
+ },
332
+ },
333
+ debug: { runtime: 'bpmnEngine' },
334
+ };
335
+ }
336
+ /**
337
+ * Build the definition for one deployed agentic process (a Maestro process
338
+ * orchestration).
339
+ *
340
+ * Unbundlable for rpa's and api-workflow's reason — the node type carries a
341
+ * tenant's process key and the platform synthesizes the contract from whatever
342
+ * someone published there. So the constants are transcribed from what the
343
+ * platform's own two oracles AGREE on, not invented:
344
+ *
345
+ * - `uip maestro flow registry get uipath.core.agentic-process.<key>` for ALL
346
+ * THREE agentic processes on the tenant (2026-07-29): `category:
347
+ * 'agentic-process'`, `sortOrder: 507` (now 610 for bpmn, 600 flow, 620 case:
348
+ * `resourceNodeSortOrder`, #1045), `supportsErrorHandling`, `icon:
349
+ * 'agentic-process'`, `version: '1.0.0'`, the `bpmn:ServiceTask` /
350
+ * `Orchestrator.StartAgenticProcess` / `v2` model, `section: 'Published'`,
351
+ * `resourceSubType: 'ProcessOrchestration'`, `orchestratorType:
352
+ * 'processorchestration'`, `debug.runtime: 'bpmnEngine'`, the
353
+ * `<bindings.name>` / `<bindings.folderPath>` context placeholders, and
354
+ * `runtimeConstraints.exclude: ['api-function']`;
355
+ * - flow-v1's own generator, which is where those come from:
356
+ * `manifest/mappers/node-manifest-builder.ts::createModel` hard-codes
357
+ * `bpmn:ServiceTask` for every published-resource node and derives
358
+ * `orchestratorType` as `resourceType.toLowerCase()`;
359
+ * `RESOURCE_TYPE_METADATA.ProcessOrchestration` gives the service type.
360
+ *
361
+ * **There is NO deployed corpus for this family** — zero `.flow` artifacts under
362
+ * `runs/` and zero tasks under `uipath-skills/tests/tasks/uipath-maestro-flow/`
363
+ * carry an agentic-process node (checked 2026-07-29). The evidence is the two
364
+ * oracles above plus one live job started from an emitted `.flow`; there is no
365
+ * majority-of-42 to lean on, and `shared/sample-flows/agentic-process.flow` is
366
+ * NOT that corpus: it is an older, fire-and-forget instance whose model type,
367
+ * service type, output sources and `bindings.values` shape all disagree with what
368
+ * `registry get` returns today.
369
+ *
370
+ * `Orchestrator.StartAgenticProcess` is the SYNCHRONOUS spelling — the one that
371
+ * waits and publishes the process's output arguments. The platform's
372
+ * fire-and-forget switch swaps in `…StartAgenticProcessAsync` and then keeps only
373
+ * the `error` output, which is why this surface does not offer it (see
374
+ * `agenticProcess`'s doc comment).
375
+ */
376
+ function agenticProcessDef(spec) {
377
+ const form = ownEntry(AGENTIC_PROCESS_FORMS, spec.form ?? 'bpmn') ?? AGENTIC_PROCESS_FORMS.bpmn;
378
+ const loc = processResourceLocation(spec);
379
+ const { resourceKey } = loc;
380
+ const properties = {};
381
+ for (const [k, v] of Object.entries(spec.inputs)) {
382
+ properties[k] = { type: typeof v === 'number' ? 'number' : typeof v === 'boolean' ? 'boolean' : 'string' };
383
+ }
384
+ return {
385
+ nodeType: `${form.prefix}${spec.key}`,
386
+ version: '1.0.0',
387
+ category: form.category,
388
+ runtimeConstraints: { exclude: ['api-function'] },
389
+ description: loc.description,
390
+ tags: loc.tags,
391
+ sortOrder: resourceNodeSortOrder(form.category),
392
+ supportsErrorHandling: true,
393
+ display: { label: spec.name, icon: form.icon },
394
+ handleConfiguration: [
395
+ { position: 'left', handles: [{ id: 'input', type: 'target', handleType: 'input' }] },
396
+ { position: 'right', handles: [{ id: 'output', type: 'source', handleType: 'output' }] },
397
+ ],
398
+ ...loc.definitionExtras,
399
+ model: {
400
+ type: 'bpmn:ServiceTask',
401
+ serviceType: form.serviceType,
402
+ version: 'v2',
403
+ ...(loc.inSolution ? {} : { section: 'Published' }),
404
+ bindings: {
405
+ resource: 'process',
406
+ resourceSubType: form.resourceSubType,
407
+ resourceKey,
408
+ orchestratorType: form.orchestratorType,
409
+ values: [
410
+ { name: 'name', propertyAttribute: 'name', default: spec.name },
411
+ { name: 'folderPath', propertyAttribute: 'folderPath', default: loc.folderPath },
412
+ ],
413
+ },
414
+ ...loc.modelExtras,
415
+ context: [
416
+ { name: 'name', type: 'string', value: '<bindings.name>' },
417
+ { name: 'folderPath', type: 'string', value: '<bindings.folderPath>' },
418
+ { name: '_label', type: 'string', value: spec.name },
419
+ ],
420
+ },
421
+ // The fire-and-forget switch, exactly as flow-v1's withFireAndForgetInput
422
+ // declares it: a boolean INPUT on the definition (default false). The sync
423
+ // serviceType stays on the model — the platform's BPMN serializer swaps in
424
+ // the async sibling when the instance opts in.
425
+ inputDefinition: { type: 'object', properties: { ...properties, fireAndForget: { type: 'boolean' } } },
426
+ inputDefaults: { fireAndForget: false },
427
+ outputDefinition: {
428
+ output: {
429
+ type: 'object',
430
+ source: '=this',
431
+ var: 'output',
432
+ // The process's output arguments, as the author declared them — the same
433
+ // shape `registry get` returns when it can reach the deployed process
434
+ // (nested under `schema`, which is the spelling gen-tests reads). Without
435
+ // it a downstream read is unresolvable, rightly: nothing offline could tell a
436
+ // real field from a typo.
437
+ ...(spec.returns && Object.keys(spec.returns).length
438
+ ? {
439
+ schema: {
440
+ type: 'object',
441
+ properties: Object.fromEntries(Object.entries(spec.returns).map(([k, t]) => [k, { type: t }])),
442
+ },
443
+ }
444
+ : {}),
445
+ },
446
+ error: {
447
+ type: 'object',
448
+ description: 'Error information if the node fails',
449
+ source: '=Error',
450
+ var: 'error',
451
+ },
452
+ },
453
+ debug: { runtime: 'bpmnEngine' },
454
+ };
455
+ }
456
+ /**
457
+ * The definition a process step's node is emitted against — what serialize
458
+ * supplies and what `errorPort` asks, so the two cannot disagree (#1040).
459
+ */
460
+ export function processDefinition(spec) {
461
+ const s = (spec.inputs ?? {});
462
+ switch (spec.kind) {
463
+ case 'publishedFunction':
464
+ return publishedFunctionDef({ key: s.key, name: s.name, folderPath: s.folderPath, inputs: s.inputs ?? {}, returns: s.returns });
465
+ case 'apiWorkflow':
466
+ return apiWorkflowDef({ key: s.key, name: s.name, folderPath: s.folderPath, inputs: s.inputs ?? {}, returns: s.returns });
467
+ case 'agenticProcess':
468
+ return agenticProcessDef({
469
+ key: s.key, name: s.name, folderPath: s.folderPath, location: s.location, projectId: s.projectId,
470
+ inputs: s.inputs ?? {}, returns: s.returns, form: s.form,
471
+ });
472
+ case 'rpaWorkflow':
473
+ return rpaWorkflowDef({
474
+ key: s.key, name: s.name, folderPath: s.folderPath, location: s.location, projectId: s.projectId,
475
+ inputs: s.inputs ?? {}, returns: s.returns,
476
+ });
477
+ }
478
+ }
479
+ /**
480
+ * One emit path for the four process-resource kinds: they differ in the
481
+ * definition they carry (node type, serviceType, subtype, the key's field name)
482
+ * and in nothing else — the node body, the binding pair and the model-stripping
483
+ * are identical, and the DATA paths that differ are all downstream in the engine.
484
+ */
485
+ function serializeProcess(spec, ctx) {
486
+ const s = spec.inputs;
487
+ const fireAndForget = spec.kind === 'agenticProcess' && s.completion === 'fire-and-forget';
488
+ const def = processDefinition(spec);
489
+ // The bindings first: two steps on the same process share ONE pair, and the
490
+ // definition's `model.context` names ids that have to exist. Read off the
491
+ // definition: one kind carries several subtypes (agenticProcess by `form`), and
492
+ // an in-solution step's resourceKey is the bare sibling key with folder `''`.
493
+ const subType = def.model.bindings.resourceSubType;
494
+ const pairFolder = def.model.bindings.values.find((v) => v.name === 'folderPath').default;
495
+ const { entries } = processResourceBindings({ name: s.name, folderPath: pairFolder, resourceSubType: subType, resourceKey: def.model.bindings.resourceKey }, ctx.bindings);
496
+ for (const b of entries)
497
+ ctx.bind(b);
498
+ const inputs = Object.fromEntries(Object.entries(s.inputs ?? {}).map(([k, v]) => [k, ctx.render(v)]));
499
+ let outputs;
500
+ if (fireAndForget) {
501
+ // The platform's fire-and-forget switch, persisted the way the canvas
502
+ // persists it: the design-time flag on the instance. The dispatch publishes
503
+ // NO normal output — only dispatch failures route — so the node keeps only
504
+ // its `error` output, and the flag never becomes a process input argument.
505
+ inputs.fireAndForget = true;
506
+ const { output: _drop, ...rest } = (def.outputDefinition ?? {});
507
+ outputs = JSON.parse(JSON.stringify(rest));
508
+ }
509
+ return {
510
+ definition: { source: 'snapshot', nodeType: def.nodeType, version: def.version, manifest: def },
511
+ // The deployed shape keeps the resource model on the DEFINITION only — 42 of
512
+ // 45 rpa instances and 42 of 42 api-workflow instances carry no node-level
513
+ // `model` at all. (agentic-process has no deployed corpus to count, so it
514
+ // follows its two siblings — and the model resolves from either place since
515
+ // #94, which is what the step-0 probe walked to a live job.)
516
+ model: 'omit',
517
+ inputs,
518
+ ...(outputs !== undefined ? { outputs, replaceOutputs: true } : {}),
519
+ };
520
+ }
521
+ /** A value as the author wrote it: an expression back to an Expr, a literal string as itself, any other JSON as is. */
522
+ function lifted(raw, ctx) {
523
+ const text = Array.isArray(raw) ? undefined : ctx.sourceText(raw);
524
+ if (text === undefined)
525
+ return raw;
526
+ return text.startsWith('=js:') ? ctx.expr(text) : text;
527
+ }
528
+ /**
529
+ * Invert a process node. The identity the runtime resolves by is the
530
+ * definition's binding pair, and the contract is its output schema, so both come
531
+ * back off the file's definition — without one there is nothing to recover them
532
+ * from.
533
+ */
534
+ function liftProcess(node, ctx, prefix, kind, form) {
535
+ const n = node;
536
+ const key = n.type.slice(prefix.length);
537
+ const def = ctx.definition();
538
+ const values = (def?.model?.bindings?.values ?? []);
539
+ const binding = (name) => {
540
+ const row = values.find((v) => v.name === name);
541
+ return typeof row?.default === 'string' ? row.default : undefined;
542
+ };
543
+ const name = binding('name');
544
+ const folderPath = binding('folderPath');
545
+ // A sibling project of the same solution (#951): the model names its project
546
+ // instead of a folder. `isInSolution` is flow-core's marker. Read back as
547
+ // `location: 'in-solution'` so the recompile takes the in-solution path
548
+ // instead of a published one with an empty folder.
549
+ const projectId = typeof def?.model?.projectId === 'string' ? def.model.projectId : undefined;
550
+ const inSolution = def?.model?.isInSolution === true || projectId !== undefined;
551
+ const inputs = { ...(n.inputs ?? {}) };
552
+ // The design-time fire-and-forget switch is an OPTION on the factory, not a
553
+ // process argument — serialize adds it to `inputs` after rendering the
554
+ // author's map, so it comes back off here.
555
+ const fireAndForget = ctx.plainValue(inputs.fireAndForget) === true;
556
+ delete inputs.fireAndForget;
557
+ const schema = def?.outputDefinition?.output?.schema?.properties;
558
+ const spec = {
559
+ key,
560
+ ...(name !== undefined ? { name } : {}),
561
+ ...(inSolution ? { location: 'in-solution' } : {}),
562
+ ...(!inSolution && folderPath !== undefined ? { folderPath } : {}),
563
+ ...(inSolution && projectId !== undefined ? { projectId } : {}),
564
+ ...(Object.keys(inputs).length ? { inputs: Object.fromEntries(Object.entries(inputs).map(([k, v]) => [k, lifted(v, ctx)])) } : {}),
565
+ ...(schema && Object.keys(schema).length > 0
566
+ ? { returns: Object.fromEntries(Object.entries(schema).map(([k, v]) => [k, String(v?.type ?? 'string')])) } : {}),
567
+ ...(form !== undefined ? { form } : {}),
568
+ ...(fireAndForget ? { completion: 'fire-and-forget' } : {}),
569
+ };
570
+ return { kind, inputs: spec };
571
+ }
572
+ function printProcess(spec, ctx, factory) {
573
+ const p = spec.inputs;
574
+ const val = (v) => (v === undefined ? 'undefined' : v instanceof Expr ? ctx.value(v) : ctx.json(v));
575
+ const obj = (entries) => `{ ${entries.map(([k, v]) => `${ctx.key(k)}: ${v}`).join(', ')} }`;
576
+ return {
577
+ factory,
578
+ options: [
579
+ ['key', ctx.string(p.key)],
580
+ ['name', p.name !== undefined ? ctx.string(p.name) : undefined],
581
+ ['location', p.location !== undefined ? ctx.string(p.location) : undefined],
582
+ ['folderPath', p.folderPath !== undefined ? ctx.string(p.folderPath) : undefined],
583
+ ['projectId', p.projectId !== undefined ? ctx.string(p.projectId) : undefined],
584
+ ['inputs', p.inputs !== undefined ? obj(Object.entries(p.inputs).map(([k, v]) => [k, val(v)])) : undefined],
585
+ ['returns', p.returns !== undefined ? obj(Object.entries(p.returns).map(([k, t]) => [k, ctx.string(t)])) : undefined],
586
+ ['form', p.form !== undefined ? ctx.string(p.form) : undefined],
587
+ ['completion', p.completion !== undefined ? ctx.string(p.completion) : undefined],
588
+ ],
589
+ };
590
+ }
591
+ /** What reading a process step's result yields: its `returns`, each kind's read rule. */
592
+ function processOutput(spec) {
593
+ switch (spec.kind) {
594
+ case 'rpaWorkflow': {
595
+ const declared = Object.keys(spec.inputs?.returns ?? {});
596
+ return {
597
+ kind: 'fields',
598
+ fields: declared.length ? Object.fromEntries(declared.map((k) => [k, 'any'])) : undefined,
599
+ read: rpaWorkflowRead(spec.inputs),
600
+ };
601
+ }
602
+ case 'apiWorkflow':
603
+ return { kind: 'reader', read: (read, report) => checkApiWorkflowRead(read, spec.inputs, report) };
604
+ case 'publishedFunction':
605
+ return { kind: 'reader', read: (read, report) => checkPublishedFunctionRead(read, spec.inputs, report) };
606
+ case 'agenticProcess':
607
+ return { kind: 'reader', read: (read, report) => checkAgenticProcessRead(read, spec.inputs, report) };
608
+ }
609
+ }
610
+ function processCheck(spec, ctx) {
611
+ switch (spec.kind) {
612
+ case 'rpaWorkflow': return checkRpaWorkflow(spec.inputs, ctx);
613
+ case 'apiWorkflow': return checkApiWorkflow(spec.inputs, ctx);
614
+ case 'publishedFunction': return checkPublishedFunction(spec.inputs, ctx);
615
+ case 'agenticProcess': return checkAgenticProcess(spec.inputs, ctx);
616
+ }
617
+ }
618
+ function processType(kind, prefix, factory, form) {
619
+ const impl = {
620
+ kind, typeName: `${prefix}*`, version: '1.0.0', factory, definition: 'per-tenant',
621
+ // The definition this SDK synthesizes is the one serialize emits, and every
622
+ // one of the four sets supportsErrorHandling; asking it keeps the two in step (#1040).
623
+ errorPort: (spec) => definitionHasErrorPort(processDefinition(spec)),
624
+ serialize: (spec, ctx) => serializeProcess(spec, ctx),
625
+ deserialize: (node, ctx) => liftProcess(node, ctx, prefix, kind, form),
626
+ print: (spec, ctx) => printProcess(spec, ctx, factory),
627
+ check: (spec, ctx) => processCheck(spec, ctx),
628
+ // check collected no expressions for a process step before the port; unchanged by it.
629
+ expressions: () => [],
630
+ output: processOutput,
631
+ };
632
+ return {
633
+ kind, typeName: `${prefix}*`, typeNamePrefix: prefix, factory, defaultVersion: '1.0.0',
634
+ versions: { '1.0.0': impl },
635
+ // The agentic process's three forms share its kind; an absent or unknown form
636
+ // is the default `bpmn` one, as the definition builder reads it.
637
+ ...(kind === 'agenticProcess' ? {
638
+ matches: ({ inputs }) => {
639
+ const f = inputs?.form;
640
+ return form === undefined ? f !== 'flow' && f !== 'case' : f === form;
641
+ },
642
+ } : {}),
643
+ };
644
+ }
645
+ export const processNodeTypes = [
646
+ processType('rpaWorkflow', RPA_WORKFLOW_PREFIX, 'rpaWorkflow'),
647
+ processType('apiWorkflow', API_WORKFLOW_PREFIX, 'apiWorkflow'),
648
+ processType('publishedFunction', FUNCTION_PREFIX, 'publishedFunction'),
649
+ processType('agenticProcess', AGENTIC_PROCESS_FORMS.bpmn.prefix, 'agenticProcess'),
650
+ processType('agenticProcess', AGENTIC_PROCESS_FORMS.flow.prefix, 'agenticProcess', 'flow'),
651
+ processType('agenticProcess', AGENTIC_PROCESS_FORMS.case.prefix, 'agenticProcess', 'case'),
652
+ ];