@uipath/maestro-builder-sdk 6.36.1 → 6.37.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.
package/dist/api-index.md CHANGED
@@ -37,7 +37,7 @@ could hand you spans for a version you do not have.
37
37
 
38
38
  ## `@uipath/maestro-builder-sdk`
39
39
 
40
- 230 symbol(s). Import from `@uipath/maestro-builder-sdk`.
40
+ 231 symbol(s). Import from `@uipath/maestro-builder-sdk`.
41
41
 
42
42
  | Symbol | Kind | Declaration | Area | Summary |
43
43
  | --- | --- | --- | --- | --- |
@@ -180,15 +180,15 @@ could hand you spans for a version you do not have.
180
180
  | `js` | function | `dist/core/expr.d.ts:247-282` | Expressions and types | Build a raw JS *expression* (a condition or computation). |
181
181
  | `lit` | function | `dist/core/expr.d.ts:22-28` | Expressions and types | A constant value baked directly into a node input. |
182
182
  | `lookup` | function | `dist/core/lookups.d.ts:95-102` | Generated descriptors | Begin resolving a lookup field on a generated descriptor. |
183
- | `lookup` | function | `dist/core/lookups.d.ts:103-122` | Generated descriptors | Begin resolving a lookup field addressed by connector key and action. |
184
- | `lookup` | function | `dist/core/lookups.d.ts:123-137` | Generated descriptors | Begin resolving a `where` parameter on a trigger descriptor that carries a lookup table. |
185
- | `lookup` | function | `dist/core/lookups.d.ts:138-159` | Generated descriptors | Begin resolving a `where` parameter on a connector-EVENT descriptor. |
183
+ | `lookup` | function | `dist/core/lookups.d.ts:103-127` | Generated descriptors | Begin resolving a lookup field addressed by connector key and action. |
184
+ | `lookup` | function | `dist/core/lookups.d.ts:128-142` | Generated descriptors | Begin resolving a `where` parameter on a trigger descriptor that carries a lookup table. |
185
+ | `lookup` | function | `dist/core/lookups.d.ts:143-164` | Generated descriptors | Begin resolving a `where` parameter on a connector-EVENT descriptor. |
186
186
  | `LookupBuilder` | type | `dist/core/lookups.d.ts:64-71` | Generated descriptors | What `lookup(...)` returns: the generic form plus any generated aliases. |
187
187
  | `lookupKey` | function | `dist/core/lookups.d.ts:55-63` | Generated descriptors | The identity of one resolution, as `prepare` records it and `compile` reads it. |
188
- | `LookupResolution` | interface | `dist/core/lookups.d.ts:160-169` | Generated descriptors | One recorded resolution, as `connectors-local/resolutions.json` stores it. |
189
- | `LookupResolutions` | type | `dist/core/lookups.d.ts:170-171` | Generated descriptors | Every recorded resolution, keyed by lookupKey. |
188
+ | `LookupResolution` | interface | `dist/core/lookups.d.ts:165-174` | Generated descriptors | One recorded resolution, as `connectors-local/resolutions.json` stores it. |
189
+ | `LookupResolutions` | type | `dist/core/lookups.d.ts:175-176` | Generated descriptors | Every recorded resolution, keyed by lookupKey. |
190
190
  | `LookupSpec` | interface | `dist/core/lookups.d.ts:11-36` | Generated descriptors | One lookup field's retrieval contract, generated from `reference`. |
191
- | `lookupSpecOf` | function | `dist/core/lookups.d.ts:199-206` | Generated descriptors | The LookupSpec for one field, or `undefined` when it earns no helper. |
191
+ | `lookupSpecOf` | function | `dist/core/lookups.d.ts:221-228` | Generated descriptors | The LookupSpec for one field, or `undefined` when it earns no helper. |
192
192
  | `LookupStrategy` | type | `dist/core/lookups.d.ts:1-10` | Generated descriptors | How `prepare` should retrieve the collection, chosen from the metadata. |
193
193
  | `LookupToken` | interface | `dist/core/lookups.d.ts:37-52` | Generated descriptors | An unresolved lookup: what `.byEmail(…)` returns. |
194
194
  | `LoopOptions` | interface | `dist/flow-sdk.d.ts:487-511` | Option shapes | Loop-specific options. |
@@ -214,10 +214,11 @@ could hand you spans for a version you do not have.
214
214
  | `QueuePriority` | type | `dist/nodes/queue/flow/spec.d.ts:9-10` | Supporting types | The three priorities an Orchestrator queue item can carry. |
215
215
  | `ran` | function | `dist/core/expr.d.ts:95-141` | Expressions and types | Did this step run? |
