@uipath/maestro-builder-sdk 6.16.14 → 6.17.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 (60) hide show
  1. package/dist/api-index.md +108 -107
  2. package/dist/api-members.md +295 -291
  3. package/dist/bindings-v2.d.ts +39 -1
  4. package/dist/bindings-v2.js +289 -9
  5. package/dist/bpmn/compile-cli.js +11 -2
  6. package/dist/bpmn/decompile.js +2 -1
  7. package/dist/bpmn/registry-fallback.js +9 -7
  8. package/dist/case/compile-cli.js +16 -6
  9. package/dist/case/decompile-cli.js +2 -1
  10. package/dist/case/decompile.js +3 -2
  11. package/dist/case/merge-cli.js +3 -2
  12. package/dist/check.js +219 -37
  13. package/dist/cli-result.d.ts +9 -0
  14. package/dist/cli-result.js +12 -0
  15. package/dist/compile-cli.js +78 -53
  16. package/dist/core/actions.d.ts +70 -16
  17. package/dist/core/agentic-process-forms.d.ts +51 -0
  18. package/dist/core/agentic-process-forms.js +49 -0
  19. package/dist/core/binding-messages.d.ts +5 -0
  20. package/dist/core/comment-text.d.ts +29 -0
  21. package/dist/core/comment-text.js +40 -0
  22. package/dist/core/contribution-bindings.d.ts +73 -0
  23. package/dist/core/contribution-bindings.js +105 -0
  24. package/dist/core/expr-check.js +7 -2
  25. package/dist/core/json-file.d.ts +1 -0
  26. package/dist/core/json-file.js +12 -0
  27. package/dist/core/script-return.js +4 -1
  28. package/dist/core/typed-raw-node.js +1 -0
  29. package/dist/core/typescript-module.d.ts +2 -0
  30. package/dist/core/typescript-module.js +18 -0
  31. package/dist/core-definitions.json +210 -0
  32. package/dist/decompile-cli.js +2 -1
  33. package/dist/decompile.js +64 -24
  34. package/dist/emit.js +4 -3
  35. package/dist/flow-sdk.d.ts +14 -9
  36. package/dist/flow-sdk.js +14 -4
  37. package/dist/flow-step-tree.d.ts +2 -3
  38. package/dist/flow-step-tree.js +2 -3
  39. package/dist/load.js +1 -1
  40. package/dist/merge-cli.js +3 -2
  41. package/dist/merge.d.ts +2 -1
  42. package/dist/merge.js +0 -0
  43. package/dist/path-within.d.ts +33 -0
  44. package/dist/path-within.js +41 -0
  45. package/dist/registry/archive/extract.js +2 -5
  46. package/dist/registry/curated-overlay.js +3 -2
  47. package/dist/registry/generators/descriptors.js +20 -18
  48. package/dist/registry/generators/triggers.js +4 -4
  49. package/dist/registry/prepare/messages.js +2 -1
  50. package/dist/registry/prepare/source-labels.js +60 -1
  51. package/dist/registry/prepare-cli.js +14 -4
  52. package/dist/registry/pull-cli.js +3 -2
  53. package/dist/registry/uip/client.d.ts +28 -3
  54. package/dist/registry/uip/client.js +95 -34
  55. package/dist/registry/uip/host.d.ts +41 -0
  56. package/dist/registry/uip/host.js +312 -0
  57. package/dist/serialize.d.ts +7 -0
  58. package/dist/serialize.js +390 -270
  59. package/dist/workdir.js +1 -1
  60. package/package.json +3 -3
