@uipath/maestro-builder-sdk 6.8.0 → 6.8.2
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/check.js +26 -7
- package/dist/core/lookups.js +61 -3
- package/dist/decompile.js +5 -1
- package/dist/migrate.js +77 -0
- package/dist/serialize.js +11 -1
- package/package.json +2 -2
package/dist/check.js
CHANGED
|
@@ -1250,7 +1250,7 @@ function checkConnectorLookups(step, spec, opts, diags) {
|
|
|
1250
1250
|
const declared = specs?.[named];
|
|
1251
1251
|
const by = value.__lookup.by;
|
|
1252
1252
|
if (declared && typeof by === 'string' && !declared.by.includes(by)) {
|
|
1253
|
-
const { target, call } = lookupPhrases(named, declared);
|
|
1253
|
+
const { target, call } = lookupPhrases(named, declared, spec);
|
|
1254
1254
|
diags.push({
|
|
1255
1255
|
level: 'error',
|
|
1256
1256
|
code: 'LOOKUP_UNKNOWN_MATCH_KEY',
|
|
@@ -1302,7 +1302,7 @@ function checkConnectorLookups(step, spec, opts, diags) {
|
|
|
1302
1302
|
const literal = value instanceof Expr ? value.value : value;
|
|
1303
1303
|
if (typeof literal !== 'string')
|
|
1304
1304
|
return;
|
|
1305
|
-
const { target, call } = lookupPhrases(field, lookup);
|
|
1305
|
+
const { target, call } = lookupPhrases(field, lookup, spec);
|
|
1306
1306
|
diags.push({
|
|
1307
1307
|
level: 'warning',
|
|
1308
1308
|
code: 'LOOKUP_LITERAL_ID',
|
|
@@ -1439,13 +1439,32 @@ function checkConnectorContract(step, spec, opts, diags) {
|
|
|
1439
1439
|
}
|
|
1440
1440
|
}
|
|
1441
1441
|
}
|
|
1442
|
-
/**
|
|
1443
|
-
|
|
1442
|
+
/**
|
|
1443
|
+
* The two phrases every lookup diagnostic needs: what the field sends, and how
|
|
1444
|
+
* to resolve it.
|
|
1445
|
+
*
|
|
1446
|
+
* The `call` names BOTH authoring forms, and names which one the alias belongs
|
|
1447
|
+
* to. Printing `lookup(<Descriptor>, 'entityName').byDisplayName('<value>')`
|
|
1448
|
+
* alone — the old text — was a remedy with an unstated precondition: aliases
|
|
1449
|
+
* are generated onto the descriptor, and `<Descriptor>` is a placeholder the
|
|
1450
|
+
* reader has to fill from somewhere. An author holding the connector key and
|
|
1451
|
+
* action instead (the form the library's Generic CRUD half is authored in, and
|
|
1452
|
+
* the majority of its lookup fields) substitutes those for the placeholder,
|
|
1453
|
+
* keeps the alias, and gets `lookup(...).byDisplayName is not a function`. Six
|
|
1454
|
+
* flow-eval tasks did precisely that, each losing a check-edit-check round to
|
|
1455
|
+
* a message that had told them to.
|
|
1456
|
+
*/
|
|
1457
|
+
function lookupPhrases(field, lookup, spec) {
|
|
1444
1458
|
const target = `${lookup.objectName ?? lookup.path}.${lookup.value}`;
|
|
1459
|
+
const by = lookup.by[0] ?? 'id';
|
|
1445
1460
|
const alias = Object.keys(lookup.aliases)[0];
|
|
1461
|
+
// The key/action form first: it needs no import, so it is the one line that
|
|
1462
|
+
// is paste-ready from the message alone.
|
|
1463
|
+
const generic = `lookup('${spec.key}', '${spec.action}', '${field}').by('${by}', '<value>')`;
|
|
1446
1464
|
const call = alias
|
|
1447
|
-
?
|
|
1448
|
-
|
|
1465
|
+
? `${generic}\n or \`lookup(D, '${field}').${alias}('<value>')\`, where D is the `
|
|
1466
|
+
+ `descriptor \`${prepareCommand(spec.key, spec.action)}\` generates — the aliases exist only there`
|
|
1467
|
+
: generic;
|
|
1449
1468
|
return { target, call };
|
|
1450
1469
|
}
|
|
1451
1470
|
/**
|
|
@@ -1502,7 +1521,7 @@ function runtimeLookupDiagnostic(step, spec, field, lookup, value) {
|
|
|
1502
1521
|
|| leaf.toLowerCase() === valueLeaf.toLowerCase()
|
|
1503
1522
|
|| (valueIsEmail && EMAIL_LEAF.test(leaf))))
|
|
1504
1523
|
return undefined;
|
|
1505
|
-
const { target, call } = lookupPhrases(field, lookup);
|
|
1524
|
+
const { target, call } = lookupPhrases(field, lookup, spec);
|
|
1506
1525
|
const searchesByEmail = lookup.by.some((b) => EMAIL_LEAF.test(b));
|
|
1507
1526
|
const isEmail = !!leaf && EMAIL_LEAF.test(leaf) && !valueIsEmail && !searchesByEmail;
|
|
1508
1527
|
const message = `Connector step "${step}": \`${field}\` is a lookup field bound to the runtime expression `
|
package/dist/core/lookups.js
CHANGED
|
@@ -46,7 +46,53 @@ export function isLookupToken(v) {
|
|
|
46
46
|
export function lookupKey(nodeType, field, by, value) {
|
|
47
47
|
return JSON.stringify([nodeType, field, by, value]);
|
|
48
48
|
}
|
|
49
|
-
|
|
49
|
+
/**
|
|
50
|
+
* An alias-shaped property: `byEmail`, `byDisplayName`, `byAccountId`.
|
|
51
|
+
*
|
|
52
|
+
* Narrow on purpose. The builder is handed to whatever inspects it next —
|
|
53
|
+
* `util.inspect`, a `then` probe on an accidental `await`, a spread — and a
|
|
54
|
+
* trap that answered for EVERY absent name would turn those into throws. Only
|
|
55
|
+
* a name the author could have meant as an alias is intercepted.
|
|
56
|
+
*/
|
|
57
|
+
const ALIAS_SHAPE = /^by[A-Z]/;
|
|
58
|
+
/**
|
|
59
|
+
* Why `.byDisplayName()` is absent, in the terms of the form that was used.
|
|
60
|
+
*
|
|
61
|
+
* Aliases are generated from the descriptor's own `lookups` table, so the
|
|
62
|
+
* string form — which has no table to read — offers only `.by(name, value)`.
|
|
63
|
+
* That is a deliberate trade (see the overload's `@remarks`), but the author
|
|
64
|
+
* who trips it learns it as `lookup(...).byDisplayName is not a function`: a
|
|
65
|
+
* bare `TypeError` naming neither the field, the form, nor the way out. Six
|
|
66
|
+
* flow-eval tasks lost turns to exactly that message, every one of them having
|
|
67
|
+
* copied an alias out of a diagnostic or a descriptor's doc comment and pasted
|
|
68
|
+
* it onto the key/action form they happened to be holding.
|
|
69
|
+
*/
|
|
70
|
+
function missingAliasMessage(nodeType, field, alias, spec, generic) {
|
|
71
|
+
const head = `lookup(${nodeType}, '${field}'): \`.${alias}()\` does not exist. `;
|
|
72
|
+
if (!spec) {
|
|
73
|
+
// The remedy names the author's OWN key and action rather than a
|
|
74
|
+
// placeholder: a remedy with a hole in it is what produced this call.
|
|
75
|
+
const here = generic
|
|
76
|
+
? `lookup('${generic.key}', '${generic.action}', '${field}')`
|
|
77
|
+
: `lookup(…, '${field}')`;
|
|
78
|
+
return head
|
|
79
|
+
+ `Aliases are generated onto the descriptor, and this call has no descriptor `
|
|
80
|
+
+ `to read them from, so only \`.by(name, value)\` exists here. Search by name:\n`
|
|
81
|
+
+ ` ${here}.by('<searchable name>', <value>)\n`
|
|
82
|
+
+ `\`check\` and \`${prepareCommand()}\` both print the names this field is `
|
|
83
|
+
+ `searchable by. Or import the descriptor `
|
|
84
|
+
+ (generic ? `\`${prepareCommand(generic.key, generic.action)}\` generates ` : '')
|
|
85
|
+
+ `and call \`.${alias}(<value>)\` on that.`;
|
|
86
|
+
}
|
|
87
|
+
const aliases = Object.keys(spec.aliases);
|
|
88
|
+
return head
|
|
89
|
+
+ (aliases.length
|
|
90
|
+
? `Generated aliases for this field: ${aliases.map((a) => `\`.${a}()\``).join(', ')}. `
|
|
91
|
+
: `This field earns no alias — two of its names camelise to one, so both were dropped. `)
|
|
92
|
+
+ `Every searchable name is reachable as `
|
|
93
|
+
+ `\`.by(${spec.by.map((b) => `'${b}'`).join(' | ')}, <value>)\`.`;
|
|
94
|
+
}
|
|
95
|
+
function makeBuilder(nodeType, field, spec, generic) {
|
|
50
96
|
const token = (by, value) => ({ __lookup: { nodeType, field, by, value } });
|
|
51
97
|
const builder = {
|
|
52
98
|
by(name, value) {
|
|
@@ -63,7 +109,19 @@ function makeBuilder(nodeType, field, spec) {
|
|
|
63
109
|
for (const [alias, byName] of Object.entries(spec?.aliases ?? {})) {
|
|
64
110
|
builder[alias] = (value) => token(byName, value);
|
|
65
111
|
}
|
|
66
|
-
|
|
112
|
+
// The throw is deferred to the CALL rather than raised on the property read,
|
|
113
|
+
// so the stack points at `.byDisplayName('Inbox')` — the line the author
|
|
114
|
+
// wrote — instead of at the property access the engine desugared it into.
|
|
115
|
+
return new Proxy(builder, {
|
|
116
|
+
get(target, prop, receiver) {
|
|
117
|
+
if (typeof prop === 'string' && !(prop in target) && ALIAS_SHAPE.test(prop)) {
|
|
118
|
+
return () => {
|
|
119
|
+
throw new Error(missingAliasMessage(nodeType, field, prop, spec, generic));
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
return Reflect.get(target, prop, receiver);
|
|
123
|
+
},
|
|
124
|
+
});
|
|
67
125
|
}
|
|
68
126
|
export function lookup(a, b, c) {
|
|
69
127
|
if (typeof a === 'string') {
|
|
@@ -71,7 +129,7 @@ export function lookup(a, b, c) {
|
|
|
71
129
|
throw new Error("lookup(key, action, field) needs all three arguments; " +
|
|
72
130
|
"did you mean lookup(descriptor, field)?");
|
|
73
131
|
}
|
|
74
|
-
return makeBuilder(`uipath.connector.${a}.${b}`, c);
|
|
132
|
+
return makeBuilder(`uipath.connector.${a}.${b}`, c, undefined, { key: a, action: b });
|
|
75
133
|
}
|
|
76
134
|
if (!('nodeType' in a) || a.nodeType === undefined) {
|
|
77
135
|
const event = a;
|
package/dist/decompile.js
CHANGED
|
@@ -1247,7 +1247,11 @@ function emitAgentCluster(node, imp, inputNames, graph) {
|
|
|
1247
1247
|
if (node.type === T.conversationalAgent) {
|
|
1248
1248
|
const st = (i.conversationalAgentSettings ?? {});
|
|
1249
1249
|
const settingExpr = (k) => {
|
|
1250
|
-
|
|
1250
|
+
// `unwrapSource` rather than a string test: since the envelope reaches
|
|
1251
|
+
// these five keys (migrate's `NESTED_BINDING_INPUTS`), a bare string is
|
|
1252
|
+
// only what a pre-1.6 file carries. Reading strings alone made every
|
|
1253
|
+
// modern file decompile to a conversational agent with no turn wiring.
|
|
1254
|
+
const raw = unwrapSource(st[k]);
|
|
1251
1255
|
if (raw === undefined || raw === '')
|
|
1252
1256
|
return undefined;
|
|
1253
1257
|
const js = raw.startsWith('=js:') ? raw.slice(4) : raw;
|
package/dist/migrate.js
CHANGED
|
@@ -48,6 +48,82 @@ function isUnwrappedInput(nodeType, key) {
|
|
|
48
48
|
return false;
|
|
49
49
|
return UNWRAPPED_INPUTS.some((r) => nodeType.startsWith(r.typePrefix) && r.keys.has(key));
|
|
50
50
|
}
|
|
51
|
+
/**
|
|
52
|
+
* Inputs whose value is an OBJECT of bindings, wrapped one level down.
|
|
53
|
+
*
|
|
54
|
+
* `wrapNodes` walks `n.inputs` one level and skips anything that is not a
|
|
55
|
+
* string, so a sub-object of bindings is never reached. For almost every input
|
|
56
|
+
* that is correct — a connector's `entityConfig` is a payload, not a binding
|
|
57
|
+
* table, and wrapping its leaves would corrupt it. `conversationalAgentSettings`
|
|
58
|
+
* is the exception: its five keys are per-turn BINDINGS, and the platform's own
|
|
59
|
+
* writer envelopes each one.
|
|
60
|
+
*
|
|
61
|
+
* **`uipath.agent.conversational` → `conversationalAgentSettings`** (measured
|
|
62
|
+
* 2026-09-23 against the same-ground A/B run):
|
|
63
|
+
*
|
|
64
|
+
* - Every binding written by the PRODUCT carries the envelope: 5 of 5 across
|
|
65
|
+
* the v1 arm's `conversationalAgentSettings`, each a
|
|
66
|
+
* `{ type: 'jsExpression', expression, fieldType }`. Every binding written
|
|
67
|
+
* by this SDK is a bare `=js:` string: 5 of 5 in the v2 arm. No mixed case
|
|
68
|
+
* anywhere in the corpus.
|
|
69
|
+
* - The bare string is the pre-1.3 spelling, and Studio Web renders it as
|
|
70
|
+
* literal text rather than evaluating it — so the flow deploys, validates
|
|
71
|
+
* `Valid`, and shows the author the text `$vars.listen.output…` where the
|
|
72
|
+
* transcript should be.
|
|
73
|
+
* - `uip maestro flow validate` accepts both, which is why this survived: the
|
|
74
|
+
* SDK's own `check`, `compile` and the product's static validator all passed
|
|
75
|
+
* on the broken shape. `skill-flow-conversational-chat-loop` scored 0.61
|
|
76
|
+
* with a green ladder.
|
|
77
|
+
*
|
|
78
|
+
* The `fieldType`s are a table rather than a schema read because the node's own
|
|
79
|
+
* manifest declares `conversationalAgentSettings` as an untyped `object`
|
|
80
|
+
* (`uipath.agent.conversational@1.6` and `@1.7`, `inputDefinition.properties`),
|
|
81
|
+
* so there is nothing underneath it to read. They are the types the product
|
|
82
|
+
* writes, from the same measurement.
|
|
83
|
+
*
|
|
84
|
+
* `mode` is deliberately absent: it is an editor-state enum (`'simple'` /
|
|
85
|
+
* `'custom'`), not a binding, and the product leaves it a plain string.
|
|
86
|
+
*/
|
|
87
|
+
const NESTED_BINDING_INPUTS = [
|
|
88
|
+
{
|
|
89
|
+
typePrefix: 'uipath.agent.conversational',
|
|
90
|
+
key: 'conversationalAgentSettings',
|
|
91
|
+
fieldTypes: {
|
|
92
|
+
context: 'object',
|
|
93
|
+
conversationId: 'string',
|
|
94
|
+
exchangeId: 'string',
|
|
95
|
+
messages: 'array',
|
|
96
|
+
userSettings: 'object',
|
|
97
|
+
},
|
|
98
|
+
},
|
|
99
|
+
];
|
|
100
|
+
/**
|
|
101
|
+
* Wrap the binding leaves of an object-valued input, in place.
|
|
102
|
+
*
|
|
103
|
+
* Only the keys the table names are touched. An unknown key is left exactly as
|
|
104
|
+
* it is: the table is evidence about specific fields, and a key nobody measured
|
|
105
|
+
* is not covered by it.
|
|
106
|
+
*/
|
|
107
|
+
function wrapNestedBindings(node) {
|
|
108
|
+
if (typeof node.type !== 'string')
|
|
109
|
+
return;
|
|
110
|
+
for (const rule of NESTED_BINDING_INPUTS) {
|
|
111
|
+
if (!node.type.startsWith(rule.typePrefix))
|
|
112
|
+
continue;
|
|
113
|
+
const settings = node.inputs?.[rule.key];
|
|
114
|
+
if (!settings || typeof settings !== 'object' || Array.isArray(settings))
|
|
115
|
+
continue;
|
|
116
|
+
for (const [key, fieldType] of Object.entries(rule.fieldTypes)) {
|
|
117
|
+
const v = settings[key];
|
|
118
|
+
if (typeof v !== 'string' || isWrappedSource(v))
|
|
119
|
+
continue;
|
|
120
|
+
// The same rule the top-level loop applies: an expression always, and a
|
|
121
|
+
// literal whenever the field is not string-typed.
|
|
122
|
+
if (v.startsWith('=') || fieldType !== 'string')
|
|
123
|
+
settings[key] = wrap(v, fieldType);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
}
|
|
51
127
|
/**
|
|
52
128
|
* The only `fieldType` values `@uipath/flow-schema`'s `expressionValueSchema`
|
|
53
129
|
* accepts (it is `.strict()`, so anything else fails the file's schema and with
|
|
@@ -155,6 +231,7 @@ function wrapNodes(nodes, schemaByType, globalTypeByName, subflows = {}) {
|
|
|
155
231
|
if (v.startsWith('=') || declared !== 'string')
|
|
156
232
|
n.inputs[k] = wrap(v, declared);
|
|
157
233
|
}
|
|
234
|
+
wrapNestedBindings(n);
|
|
158
235
|
}
|
|
159
236
|
if (n.outputs) {
|
|
160
237
|
for (const [k, vobj] of Object.entries(n.outputs)) {
|
package/dist/serialize.js
CHANGED
|
@@ -1628,7 +1628,17 @@ function conversationalAgentJson(spec) {
|
|
|
1628
1628
|
// agent's — a debug run without both is refused before it starts.
|
|
1629
1629
|
maxTokens: spec.maxTokenPerResponse ?? 16384,
|
|
1630
1630
|
temperature: spec.temperature ?? 0,
|
|
1631
|
-
|
|
1631
|
+
// `basic-v2` is the NON-conversational inline agent's engine (the other
|
|
1632
|
+
// `agent.json` writer in this file, 100 lines down), and it was copied in
|
|
1633
|
+
// with the rest of that shape. Every instance of THIS node is
|
|
1634
|
+
// conversational — `metadata.isConversational` below says so
|
|
1635
|
+
// unconditionally — and `voiceAgentJson`, the sibling conversational
|
|
1636
|
+
// family, already spells it the other way. Measured across the 2026-09-23
|
|
1637
|
+
// A/B run: every product-written `agent.json` with `isConversational: true`
|
|
1638
|
+
// carries `conversational-v1` (3 of 3) and every one with `false` carries
|
|
1639
|
+
// `basic-v2` (6 of 6). The SDK's own voice path agreed (2 of 2); only this
|
|
1640
|
+
// path disagreed, and its task graded the sidecar as not conversational.
|
|
1641
|
+
engine: 'conversational-v1',
|
|
1632
1642
|
...(spec.maxIterations === undefined ? {} : { maxIterations: spec.maxIterations }),
|
|
1633
1643
|
mode: 'standard',
|
|
1634
1644
|
},
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@uipath/maestro-builder-sdk",
|
|
3
|
-
"version": "6.8.
|
|
3
|
+
"version": "6.8.2",
|
|
4
4
|
"description": "Build UiPath Flow, Case, and BPMN artifacts by writing TypeScript.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"homepage": "https://docs.uipath.com/maestro",
|
|
@@ -87,5 +87,5 @@
|
|
|
87
87
|
"@types/node": "^22.7.0",
|
|
88
88
|
"esbuild": "^0.28.1"
|
|
89
89
|
},
|
|
90
|
-
"gitref": "
|
|
90
|
+
"gitref": "34be5c196660ad049240339a969f508f67898e19"
|
|
91
91
|
}
|