216
216
  | `rawNode` | function | `dist/raw-node.d.ts:9-71` | Supporting types | Place a node this SDK has no factory for, carrying its definition verbatim (`uipath.exotic.thing@2.1` and the manifest… |
217
- | `RawReference` | type | `dist/core/lookups.d.ts:190-198` | Supporting types | |
217
+ | `RawReference` | type | `dist/core/lookups.d.ts:212-220` | Supporting types | |
218
218
  | `RemoteA2aToolRef` | interface | `dist/core/actions.d.ts:1285-1305` | Supporting types | A remote A2A (agent-to-agent) endpoint the agent may delegate to. |
219
- | `resolvedValue` | function | `dist/core/lookups.d.ts:172-179` | Generated descriptors | The recorded id for a token, or `undefined`. |
220
- | `resolveEventWhere` | function | `dist/core/lookups.d.ts:207-224` | Generated descriptors | An event subscription's `where` with every lookup token swapped for the id `registry prepare --resolve` recorded, or a… |
219
+ | `resolvedEventValue` | function | `dist/core/lookups.d.ts:185-201` | Generated descriptors | resolvedValue for a token on an event's `where`. |
220
+ | `resolvedValue` | function | `dist/core/lookups.d.ts:177-184` | Generated descriptors | The recorded id for a token, or `undefined`. |
221
+ | `resolveEventWhere` | function | `dist/core/lookups.d.ts:229-246` | Generated descriptors | An event subscription's `where` with every lookup token swapped for the id `registry prepare --resolve` recorded, or a… |
221
222
  | `ReturnFieldType` | type | `dist/core/actions.d.ts:821-838` | Supporting types | A JSON-schema-ish type for one field of a step's declared `returns`. |
222
223
  | `ReturnOptions` | interface | `dist/flow-sdk.d.ts:445-486` | Option shapes | `.return(...)`-specific options. |
223
224
  | `rpaWorkflow` | function | `dist/core/actions.d.ts:1797-1841` | Actions | Invoke a deployed RPA (robotic process automation) workflow — a classic UI-automation process, published to… |
@@ -259,7 +260,7 @@ could hand you spans for a version you do not have.
259
260
  | `TriggerSpec` | type | `dist/flow-sdk.d.ts:162-180` | Supporting types | What starts the flow. |
260
261
  | `TypeDesc` | type | `dist/core/expr.d.ts:385` | Supporting types | |
261
262
  | `types` | const | `dist/core/expr.d.ts:369-384` | Expressions and types | Variable type descriptors (map to Flow variable `type` values). |
262
- | `unresolvedLookupMessage` | function | `dist/core/lookups.d.ts:180-189` | Generated descriptors | The message for a lookup nothing has resolved — the command that fixes it. |
263
+ | `unresolvedLookupMessage` | function | `dist/core/lookups.d.ts:202-211` | Generated descriptors | The message for a lookup nothing has resolved — the command that fixes it. |
263
264
  | `v` | function | `dist/core/expr.d.ts:29-41` | Expressions and types | Reference a flow variable or output by name → `$vars.<name>`. |
264
265
  | `VarDecl` | interface | `dist/flow-sdk.d.ts:59-71` | Supporting types | 7 field(s) |
265
266
  | `VarSpec` | interface | `dist/flow-sdk.d.ts:72-127` | Supporting types | The rich spelling of a variable declaration: pass it instead of a bare `types.*` descriptor wherever `.input()` /… |
@@ -109,7 +109,7 @@ not find it either.
109
109
  | `businessRule` | property | `BuiltTask` | `dist/case/case-sdk.d.ts:542-543` |
110
110
  | `businessRule` | method | `ScopeBuilder` | `dist/bpmn/bpmn-sdk.d.ts:2538-2602` |
111
111
  | `businessRule` | method | `TaskBuilder` | `dist/case/case-sdk.d.ts:833-861` |
112
- | `by` | property | `LookupResolution` | `dist/core/lookups.d.ts:164` |
112
+ | `by` | property | `LookupResolution` | `dist/core/lookups.d.ts:169` |
113
113
  | `by` | property | `LookupSpec` | `dist/core/lookups.d.ts:27-28` |
114
114
  | `cachedAgentCard` | property | `RemoteA2aToolRef` | `dist/core/actions.d.ts:1299-1300` |
115
115
  | `callContext` | property | `EndCallInputs` | `dist/core/actions.d.ts:332-337` |
@@ -361,7 +361,7 @@ not find it either.
361
361
  | `field` | property | `DataFabricSort` | `dist/nodes/data-fabric/flow/spec.d.ts:54-55` |
362
362
  | `field` | property | `FieldMapping` | `dist/nodes/transform/flow/spec.d.ts:28-29` |
363
363
  | `field` | property | `FilterRule` | `dist/nodes/transform/flow/spec.d.ts:13-14` |
364
- | `field` | property | `LookupResolution` | `dist/core/lookups.d.ts:163` |
364
+ | `field` | property | `LookupResolution` | `dist/core/lookups.d.ts:168` |
365
365
  | `fields` | property | `DataFabricReadInputs` | `dist/nodes/data-fabric/flow/spec.d.ts:105-106` |
366
366
  | `fields` | property | `EscalationRef` | `dist/core/actions.d.ts:1538-1542` |
367
367
  | `fields` | property | `HitlInputs` | `dist/nodes/hitl/flow/spec.d.ts:201-210` |
@@ -583,7 +583,7 @@ not find it either.
583
583
  | `marksCaseComplete` | property | `BuiltCaseExitCondition` | `dist/case/case-sdk.d.ts:372` |
584
584
  | `marksStageComplete` | property | `BuiltExitCondition` | `dist/case/case-sdk.d.ts:339` |
585
585
  | `marksStageComplete` | property | `ExitOpts` | `dist/case/case-sdk.d.ts:710-711` |
586
- | `match` | property | `LookupResolution` | `dist/core/lookups.d.ts:165-166` |
586
+ | `match` | property | `LookupResolution` | `dist/core/lookups.d.ts:170-171` |
587
587
  | `matches` | property | `FormFieldCondition` | `dist/core/actions.d.ts:129-130` |
588
588
  | `matchNames` | property | `GuardrailSelector` | `dist/core/actions.d.ts:844-845` |
589
589
  | `maxDuration` | property | `RetrySpec` | `dist/bpmn/bpmn-sdk.d.ts:805-806` |
@@ -717,7 +717,7 @@ not find it either.
717
717
  | `nodes` | property | `FlowLayout` | `dist/flow-sdk.d.ts:780-781` |
718
718
  | `nodes` | property | `ScopeStorage` | `dist/bpmn/bpmn-sdk.d.ts:1935-1936` |
719
719
  | `nodeType` | property | `ConnectorMeta` | `dist/core/connectors.d.ts:62` |
720
- | `nodeType` | property | `LookupResolution` | `dist/core/lookups.d.ts:162` |
720
+ | `nodeType` | property | `LookupResolution` | `dist/core/lookups.d.ts:167` |
721
721
  | `notify` | property | `EscalationOpts` | `dist/case/case-sdk.d.ts:130-131` |
722
722
  | `notIn` | property | `FormFieldCondition` | `dist/core/actions.d.ts:127-128` |
723
723
  | `numExchanges` | property | `WaitForMessageInputs` | `dist/nodes/conversation/flow/spec.d.ts:31-35` |
@@ -1130,7 +1130,7 @@ not find it either.
1130
1130
  | `value` | property | `FilterRule` | `dist/nodes/transform/flow/spec.d.ts:16-23` |
1131
1131
  | `value` | property | `FormFieldOption` | `dist/core/actions.d.ts:100-101` |
1132
1132
  | `value` | property | `InOutField` | `dist/core/actions.d.ts:245-246` |
1133
- | `value` | property | `LookupResolution` | `dist/core/lookups.d.ts:167-168` |
1133
+ | `value` | property | `LookupResolution` | `dist/core/lookups.d.ts:172-173` |
1134
1134
  | `value` | property | `LookupSpec` | `dist/core/lookups.d.ts:29-30` |
1135
1135
  | `value` | property | `PortArm` | `dist/flow-sdk.d.ts:386-387` |
1136
1136
  | `value` | property | `ShownField` | `dist/core/actions.d.ts:213-219` |
@@ -69,6 +69,7 @@ export function check(built, opts = {}) {
69
69
  checkFolderBindings(built, diags);
70
70
  checkEntryPointId(built, diags);
71
71
  checkMethodParentheses(built, diags);
72
+ checkConnectorRows(built, diags);
72
73
  checkConnectors(built, opts, diags);
73
74
  checkConnectorEvents(built, opts, diags);
74
75
  return diags;
@@ -219,6 +220,69 @@ function checkMethodParentheses(built, diags) {
219
220
  };
220
221
  walk(built.nodes, built.flows);
221
222
  }
223
+ /** The Integration Service activity types, whose payload rows the platform reads only by `target`. */
224
+ const CONNECTOR_ACTIVITY_TYPES = new Set(['Intsvc.ActivityExecution', 'Intsvc.AsyncExecution']);
225
+ /**
226
+ * The targets the platform's reader knows (`UiPathExtensionsProvider`): anything else —
227
+ * an empty string included — falls through its switch to `Body`, and the row becomes the
228
+ * whole request body. `pathParameters` / `queryParameters` are the aggregate forms.
229
+ */
230
+ const PLATFORM_ROW_TARGETS = new Set(['body', 'bodyField', 'header', 'path', 'query', 'file', 'pathParameters', 'queryParameters']);
231
+ /**
232
+ * A connector activity's payload rows, as the platform reads them.
233
+ *
234
+ * Integration Service reads a connector's payload rows by target only, and its reader
235
+ * maps a missing, empty or unknown target to `Body` — so such a row does not vanish,
236
+ * it REPLACES the request body. The 2026-10-16 eval run's agent wrote
237
+ * `inputRows: [{ name: 'script', type: 'string', value }]` for a JDBC query: it
238
+ * validated, and faulted live with `Value for required parameter 'script' not found`,
239
+ * while the v1 arm's `<uipath:input name="body" target="body">{"script":"…"}</uipath:input>`
240
+ * ran. `inputs: { script }` folds a request field into the body row, so the remedy is
241
+ * the plain record for a body field, or the row's own target when it is a real slot.
242
+ * And because an `inputRows` list REPLACES `inputs` at emission (typed-node.ts), a
243
+ * node that sets both silently drops every `inputs` field — the same live fault.
244
+ */
245
+ function checkConnectorRows(built, diags) {
246
+ for (const n of allNodes(built.nodes)) {
247
+ if (n.kind !== 'typed' || !CONNECTOR_ACTIVITY_TYPES.has(n.type))
248
+ continue;
249
+ if (n.inputRows && Object.keys(n.inputs ?? {}).length > 0) {
250
+ diags.push({
251
+ level: 'error',
252
+ code: 'CONNECTOR_INPUTS_SHADOWED',
253
+ where: n.id,
254
+ message: `Connector activity "${n.id}" sets both \`inputs\` (${Object.keys(n.inputs).map((k) => `"${k}"`).join(', ')}) and \`inputRows\`; ` +
255
+ `an \`inputRows\` list replaces \`inputs\` entirely at emission, so those fields are never sent. Spell them as rows too ` +
256
+ `(a body field as \`{ name: 'body', type: 'json', target: 'body', body: { … } }\`), or drop \`inputRows\` and write every field in \`inputs\`.`,
257
+ });
258
+ }
259
+ for (const row of n.inputRows ?? []) {
260
+ const target = row.target ?? '';
261
+ if (target === '') {
262
+ diags.push({
263
+ level: 'error',
264
+ code: 'CONNECTOR_INPUT_ROW_NO_TARGET',
265
+ where: n.id,
266
+ message: `Connector activity "${n.id}" input row "${row.name}" has no target. Integration Service reads a connector's ` +
267
+ `payload rows by target, and its reader maps a missing or empty one to the request body — so this row validates and ` +
268
+ `then becomes the WHOLE body, replacing the body row. Give the row its target — \`target: 'body'\` folds it into ` +
269
+ `the request body, \`path\`/\`query\`/\`file\` are the other slots — or move EVERY row to \`inputs: { ${row.name}: … }\` ` +
270
+ `and drop \`inputRows\`: an \`inputRows\` list, when present, replaces \`inputs\` entirely.`,
271
+ });
272
+ }
273
+ else if (!PLATFORM_ROW_TARGETS.has(target)) {
274
+ diags.push({
275
+ level: 'error',
276
+ code: 'CONNECTOR_INPUT_ROW_TARGET_UNKNOWN',
277
+ where: n.id,
278
+ message: `Connector activity "${n.id}" input row "${row.name}" has target "${target}", which the platform's reader does not know; ` +
279
+ `it falls through to the request body, so this row would replace the body. Use one of ` +
280
+ `${[...PLATFORM_ROW_TARGETS].map((t) => `'${t}'`).join(', ')}.`,
281
+ });
282
+ }
283
+ }
284
+ }
285
+ }
222
286
  const GUID = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
223
287
  /**
224
288
  * `.metadata({ entryPointId })` must be a GUID.
@@ -26,6 +26,9 @@ export function createConnectorTask(ctx, n) {
26
26
  // authored for rather than an inferred one.
27
27
  ctxInput(m, 'operation', 'string', op.name ?? op.objectName ?? n.action),
28
28
  ctxInput(m, 'connectorKey', 'string', rc.connectorKey),
29
+ // The registry template's own value, and the one the CLI's V1 packaging path keys
30
+ // on; V2 (`Data.V2`) is tracked in https://github.com/UiPath/builder-sdk/issues/1298, blocked on UiPath/cli#5209.
31
+ ctxInput(m, 'activityConfigurationVersion', 'string', 'v1'),
29
32
  ];
30
33
  const connRef = bindingRef(ctx, n.connection, 'Connection', 'ConnectionId', `${rc.connectorKey} connection`);
31
34
  if (connRef) {
@@ -58,8 +58,15 @@ export function nodeDeclaredVars(n) {
58
58
  }
59
59
  if (n.kind === 'typed')
60
60
  return typedNodeDeclaredVars(n);
61
- if (n.kind === 'scriptTask')
61
+ // Spelled-out rows REPLACE the derived ones, so they are the whole answer — the
62
+ // same rule as `typedNodeDeclaredVars`. Reading only `outputs` here left a script
63
+ // task authored with `outputRows` (the form decompile writes) declaring nothing,
64
+ // so every `vars.<row.var>` downstream was EXPR_UNDECLARED_REF.
65
+ if (n.kind === 'scriptTask') {
66
+ if (n.outputRows)
67
+ return n.outputRows.map((r) => r.var).filter((v) => v !== undefined);
62
68
  return Object.keys(n.outputs);
69
+ }
63
70
  if (n.kind === 'task')
64
71
  return Object.keys(n.set);
65
72
  return [];
@@ -2032,15 +2032,23 @@ rehome) {
2032
2032
  // a bodied declaration through: ten `error<n>` declarations owned by boundary
2033
2033
  // events in `claims-triage` were skipped as derived and then nobody derived them
2034
2034
  // — they vanished from the recompiled process.
2035
- if (payload && type && type !== 'BPMN.Variables' && typeDescribesElement(localName(el.$type), type)) {
2035
+ // A script task's default payload type is `BPMN.Variables` — the same type a
2036
+ // variable-assignment task carries, which derives nothing. The script task with
2037
+ // spelled-out rows is admitted by its element, not its type: those rows are the
2038
+ // declaration on the way back in (`implicitNodeVars`' script arm), so they are
2039
+ // skipped here too, or the source redeclares every one of them in `.vars()`.
2040
+ const spelledOutScriptRows = localName(el.$type) === 'scriptTask' && type === 'BPMN.Variables' && outputRowsNeedSpellingOut(payload);
2041
+ if (payload && type && (spelledOutScriptRows || (type !== 'BPMN.Variables' && typeDescribesElement(localName(el.$type), type)))) {
2036
2042
  for (const row of payload.output ?? []) {
2037
2043
  if (!row.var)
2038
2044
  continue;
2039
- // A script task's rows are NOT spelled-out `outputRows` on the way back —
2040
- // `implicitNodeVars` derives its declarations from `outputs` keys, so they
2041
- // carry the id as the name and that arm's own wire type. Every other typed
2042
- // node's rows go through `rowDeclaration`, the same call the serializer makes.
2043
- candidates.set(row.var, type === 'BPMN.ScriptTask'
2045
+ // A script task's rows come back as `outputs` when that form reproduces
2046
+ // them, and `implicitNodeVars` then derives their declarations from the
2047
+ // keys: id as the name, that arm's own wire type. Rows that need spelling
2048
+ // out (`emitScriptTask` writes `outputRows` exactly when this holds) are
2049
+ // declared through `rowDeclaration` on the way back in, like every other
2050
+ // typed node's, so they are matched the same way here.
2051
+ candidates.set(row.var, type === 'BPMN.ScriptTask' && !outputRowsNeedSpellingOut(payload)
2044
2052
  ? { owner: el.id, wire: 'string' }
2045
2053
  : { owner: el.id, ...rowDeclaration({ name: row.name, type: row.type, schema: row.body }) });
2046
2054
  }
@@ -35,11 +35,11 @@ type ExternalNode = Extract<BpmnNode, {
35
35
  /**
36
36
  * The extension type a Phase 3d node lowers to, WITHOUT consulting the library.
37
37
  *
38
- * The emitters below take a connector event's type from the library's
39
- * `runtime.serviceType`, which is the authority. This is the same answer derived
40
- * from the node alone, so the serializer can declare the node's output variable
41
- * before it has resolved anything — and the emitter asserts the two agree, so the
42
- * shortcut cannot silently diverge.
38
+ * The method the author called decides the placement — `.eventTrigger()` a start
39
+ * event, `.waitForEvent()` a receive task — so the type is known from the node alone,
40
+ * and the serializer can declare the node's output variable before it has resolved
41
+ * anything. The emitter uses this same value; the library only has to say the
42
+ * operation is a connector event.
43
43
  */
44
44
  export declare function intsvcServiceType(n: EventNode | ExternalNode): string;
45
45
  /**
@@ -55,11 +55,10 @@ export declare function intsvcOutputVar(n: EventNode | ExternalNode): string | u
55
55
  /**
56
56
  * A connector event node — `Intsvc.EventTrigger` or `Intsvc.WaitForEvent`.
57
57
  *
58
- * The extension type comes from the LIBRARY (`runtime.serviceType`), not from the
59
- * method the author called. That is deliberate: the descriptor is the authority on
60
- * which placement it describes, so a `trigger` descriptor reached through
61
- * `.waitForEvent()` is caught here rather than emitting a start-event type onto a
62
- * receive task — a mismatch both offline validators accept.
58
+ * The extension type comes from the method the author called; the library only has
59
+ * to say the operation is a connector event (`runtime.serviceType` is one of the
60
+ * event types). A prepared descriptor carries the START form's runtime whichever
61
+ * method reaches it, so the placement, not the descriptor, decides the type.
63
62
  *
64
63
  * @param ctx - Serializer context: moddle, library, bindings, binding sink.
65
64
  * @param n - The authored node.
@@ -1,6 +1,7 @@
1
1
  import { createTypedNode, typedNodeOutputVar } from './typed-node.js';
2
2
  import { connectorBindingRef } from './connector.js';
3
3
  import { eventFilterJmes, eventFilterProblem, jmesString, jmesStringProblem, readEventFilter } from '../core/event-filters.js';
4
+ import { resolveEventWhere } from '../core/lookups.js';
4
5
  /**
5
6
  * Which extension type an external invocation lowers to.
6
7
  *
@@ -19,11 +20,11 @@ const EVENT_SERVICE_TYPE = { trigger: 'Intsvc.EventTrigger', event: 'Intsvc.Wait
19
20
  /**
20
21
  * The extension type a Phase 3d node lowers to, WITHOUT consulting the library.
21
22
  *
22
- * The emitters below take a connector event's type from the library's
23
- * `runtime.serviceType`, which is the authority. This is the same answer derived
24
- * from the node alone, so the serializer can declare the node's output variable
25
- * before it has resolved anything — and the emitter asserts the two agree, so the
26
- * shortcut cannot silently diverge.
23
+ * The method the author called decides the placement — `.eventTrigger()` a start
24
+ * event, `.waitForEvent()` a receive task — so the type is known from the node alone,
25
+ * and the serializer can declare the node's output variable before it has resolved
26
+ * anything. The emitter uses this same value; the library only has to say the
27
+ * operation is a connector event.
27
28
  */
28
29
  export function intsvcServiceType(n) {
29
30
  return n.kind === 'connectorEvent'
@@ -45,11 +46,10 @@ export function intsvcOutputVar(n) {
45
46
  /**
46
47
  * A connector event node — `Intsvc.EventTrigger` or `Intsvc.WaitForEvent`.
47
48
  *
48
- * The extension type comes from the LIBRARY (`runtime.serviceType`), not from the
49
- * method the author called. That is deliberate: the descriptor is the authority on
50
- * which placement it describes, so a `trigger` descriptor reached through
51
- * `.waitForEvent()` is caught here rather than emitting a start-event type onto a
52
- * receive task — a mismatch both offline validators accept.
49
+ * The extension type comes from the method the author called; the library only has
50
+ * to say the operation is a connector event (`runtime.serviceType` is one of the
51
+ * event types). A prepared descriptor carries the START form's runtime whichever
52
+ * method reaches it, so the placement, not the descriptor, decides the type.
53
53
  *
54
54
  * @param ctx - Serializer context: moddle, library, bindings, binding sink.
55
55
  * @param n - The authored node.
@@ -67,14 +67,20 @@ export function createConnectorEvent(ctx, n) {
67
67
  throw new Error(`connector event ${n.key}/${n.eventName} has no runtime.serviceType in the library; ` +
68
68
  `it cannot be authored as a BPMN node.`);
69
69
  }
70
- // The library is the authority, but the serializer has already declared this
71
- // node's output variable from the placement alone (`intsvcServiceType`). If the
72
- // two ever disagree, that variable is wrong — so say so rather than emit it.
70
+ // The PLACEMENT decides the extension type — `.eventTrigger()` starts the
71
+ // process (`Intsvc.EventTrigger`), `.waitForEvent()` pauses it (`Intsvc.WaitForEvent`)
72
+ // — and the serializer has already declared this node's output variable from it
73
+ // (`intsvcServiceType`). The library's `runtime.serviceType` only has to agree
74
+ // that the operation IS a connector event: the platform ships one operation for
75
+ // both placements, the baked library carries the wait form under `.event.`, and
76
+ // `prepare` writes the same entry with the START form's runtime block (measured
77
+ // on `uipath-microsoft-outlook365/email-received`, 2026-10-16) — so reading the
78
+ // placement off that field refused every prepared `.waitForEvent()` with "use
79
+ // .eventTrigger() instead", while the unprepared same node compiled.
73
80
  const expected = intsvcServiceType(n);
74
- if (type !== expected) {
81
+ if (type !== 'Intsvc.EventTrigger' && type !== 'Intsvc.WaitForEvent') {
75
82
  throw new Error(`connector event "${n.id}" was authored as a ${n.subscribe} (${expected}), but the library ` +
76
- `describes ${n.key}/${n.eventName} as ${type}. Use ` +
77
- `${type === 'Intsvc.EventTrigger' ? '.eventTrigger()' : '.waitForEvent()'} instead.`);
83
+ `describes ${n.key}/${n.eventName} as ${type}, which is not a connector event.`);
78
84
  }
79
85
  const conn = connectorBindingRef(ctx, n.connection, 'Connection', 'ConnectionId', `${rc.connectorKey} connection`);
80
86
  const folder = connectorBindingRef(ctx, n.folder, 'Connection', 'folderKey', 'FolderKey', conn?.resourceKey);
@@ -108,10 +114,17 @@ export function createConnectorEvent(ctx, n) {
108
114
  // The expression is the scope's equality clauses AND-joined with the authored
109
115
  // `filters`, each lowered by the designer's own grammar (`core/event-filters.ts`) — the
110
116
  // same lowering the Flow serializer does for `onEvent`/`waitForEvent`.
111
- inputs.body = eventFilterBody(n.id, n.filter, n.filters);
117
+ // A `lookup()` token in the scope is replaced by the id `prepare --resolve`
118
+ // recorded, as Flow's and Case's serializers do; an unrecorded one is refused
119
+ // with the command that records it. Stringified, a token was `[object Object]`.
120
+ const scope = resolveEventWhere(n.filter, ctx.library.lookupResolutions, `connector event "${n.id}"`);
121
+ inputs.body = eventFilterBody(n.id, scope, n.filters);
112
122
  if (n.parameters !== undefined)
113
123
  inputs.parameters = n.parameters;
114
- return createTypedNode(ctx.m, n.id, type, {
124
+ return createTypedNode(ctx.m, n.id,
125
+ // The placement's type, so a prepared `.event.` entry carrying the start form's
126
+ // runtime cannot turn a wait into a start event (see the check above).
127
+ expected, {
115
128
  name: n.name ?? rc.entry.display?.label ?? n.eventName,
116
129
  inputs,
117
130
  outputVar: n.outputVar,
@@ -154,14 +154,7 @@ export async function serialize(built, opts = {}) {
154
154
  version: meta.caseManagement.version,
155
155
  value: meta.caseManagement.value,
156
156
  }));
157
- // Process-level marker that accompanies an Integration Service connector graph.
158
- // Derived from the GRAPH, not from whether connector wiring happened to produce
159
- // a binding: a connector with no connection/folder produced no binding and so
160
- // used to lose the marker, and a DECOMPILED connector — which carries explicit
161
- // `.binding()` calls rather than derived ones — lost it too.
162
- if (allNodes(built.nodes).some(isIntsvcConnectorNode)) {
163
- processExt.push(m.create('uipath:IntsvcActivityConfig', { version: 'v2' }));
164
- }
157
+ // No process `intsvcActivityConfig`: the CLI packs Connection rows only for V1 nodes (https://github.com/UiPath/builder-sdk/issues/1298 tracks V2).
165
158
  if (processExt.length)
166
159
  processProps.extensionElements = m.create('bpmn:ExtensionElements', { values: processExt });
167
160
  processProps.flowElements = flowElements;
@@ -711,13 +704,27 @@ export function implicitNodeVars(nodes, declared) {
711
704
  claim(typedNodeOutputVar(n.id, n.trigger.type, n.trigger.outputVar), n.id);
712
705
  }
713
706
  else if (n.kind === 'scriptTask') {
714
- // `object` (emitted as `json`), not `string`. Under the v3 contract a script
715
- // returns `{ response: <whatever it returned> }`, so an output nobody declared
716
- // holds arbitrary JSON — declaring a number or an object as `string` is the
717
- // mis-declaration this fallback exists to avoid. An output the AUTHOR declared
718
- // keeps its own type: `claim` skips an id already taken.
719
- for (const varId of Object.keys(n.outputs))
720
- claim(varId, n.id);
707
+ if (n.outputRows) {
708
+ // Spelled-out rows state their declaration, as a typed node's do — the same
709
+ // `rowDeclaration` call, so `nodeDeclaredVars`, this, and decompile's skip set
710
+ // agree. Deriving from `outputs` alone here left a script task authored with
711
+ // `outputRows` declaring nothing on the wire: the checker (once it counted the
712
+ // rows) passed, and `uip maestro bpmn validate` reported MISSING_ROOT_VARIABLE
713
+ // on the task and VARIABLE_DOES_NOT_EXIST on every read of its variables.
714
+ for (const row of n.outputRows) {
715
+ const d = rowDeclaration(row);
716
+ claim(row.var, n.id, d.type, d);
717
+ }
718
+ }
719
+ else {
720
+ // `object` (emitted as `json`), not `string`. Under the v3 contract a script
721
+ // returns `{ response: <whatever it returned> }`, so an output nobody declared
722
+ // holds arbitrary JSON — declaring a number or an object as `string` is the
723
+ // mis-declaration this fallback exists to avoid. An output the AUTHOR declared
724
+ // keeps its own type: `claim` skips an id already taken.
725
+ for (const varId of Object.keys(n.outputs))
726
+ claim(varId, n.id);
727
+ }
721
728
  }
722
729
  }
723
730
  return out;
@@ -950,15 +957,6 @@ export function publicDeclarationScope(nodes, processId) {
950
957
  function cap(s) {
951
958
  return s.charAt(0).toUpperCase() + s.slice(1);
952
959
  }
953
- /**
954
- * Whether a node is an Integration Service connector activity — the `.connector()`
955
- * method, or the same extension types reached through `.activity()`.
956
- */
957
- function isIntsvcConnectorNode(n) {
958
- if (n.kind === 'connector')
959
- return true;
960
- return n.kind === 'typed' && (n.type === 'Intsvc.ActivityExecution' || n.type === 'Intsvc.AsyncExecution');
961
- }
962
960
  /** Flatten a scope's nodes (recursing sub-processes) for global scans. */
963
961
  export function allNodes(nodes) {
964
962
  const out = [];
@@ -1,4 +1,5 @@
1
1
  import { connectorCheckOpts } from './core/cli-library.js';
2
+ import { synthesizedDefinitionRefusal } from './core/synthesized-definition.js';
2
3
  import { connectorInputErrors } from './serialize.js';
3
4
  export { openCliLibrary, connectorCheckOpts, eventResolveErrorFor, byoaCheckOpts } from './core/cli-library.js';
4
5
  /**
@@ -10,7 +11,8 @@ export { openCliLibrary, connectorCheckOpts, eventResolveErrorFor, byoaCheckOpts
10
11
  * cannot disagree.
11
12
  */
12
13
  export function flowCheckOpts(library, bindings) {
13
- const shared = connectorCheckOpts(library, bindings, connectorInputErrors);
14
+ // Flow's own refusal of an operation `prepare` synthesized from Integration Service (BPMN-only).
15
+ const shared = connectorCheckOpts(library, bindings, connectorInputErrors, synthesizedDefinitionRefusal);
14
16
  if (!library)
15
17
  return shared;
16
18
  return {
@@ -1,4 +1,4 @@
1
- import { Library, type ResolvedConnector } from './library.js';
1
+ import { ConnectorResolveError, Library, type ResolvedConnector } from './library.js';
2
2
  import type { BindingsView } from './binding-messages.js';
3
3
  import { type ConnectorCheckOpts } from './connector-checks.js';
4
4
  import type { LookupResolutions, LookupSpec } from './lookups.js';
@@ -32,9 +32,15 @@ export declare function openCliLibrary(argv: readonly string[]): Library | undef
32
32
  * recorded by `registry prepare --resolve` would be reported as still owed,
33
33
  * telling the author to re-run a prepare they already ran.
34
34
  */
35
- export declare function connectorCheckOpts(library: Library | undefined, bindings?: BindingsView, inputErrors?: ConnectorInputValidator): ConnectorCheckOpts & EventCheckOpts;
35
+ export declare function connectorCheckOpts(library: Library | undefined, bindings?: BindingsView, inputErrors?: ConnectorInputValidator, refuse?: ConnectorRefusal): ConnectorCheckOpts & EventCheckOpts;
36
36
  /** A family's field-contract validator: `compile`'s refusal for these inputs, or `undefined`. */
37
37
  export type ConnectorInputValidator = (rc: ResolvedConnector, inputs: Record<string, unknown>, resolutions: LookupResolutions, lookups: Readonly<Record<string, LookupSpec>>) => string | undefined;
38
+ /**
39
+ * A family's own refusal of an operation the library DID resolve — Flow's
40
+ * `synthesizedDefinitionRefusal` — applied after `library.resolve`, so check
41
+ * and compile refuse the same entry with the same code and text.
42
+ */
43
+ export type ConnectorRefusal = (rc: ResolvedConnector) => ConnectorResolveError | undefined;
38
44
  /**
39
45
  * `check`'s EVENT_UNKNOWN verdict: the message `library.resolveEvent` throws
40
46
  * for a subscription, or `undefined` when it resolves.
@@ -57,12 +57,12 @@ export function openCliLibrary(argv) {
57
57
  * recorded by `registry prepare --resolve` would be reported as still owed,
58
58
  * telling the author to re-run a prepare they already ran.
59
59
  */
60
- export function connectorCheckOpts(library, bindings, inputErrors = (rc, inputs, resolutions, lookups) => connectorInputErrors(rc, inputs, resolutions, lookups, { dialect: EQUALS_EXPR_DIALECT })) {
60
+ export function connectorCheckOpts(library, bindings, inputErrors = (rc, inputs, resolutions, lookups) => connectorInputErrors(rc, inputs, resolutions, lookups, { dialect: EQUALS_EXPR_DIALECT }), refuse) {
61
61
  if (!library)
62
62
  return bindings ? { bindings } : {};
63
63
  // One memoized resolve per connector step, shared by `connectorResolveError`
64
64
  // and `connectorInputs`, so the second hook does not resolve again.
65
- const resolve = connectorResolver(library);
65
+ const resolve = connectorResolver(library, refuse);
66
66
  return {
67
67
  eventResolveError: eventResolveErrorFor(library),
68
68
  ...byoaCheckOpts(library),
@@ -129,14 +129,16 @@ const CONNECTOR_RESOLVE_FAILED = 'CONNECTOR_RESOLVE_FAILED';
129
129
  * "unknown" used to let `check` pass a step `compile` then died on (#1015); the
130
130
  * event twin, `eventResolveErrorFor`, already reported every throw.
131
131
  */
132
- function connectorResolver(library) {
132
+ function connectorResolver(library, refuse) {
133
133
  const memo = new Map();
134
134
  return (spec) => {
135
135
  const id = JSON.stringify([spec.key, spec.action, spec.version ?? null, spec.object ?? null]);
136
136
  let r = memo.get(id);
137
137
  if (!r) {
138
138
  try {
139
- r = { rc: library.resolve(spec.key, spec.action, spec.version, spec.object) };
139
+ const rc = library.resolve(spec.key, spec.action, spec.version, spec.object);
140
+ const refused = refuse?.(rc);
141
+ r = refused ? { error: { code: refused.code, message: refused.message } } : { rc };
140
142
  }
141
143
  catch (error) {
142
144
  r = error instanceof ConnectorResolveError
@@ -15,7 +15,13 @@ export type ConnectorResolveCode =
15
15
  * The index lists the operation, but its entry file or `.v1def.json` sidecar is
16
16
  * missing or not valid JSON: the library on disk is incomplete (#1015).
17
17
  */
18
- | 'CONNECTOR_LIBRARY_ENTRY_UNREADABLE';
18
+ | 'CONNECTOR_LIBRARY_ENTRY_UNREADABLE'
19
+ /**
20
+ * Flow only: the sidecar was synthesized from Integration Service by `registry
21
+ * prepare` (no registry node exists), and a Flow node needs the registry's
22
+ * definition. BPMN compiles the same entry (`core/synthesized-definition.ts`).
23
+ */
24
+ | 'CONNECTOR_NO_FLOW_DEFINITION';
19
25
  /**
20
26
  * The refusal `Library.resolve` throws, with a `code` for each reason.
21
27
  *
@@ -18,7 +18,7 @@
18
18
  * `skill-bpmn-outlook-waitfor-email` in `flow-v2-run-8` failed on exactly that last
19
19
  * question, and in run 12 on having nowhere to put a payload condition.
20
20
  */
21
- import { isLookupToken, resolvedValue, unresolvedLookupMessage, } from './lookups.js';
21
+ import { isLookupToken, resolvedEventValue, unresolvedLookupMessage, } from './lookups.js';
22
22
  import { prepareCommand } from './cli-spelling.js';
23
23
  import { EVENT_FILTER_OPERATORS, readEventFilter, eventFilterProblem, jmesStringProblem } from './event-filters.js';
24
24
  import { EVENT_BYOA_UNVERIFIED, byoaUnverified } from './byoa.js';
@@ -129,7 +129,7 @@ export function checkEventResolution(subscription, where, kind, opts, error, war
129
129
  for (const [key, value] of scope) {
130
130
  if (!isLookupToken(value))
131
131
  continue;
132
- if (resolvedValue(value, resolutions) !== undefined)
132
+ if (resolvedEventValue(value, resolutions) !== undefined)
133
133
  continue;
134
134
  warn('LOOKUP_UNRESOLVED', `${where}: \`where.${key}\` ` + unresolvedLookupMessage(value)
135
135
  + `\nCompile will fail until this is recorded.`);
@@ -114,6 +114,11 @@ export declare function lookup<D extends LookupBearing, F extends keyof NonNulla
114
114
  * and there are no typed aliases, so only the generic `.by(name, value)` is
115
115
  * available. A consistent trade rather than a new one.
116
116
  *
117
+ * On a connector EVENT's `where` (`.waitForEvent()` / `.eventTrigger()` /
118
+ * `onEvent()`), `action` is the event id and the token is matched against the
119
+ * resolution `prepare` recorded for the event as well, so this form and
120
+ * `lookup({ connector, event }, field)` resolve alike.
121
+ *
117
122
  * @param key - The connector library key, e.g. `'uipath-salesforce-slack'`.
118
123
  * @param action - The operation id, e.g. `'send-message-to-user'`.
119
124
  * @param field - The lookup field, e.g. `'channel'`.
@@ -177,6 +182,23 @@ export type LookupResolutions = Readonly<Record<string, LookupResolution>>;
177
182
  * @returns The connector-facing id, when this exact `(nodeType, field, by, value)` was resolved.
178
183
  */
179
184
  export declare function resolvedValue(token: LookupToken, resolutions: LookupResolutions): string | undefined;
185
+ /**
186
+ * {@link resolvedValue} for a token on an event's `where`.
187
+ *
188
+ * `prepare` records an event's resolutions under the EVENT form of the node type,
189
+ * which the descriptor form `lookup({ connector, event }, field)` keys a token
190
+ * under too. The string form `lookup(key, action, field)` cannot know it is being
191
+ * used on an event and keys the token under the activity form, so a recorded
192
+ * resolution was never found for it: on the 2026-10-16 run an author whose
193
+ * `prepare --resolve parentFolderId:displayName=Inbox` had succeeded still saw
194
+ * `LOOKUP_UNRESOLVED` from `check` and a refusal from `compile`, and pasted the
195
+ * id in by hand. The token's own key is tried first; the event form second.
196
+ *
197
+ * @param token - The unresolved lookup, from either `lookup()` form.
198
+ * @param resolutions - Everything `prepare --resolve` has recorded.
199
+ * @returns The connector-facing id, when either key was resolved.
200
+ */
201
+ export declare function resolvedEventValue(token: LookupToken, resolutions: LookupResolutions): string | undefined;
180
202
  /**
181
203
  * The message for a lookup nothing has resolved — the command that fixes it.
182
204
  *