package/dist/serialize.js CHANGED
@@ -14,6 +14,7 @@ import { Expr, toExpr, SCHEDULE_PRESETS, DELAY_PRESETS, parseIxpProjectType, fla
14
14
  import { FlowAction, FlowTrigger } from './core/node-classes.js';
15
15
  import { connectorRawNodeRefusal } from './core/connector-raw-node.js';
16
16
  import { dataFabricUnknownInputs } from './core/typed-raw-node.js';
17
+ import { planContributionBindings } from './core/contribution-bindings.js';
17
18
  import { isAnswerField } from './core/hitl-answers.js';
18
19
  import { connectionConnectors } from './bindings-v2.js';
19
20
  import { BindingRefusal, GUID_RE, bindingMissingMessage, bindingSelfNameMessage, bindingUndeclaredMessage, missingBindingLabels, undeclaredBinding } from './core/binding-messages.js';
@@ -49,6 +50,8 @@ import { resolveSchemaVersion } from './schema-version.js';
49
50
  import { planFlowGlobals, planSharedInputs, sharedInputRoots } from './flow-variables.js';
50
51
  import { prepareCommand } from './core/cli-spelling.js';
51
52
  import { QUEUE_ITEM_RECORD_SCHEMA } from './core/queue-record.js';
53
+ import { AGENTIC_PROCESS_FORMS } from './core/agentic-process-forms.js';
54
+ import { types } from './core/expr.js';
52
55
  const HERE = dirname(fileURLToPath(import.meta.url));
53
56
  // core-definitions.json is an object keyed by "<nodeType>@<version>".
54
57
  const DEFS = JSON.parse(readFileSync(join(HERE, 'core-definitions.json'), 'utf8'));
@@ -143,6 +146,53 @@ const SUBFLOW_OUTPUT = {
143
146
  };
144
147
  /** The RPA-workflow node type family. The process's release key is the suffix. */
145
148
  const RPA_WORKFLOW_PREFIX = 'uipath.core.rpa-workflow.';
149
+ /**
150
+ * Where a process resource lives, as ONE slot every process-resource definition
151
+ * shares. In the platform model "in this solution" is orthogonal to the
152
+ * resource type: flow-core's single `createModel` (`@uipath/flow-core`
153
+ * `manifest/mappers/node-manifest-builder.ts`, what `uip maestro flow registry
154
+ * get --local` and Studio Web both run) adds `projectId`, `projectName` and
155
+ * `isInSolution: true` to the model of ANY process resource built from a sibling
156
+ * project.
157
+ *
158
+ * The in-solution shape, transcribed from `registry get --local` for a sibling
159
+ * Flow (2026-10-08) and from uipath-cli's `buildInSolutionNodeManifests`:
160
+ * - `resourceKey` is the bare key (the sibling's `resource.key`), not
161
+ * `<folderPath>.<name>`; the folder default is `''`;
162
+ * - no `section: 'Published'`;
163
+ * - `projectId` on the definition AND on the model, with `projectName` and
164
+ * `isInSolution: true` (the marker the canvas reads);
165
+ * - description `(In-solution) <name>`, tags `in-solution` / `local`, and the
166
+ * `open-workflow` toolbar action.
167
+ */
168
+ function processResourceLocation(spec) {
169
+ if (spec.location === 'in-solution') {
170
+ return {
171
+ inSolution: true,
172
+ folderPath: '',
173
+ resourceKey: spec.key,
174
+ description: `(In-solution) ${spec.name}`,
175
+ tags: ['in-solution', 'local'],
176
+ definitionExtras: {
177
+ toolbarExtensions: {
178
+ design: { actions: [{ id: 'open-workflow', icon: 'external-link', label: 'Open' }] },
179
+ },
180
+ projectId: spec.projectId,
181
+ },
182
+ modelExtras: { projectId: spec.projectId, projectName: spec.name, isInSolution: true },
183
+ };
184
+ }
185
+ const folderPath = spec.folderPath ?? '';
186
+ return {
187
+ inSolution: false,
188
+ folderPath,
189
+ resourceKey: `${folderPath}.${spec.name}`,
190
+ description: `(${folderPath})`,
191
+ tags: [],
192
+ definitionExtras: {},
193
+ modelExtras: {},
194
+ };
195
+ }
146
196
  /**
147
197
  * Build the definition for one deployed RPA process.
148
198
  *
@@ -177,7 +227,8 @@ const RPA_WORKFLOW_PREFIX = 'uipath.core.rpa-workflow.';
177
227
  * element while publishing a null step output.
178
228
  */
179
229
  function rpaWorkflowDef(spec) {
180
- const resourceKey = `${spec.folderPath}.${spec.name}`;
230
+ const loc = processResourceLocation(spec);
231
+ const { resourceKey } = loc;
181
232
  const properties = {};
182
233
  for (const [k, v] of Object.entries(spec.inputs)) {
183
234
  properties[k] = { type: typeof v === 'number' ? 'number' : typeof v === 'boolean' ? 'boolean' : 'string' };
@@ -187,8 +238,8 @@ function rpaWorkflowDef(spec) {
187
238
  version: '1.0.0',
188
239
  category: 'rpa-workflow',
189
240
  runtimeConstraints: { exclude: ['api-function'] },
190
- description: `(${spec.folderPath})`,
191
- tags: [],
241
+ description: loc.description,
242
+ tags: loc.tags,
192
243
  sortOrder: 530,
193
244
  supportsErrorHandling: true,
194
245
  display: { label: spec.name, icon: 'rpa' },
@@ -196,11 +247,12 @@ function rpaWorkflowDef(spec) {
196
247
  { position: 'left', handles: [{ id: 'input', type: 'target', handleType: 'input' }] },
197
248
  { position: 'right', handles: [{ id: 'output', type: 'source', handleType: 'output' }] },
198
249
  ],
250
+ ...loc.definitionExtras,
199
251
  model: {
200
252
  type: 'bpmn:ServiceTask',
201
253
  serviceType: 'Orchestrator.StartJob',
202
254
  version: 'v2',
203
- section: 'Published',
255
+ ...(loc.inSolution ? {} : { section: 'Published' }),
204
256
  bindings: {
205
257
  resource: 'process',
206
258
  resourceSubType: 'Process',
@@ -208,9 +260,10 @@ function rpaWorkflowDef(spec) {
208
260
  orchestratorType: 'process',
209
261
  values: [
210
262
  { name: 'name', propertyAttribute: 'name', default: spec.name },
211
- { name: 'folderPath', propertyAttribute: 'folderPath', default: spec.folderPath },
263
+ { name: 'folderPath', propertyAttribute: 'folderPath', default: loc.folderPath },
212
264
  ],
213
265
  },
266
+ ...loc.modelExtras,
214
267
  context: [
215
268
  { name: 'name', type: 'string', value: '<bindings.name>' },
216
269
  { name: 'folderPath', type: 'string', value: '<bindings.folderPath>' },
@@ -453,41 +506,6 @@ function publishedFunctionDef(spec) {
453
506
  debug: { runtime: 'bpmnEngine' },
454
507
  };
455
508
  }
456
- const AGENTIC_PROCESS_PREFIX = 'uipath.core.agentic-process.';
457
- /**
458
- * The three published forms under the Agentic Process umbrella (design §2.1).
459
- * One public factory, three wire identities — transcribed from flow-v1's
460
- * `RESOURCE_TYPE_METADATA` (ProcessOrchestration / Flow / CaseManagement):
461
- * node-type segment, category, icon, resource subtype, `orchestratorType`
462
- * (`resourceType.toLowerCase()`, per `createModel`), and the sync/async
463
- * service-type pair. All three declare the fire-and-forget switch.
464
- */
465
- const AGENTIC_PROCESS_FORMS = {
466
- bpmn: {
467
- prefix: AGENTIC_PROCESS_PREFIX,
468
- category: 'agentic-process',
469
- icon: 'agentic-process',
470
- resourceSubType: 'ProcessOrchestration',
471
- orchestratorType: 'processorchestration',
472
- serviceType: 'Orchestrator.StartAgenticProcess',
473
- },
474
- flow: {
475
- prefix: 'uipath.core.flow.',
476
- category: 'flow',
477
- icon: 'flow-project',
478
- resourceSubType: 'Flow',
479
- orchestratorType: 'flow',
480
- serviceType: 'Orchestrator.StartFlowProcess',
481
- },
482
- case: {
483
- prefix: 'uipath.core.case-management.',
484
- category: 'case-management',
485
- icon: 'case-management',
486
- resourceSubType: 'CaseManagement',
487
- orchestratorType: 'casemanagement',
488
- serviceType: 'Orchestrator.StartCaseMgmtProcess',
489
- },
490
- };
491
509
  /**
492
510
  * Build the definition for one deployed agentic process (a Maestro process
493
511
  * orchestration).
@@ -529,7 +547,8 @@ const AGENTIC_PROCESS_FORMS = {
529
547
  */
530
548
  function agenticProcessDef(spec) {
531
549
  const form = AGENTIC_PROCESS_FORMS[spec.form ?? 'bpmn'];
532
- const resourceKey = `${spec.folderPath}.${spec.name}`;
550
+ const loc = processResourceLocation(spec);
551
+ const { resourceKey } = loc;
533
552
  const properties = {};
534
553
  for (const [k, v] of Object.entries(spec.inputs)) {
535
554
  properties[k] = { type: typeof v === 'number' ? 'number' : typeof v === 'boolean' ? 'boolean' : 'string' };
@@ -539,8 +558,8 @@ function agenticProcessDef(spec) {
539
558
  version: '1.0.0',
540
559
  category: form.category,
541
560
  runtimeConstraints: { exclude: ['api-function'] },
542
- description: `(${spec.folderPath})`,
543
- tags: [],
561
+ description: loc.description,
562
+ tags: loc.tags,
544
563
  sortOrder: 507,
545
564
  supportsErrorHandling: true,
546
565
  display: { label: spec.name, icon: form.icon },
@@ -548,11 +567,12 @@ function agenticProcessDef(spec) {
548
567
  { position: 'left', handles: [{ id: 'input', type: 'target', handleType: 'input' }] },
549
568
  { position: 'right', handles: [{ id: 'output', type: 'source', handleType: 'output' }] },
550
569
  ],
570
+ ...loc.definitionExtras,
551
571
  model: {
552
572
  type: 'bpmn:ServiceTask',
553
573
  serviceType: form.serviceType,
554
574
  version: 'v2',
555
- section: 'Published',
575
+ ...(loc.inSolution ? {} : { section: 'Published' }),
556
576
  bindings: {
557
577
  resource: 'process',
558
578
  resourceSubType: form.resourceSubType,
@@ -560,9 +580,10 @@ function agenticProcessDef(spec) {
560
580
  orchestratorType: form.orchestratorType,
561
581
  values: [
562
582
  { name: 'name', propertyAttribute: 'name', default: spec.name },
563
- { name: 'folderPath', propertyAttribute: 'folderPath', default: spec.folderPath },
583
+ { name: 'folderPath', propertyAttribute: 'folderPath', default: loc.folderPath },
564
584
  ],
565
585
  },
586
+ ...loc.modelExtras,
566
587
  context: [
567
588
  { name: 'name', type: 'string', value: '<bindings.name>' },
568
589
  { name: 'folderPath', type: 'string', value: '<bindings.folderPath>' },
@@ -741,28 +762,6 @@ function agentDef(spec) {
741
762
  debug: { runtime: 'bpmnEngine' },
742
763
  };
743
764
  }
744
- /**
745
- * Build the definition for the INLINE autonomous agent.
746
- *
747
- * This is the one node type in the agent family that is the SAME on every tenant —
748
- * it carries its own configuration instead of referencing a resource — so
749
- * `uip maestro flow registry get uipath.agent.autonomous` is a direct oracle rather
750
- * than a per-instance answer, and the def can be built from constants without a
751
- * corpus vote. Transcribed 2026-07-29: `version: '1.2'`, `sortOrder: 5`,
752
- * `supportsErrorHandling: true`, `model.source: true`,
753
- * `Orchestrator.StartInlineAgentJob`, the three required inputs, the artifact
754
- * handles (escalation / context / tool) and `success` + `error` on the right with
755
- * `isDefaultForType` on `success`.
756
- *
757
- * The deployed corpus (29 `uipath.agent.autonomous` nodes, 05-18 → 07-29) agrees on
758
- * everything a `.flow` carries and settles the one thing the def leaves open:
759
- * `outputs.output.source` is `=result.response` in 27 of 27 that declare outputs —
760
- * unanimous here, unlike the published family, because the platform's
761
- * append-`=this` fallback only fires for a node that declares no `output` at all.
762
- * `typeVersion` in that corpus is 1.1 ×10, 1.2 ×15, 1.3 ×4; we emit the registry's
763
- * 1.2. (`shared/sample-flows/inline-agent.flow` says 1.0, which appears in **none**
764
- * of the 29 — the same bundled-sample era gap as the published family's.)
765
- */
766
765
  /**
767
766
  * The definition for ONE Context Grounding index resource, transcribed from the
768
767
  * platform's own registry entry (`uip maestro flow registry get
@@ -984,6 +983,45 @@ const CONNECTOR_TOOL_RESOURCE_TYPE = 'integration';
984
983
  * on its own it says nothing about who owns a file: `properties.toolType` does.
985
984
  */
986
985
  const INTERNAL_TOOL_RESOURCE_TYPE = 'internal';
986
+ /**
987
+ * Where an agent schema property points to declare "this is a FILE":
988
+ * `{ $ref: JOB_ATTACHMENT_REF }`, with {@link jobAttachmentDefinition} under the
989
+ * same schema's `definitions['job-attachment']`.
990
+ */
991
+ const JOB_ATTACHMENT_REF = '#/definitions/job-attachment';
992
+ /**
993
+ * The canonical `job-attachment` definition an agent schema's file field
994
+ * `$ref`s into — the uipath-agents skill's `agent-definition.md` § File
995
+ * Attachments ("Schema is fixed — copy verbatim"), and the same block the
996
+ * platform's own analyze-attachments tool contract carries.
997
+ *
998
+ * It tells the agent runtime that an input is an Orchestrator attachment, so a
999
+ * file tool (Analyze Files) can resolve the attachment's `ID`.
1000
+ *
1001
+ * A function, not a shared object: each schema gets its own copy, so an edit
1002
+ * to one emitted file cannot reach another.
1003
+ *
1004
+ * (The case serializer keeps its own copy in `case/entry-points.ts`: that one
1005
+ * mirrors the platform's `entry-points.json` packager byte for byte, and its
1006
+ * `MimeType` description differs.)
1007
+ */
1008
+ function jobAttachmentDefinition() {
1009
+ return {
1010
+ type: 'object',
1011
+ properties: {
1012
+ ID: { type: 'string', description: 'Orchestrator attachment key' },
1013
+ FullName: { type: 'string', description: 'File name' },
1014
+ MimeType: { type: 'string', description: 'MIME type, e.g. "application/pdf", "image/png"' },
1015
+ Metadata: {
1016
+ type: 'object',
1017
+ description: 'Dictionary<string, string> of metadata',
1018
+ additionalProperties: { type: 'string' },
1019
+ },
1020
+ },
1021
+ required: ['ID'],
1022
+ 'x-uipath-resource-kind': 'JobAttachment',
1023
+ };
1024
+ }
987
1025
  /** The `properties.toolType` of the built-in HTTP-request tool's file (#935). */
988
1026
  const HTTP_REQUEST_TOOL_TYPE = 'http-request';
989
1027
  /**
@@ -1139,29 +1177,13 @@ function builtinToolResourceJson(tool, label, inputs, spec) {
1139
1177
  properties: {
1140
1178
  attachments: {
1141
1179
  type: 'array',
1142
- items: { $ref: '#/definitions/job-attachment' },
1180
+ items: { $ref: JOB_ATTACHMENT_REF },
1143
1181
  description: String(inputs.attachmentsDescription ?? ''),
1144
1182
  },
1145
1183
  analysisTask: { type: 'string', description: String(inputs.analysisTaskDescription ?? '') },
1146
1184
  },
1147
1185
  required: ['attachments', 'analysisTask'],
1148
- definitions: {
1149
- 'job-attachment': {
1150
- type: 'object',
1151
- properties: {
1152
- ID: { type: 'string', description: 'Orchestrator attachment key' },
1153
- FullName: { type: 'string', description: 'File name' },
1154
- MimeType: { type: 'string', description: 'MIME type, e.g. "application/pdf", "image/png"' },
1155
- Metadata: {
1156
- type: 'object',
1157
- description: 'Dictionary<string, string> of metadata',
1158
- additionalProperties: { type: 'string' },
1159
- },
1160
- },
1161
- required: ['ID'],
1162
- 'x-uipath-resource-kind': 'JobAttachment',
1163
- },
1164
- },
1186
+ definitions: { 'job-attachment': jobAttachmentDefinition() },
1165
1187
  },
1166
1188
  outputSchema: {
1167
1189
  type: 'object',
@@ -1324,6 +1346,24 @@ const QUICK_FORM_FIELD_SCHEMA = {
1324
1346
  text: { type: 'string' }, number: { type: 'number' }, boolean: { type: 'boolean' },
1325
1347
  'date-time': { type: 'string', format: 'date-time' },
1326
1348
  };
1349
+ /**
1350
+ * The folder an app escalation names: the author's `folderPath`, or
1351
+ * `solution_folder` when they gave none — an app that ships in this solution.
1352
+ *
1353
+ * `solution_folder` is the platform's own spelling for that case: it is the
1354
+ * `Folder` that `uip solution resources list` reports for a solution-internal
1355
+ * app, and what the agents skill tells authors to copy into
1356
+ * `channels[].properties.folderName`. The SDK used to write `""`, which no
1357
+ * product writer produces (flow-builder-sdk#967). Measured, the two are the
1358
+ * same everywhere it counts: the agent tool's binding generator drops both
1359
+ * from the App binding (`resolveFolderPathBindingValue`), `uip agent refresh`
1360
+ * plus `uip solution resources refresh` derive byte-identical files from
1361
+ * either, and a live debug resolves the app in the debug folder for both. So
1362
+ * this changes the vocabulary, not the behaviour.
1363
+ */
1364
+ function escalationAppFolder(app) {
1365
+ return app.folderPath ? app.folderPath : 'solution_folder';
1366
+ }
1327
1367
  /**
1328
1368
  * The `resources/<id>/resource.json` an inline agent's ESCALATION needs — the
1329
1369
  * escalation half of what `toolResourceJson` does for a tool, and inert without
@@ -1400,7 +1440,7 @@ function escalationResourceJson(esc, spec) {
1400
1440
  // refresh` imports; `folderName` is the app's Orchestrator folder.
1401
1441
  resourceKey: app.key,
1402
1442
  appName: app.name,
1403
- folderName: app.folderPath ?? '',
1443
+ folderName: escalationAppFolder(app),
1404
1444
  // The app's deploy version (its action schema's `version`). The SDK does
1405
1445
  // not read the action schema, so this is the platform's own default — the
1406
1446
  // storage migration that introduced the field writes `1` when absent.
@@ -2183,134 +2223,88 @@ function memoryResourceDef(spec) {
2183
2223
  },
2184
2224
  };
2185
2225
  }
2186
- function inlineAgentDef(spec) {
2187
- return {
2188
- nodeType: INLINE_AGENT_TYPE,
2189
- version: '1.2',
2190
- category: 'agent',
2191
- description: 'AI agent that completes tasks autonomously',
2192
- tags: ['agentic', 'ai', 'autonomous', 'agent'],
2193
- sortOrder: 5,
2194
- supportsErrorHandling: true,
2195
- display: { label: 'Autonomous Agent', icon: 'autonomous-agent', shape: 'rectangle' },
2196
- handleConfiguration: [
2197
- {
2198
- position: 'top',
2199
- handles: [{
2200
- id: 'escalation', type: 'source', handleType: 'artifact', label: 'Escalations', showButton: true,
2201
- constraints: {
2202
- // BOTH spellings, as the platform's own def declares them (`registry
2203
- // get uipath.agent.autonomous`, 2026-07-31): the bare type is the
2204
- // pre-split era's escalation node, the wildcard is today's suffixed
2205
- // variants (`.coded-action-app` / `.quick-form`). Emitting only the
2206
- // bare one — as this did until card G16 — declared a handle that
2207
- // rejected every node the current registry can actually serve.
2208
- allowedTargets: [
2209
- { nodeType: 'uipath.agent.resource.escalation' },
2210
- { nodeType: 'uipath.agent.resource.escalation.*' },
2211
- ],
2212
- validationMessage: 'Only Agent Escalation nodes can connect here',
2213
- },
2214
- }],
2215
- },
2216
- {
2217
- position: 'bottom',
2218
- handles: [
2219
- {
2220
- id: 'context', type: 'source', handleType: 'artifact', label: 'Context', showButton: true,
2221
- constraints: {
2222
- allowedTargets: [{ nodeType: 'uipath.agent.resource.context.*' }],
2223
- validationMessage: 'Only Context resource nodes can connect here',
2224
- },
2225
- },
2226
- {
2227
- id: 'tool', type: 'source', handleType: 'artifact', label: 'Tools', showButton: true,
2228
- constraints: {
2229
- allowedTargets: [{ nodeType: 'uipath.agent.resource.tool.*' }],
2230
- validationMessage: 'Only Tool resource nodes can connect here',
2231
- },
2232
- },
2233
- ],
2234
- },
2235
- { position: 'left', handles: [{ id: 'input', type: 'target', handleType: 'input' }] },
2236
- {
2237
- position: 'right',
2238
- handles: [
2239
- { id: 'success', type: 'source', handleType: 'output', isDefaultForType: true },
2240
- {
2241
- id: 'error', type: 'source', handleType: 'output', label: 'Error',
2242
- visible: '{inputs.errorHandlingEnabled}',
2243
- constraints: { maxConnections: 1 },
2244
- },
2245
- ],
2246
- },
2247
- ],
2248
- inputDefinition: {
2249
- type: 'object',
2250
- properties: {
2251
- systemPrompt: { type: 'string', minLength: 1, errorMessage: { minLength: 'System prompt is required' } },
2252
- userPrompt: { type: 'string', minLength: 1, errorMessage: { minLength: 'User prompt is required' } },
2253
- model: { type: 'string', minLength: 1, errorMessage: { minLength: 'Model is required' } },
2254
- temperature: { type: 'number', minimum: 0, maximum: 1 },
2255
- maxTokenPerResponse: { type: 'number', minimum: 0, maximum: 16384 },
2256
- modelMaxTokens: { type: 'number', minimum: 0 },
2257
- maxIterations: { type: 'number', minimum: 1, maximum: 100 },
2258
- guardrails: { type: 'array' },
2259
- agentInputVariables: { type: 'array' },
2260
- agentOutputVariables: { type: 'array' },
2261
- },
2262
- required: ['systemPrompt', 'userPrompt', 'model'],
2263
- },
2264
- inputDefaults: {},
2265
- outputDefinition: {
2266
- output: {
2267
- type: 'object',
2268
- description: 'Agent response',
2269
- // `=result.response`, not `=this` — 27 of 27 deployed instances, and the
2270
- // engine's own inline dispatch returns `{ response: { …declared keys } }`.
2271
- source: '=result.response',
2272
- var: 'output',
2273
- // NO per-instance schema here, and that is the whole point of this comment:
2274
- // `definitions[]` is keyed by node TYPE, and every inline agent in a flow
2275
- // shares this ONE entry. A flow with two agents (an analyst and a writer,
2276
- // say) therefore cannot have both contracts here — whichever was emitted
2277
- // last would win, and every read against the other resolved to the wrong
2278
- // shape. Measured on GA row 7's port, whose four `.return()` mappings off
2279
- // the ANALYST drew 25 undeclared-field errors quoting the WRITER's fields
2280
- // (`Available at "$vars.disputeAnalystAgent.output": "subject", "body"`) on
2281
- // a flow that is correct.
2282
- //
2283
- // The instance is where an authored contract belongs, and the SDK already
2284
- // writes it there (`node.outputs.output.schema`, below). A reader that
2285
- // refines per node then picks it up — applying an instance schema when the
2286
- // definition's is GENERIC and never overriding a definition that declares
2287
- // a shape, which is right in general and was
2288
- // exactly what this def defeated.
2289
- //
2290
- // Deployed truth agrees: v1's own two-agent BillingDisputeResolution
2291
- // artifact (1.0, 2026-07-15) carries `properties: { content: {…} }` on the
2292
- // shared definition and `schema: null` on both agent nodes.
2293
- },
2294
- error: {
2295
- type: 'object',
2296
- description: 'Error information if the node fails',
2297
- source: '=Error',
2298
- var: 'error',
2299
- },
2300
- },
2301
- model: {
2302
- source: true,
2303
- type: 'bpmn:ServiceTask',
2304
- serviceType: 'Orchestrator.StartInlineAgentJob',
2305
- version: 'v2',
2306
- context: [
2307
- { name: '_label', type: 'string', value: '' },
2308
- { name: 'name', type: 'string', value: '' },
2309
- { name: 'entryPoint', type: 'string', value: '' },
2310
- ],
2311
- },
2312
- debug: { runtime: 'bpmnEngine' },
2313
- };
2226
+ /** Numeric dotted compare: '1.10' > '1.9'. */
2227
+ function compareVersions(a, b) {
2228
+ const pa = a.split('.').map(Number);
2229
+ const pb = b.split('.').map(Number);
2230
+ for (let i = 0; i < Math.max(pa.length, pb.length); i++) {
2231
+ const d = (pa[i] ?? 0) - (pb[i] ?? 0);
2232
+ if (d !== 0)
2233
+ return d;
2234
+ }
2235
+ return 0;
2236
+ }
2237
+ /**
2238
+ * The inline agent's bundled versions, oldest first, and the newest of them.
2239
+ * The newest bundled pair is the default, so there is no version literal in
2240
+ * this file: when the registry serves a newer version, `check:definitions`
2241
+ * reports the missing pair, `sync:definitions` adds it, and the default follows.
2242
+ */
2243
+ const INLINE_AGENT_VERSIONS = Object.keys(DEFS)
2244
+ .filter((k) => k.startsWith(`${INLINE_AGENT_TYPE}@`))
2245
+ .map((k) => k.slice(INLINE_AGENT_TYPE.length + 1))
2246
+ .sort(compareVersions);
2247
+ const INLINE_AGENT_DEFAULT_VERSION = INLINE_AGENT_VERSIONS[INLINE_AGENT_VERSIONS.length - 1];
2248
+ /** True when a definition declares the agent's `memory` artifact handle. */
2249
+ function declaresMemoryHandle(def) {
2250
+ return (def?.handleConfiguration ?? []).some((g) => (g?.handles ?? []).some((h) => h?.id === 'memory'));
2251
+ }
2252
+ /**
2253
+ * The definition for the INLINE autonomous agent: the bundled pair for
2254
+ * `version` (the newest by default), plus an explicit overlay of the details
2255
+ * the registry payload does not carry.
2256
+ *
2257
+ * This is the one node type in the agent family that is the SAME on every
2258
+ * tenant — it carries its own configuration instead of referencing a resource —
2259
+ * so `uip maestro flow registry get uipath.agent.autonomous` is a direct oracle
2260
+ * and the bundle entry is the registry's definition as last synced
2261
+ * (`sync:definitions` writes it through unchanged; `check:definitions --deep`
2262
+ * reports content that has moved since). Versions and content both come from
2263
+ * there.
2264
+ *
2265
+ * The overlay, and nothing else:
2266
+ *
2267
+ * * `outputDefinition.output.source` is `=result.response`. The registry's
2268
+ * definition declares no `source`; the deployed corpus (29 `uipath.agent.autonomous` nodes, 05-18 → 07-29) has
2269
+ * `=result.response` in 27 of 27 that declare outputs, and the engine's own
2270
+ * inline dispatch returns `{ response: { …declared keys } }`. Without it the
2271
+ * node's `output` is the whole result envelope rather than the declared keys.
2272
+ * It is applied at every version; the registry payload has no `source`.
2273
+ * * The `memory` handle is kept only when the flow attaches memory. It is
2274
+ * LOCAL content (#431) on the bundled 1.4, absent from the registry's 1.4
2275
+ * (`check.ts` AGENT_MEMORY_NOT_AUTHORABLE), so an agent without memory gets
2276
+ * the registry's handle set.
2277
+ *
2278
+ * NO per-instance schema goes on the definition: `definitions[]` is keyed by
2279
+ * node TYPE, and every inline agent in a flow shares this one entry. A flow
2280
+ * with two agents (an analyst and a writer, say) therefore cannot have both
2281
+ * contracts here — whichever was emitted last would win, and every read against
2282
+ * the other resolved to the wrong shape (GA row 7's port: 25 undeclared-field
2283
+ * errors on a correct flow). The authored contract goes on the NODE
2284
+ * (`node.outputs.output.schema`, in the `inlineAgent` arm), where a reader that
2285
+ * refines per node picks it up.
2286
+ *
2287
+ * The bundled 1.2 is the pre-#956 default, kept so a flow this SDK compiled
2288
+ * before — and a decompile of one, which pins `{ version: '1.2' }` — still
2289
+ * compiles. The corpus also carries 1.1 and 1.3; `shared/sample-flows/
2290
+ * inline-agent.flow` says 1.0, which appears in none of the 29.
2291
+ */
2292
+ function inlineAgentDef(version, withMemory) {
2293
+ const bundled = DEFS[`${INLINE_AGENT_TYPE}@${version}`];
2294
+ if (!bundled)
2295
+ return undefined;
2296
+ const def = clone(bundled);
2297
+ const output = def.outputDefinition?.output;
2298
+ if (output)
2299
+ output.source = '=result.response';
2300
+ if (!withMemory) {
2301
+ for (const group of def.handleConfiguration ?? []) {
2302
+ if (Array.isArray(group?.handles))
2303
+ group.handles = group.handles.filter((h) => h?.id !== 'memory');
2304
+ }
2305
+ def.handleConfiguration = (def.handleConfiguration ?? []).filter((g) => g?.handles?.length);
2306
+ }
2307
+ return def;
2314
2308
  }
2315
2309
  /**
2316
2310
  * Classify a RENDERED input value (the output of {@link renderValue}).
@@ -3934,7 +3928,7 @@ function describeDeclared(bindings) {
3934
3928
  * exactly (design §2.6/§4.2): the named version resolves or compilation fails,
3935
3929
  * and a contributor that ignored the author's requested version is rejected.
3936
3930
  */
3937
- function resolveContributionDefinition(ref, requestedVersion, where) {
3931
+ export function resolveContributionDefinition(ref, requestedVersion, where) {
3938
3932
  if (!ref || typeof ref.nodeType !== 'string' || !ref.nodeType || typeof ref.version !== 'string' || !ref.version) {
3939
3933
  throw new Error(`${where}: contribute() returned a DefinitionReference without an exact nodeType + version.`);
3940
3934
  }
@@ -4419,10 +4413,16 @@ function typeAgentInputSchemas(scope, globals) {
4419
4413
  if (g.direction === 'in' && g.triggerNodeId)
4420
4414
  lookup.set(`${g.triggerNodeId}.output.${g.id}`, { type: g.type, schema: g.schema });
4421
4415
  }
4422
- for (const { properties, refs } of scope.agentSchemas) {
4416
+ for (const { schema, refs } of scope.agentSchemas) {
4423
4417
  for (const r of refs) {
4424
- properties[r.flatName] = jsonSchemaForRef(r.binding, lookup);
4418
+ schema.properties[r.flatName] = jsonSchemaForRef(r.binding, lookup);
4425
4419
  }
4420
+ // A file property is a `$ref`, and a `$ref` resolves against THIS schema's
4421
+ // `definitions` — so the block is added exactly when a file property is
4422
+ // present (a schema with no file keeps the shape it always had).
4423
+ const hasFile = Object.values(schema.properties).some((p) => p?.$ref === JOB_ATTACHMENT_REF);
4424
+ if (hasFile)
4425
+ schema.definitions = { ...schema.definitions, 'job-attachment': jobAttachmentDefinition() };
4426
4426
  }
4427
4427
  }
4428
4428
  /** One property's schema — `{}` when the type cannot be established. See {@link typeAgentInputSchemas}. */
@@ -4512,17 +4512,58 @@ function walkSchemaType(schema, segments) {
4512
4512
  }
4513
4513
  return typeof current?.type === 'string' ? current.type : undefined;
4514
4514
  }
4515
- /** A resolved type → its JSON-Schema property, with v1's two normalisations. */
4515
+ /**
4516
+ * A resolved type → its JSON-Schema property.
4517
+ *
4518
+ * Every type a flow can DECLARE (`types.*`) has its own case, and the switch is
4519
+ * exhaustive over {@link TypeDesc}: adding a type to `types` without deciding
4520
+ * what an agent sees for it is a compile error here, not a silent `{}`.
4521
+ *
4522
+ * `file` → `{ $ref: '#/definitions/job-attachment' }`; the caller adds the
4523
+ * definition (see `typeAgentInputSchemas`).
4524
+ *
4525
+ * Typeless `{}` is the answer for `any` and for "unknown" (`undefined`,
4526
+ * or a type string from a node definition that is not in the SDK's
4527
+ * vocabulary): that is v1's own rule for a type it cannot establish
4528
+ * (`buildAgentInputTypeResolver` → `'any'` → a property with no `type`), and it
4529
+ * keeps the runtime's pydantic model from refusing a value the SDK only
4530
+ * guessed at.
4531
+ */
4516
4532
  function jsonSchemaForType(type) {
4517
- // `any`, `undefined` and the file/octet aliases all mean "do not constrain".
4518
- // (v1 maps `octet-stream`/`file` to `object`; the SDK declares nothing rather
4519
- // than guess, because a file input reaches an agent through a `$ref` the
4520
- // sidecar does not carry.)
4521
- if (!type || type === 'any' || type === 'file' || type === 'octet-stream')
4533
+ if (type === 'octet-stream')
4534
+ return { $ref: JOB_ATTACHMENT_REF }; // v1's alias for file
4535
+ if (type === undefined || !isTypeDesc(type))
4522
4536
  return {};
4523
- if (type === 'array')
4524
- return { type: 'array', items: {} };
4525
- return { type };
4537
+ switch (type) {
4538
+ case 'file': return { $ref: JOB_ATTACHMENT_REF };
4539
+ case 'any': return {};
4540
+ // A bare `type: 'array'` makes the runtime's `jsonschema_to_pydantic`
4541
+ // reject a valid array (v1's note at `uipath.ts:118-123`, MST-9544).
4542
+ case 'array': return { type: 'array', items: {} };
4543
+ case 'string':
4544
+ case 'number':
4545
+ case 'integer':
4546
+ case 'boolean':
4547
+ case 'object':
4548
+ return { type };
4549
+ // The case vocabulary's widths and date kinds, as JSON Schema spells them
4550
+ // (the same table `case/entry-points.ts` projects with).
4551
+ case 'float': return { type: 'number', format: 'float' };
4552
+ case 'double': return { type: 'number', format: 'double' };
4553
+ case 'date': return { type: 'string', format: 'date' };
4554
+ case 'datetime': return { type: 'string', format: 'date-time' };
4555
+ // A case `jsonSchema` variable's shape lives in its `body`, which this
4556
+ // lookup does not carry — so its type is not established here.
4557
+ case 'jsonSchema': return {};
4558
+ default: {
4559
+ const unmapped = type;
4560
+ throw new Error(`jsonSchemaForType: no agent schema mapping for type '${String(unmapped)}'`);
4561
+ }
4562
+ }
4563
+ }
4564
+ const TYPE_DESCS = new Set(Object.values(types));
4565
+ function isTypeDesc(type) {
4566
+ return TYPE_DESCS.has(type);
4526
4567
  }
4527
4568
  /**
4528
4569
  * An End node's `outputs` map: every variable this scope exposes as a flow
@@ -4638,6 +4679,61 @@ export function serialize(built, opts = {}) {
4638
4679
  * A bindings entry the node does not reference is not a bound resource.
4639
4680
  */
4640
4681
  const resourceBindings = new Map();
4682
+ /**
4683
+ * Hoist a contribution's own bindings, then close every `<bindings.NAME>` its
4684
+ * definition references and it did not supply, from the definition's own
4685
+ * `model.bindings` (flow-builder-sdk#942). Shared by the three contribution
4686
+ * sites (step, trigger, entry point), so a `rawNode` and a hand-written
4687
+ * subclass get the same entries a typed emitter would. `check` runs the same
4688
+ * plan dry (`checkContribution` in `check.ts`), so it reports what this throws.
4689
+ */
4690
+ function hoistContributionBindings(def, supplied, where) {
4691
+ for (const b of supplied ?? [])
4692
+ resourceBindings.set(b.id, b);
4693
+ const plan = planContributionBindings(def, supplied);
4694
+ if (plan.problems.length) {
4695
+ throw new BindingRefusal('CONTRIBUTION_BINDING_UNRESOLVED', `${where}: ${plan.problems.join(' ')}`);
4696
+ }
4697
+ if (plan.entries.length === 0)
4698
+ return;
4699
+ // A process reference is the name + folderPath pair every typed process step
4700
+ // emits: go through the same builder, so a raw node and a typed step on ONE
4701
+ // process share one pair instead of shadowing each other.
4702
+ const byName = new Map(plan.entries.map((e) => [e.name, e]));
4703
+ const pairName = byName.get('name');
4704
+ const pairFolder = byName.get('folderPath');
4705
+ if (plan.resource === 'process' && plan.entries.length === 2 && pairName?.propertyAttribute === 'name'
4706
+ && pairFolder?.propertyAttribute === 'folderPath' && pairName.default !== undefined
4707
+ && pairFolder.default !== undefined) {
4708
+ const { entries } = processResourceBindings({
4709
+ name: pairName.default,
4710
+ folderPath: pairFolder.default,
4711
+ resourceSubType: pairName.resourceSubType,
4712
+ resourceKey: plan.resourceKey,
4713
+ }, resourceBindings);
4714
+ for (const b of entries) {
4715
+ // processResourceBindings types the subtype to the families it knows; keep
4716
+ // the definition's own spelling (or none) verbatim.
4717
+ if (pairName.resourceSubType === undefined)
4718
+ delete b.resourceSubType;
4719
+ resourceBindings.set(b.id, b);
4720
+ }
4721
+ return;
4722
+ }
4723
+ for (const e of plan.entries) {
4724
+ const same = [...resourceBindings.values()].some((b) => b.resource === e.resource
4725
+ && b.resourceKey === e.resourceKey && b.name === e.name && b.propertyAttribute === e.propertyAttribute);
4726
+ if (same)
4727
+ continue;
4728
+ const slug = e.resourceKey.replace(/[^A-Za-z0-9]/g, '') || 'Resource';
4729
+ const cap = (t) => t.charAt(0).toUpperCase() + t.slice(1);
4730
+ const stem = `b${cap(slug)}${cap(e.name.replace(/[^A-Za-z0-9]/g, ''))}`;
4731
+ let id = stem;
4732
+ for (let n = 2; resourceBindings.has(id); n++)
4733
+ id = `${stem}${n}`;
4734
+ resourceBindings.set(id, { id, ...e });
4735
+ }
4736
+ }
4641
4737
  /**
4642
4738
  * Extra files the flow needs beside the `.flow` — see `SerializeOpts.sidecars`.
4643
4739
  * Written into the caller's array when one was passed, so a caller that wants them
@@ -4733,13 +4829,15 @@ export function serialize(built, opts = {}) {
4733
4829
  * (design §2.6): resolution is exact — `<type>@<version>` from the bundle, or
4734
4830
  * the version of the definition already in hand — and a version the SDK
4735
4831
  * cannot resolve exactly fails compilation rather than floating to a default.
4832
+ * An undefined `label` takes the definition's `display.label`, which is what
4833
+ * the canvas shows on a freshly dropped node.
4736
4834
  */
4737
4835
  function makeNode(type, id, label, o = {}) {
4738
4836
  let def = o.def ?? defByType.get(type);
4739
4837
  if (o.requestedVersion !== undefined && def?.version !== o.requestedVersion) {
4740
4838
  const exact = o.def ? undefined : DEFS[`${type}@${o.requestedVersion}`];
4741
4839
  if (!exact) {
4742
- throw new Error(`"${label}" requests ${type}@${o.requestedVersion}, but this SDK resolves only `
4840
+ throw new Error(`"${label ?? id}" requests ${type}@${o.requestedVersion}, but this SDK resolves only `
4743
4841
  + `${type}@${def?.version ?? '<none>'}. Versions are exact — omit { version } to use `
4744
4842
  + `the pinned default, or request a version this SDK ships.`);
4745
4843
  }
@@ -4752,7 +4850,7 @@ export function serialize(built, opts = {}) {
4752
4850
  type,
4753
4851
  typeVersion: def?.version ?? '1.0.0',
4754
4852
  ui: { position: { x: 256 + col * 288, y: 144 + (o.parentId ? 160 : 0) }, size: { width: 96, height: 96 }, collapsed: false },
4755
- display: { label, ...(def?.display?.icon ? { icon: def.display.icon } : {}) },
4853
+ display: { label: label ?? def?.display?.label ?? id, ...(def?.display?.icon ? { icon: def.display.icon } : {}) },
4756
4854
  };
4757
4855
  col++;
4758
4856
  // A Data Fabric node gets no node-level `model`, whichever path emits it
@@ -4876,7 +4974,7 @@ export function serialize(built, opts = {}) {
4876
4974
  if (customTrigger) {
4877
4975
  const contribution = customTrigger.contribute(built.triggerOptions?.version !== undefined ? { requestedVersion: built.triggerOptions.version } : {});
4878
4976
  const def = resolveContributionDefinition(contribution.definition, built.triggerOptions?.version, 'trigger');
4879
- const node = makeNode(def.nodeType, triggerId, def.display?.label ?? 'Start', { def, requestedVersion: built.triggerOptions?.version });
4977
+ const node = makeNode(def.nodeType, triggerId, built.triggerOptions?.label ?? def.display?.label ?? 'Start', { def, requestedVersion: built.triggerOptions?.version });
4880
4978
  const inputs = renderDeep(contribution.inputs ?? {}, NO_RENAME);
4881
4979
  if (def.model?.entryPointId === true && inputs.entryPointId === undefined) {
4882
4980
  inputs.entryPointId = stableId(`${built.id}:start:entryPoint`);
@@ -4884,25 +4982,26 @@ export function serialize(built, opts = {}) {
4884
4982
  inputs.isDefaultEntryPoint = true;
4885
4983
  }
4886
4984
  node.inputs = inputs;
4887
- for (const b of contribution.bindings ?? [])
4888
- resourceBindings.set(b.id, b);
4985
+ hoistContributionBindings(def, contribution.bindings, 'trigger');
4889
4986
  root.nodes.push(node);
4890
4987
  }
4988
+ // The author's `.trigger(spec, { label })`; makeNode falls back to the definition's label.
4989
+ const rootLabel = built.triggerOptions?.label;
4891
4990
  const trigger = customTrigger
4892
4991
  ? root.nodes[root.nodes.length - 1]
4893
4992
  : eventTriggerRc
4894
- ? makeNode(eventTriggerRc.nodeType, triggerId, built.trigger?.kind === 'event' ? 'Event trigger' : 'Start', { def: eventTriggerRc.v1def, requestedVersion: built.triggerOptions?.version })
4993
+ ? makeNode(eventTriggerRc.nodeType, triggerId, rootLabel, { def: eventTriggerRc.v1def, requestedVersion: built.triggerOptions?.version })
4895
4994
  : built.trigger?.kind === 'scheduled'
4896
- ? makeNode(NODE_TYPE.scheduledTrigger, triggerId, 'Scheduled trigger', {
4995
+ ? makeNode(NODE_TYPE.scheduledTrigger, triggerId, rootLabel, {
4897
4996
  requestedVersion: scheduledVersion(built.trigger.inputs, built.triggerOptions?.version),
4898
4997
  })
4899
4998
  : built.trigger?.kind === 'form'
4900
- ? makeNode(NODE_TYPE.formTrigger, triggerId, 'Form trigger', { requestedVersion: built.triggerOptions?.version })
4999
+ ? makeNode(NODE_TYPE.formTrigger, triggerId, rootLabel, { requestedVersion: built.triggerOptions?.version })
4901
5000
  : built.trigger?.kind === 'conversation'
4902
- ? makeNode(NODE_TYPE.conversationTrigger, triggerId, 'Conversation trigger', { requestedVersion: built.triggerOptions?.version })
5001
+ ? makeNode(NODE_TYPE.conversationTrigger, triggerId, rootLabel, { requestedVersion: built.triggerOptions?.version })
4903
5002
  : built.trigger?.kind === 'voice'
4904
- ? makeNode(NODE_TYPE.voiceTrigger, triggerId, 'Incoming call trigger', { requestedVersion: built.triggerOptions?.version })
4905
- : makeNode(NODE_TYPE.trigger, triggerId, 'Manual trigger', { requestedVersion: built.triggerOptions?.version });
5003
+ ? makeNode(NODE_TYPE.voiceTrigger, triggerId, rootLabel, { requestedVersion: built.triggerOptions?.version })
5004
+ : makeNode(NODE_TYPE.trigger, triggerId, rootLabel, { requestedVersion: built.triggerOptions?.version });
4906
5005
  if (!customTrigger) {
4907
5006
  trigger.inputs = eventTriggerRc && built.trigger?.kind === 'event'
4908
5007
  ? {
@@ -5020,8 +5119,7 @@ export function serialize(built, opts = {}) {
5020
5119
  if (contribution.outputs) {
5021
5120
  node.outputs = { ...(node.outputs ?? {}), ...subNodeId(clone(contribution.outputs), id) };
5022
5121
  }
5023
- for (const b of contribution.bindings ?? [])
5024
- resourceBindings.set(b.id, b);
5122
+ hoistContributionBindings(def, contribution.bindings, where);
5025
5123
  scope.nodes.push(node);
5026
5124
  for (const t of tails)
5027
5125
  addEdge(scope, t, id);
@@ -5179,16 +5277,30 @@ export function serialize(built, opts = {}) {
5179
5277
  // required: it is what the platform scaffolds, so a flow that reads
5180
5278
  // `content` is a real (if toy) flow. `check` says so.
5181
5279
  : { content: 'string' };
5182
- // Rich options select a newer definition: `mode` needs the registry's
5183
- // CURRENT 1.3 (which adds the field), `memory` needs 1.4 (which adds the
5184
- // handle — derived from 1.3 by the workbench v1.4 migration; the tenant
5185
- // registry still serves 1.3). Without either, the 1.2 literal is emitted
5186
- // byte-identically.
5187
- const def = s.memory !== undefined
5188
- ? clone(DEFS[`${INLINE_AGENT_TYPE}@1.4`])
5189
- : s.mode !== undefined
5190
- ? clone(DEFS[`${INLINE_AGENT_TYPE}@1.3`])
5191
- : inlineAgentDef({ returns });
5280
+ // ONE default version for every call shape — the newest bundled pair (see
5281
+ // `inlineAgentDef`). `mode` and `memory` used to pick the version too (1.3
5282
+ // and 1.4), so a plain agent got a superseded 1.2 and each option moved
5283
+ // the node to another version as a side effect (#956). Both now live in
5284
+ // the sidecar and on edges; neither selects a definition. An explicit
5285
+ // `{ version }` (what decompile writes back) resolves exactly.
5286
+ const version = step.options?.version ?? INLINE_AGENT_DEFAULT_VERSION;
5287
+ // `definitions[]` holds one entry per type@version, so a flow where any
5288
+ // agent attaches memory keeps the handle for all of them.
5289
+ const withMemory = s.memory !== undefined
5290
+ || declaresMemoryHandle(emittedDefs.get(`${INLINE_AGENT_TYPE}@${version}`));
5291
+ const def = inlineAgentDef(version, withMemory);
5292
+ if (!def) {
5293
+ throw new Error(`"${step.name}" requests ${INLINE_AGENT_TYPE}@${version}, but this SDK resolves only `
5294
+ + `${INLINE_AGENT_VERSIONS.map((v) => `${INLINE_AGENT_TYPE}@${v}`).join(', ')}. Versions are exact — `
5295
+ + `omit { version } to use the default (${INLINE_AGENT_DEFAULT_VERSION}), or request a version this SDK ships.`);
5296
+ }
5297
+ if (s.memory !== undefined && !declaresMemoryHandle(def)) {
5298
+ throw new Error(`"${step.name}" attaches \`memory\`, but ${INLINE_AGENT_TYPE}@${version} declares no \`memory\` handle. `
5299
+ + (version === INLINE_AGENT_DEFAULT_VERSION
5300
+ ? `Pin a version that has it with inlineAgent({ ..., version: '<x.y>' }) `
5301
+ + `(bundled with memory: ${INLINE_AGENT_VERSIONS.filter((v) => declaresMemoryHandle(DEFS[`${INLINE_AGENT_TYPE}@${v}`])).join(', ') || 'none'}), or drop \`memory\`.`
5302
+ : `Drop { version: '${version}' } to use ${INLINE_AGENT_DEFAULT_VERSION}, or drop \`memory\`.`));
5303
+ }
5192
5304
  const id = stepUid(scope, step.name);
5193
5305
  // `source` is the agent's directory name. Derived from the flow id + step
5194
5306
  // name rather than random so a recompile is byte-identical — a v4-shaped uuid
@@ -5277,7 +5389,7 @@ export function serialize(built, opts = {}) {
5277
5389
  // The schema's property TYPES need this scope's finished variables, which
5278
5390
  // do not exist yet — park them (the `properties` object is filled in
5279
5391
  // place when the scope closes). See `typeAgentInputSchemas`.
5280
- scope.agentSchemas.push({ properties: agentJson.inputSchema.properties, refs: plan.refs });
5392
+ scope.agentSchemas.push({ schema: agentJson.inputSchema, refs: plan.refs });
5281
5393
  // GROUNDING — one resource node per context index, joined to the agent's own
5282
5394
  // `context` handle. An ARTIFACT, not a step: it takes no incoming control
5283
5395
  // flow, produces no output, and never reaches the End node. The edge runs OUT
@@ -5626,7 +5738,7 @@ export function serialize(built, opts = {}) {
5626
5738
  name: app.name,
5627
5739
  appName: app.name,
5628
5740
  displayName: app.name,
5629
- folderPath: app.folderPath ?? '',
5741
+ folderPath: escalationAppFolder(app),
5630
5742
  inputSchema: fieldSchema(app.inputs),
5631
5743
  outputSchema: fieldSchema(app.outputs),
5632
5744
  },
@@ -5693,21 +5805,28 @@ export function serialize(built, opts = {}) {
5693
5805
  // downstream in the engine.
5694
5806
  const s = spec.inputs;
5695
5807
  const nodeInputs = s.inputs ?? {};
5696
- const subType = spec.kind === 'apiWorkflow' ? 'Api'
5697
- : spec.kind === 'agenticProcess' ? 'ProcessOrchestration'
5698
- : spec.kind === 'publishedFunction' ? 'Function'
5699
- : 'Process';
5700
5808
  const fireAndForget = spec.kind === 'agenticProcess' && spec.inputs.completion === 'fire-and-forget';
5701
5809
  const def = spec.kind === 'publishedFunction'
5702
- ? publishedFunctionDef({ key: spec.inputs.key, name: s.name, folderPath: s.folderPath, inputs: nodeInputs, returns: spec.inputs.returns })
5810
+ ? publishedFunctionDef({ key: spec.inputs.key, name: s.name, folderPath: spec.inputs.folderPath, inputs: nodeInputs, returns: spec.inputs.returns })
5703
5811
  : spec.kind === 'apiWorkflow'
5704
- ? apiWorkflowDef({ key: spec.inputs.key, name: s.name, folderPath: s.folderPath, inputs: nodeInputs, returns: spec.inputs.returns })
5812
+ ? apiWorkflowDef({ key: spec.inputs.key, name: s.name, folderPath: spec.inputs.folderPath, inputs: nodeInputs, returns: spec.inputs.returns })
5705
5813
  : spec.kind === 'agenticProcess'
5706
- ? agenticProcessDef({ key: spec.inputs.key, name: s.name, folderPath: s.folderPath, inputs: nodeInputs, returns: spec.inputs.returns, form: spec.inputs.form })
5707
- : rpaWorkflowDef({ key: spec.inputs.key, name: s.name, folderPath: s.folderPath, inputs: nodeInputs, returns: spec.inputs.returns });
5814
+ ? agenticProcessDef({
5815
+ key: spec.inputs.key, name: s.name, folderPath: s.folderPath, location: spec.inputs.location,
5816
+ projectId: spec.inputs.projectId, inputs: nodeInputs, returns: spec.inputs.returns, form: spec.inputs.form,
5817
+ })
5818
+ : rpaWorkflowDef({
5819
+ key: spec.inputs.key, name: s.name, folderPath: s.folderPath, location: spec.inputs.location,
5820
+ projectId: spec.inputs.projectId, inputs: nodeInputs, returns: spec.inputs.returns,
5821
+ });
5708
5822
  // The bindings first: two steps on the same process share ONE pair, and the
5709
5823
  // definition's `model.context` names ids that have to exist.
5710
- const { entries } = processResourceBindings({ name: s.name, folderPath: s.folderPath, resourceSubType: subType }, resourceBindings);
5824
+ // Read off the definition: one kind carries several subtypes (agenticProcess by `form`).
5825
+ // The pair's key and folder come off it too: an in-solution step's resourceKey is the
5826
+ // bare sibling key and its folder is `''`.
5827
+ const subType = def.model.bindings.resourceSubType;
5828
+ const pairFolder = def.model.bindings.values.find((v) => v.name === 'folderPath').default;
5829
+ const { entries } = processResourceBindings({ name: s.name, folderPath: pairFolder, resourceSubType: subType, resourceKey: def.model.bindings.resourceKey }, resourceBindings);
5711
5830
  for (const b of entries)
5712
5831
  resourceBindings.set(b.id, b);
5713
5832
  const id = stepUid(scope, step.name);
@@ -5917,7 +6036,7 @@ export function serialize(built, opts = {}) {
5917
6036
  });
5918
6037
  if (plan.refs.length > 0) {
5919
6038
  const agentJson = sidecars[sidecars.length - 1].json;
5920
- scope.agentSchemas.push({ properties: agentJson.inputSchema.properties, refs: plan.refs });
6039
+ scope.agentSchemas.push({ schema: agentJson.inputSchema, refs: plan.refs });
5921
6040
  }
5922
6041
  for (const t of tails)
5923
6042
  addEdge(scope, t, id);
@@ -6885,19 +7004,20 @@ export function serialize(built, opts = {}) {
6885
7004
  takenIds.add(ep.id);
6886
7005
  const spec = ep.trigger;
6887
7006
  const requestedVersion = ep.options?.version;
7007
+ // The author's label; makeNode falls back to the definition's label.
7008
+ const epLabel = ep.options?.label;
6888
7009
  const customEp = spec instanceof FlowTrigger && !('kind' in spec) ? spec : undefined;
6889
7010
  const epSpec = spec;
6890
7011
  let node;
6891
7012
  if (customEp) {
6892
7013
  const contribution = customEp.contribute(requestedVersion !== undefined ? { requestedVersion } : {});
6893
7014
  const def = resolveContributionDefinition(contribution.definition, requestedVersion, `entryPoint "${ep.id}"`);
6894
- node = makeNode(def.nodeType, ep.id, def.display?.label ?? ep.id, { def, requestedVersion });
7015
+ node = makeNode(def.nodeType, ep.id, ep.options?.label ?? def.display?.label ?? ep.id, { def, requestedVersion });
6895
7016
  node.inputs = {
6896
7017
  entryPointId: stableId(`${built.id}:${ep.id}:entryPoint`),
6897
7018
  ...renderDeep(contribution.inputs ?? {}, NO_RENAME),
6898
7019
  };
6899
- for (const b of contribution.bindings ?? [])
6900
- resourceBindings.set(b.id, b);
7020
+ hoistContributionBindings(def, contribution.bindings, `entryPoint "${ep.id}"`);
6901
7021
  }
6902
7022
  else if (epSpec.kind === 'event') {
6903
7023
  if (!opts.library)
@@ -6905,7 +7025,7 @@ export function serialize(built, opts = {}) {
6905
7025
  const sub = epSpec.subscription;
6906
7026
  const rc = opts.library.resolveEvent('trigger', sub.connector, sub.event, sub.version, sub.object);
6907
7027
  refuseUnverifiedByoa(opts, 'trigger', sub);
6908
- node = makeNode(rc.nodeType, ep.id, 'Event trigger', { def: rc.v1def, requestedVersion });
7028
+ node = makeNode(rc.nodeType, ep.id, epLabel, { def: rc.v1def, requestedVersion });
6909
7029
  node.inputs = {
6910
7030
  entryPointId: stableId(`${built.id}:${ep.id}:entryPoint`),
6911
7031
  detail: eventDetail(rc, sub, ep.id, opts, `onEvent() entry point "${ep.id}"`),
@@ -6920,7 +7040,7 @@ export function serialize(built, opts = {}) {
6920
7040
  resourceBindings.set(b.id, b);
6921
7041
  }
6922
7042
  else if (epSpec.kind === 'scheduled') {
6923
- node = makeNode(NODE_TYPE.scheduledTrigger, ep.id, 'Scheduled trigger', {
7043
+ node = makeNode(NODE_TYPE.scheduledTrigger, ep.id, epLabel, {
6924
7044
  requestedVersion: scheduledVersion(epSpec.inputs, requestedVersion),
6925
7045
  });
6926
7046
  node.inputs = {
@@ -6929,22 +7049,22 @@ export function serialize(built, opts = {}) {
6929
7049
  };
6930
7050
  }
6931
7051
  else if (epSpec.kind === 'form') {
6932
- node = makeNode(NODE_TYPE.formTrigger, ep.id, 'Form trigger', { requestedVersion });
7052
+ node = makeNode(NODE_TYPE.formTrigger, ep.id, epLabel, { requestedVersion });
6933
7053
  node.inputs = {
6934
7054
  entryPointId: stableId(`${built.id}:${ep.id}:entryPoint`),
6935
7055
  schema: { fields: formTriggerFields(ep.inputs), outcomes: [] },
6936
7056
  };
6937
7057
  }
6938
7058
  else if (epSpec.kind === 'conversation') {
6939
- node = makeNode(NODE_TYPE.conversationTrigger, ep.id, 'Conversation trigger', { requestedVersion });
7059
+ node = makeNode(NODE_TYPE.conversationTrigger, ep.id, epLabel, { requestedVersion });
6940
7060
  node.inputs = { entryPointId: stableId(`${built.id}:${ep.id}:entryPoint`) };
6941
7061
  }
6942
7062
  else if (epSpec.kind === 'voice') {
6943
- node = makeNode(NODE_TYPE.voiceTrigger, ep.id, 'Incoming call trigger', { requestedVersion });
7063
+ node = makeNode(NODE_TYPE.voiceTrigger, ep.id, epLabel, { requestedVersion });
6944
7064
  node.inputs = { entryPointId: stableId(`${built.id}:${ep.id}:entryPoint`) };
6945
7065
  }
6946
7066
  else if (epSpec.kind === 'manual') {
6947
- node = makeNode(NODE_TYPE.trigger, ep.id, 'Manual trigger', { requestedVersion });
7067
+ node = makeNode(NODE_TYPE.trigger, ep.id, epLabel, { requestedVersion });
6948
7068
  node.inputs = { entryPointId: stableId(`${built.id}:${ep.id}:entryPoint`) };
6949
7069
  }
6950
7070
  else {