@uipath/maestro-builder-sdk 6.32.8 → 6.33.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 +38 -37
- package/dist/api-members.md +123 -120
- package/dist/check.js +107 -5
- package/dist/core/actions.d.ts +47 -5
- package/dist/core/agent-resource-identity.d.ts +34 -0
- package/dist/core/agent-resource-identity.js +37 -0
- package/dist/decompile.js +57 -14
- package/dist/serialize.js +64 -29
- package/package.json +2 -2
package/dist/check.js
CHANGED
|
@@ -34,9 +34,29 @@ import { plainHttpOnErrorMessage } from './nodes/http/flow/family.js';
|
|
|
34
34
|
import { resolveVersion, resolveVersionCarried } from './core/nodes/versioned.js';
|
|
35
35
|
import { declaredErrorPortRefusal } from './core/error-port.js';
|
|
36
36
|
import { bodyUsesBreak, loopParallelWarning, loopVersionRefusal } from './loop-version.js';
|
|
37
|
+
import { derivedResourceNodeId } from './core/agent-resource-identity.js';
|
|
38
|
+
const newResourceIdLedger = () => ({ explicit: new Map(), taken: new Map(), agentSources: new Map() });
|
|
39
|
+
/** INLINE_AGENT_RESOURCE_NODE_ID for each explicit `nodeId` another node of the file also gets. */
|
|
40
|
+
function resourceNodeIdClashes(ledger) {
|
|
41
|
+
const out = [];
|
|
42
|
+
for (const [id, { step, what }] of ledger.explicit) {
|
|
43
|
+
const other = ledger.taken.get(id);
|
|
44
|
+
if (other === undefined)
|
|
45
|
+
continue;
|
|
46
|
+
out.push({
|
|
47
|
+
level: 'error', code: 'INLINE_AGENT_RESOURCE_NODE_ID', step,
|
|
48
|
+
message: `Inline agent step "${step}" gives its ${what} the \`nodeId\` "${id}", which is already `
|
|
49
|
+
+ `${other}. Node ids are unique in a flow, so compile cannot write both. Give the resource another id, `
|
|
50
|
+
+ 'or omit `nodeId` to derive one.',
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
return out;
|
|
54
|
+
}
|
|
37
55
|
export function check(built, opts = {}) {
|
|
38
56
|
let diags = [];
|
|
39
|
-
const
|
|
57
|
+
const ledger = newResourceIdLedger();
|
|
58
|
+
const uses = collectConnectionUses(() => { diags = checkFlow(built, [], opts, ledger); });
|
|
59
|
+
diags.push(...resourceNodeIdClashes(ledger));
|
|
40
60
|
for (const { id, message } of connectionConnectorConflicts(uses, opts)) {
|
|
41
61
|
diags.push({ level: 'error', code: 'CONNECTION_CONNECTOR_CONFLICT', step: id, message });
|
|
42
62
|
}
|
|
@@ -87,7 +107,7 @@ function checkStepIds(stepNames, trigger, diags) {
|
|
|
87
107
|
* to notice a flow that (transitively) calls itself before the serializer
|
|
88
108
|
* recurses forever.
|
|
89
109
|
*/
|
|
90
|
-
function checkFlow(built, ancestors, opts = {}) {
|
|
110
|
+
function checkFlow(built, ancestors, opts = {}, ledger = newResourceIdLedger()) {
|
|
91
111
|
const diags = [];
|
|
92
112
|
if (!built.id)
|
|
93
113
|
diags.push({ level: 'error', code: 'NO_ID', message: 'flow() requires an id.' });
|
|
@@ -516,7 +536,7 @@ function checkFlow(built, ancestors, opts = {}) {
|
|
|
516
536
|
checkAgent(s.name, s.spec.inputs, diags);
|
|
517
537
|
}
|
|
518
538
|
if (s.spec.kind === 'inlineAgent') {
|
|
519
|
-
checkInlineAgent(s.name, s.spec.inputs, diags, opts);
|
|
539
|
+
checkInlineAgent(s.name, s.spec.inputs, diags, opts, ledger);
|
|
520
540
|
}
|
|
521
541
|
{
|
|
522
542
|
const rule = OUTPUT_READ_RULES[s.spec.kind];
|
|
@@ -878,6 +898,15 @@ function checkFlow(built, ancestors, opts = {}) {
|
|
|
878
898
|
}
|
|
879
899
|
reportUnreachableHitlOutcomes(hitlChoiceNoPorts, exprs, diags);
|
|
880
900
|
checkRegisteredFamilies(registeredSteps, unresolvedSteps, stepNames, diags);
|
|
901
|
+
// The ids this scope's other nodes get, for the file-wide nodeId check in `check`.
|
|
902
|
+
for (const n of stepNames)
|
|
903
|
+
if (!ledger.taken.has(n))
|
|
904
|
+
ledger.taken.set(n, `the id of step "${n}"`);
|
|
905
|
+
if (!ledger.taken.has(triggerId))
|
|
906
|
+
ledger.taken.set(triggerId, 'the id of the trigger');
|
|
907
|
+
for (const ep of built.entryPoints ?? [])
|
|
908
|
+
if (!ledger.taken.has(ep.id))
|
|
909
|
+
ledger.taken.set(ep.id, `the id of entry point "${ep.id}"`);
|
|
881
910
|
// Entry points: unique roots with valid triggers, colliding with nothing.
|
|
882
911
|
const epIds = new Set();
|
|
883
912
|
for (const ep of built.entryPoints ?? []) {
|
|
@@ -1294,7 +1323,7 @@ function checkFlow(built, ancestors, opts = {}) {
|
|
|
1294
1323
|
for (const [stepName, child] of subflowChildren) {
|
|
1295
1324
|
if (ancestors.includes(child) || child === built)
|
|
1296
1325
|
continue;
|
|
1297
|
-
for (const d of checkFlow(child, [...ancestors, built], opts)) {
|
|
1326
|
+
for (const d of checkFlow(child, [...ancestors, built], opts, ledger)) {
|
|
1298
1327
|
diags.push({
|
|
1299
1328
|
...d,
|
|
1300
1329
|
code: d.code,
|
|
@@ -3061,7 +3090,7 @@ export function jsonTextDirective(text, returnKeys) {
|
|
|
3061
3090
|
* ALSO asks for JSON text makes the model pack its whole answer, serialized, into
|
|
3062
3091
|
* one string field. See {@link jsonTextDirective}.
|
|
3063
3092
|
*/
|
|
3064
|
-
function checkInlineAgent(step, inputs, diags, opts = {}) {
|
|
3093
|
+
function checkInlineAgent(step, inputs, diags, opts = {}, ledger = newResourceIdLedger()) {
|
|
3065
3094
|
const at = (code, message, suggestion) => {
|
|
3066
3095
|
diags.push({ level: 'error', code, message, step, ...(suggestion ? { suggestion } : {}) });
|
|
3067
3096
|
};
|
|
@@ -3168,6 +3197,79 @@ function checkInlineAgent(step, inputs, diags, opts = {}) {
|
|
|
3168
3197
|
+ `and the one v1's smoke check for this node type FAILS a flow for keeping (it is the definition's `
|
|
3169
3198
|
+ `\`inputDefaults.model\`, not a choice). Pick a current model: \`uip agent model list\`.`);
|
|
3170
3199
|
}
|
|
3200
|
+
// Where each resource node sits (`nodeId`, `source`): decompile writes them
|
|
3201
|
+
// for a node the canvas placed, and compile writes them as given. A blank or
|
|
3202
|
+
// repeated id would make compile refuse the flow or overwrite another node,
|
|
3203
|
+
// and a `source` that is not a uuid names a directory the platform does not
|
|
3204
|
+
// expect (the agent's own `source` has the same rule).
|
|
3205
|
+
const asList = (v) => (v === undefined ? [] : Array.isArray(v) ? v : [v]);
|
|
3206
|
+
const placed = [
|
|
3207
|
+
...asList(inputs?.context).map((r) => ({ what: `context index "${r?.name ?? ''}"`, kind: 'Context', nodeId: r?.nodeId, source: r?.source })),
|
|
3208
|
+
...asList(inputs?.tools).map((r) => ({ what: `${r?.kind ?? ''} tool`, kind: 'Tool', nodeId: r?.nodeId, source: r?.source })),
|
|
3209
|
+
...asList(inputs?.escalation).map((r) => ({ what: `escalation "${r?.name ?? ''}"`, kind: 'Escalation', nodeId: r?.nodeId, source: r?.source })),
|
|
3210
|
+
];
|
|
3211
|
+
// The ids compile derives for the resources with no `nodeId`: the n-th one of
|
|
3212
|
+
// a kind gets `<step><Kind>`, then `<step><Kind>2`, ... An explicit id equal to
|
|
3213
|
+
// one of them makes compile refuse the flow, or rename the derived node.
|
|
3214
|
+
for (const kind of ['Context', 'Tool', 'Escalation']) {
|
|
3215
|
+
const derived = placed.filter((r) => r.kind === kind && r.nodeId === undefined);
|
|
3216
|
+
derived.forEach((r, n) => {
|
|
3217
|
+
const id = derivedResourceNodeId(step, kind, n);
|
|
3218
|
+
if (!ledger.taken.has(id))
|
|
3219
|
+
ledger.taken.set(id, `the id compile derives for the ${r.what} of inline agent step "${step}"`);
|
|
3220
|
+
});
|
|
3221
|
+
}
|
|
3222
|
+
/** Explicit `source` -> the resource on THIS agent that has it: two would write one resource.json. */
|
|
3223
|
+
const sources = new Map();
|
|
3224
|
+
for (const r of placed) {
|
|
3225
|
+
if (r.nodeId !== undefined) {
|
|
3226
|
+
const id = typeof r.nodeId === 'string' ? r.nodeId : '';
|
|
3227
|
+
const other = ledger.explicit.get(id);
|
|
3228
|
+
if (id.trim() === '') {
|
|
3229
|
+
at('INLINE_AGENT_RESOURCE_NODE_ID', `Inline agent step "${step}" gives its ${r.what} the \`nodeId\` ${JSON.stringify(r.nodeId)}, which is `
|
|
3230
|
+
+ `not a node id. Omit \`nodeId\` to derive one, or give the id the node has in the flow.`);
|
|
3231
|
+
}
|
|
3232
|
+
else if (other !== undefined) {
|
|
3233
|
+
at('INLINE_AGENT_RESOURCE_NODE_ID', `Inline agent step "${step}" gives its ${r.what} the \`nodeId\` "${id}", which the ${other.what} `
|
|
3234
|
+
+ `(on step "${other.step}") already has. Node ids are unique in a flow.`);
|
|
3235
|
+
}
|
|
3236
|
+
else {
|
|
3237
|
+
ledger.explicit.set(id, { step, what: r.what });
|
|
3238
|
+
}
|
|
3239
|
+
}
|
|
3240
|
+
if (r.source === undefined)
|
|
3241
|
+
continue;
|
|
3242
|
+
// Not trimmed: compile writes `source` as given, into `inputs.source` and the directory name.
|
|
3243
|
+
if (!GUID_RE.test(String(r.source))) {
|
|
3244
|
+
at('INLINE_AGENT_RESOURCE_SOURCE_NOT_GUID', `Inline agent step "${step}" gives its ${r.what} the \`source\` ${JSON.stringify(r.source)}, which is not `
|
|
3245
|
+
+ `a uuid${/^\s|\s$/.test(String(r.source)) ? ' (it has spaces around it)' : ''}. \`source\` is the resource's `
|
|
3246
|
+
+ `directory under the agent's own (\`resources/<source>/resource.json\`) and the node's \`inputs.source\`. `
|
|
3247
|
+
+ 'Omit it and one is derived from the flow id, the step and the resource.');
|
|
3248
|
+
continue;
|
|
3249
|
+
}
|
|
3250
|
+
const key = String(r.source).toLowerCase();
|
|
3251
|
+
const first = sources.get(key);
|
|
3252
|
+
if (first !== undefined) {
|
|
3253
|
+
at('INLINE_AGENT_RESOURCE_SOURCE_DUP', `Inline agent step "${step}" gives its ${first} and its ${r.what} the same \`source\` "${String(r.source)}". `
|
|
3254
|
+
+ 'Both would write `resources/<source>/resource.json` under this agent, and the second file would replace '
|
|
3255
|
+
+ 'the first. Give each resource its own `source`, or omit `source` to derive one.');
|
|
3256
|
+
}
|
|
3257
|
+
else {
|
|
3258
|
+
sources.set(key, r.what);
|
|
3259
|
+
}
|
|
3260
|
+
}
|
|
3261
|
+
if (typeof inputs?.source === 'string' && GUID_RE.test(inputs.source.trim())) {
|
|
3262
|
+
const key = inputs.source.trim().toLowerCase();
|
|
3263
|
+
const other = ledger.agentSources.get(key);
|
|
3264
|
+
if (other !== undefined && other !== step) {
|
|
3265
|
+
at('INLINE_AGENT_RESOURCE_SOURCE_DUP', `Inline agent steps "${other}" and "${step}" have the same \`source\` "${inputs.source}". Both would write `
|
|
3266
|
+
+ '`<source>/agent.json`, and the second file would replace the first. Give each agent its own `source`, '
|
|
3267
|
+
+ 'or omit it to derive one.');
|
|
3268
|
+
}
|
|
3269
|
+
else {
|
|
3270
|
+
ledger.agentSources.set(key, step);
|
|
3271
|
+
}
|
|
3272
|
+
}
|
|
3171
3273
|
if (inputs?.source !== undefined && !GUID_RE.test(String(inputs.source).trim())) {
|
|
3172
3274
|
at('INLINE_AGENT_SOURCE_NOT_GUID', `Inline agent step "${step}"'s \`source\` is ${JSON.stringify(inputs.source)}, which is not a uuid. `
|
|
3173
3275
|
+ `\`source\` is the agent's own DIRECTORY name, and the platform resolves its configuration at `
|
package/dist/core/actions.d.ts
CHANGED
|
@@ -1131,7 +1131,43 @@ export type BuiltinToolName = 'analyzefiles' | 'summarize' | 'batchtransform';
|
|
|
1131
1131
|
* `tool.mcp.*`/`uipath.agent.resource.mcp.*` resolves to nothing on this tenant
|
|
1132
1132
|
* (no MCP server exists to point at, so there is no definition to derive).
|
|
1133
1133
|
*/
|
|
1134
|
-
export type ToolRef = BuiltinToolRef | ConnectorToolRef | ProcessToolRef | IxpToolRef | McpToolRef | RemoteA2aToolRef | ClientSideToolRef | HttpRequestToolRef;
|
|
1134
|
+
export type ToolRef = (BuiltinToolRef | ConnectorToolRef | ProcessToolRef | IxpToolRef | McpToolRef | RemoteA2aToolRef | ClientSideToolRef | HttpRequestToolRef) & AgentResourcePlacement;
|
|
1135
|
+
/**
|
|
1136
|
+
* Where one of an inline agent's resources (a context index, a tool, an
|
|
1137
|
+
* escalation) sits in the `.flow`: the id of its own resource NODE and the
|
|
1138
|
+
* directory that holds its `resource.json`.
|
|
1139
|
+
*
|
|
1140
|
+
* @remarks
|
|
1141
|
+
* Omit both when you write a flow: compile derives them, so a recompile is not
|
|
1142
|
+
* a diff. Decompile writes them only for a file whose resource node another
|
|
1143
|
+
* tool placed (the canvas, `uip maestro flow node add`) under an id or directory
|
|
1144
|
+
* compile would not derive, so a no-edit round trip keeps the node, its layout
|
|
1145
|
+
* entry and its directory instead of replacing them.
|
|
1146
|
+
*
|
|
1147
|
+
* ```ts
|
|
1148
|
+
* // decompiled from a canvas file that named the index's node `ctx_7f2a1b`
|
|
1149
|
+
* context: [{
|
|
1150
|
+
* name: 'Example SOP Index', id: '11111111-2222-4333-8444-555555555555',
|
|
1151
|
+
* nodeId: 'ctx_7f2a1b', source: 'd84e2c17-6a3f-4c59-b0e2-7f1a9c5b3d28',
|
|
1152
|
+
* }]
|
|
1153
|
+
* ```
|
|
1154
|
+
*/
|
|
1155
|
+
export interface AgentResourcePlacement {
|
|
1156
|
+
/**
|
|
1157
|
+
* The resource node's id. Defaults to `<agent step>Context`, `<agent step>Tool`
|
|
1158
|
+
* or `<agent step>Escalation`, numbered from 2 for a second resource of the
|
|
1159
|
+
* same kind (`<agent step>Context2`). Compile writes it as given and refuses an
|
|
1160
|
+
* id another node in the flow already has (`check`: `INLINE_AGENT_RESOURCE_NODE_ID`).
|
|
1161
|
+
*/
|
|
1162
|
+
nodeId?: string;
|
|
1163
|
+
/**
|
|
1164
|
+
* The resource's directory id, a uuid: the node's `inputs.source` and the
|
|
1165
|
+
* `id` of `<agent source>/resources/<source>/resource.json`. Defaults to one
|
|
1166
|
+
* derived from the flow id, the agent step and the resource, so a given flow
|
|
1167
|
+
* emits the same uuid every time.
|
|
1168
|
+
*/
|
|
1169
|
+
source?: string;
|
|
1170
|
+
}
|
|
1135
1171
|
/**
|
|
1136
1172
|
* An MCP server the agent may call tools on. The node type is minted from the
|
|
1137
1173
|
* server's name and key exactly as the platform's own mapper does
|
|
@@ -1398,9 +1434,9 @@ export interface IxpToolRef {
|
|
|
1398
1434
|
/**
|
|
1399
1435
|
* One escalation an inline agent may raise — an Action Center app task a human
|
|
1400
1436
|
* completes (the `coded-action-app` variant, the one this tenant's registry
|
|
1401
|
-
* serves).
|
|
1437
|
+
* serves), placed in the flow by {@link AgentResourcePlacement}.
|
|
1402
1438
|
*/
|
|
1403
|
-
export interface EscalationRef {
|
|
1439
|
+
export interface EscalationRef extends AgentResourcePlacement {
|
|
1404
1440
|
/** The escalation's name — required by the definition (`minLength: 1`). */
|
|
1405
1441
|
name: string;
|
|
1406
1442
|
/** When the agent should escalate — what a reviewer sees as the reason. */
|
|
@@ -1463,13 +1499,14 @@ export interface EscalationRef {
|
|
|
1463
1499
|
labels?: string[];
|
|
1464
1500
|
}
|
|
1465
1501
|
/**
|
|
1466
|
-
* One Context Grounding index an inline agent is grounded on
|
|
1502
|
+
* One Context Grounding index an inline agent is grounded on, placed in the
|
|
1503
|
+
* flow by {@link AgentResourcePlacement}.
|
|
1467
1504
|
*
|
|
1468
1505
|
* @remarks
|
|
1469
1506
|
* The optional fields mirror the platform's own `inputDefaults` for an index
|
|
1470
1507
|
* resource, so omitting them emits exactly what the designer would.
|
|
1471
1508
|
*/
|
|
1472
|
-
export interface ContextIndexRef {
|
|
1509
|
+
export interface ContextIndexRef extends AgentResourcePlacement {
|
|
1473
1510
|
/** The index's display name on the tenant, e.g. `'Billing Dispute SOP Index'`. */
|
|
1474
1511
|
name: string;
|
|
1475
1512
|
/**
|
|
@@ -1496,6 +1533,11 @@ export interface ContextIndexRef {
|
|
|
1496
1533
|
threshold?: number;
|
|
1497
1534
|
/** Restrict to one file type; `'All'` (the platform's default) or e.g. `'pdf'`. */
|
|
1498
1535
|
fileExtension?: string;
|
|
1536
|
+
/**
|
|
1537
|
+
* The resource node's label on the canvas. Defaults to `name`. Only the
|
|
1538
|
+
* canvas shows it; the index is found by `name` and `id`.
|
|
1539
|
+
*/
|
|
1540
|
+
label?: string;
|
|
1499
1541
|
}
|
|
1500
1542
|
/**
|
|
1501
1543
|
* Every action kind, keyed by kind, to the body of its spec — the arm's fields
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The identity compile derives for an inline agent's resource node when the
|
|
3
|
+
* author gives none: its node id and its resource directory (`inputs.source`).
|
|
4
|
+
*
|
|
5
|
+
* Serialize uses these to name what it emits. Decompile uses the same functions
|
|
6
|
+
* to decide whether a file's resource node carries the identity compile would
|
|
7
|
+
* derive anyway; when it does not (the canvas or the CLI placed the node), the
|
|
8
|
+
* decompiled source spells it out as `nodeId` / `source`
|
|
9
|
+
* (`AgentResourcePlacement`). One definition for both directions keeps
|
|
10
|
+
* them inverses: a second copy in decompile would drift from serialize's.
|
|
11
|
+
*/
|
|
12
|
+
import type { ToolRef } from './actions.js';
|
|
13
|
+
/** The resource handles whose nodes compile names after the agent step. */
|
|
14
|
+
export type AgentResourceKind = 'Context' | 'Tool' | 'Escalation' | 'Memory';
|
|
15
|
+
/**
|
|
16
|
+
* The node id compile gives the `n`-th (0-based) resource of `kind` on agent
|
|
17
|
+
* step `agentStep` when no `nodeId` is set: `<step><Kind>`, then
|
|
18
|
+
* `<step><Kind>2`, `<step><Kind>3`, ... Characters a node id cannot hold become
|
|
19
|
+
* `_`, as compile's own id claim does. This assumes no other node already holds
|
|
20
|
+
* that id, which is the case for a flow compile wrote.
|
|
21
|
+
*/
|
|
22
|
+
export declare function derivedResourceNodeId(agentStep: string, kind: AgentResourceKind, n: number): string;
|
|
23
|
+
/** The directory compile derives for a context index (`id` = the index id). */
|
|
24
|
+
export declare function derivedContextSource(flowId: string, agentStep: string, indexId: string): string;
|
|
25
|
+
/**
|
|
26
|
+
* What tells one tool on an agent from another for its derived directory: the
|
|
27
|
+
* built-in's name, the connector operation, the IxP project, the client-side or
|
|
28
|
+
* HTTP tool's name, else the resource key.
|
|
29
|
+
*/
|
|
30
|
+
export declare function toolResourceSeed(tr: ToolRef): string;
|
|
31
|
+
/** The directory compile derives for a tool. */
|
|
32
|
+
export declare function derivedToolSource(flowId: string, agentStep: string, tr: ToolRef): string;
|
|
33
|
+
/** The directory compile derives for an escalation (`name` = the escalation's name). */
|
|
34
|
+
export declare function derivedEscalationSource(flowId: string, agentStep: string, name: string): string;
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { fnv1aX4Uuid } from './stable-id.js';
|
|
2
|
+
/**
|
|
3
|
+
* The node id compile gives the `n`-th (0-based) resource of `kind` on agent
|
|
4
|
+
* step `agentStep` when no `nodeId` is set: `<step><Kind>`, then
|
|
5
|
+
* `<step><Kind>2`, `<step><Kind>3`, ... Characters a node id cannot hold become
|
|
6
|
+
* `_`, as compile's own id claim does. This assumes no other node already holds
|
|
7
|
+
* that id, which is the case for a flow compile wrote.
|
|
8
|
+
*/
|
|
9
|
+
export function derivedResourceNodeId(agentStep, kind, n) {
|
|
10
|
+
const base = `${agentStep}${kind}`.replace(/[^A-Za-z0-9_]/g, '_');
|
|
11
|
+
return n === 0 ? base : `${base}${n + 1}`;
|
|
12
|
+
}
|
|
13
|
+
/** The directory compile derives for a context index (`id` = the index id). */
|
|
14
|
+
export function derivedContextSource(flowId, agentStep, indexId) {
|
|
15
|
+
return fnv1aX4Uuid(`${flowId}/${agentStep}/context/${indexId}`);
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* What tells one tool on an agent from another for its derived directory: the
|
|
19
|
+
* built-in's name, the connector operation, the IxP project, the client-side or
|
|
20
|
+
* HTTP tool's name, else the resource key.
|
|
21
|
+
*/
|
|
22
|
+
export function toolResourceSeed(tr) {
|
|
23
|
+
return tr.kind === 'builtin' ? tr.tool
|
|
24
|
+
: tr.kind === 'connector' ? `${tr.connector}.${tr.operation}`
|
|
25
|
+
: tr.kind === 'ixp' ? tr.projectId
|
|
26
|
+
: tr.kind === 'clientside' ? `clientside.${tr.name}`
|
|
27
|
+
: tr.kind === 'httpRequest' ? `httprequest.${tr.name ?? 'default'}`
|
|
28
|
+
: tr.key;
|
|
29
|
+
}
|
|
30
|
+
/** The directory compile derives for a tool. */
|
|
31
|
+
export function derivedToolSource(flowId, agentStep, tr) {
|
|
32
|
+
return fnv1aX4Uuid(`${flowId}/${agentStep}/tool/${toolResourceSeed(tr)}`);
|
|
33
|
+
}
|
|
34
|
+
/** The directory compile derives for an escalation (`name` = the escalation's name). */
|
|
35
|
+
export function derivedEscalationSource(flowId, agentStep, name) {
|
|
36
|
+
return fnv1aX4Uuid(`${flowId}/${agentStep}/escalation/${name}`);
|
|
37
|
+
}
|
package/dist/decompile.js
CHANGED
|
@@ -38,6 +38,7 @@ import { commentText } from './core/comment-text.js';
|
|
|
38
38
|
import { tsLiteral, tsNumber, tsText } from './core/ts-literal.js';
|
|
39
39
|
import { ownEntry } from './core/own-entry.js';
|
|
40
40
|
import { CONTEXT_INDEX_DEFAULTS, CONTEXT_INDEX_DEFAULT_FILE_EXTENSION, contextIndexSlug, parseContextIndexType, } from './core/context-index.js';
|
|
41
|
+
import { derivedContextSource, derivedEscalationSource, derivedResourceNodeId, derivedToolSource, } from './core/agent-resource-identity.js';
|
|
41
42
|
import { wireExprBody } from './core/wire-expr.js';
|
|
42
43
|
import { hasExits } from './core/step-ports.js';
|
|
43
44
|
import { parseIxpProjectType } from './core/actions.js';
|
|
@@ -107,6 +108,12 @@ let runWarned = new Set();
|
|
|
107
108
|
* `.layout()` key, must use the id compile will produce, not the file's.
|
|
108
109
|
*/
|
|
109
110
|
let runAbsorbed = new Map();
|
|
111
|
+
/**
|
|
112
|
+
* The id the recompiled flow will have: the `flow(...)` argument decompile
|
|
113
|
+
* writes. Compile derives a resource's directory from it, so decompile needs it
|
|
114
|
+
* to tell a derived directory from one the file chose.
|
|
115
|
+
*/
|
|
116
|
+
let runFlowId = 'flow';
|
|
110
117
|
/** `DecompileOptions.readSidecar` for the current run. */
|
|
111
118
|
let runReadSidecar;
|
|
112
119
|
function warnOnce(message) {
|
|
@@ -1160,20 +1167,45 @@ function emitAgentCluster(node, imp, inputNames, graph) {
|
|
|
1160
1167
|
// spec order is this edge order: the first is `<agent><Kind>`, the n-th
|
|
1161
1168
|
// `<agent><Kind><n>` (claimId's dedupe). Only the first memory is recovered,
|
|
1162
1169
|
// so every further memory maps to `undefined`.
|
|
1170
|
+
//
|
|
1171
|
+
// When a port's nodes do not ALL carry those ids (the canvas or the CLI named
|
|
1172
|
+
// them), every resource on the port is written with its own `nodeId`, and
|
|
1173
|
+
// compile keeps the file's ids. All of them, not only the odd one: compile
|
|
1174
|
+
// numbers the derived ids in order, so a derived id after an explicit one
|
|
1175
|
+
// would come out one lower than the file's.
|
|
1163
1176
|
// This reads the edges directly, not through `resource()`: it must see EVERY
|
|
1164
1177
|
// artifact edge, including ones the spec does not carry, and it must not
|
|
1165
1178
|
// account for any edge.
|
|
1179
|
+
const keepIds = new Set();
|
|
1166
1180
|
for (const [port, kind, emittedCount] of [
|
|
1167
1181
|
['context', 'Context', Infinity], ['tool', 'Tool', Infinity],
|
|
1168
1182
|
['escalation', 'Escalation', Infinity], ['memory', 'Memory', 1],
|
|
1169
1183
|
]) {
|
|
1170
|
-
const
|
|
1171
|
-
(graph?.outEdges(node.id) ?? [])
|
|
1184
|
+
const targets = (graph?.outEdges(node.id) ?? [])
|
|
1172
1185
|
.filter((e) => e.sourcePort === port && graph.byId.has(e.targetNodeId))
|
|
1173
|
-
.
|
|
1174
|
-
|
|
1186
|
+
.map((e) => e.targetNodeId);
|
|
1187
|
+
const keep = port !== 'memory'
|
|
1188
|
+
&& targets.some((id, k) => id !== derivedResourceNodeId(node.id, kind, k));
|
|
1189
|
+
targets.forEach((id, k) => {
|
|
1190
|
+
if (keep)
|
|
1191
|
+
keepIds.add(id);
|
|
1192
|
+
runAbsorbed.set(id, keep ? id : k >= emittedCount ? undefined : derivedResourceNodeId(node.id, kind, k));
|
|
1175
1193
|
});
|
|
1176
1194
|
}
|
|
1195
|
+
/**
|
|
1196
|
+
* The `nodeId` / `source` entries for resource node `r`: each only when it
|
|
1197
|
+
* differs from what compile derives (`derivedSource`), so a flow compile
|
|
1198
|
+
* wrote decompiles without them.
|
|
1199
|
+
*/
|
|
1200
|
+
const placement = (r, derivedSource) => {
|
|
1201
|
+
const source = unwrapSource((r.inputs ?? {}).source);
|
|
1202
|
+
return [
|
|
1203
|
+
['nodeId', keepIds.has(r.id) ? tsLiteral(r.id) : undefined],
|
|
1204
|
+
['source', source !== undefined && source !== '' && source !== derivedSource ? tsLiteral(source) : undefined],
|
|
1205
|
+
];
|
|
1206
|
+
};
|
|
1207
|
+
/** A tool's options plus its placement; `seed` is the ref compile derives the tool's directory from. */
|
|
1208
|
+
const toolOpts = (t, seed, entries) => opts([...entries, ...placement(t, derivedToolSource(runFlowId, node.id, seed))]);
|
|
1177
1209
|
const rInputs = (n) => (n.inputs ?? {});
|
|
1178
1210
|
const rPlain = (n, k) => {
|
|
1179
1211
|
const raw = unwrapSource(rInputs(n)[k]);
|
|
@@ -1189,7 +1221,7 @@ function emitAgentCluster(node, imp, inputNames, graph) {
|
|
|
1189
1221
|
return undefined;
|
|
1190
1222
|
return objLit(Object.entries(props).map(([k, x]) => [k, tsLiteral(String(x?.type ?? 'string'))]));
|
|
1191
1223
|
};
|
|
1192
|
-
return
|
|
1224
|
+
return toolOpts(t, { kind: 'clientside', name: rPlain(t, 'name') ?? '' }, [
|
|
1193
1225
|
['kind', tsLiteral('clientside')],
|
|
1194
1226
|
['name', tsLiteral(rPlain(t, 'name') ?? '')],
|
|
1195
1227
|
['description', rPlain(t, 'description') !== undefined ? tsLiteral(rPlain(t, 'description')) : undefined],
|
|
@@ -1206,7 +1238,8 @@ function emitAgentCluster(node, imp, inputNames, graph) {
|
|
|
1206
1238
|
const v = row.textValue;
|
|
1207
1239
|
return typeof v === 'number' ? String(v) : typeof v === 'string' ? tsLiteral(v) : JSON.stringify(v);
|
|
1208
1240
|
};
|
|
1209
|
-
|
|
1241
|
+
// `name` is not recovered, so compile derives the directory from the default.
|
|
1242
|
+
return toolOpts(t, { kind: 'httpRequest' }, [
|
|
1210
1243
|
['kind', tsLiteral('httpRequest')],
|
|
1211
1244
|
['url', fixed('url')], ['method', fixed('method')],
|
|
1212
1245
|
['headers', fixed('headers')], ['params', fixed('params')],
|
|
@@ -1214,7 +1247,7 @@ function emitAgentCluster(node, imp, inputNames, graph) {
|
|
|
1214
1247
|
]);
|
|
1215
1248
|
}
|
|
1216
1249
|
if (type.startsWith('uipath.agent.resource.tool.mcp.')) {
|
|
1217
|
-
return
|
|
1250
|
+
return toolOpts(t, { kind: 'mcp', name: '', key: rPlain(t, 'referenceKey') ?? '' }, [
|
|
1218
1251
|
['kind', tsLiteral('mcp')],
|
|
1219
1252
|
['name', tsLiteral(rPlain(t, 'name') ?? '')],
|
|
1220
1253
|
['key', tsLiteral(rPlain(t, 'referenceKey') ?? '')],
|
|
@@ -1225,7 +1258,7 @@ function emitAgentCluster(node, imp, inputNames, graph) {
|
|
|
1225
1258
|
]);
|
|
1226
1259
|
}
|
|
1227
1260
|
if (type.startsWith('uipath.agent.resource.tool.a2a.')) {
|
|
1228
|
-
return
|
|
1261
|
+
return toolOpts(t, { kind: 'a2a', name: '', slug: '', key: rPlain(t, 'referenceKey') ?? '' }, [
|
|
1229
1262
|
['kind', tsLiteral('a2a')],
|
|
1230
1263
|
['name', tsLiteral(rPlain(t, 'name') ?? '')],
|
|
1231
1264
|
['key', tsLiteral(rPlain(t, 'referenceKey') ?? '')],
|
|
@@ -1234,17 +1267,19 @@ function emitAgentCluster(node, imp, inputNames, graph) {
|
|
|
1234
1267
|
]);
|
|
1235
1268
|
}
|
|
1236
1269
|
if (type.startsWith('uipath.agent.resource.tool.builtin.')) {
|
|
1237
|
-
|
|
1270
|
+
const tool = type.slice('uipath.agent.resource.tool.builtin.'.length);
|
|
1271
|
+
return toolOpts(t, { kind: 'builtin', tool }, [
|
|
1238
1272
|
['kind', tsLiteral('builtin')],
|
|
1239
|
-
['tool', tsLiteral(
|
|
1273
|
+
['tool', tsLiteral(tool)],
|
|
1240
1274
|
['description', rPlain(t, 'description') !== undefined ? tsLiteral(rPlain(t, 'description')) : undefined],
|
|
1241
1275
|
]);
|
|
1242
1276
|
}
|
|
1243
1277
|
if (type.startsWith('uipath.agent.resource.tool.ixp.')) {
|
|
1244
1278
|
const tail = type.slice('uipath.agent.resource.tool.ixp.'.length);
|
|
1245
|
-
|
|
1279
|
+
const projectId = tail.slice(tail.indexOf('.') + 1);
|
|
1280
|
+
return toolOpts(t, { kind: 'ixp', name: '', projectId }, [
|
|
1246
1281
|
['kind', tsLiteral('ixp')],
|
|
1247
|
-
['projectId', tsLiteral(
|
|
1282
|
+
['projectId', tsLiteral(projectId)],
|
|
1248
1283
|
['name', tsLiteral(rPlain(t, 'name') ?? '')],
|
|
1249
1284
|
]);
|
|
1250
1285
|
}
|
|
@@ -1266,9 +1301,10 @@ function emitAgentCluster(node, imp, inputNames, graph) {
|
|
|
1266
1301
|
const row = values.find((v) => v.name === n);
|
|
1267
1302
|
return typeof row?.default === 'string' ? row.default : undefined;
|
|
1268
1303
|
};
|
|
1269
|
-
|
|
1304
|
+
const key = type.slice(ns[0].length);
|
|
1305
|
+
return toolOpts(t, { kind: 'process', name: '', folderPath: '', key }, [
|
|
1270
1306
|
['kind', tsLiteral(ns[1])],
|
|
1271
|
-
['key', tsLiteral(
|
|
1307
|
+
['key', tsLiteral(key)],
|
|
1272
1308
|
['name', tsLiteral(bound('name') ?? rPlain(t, 'name') ?? '')],
|
|
1273
1309
|
['folderPath', tsLiteral(bound('folderPath') ?? '')],
|
|
1274
1310
|
]);
|
|
@@ -1277,6 +1313,9 @@ function emitAgentCluster(node, imp, inputNames, graph) {
|
|
|
1277
1313
|
});
|
|
1278
1314
|
const contextSrc = resource('context').map((c) => {
|
|
1279
1315
|
const { name, id, folderPath, sidecar } = contextIndexIdentity(node, c, rPlain);
|
|
1316
|
+
// Compile labels the node with `name`; a canvas rename is kept as `label`.
|
|
1317
|
+
const label = typeof c.display?.label === 'string' && c.display.label !== '' && c.display.label !== name
|
|
1318
|
+
? c.display.label : undefined;
|
|
1280
1319
|
return opts([
|
|
1281
1320
|
['name', tsLiteral(name)],
|
|
1282
1321
|
['id', tsLiteral(id)],
|
|
@@ -1284,6 +1323,8 @@ function emitAgentCluster(node, imp, inputNames, graph) {
|
|
|
1284
1323
|
['folderKey', rPlain(c, 'folderKey') !== undefined ? tsLiteral(rPlain(c, 'folderKey')) : undefined],
|
|
1285
1324
|
// `query` and the retrieval settings, from resource.json or the node (#1183).
|
|
1286
1325
|
...contextIndexSettings(node, c, sidecar),
|
|
1326
|
+
['label', label !== undefined ? tsLiteral(label) : undefined],
|
|
1327
|
+
...placement(c, derivedContextSource(runFlowId, node.id, id)),
|
|
1287
1328
|
]);
|
|
1288
1329
|
});
|
|
1289
1330
|
const escalationSrc = resource('escalation').map((e) => {
|
|
@@ -1323,6 +1364,7 @@ function emitAgentCluster(node, imp, inputNames, graph) {
|
|
|
1323
1364
|
['taskTitle', typeof extra.taskTitle === 'string' && extra.taskTitle !== '' ? tsLiteral(extra.taskTitle) : undefined],
|
|
1324
1365
|
// 'medium' is the platform default.
|
|
1325
1366
|
['priority', typeof extra.priority === 'string' && extra.priority !== 'medium' ? tsLiteral(extra.priority) : undefined],
|
|
1367
|
+
...placement(e, derivedEscalationSource(runFlowId, node.id, rPlain(e, 'name') ?? '')),
|
|
1326
1368
|
]);
|
|
1327
1369
|
});
|
|
1328
1370
|
const mem = resource('memory', 1)[0];
|
|
@@ -3249,6 +3291,7 @@ export function decompile(flow, options = {}) {
|
|
|
3249
3291
|
runDefs = new Map();
|
|
3250
3292
|
runRawManifests = [];
|
|
3251
3293
|
runAbsorbed = new Map();
|
|
3294
|
+
runFlowId = slugId(flow);
|
|
3252
3295
|
for (const d of (flow.definitions ?? [])) {
|
|
3253
3296
|
if (typeof d?.nodeType !== 'string')
|
|
3254
3297
|
continue;
|