@uipath/maestro-builder-sdk 6.42.0 → 6.44.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 +104 -103
- package/dist/api-members.md +319 -317
- package/dist/check.d.ts +0 -19
- package/dist/check.js +140 -1042
- package/dist/compile-cli.js +2 -1
- package/dist/core/actions.d.ts +10 -713
- package/dist/core/actions.js +0 -193
- package/dist/core/agent-sidecar.d.ts +186 -0
- package/dist/core/agent-sidecar.js +409 -0
- package/dist/core/agent-tool-resources.d.ts +62 -0
- package/dist/core/agent-tool-resources.js +81 -0
- package/dist/{config.js → core/connector-configuration.js} +2 -2
- package/dist/core/form-field-type.d.ts +7 -0
- package/dist/core/form-field-type.js +12 -2
- package/dist/core/node-id-placeholder.d.ts +8 -0
- package/dist/core/node-id-placeholder.js +10 -0
- package/dist/core/ts-literal.d.ts +6 -0
- package/dist/core/ts-literal.js +15 -0
- package/dist/core/typed-raw-node.js +0 -5
- package/dist/decompile.js +239 -857
- package/dist/flow-expr-check.js +5 -0
- package/dist/flow-node-registry.js +2 -1
- package/dist/flow-node-type.d.ts +139 -6
- package/dist/flow-sdk.d.ts +41 -5
- package/dist/flow-sdk.js +15 -1
- package/dist/flow-step-variables.d.ts +4 -0
- package/dist/flow-step-variables.js +22 -0
- package/dist/loop-version.d.ts +23 -6
- package/dist/loop-version.js +37 -15
- package/dist/nodes/agent/flow/check.d.ts +88 -0
- package/dist/nodes/agent/flow/check.js +873 -0
- package/dist/nodes/agent/flow/factories.d.ts +134 -0
- package/dist/nodes/agent/flow/factories.js +143 -0
- package/dist/nodes/agent/flow/family.d.ts +3 -0
- package/dist/nodes/agent/flow/family.js +8 -0
- package/dist/nodes/agent/flow/index.d.ts +3 -0
- package/dist/nodes/agent/flow/index.js +3 -0
- package/dist/nodes/agent/flow/inline-agent-lift.d.ts +44 -0
- package/dist/nodes/agent/flow/inline-agent-lift.js +572 -0
- package/dist/nodes/agent/flow/inline-agent.d.ts +15 -0
- package/dist/nodes/agent/flow/inline-agent.js +526 -0
- package/dist/nodes/agent/flow/node-type.d.ts +20 -0
- package/dist/nodes/agent/flow/node-type.js +422 -0
- package/dist/nodes/agent/flow/resources.d.ts +502 -0
- package/dist/nodes/agent/flow/resources.js +1409 -0
- package/dist/nodes/agent/flow/spec.d.ts +448 -0
- package/dist/nodes/agent/flow/spec.js +1 -0
- package/dist/nodes/hitl/flow/check.js +15 -0
- package/dist/nodes/hitl/flow/node-type.js +18 -3
- package/dist/nodes/process/flow/check.d.ts +40 -2
- package/dist/nodes/process/flow/check.js +156 -0
- package/dist/nodes/process/flow/factories.d.ts +58 -3
- package/dist/nodes/process/flow/factories.js +59 -2
- package/dist/nodes/process/flow/node-type.d.ts +4 -1
- package/dist/nodes/process/flow/node-type.js +198 -12
- package/dist/nodes/process/flow/spec.d.ts +112 -5
- package/dist/serialize.d.ts +9 -54
- package/dist/serialize.js +102 -2777
- package/package.json +2 -2
- /package/dist/{config.d.ts → core/connector-configuration.d.ts} +0 -0
- /package/dist/{ixp-definition.json → core/ixp-definition.json} +0 -0
|
@@ -0,0 +1,1409 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What an inline agent attaches: the definition of each resource node (a
|
|
3
|
+
* context index, a tool, an escalation, a memory) and the
|
|
4
|
+
* `<source>/resources/<id>/resource.json` the platform resolves it from, plus
|
|
5
|
+
* the agent's own per-flow definition (`inlineAgentDef`). Moved from
|
|
6
|
+
* serialize.ts in Phase 4 of docs/NODE_TYPE_ARCHITECTURE_DESIGN.md; every
|
|
7
|
+
* definition and file is unchanged.
|
|
8
|
+
*/
|
|
9
|
+
import ixpDefinition from '../../../core/ixp-definition.json' with { type: 'json' };
|
|
10
|
+
import { JOB_ATTACHMENT_REF, jobAttachmentDefinition } from '../../../core/agent-sidecar.js';
|
|
11
|
+
import { BUILTIN_TOOL_TYPE, CONNECTOR_TOOL_RESOURCE_TYPE, HTTP_REQUEST_TOOL_TYPE, INTERNAL_TOOL_RESOURCE_TYPE, TOOL_RESOURCE_TYPE, } from '../../../core/agent-tool-resources.js';
|
|
12
|
+
import { buildConfiguration } from '../../../core/connector-configuration.js';
|
|
13
|
+
import { multipartBodyFieldName, transportHttpMethod } from '../../../core/connector-inputs.js';
|
|
14
|
+
import { CONTEXT_INDEX_DEFAULTS, CONTEXT_INDEX_DEFAULT_FILE_EXTENSION, contextIndexType } from '../../../core/context-index.js';
|
|
15
|
+
import { formFieldJsonSchema } from '../../../core/form-field-type.js';
|
|
16
|
+
import { jsonClone } from '../../../core/json-clone.js';
|
|
17
|
+
import { ownEntry } from '../../../core/own-entry.js';
|
|
18
|
+
/** The inline (autonomous) agent's node type — fixed, not per-instance. */
|
|
19
|
+
export const INLINE_AGENT = 'uipath.agent.autonomous';
|
|
20
|
+
const IXP_DEF = ixpDefinition;
|
|
21
|
+
/**
|
|
22
|
+
* The definition for ONE Context Grounding index resource, transcribed from the
|
|
23
|
+
* platform's own registry entry (`uip maestro flow registry get
|
|
24
|
+
* uipath.agent.resource.context.index.<slug>.<uuid>`, read 2026-07-31 against the
|
|
25
|
+
* tenant's "Billing Dispute SOP Index"), and confirmed against the `.flow` v1
|
|
26
|
+
* emits for the same index.
|
|
27
|
+
*
|
|
28
|
+
* Per-INSTANCE, unlike `inlineAgentDef`: the index's identity is in the node type,
|
|
29
|
+
* so every tenant index is its own type — the same shape as a published agent's
|
|
30
|
+
* release key. What is constant is everything else, and this builds that from the
|
|
31
|
+
* one thing an author supplies (name + uuid).
|
|
32
|
+
*
|
|
33
|
+
* Two details are load-bearing rather than decorative:
|
|
34
|
+
*
|
|
35
|
+
* * `handleConfiguration` declares a single TARGET handle `input` whose
|
|
36
|
+
* `allowedSources` are the two agent types' `context` handles, with
|
|
37
|
+
* `maxConnections: 1`. That is the mirror of the agent's own
|
|
38
|
+
* `allowedTargets: [{ nodeType: 'uipath.agent.resource.context.*' }]`, and it is
|
|
39
|
+
* what makes the designer draw the join.
|
|
40
|
+
* * `model: { source: true }` and nothing else. A resource node has no
|
|
41
|
+
* `bpmn:` type, no serviceType and no bindings — it is not a step, it is an
|
|
42
|
+
* artifact hanging off one. The v1 artifact carries no `bindings[]` entry for it
|
|
43
|
+
* at all.
|
|
44
|
+
*/
|
|
45
|
+
export function contextIndexDef(spec) {
|
|
46
|
+
return {
|
|
47
|
+
nodeType: contextIndexType(spec),
|
|
48
|
+
version: '1.0.0',
|
|
49
|
+
category: 'agent',
|
|
50
|
+
tags: ['agent-context', 'index'],
|
|
51
|
+
description: `Context grounding index "${spec.name}".`,
|
|
52
|
+
sortOrder: 505,
|
|
53
|
+
display: { label: spec.name, description: `Context grounding index "${spec.name}".`, icon: 'file-text', shape: 'circle' },
|
|
54
|
+
handleConfiguration: [
|
|
55
|
+
{
|
|
56
|
+
position: 'top',
|
|
57
|
+
handles: [{
|
|
58
|
+
id: 'input', type: 'target', handleType: 'artifact',
|
|
59
|
+
constraints: {
|
|
60
|
+
maxConnections: 1,
|
|
61
|
+
allowedSources: [
|
|
62
|
+
{ nodeType: 'uipath.agent.autonomous', handleId: 'context' },
|
|
63
|
+
{ nodeType: 'uipath.agent.conversational', handleId: 'context' },
|
|
64
|
+
],
|
|
65
|
+
validationMessage: "Only Agent's context handle can connect here",
|
|
66
|
+
},
|
|
67
|
+
}],
|
|
68
|
+
},
|
|
69
|
+
],
|
|
70
|
+
model: { source: true },
|
|
71
|
+
// The platform's own `inputDefinition.properties`, transcribed. Carried so the
|
|
72
|
+
// node's inputs are CHECKABLE — without an input schema nothing can validate
|
|
73
|
+
// this node's inputs at all, and a misspelled `resultCount` reaches the tenant
|
|
74
|
+
// unremarked.
|
|
75
|
+
//
|
|
76
|
+
// The registry's `allOf` conditionals (which retrieval modes require a query,
|
|
77
|
+
// and which of the query object's three modes is live) are deliberately NOT
|
|
78
|
+
// copied: they are the designer FORM's logic, and the serializer already emits
|
|
79
|
+
// a well-formed `query` for the mode it sets.
|
|
80
|
+
inputDefinition: {
|
|
81
|
+
type: 'object',
|
|
82
|
+
properties: {
|
|
83
|
+
indexId: { type: 'string' },
|
|
84
|
+
indexName: { type: 'string' },
|
|
85
|
+
folderKey: { type: 'string' },
|
|
86
|
+
folderPath: { type: 'string' },
|
|
87
|
+
retrievalMode: { type: 'string' },
|
|
88
|
+
query: { type: 'object' },
|
|
89
|
+
threshold: { type: 'number', minimum: 0, maximum: 1 },
|
|
90
|
+
resultCount: { type: 'number', minimum: 1, maximum: 40 },
|
|
91
|
+
fileExtension: { type: 'string' },
|
|
92
|
+
folderPathPrefix: { type: 'object' },
|
|
93
|
+
citations: { type: 'string' },
|
|
94
|
+
outputColumns: { type: 'array' },
|
|
95
|
+
webSearchGrounding: { type: 'object', properties: { value: { type: 'string' } } },
|
|
96
|
+
},
|
|
97
|
+
},
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* Render an index's retrieval query into the platform's MODE-TAGGED object.
|
|
102
|
+
*
|
|
103
|
+
* The index definition's own schema (`inputDefinition.allOf`) says `query` is a
|
|
104
|
+
* `oneOf` over three shapes, each identified by `mode`: `prompt` → `promptValue`,
|
|
105
|
+
* `text-builder` → `textValue`, `variable` → `argumentPath`. All four fields are
|
|
106
|
+
* present in the platform's `inputDefaults`; only the one the mode names is read.
|
|
107
|
+
*
|
|
108
|
+
* A plain string is a `prompt` (what a designer types); anything expression-shaped
|
|
109
|
+
* is `text-builder`, whose `textValue` the runtime templates — the same slot the
|
|
110
|
+
* `folderPathPrefix` field uses, and the reason that field ships with
|
|
111
|
+
* `mode: 'text-builder'` by default.
|
|
112
|
+
*/
|
|
113
|
+
export function renderContextQuery(query, render) {
|
|
114
|
+
const base = { mode: 'prompt', textValue: '', promptValue: '', argumentPath: '' };
|
|
115
|
+
if (query === undefined || query === null)
|
|
116
|
+
return base;
|
|
117
|
+
const rendered = render(query);
|
|
118
|
+
const asText = typeof rendered === 'string' ? rendered : JSON.stringify(rendered);
|
|
119
|
+
if (typeof rendered === 'string' && rendered.startsWith('=js:')) {
|
|
120
|
+
return { ...base, mode: 'text-builder', textValue: asText };
|
|
121
|
+
}
|
|
122
|
+
return { ...base, promptValue: asText };
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* The `resources/<id>/resource.json` an inline agent's context index needs.
|
|
126
|
+
*
|
|
127
|
+
* The node is only half of a grounded agent, the same split as the agent itself
|
|
128
|
+
* (see `inlineAgentJson`). The platform resolves an agent's capabilities from
|
|
129
|
+
* `<source>/resources/<resource-id>/resource.json`, and `uip agent refresh` turns
|
|
130
|
+
* those into the `bindings_v2.json` entries `uip solution resources refresh`
|
|
131
|
+
* syncs. Keyed by the node's `inputs.source`, which is what joins the two halves.
|
|
132
|
+
*
|
|
133
|
+
* On the CLI path the file is the only copy: refresh strips the node and
|
|
134
|
+
* re-derives it from here, so a `.flow` with the node and no file validates clean
|
|
135
|
+
* and leaves the agent ungrounded. (Studio Web is the other direction — its
|
|
136
|
+
* `mapContextResource` builds this file FROM the canvas node.)
|
|
137
|
+
*
|
|
138
|
+
* Shape and enums are the platform's `currentResourceStorageSchema`
|
|
139
|
+
* (`@uipath/agents-storage-schemas`): `description`, `folderPath`, `indexName` and
|
|
140
|
+
* all six `settings` keys are required, `fileExtension` is `{value}` rather than a
|
|
141
|
+
* bare string, and the retrieval mode pins which extensions it accepts — hence the
|
|
142
|
+
* per-mode default here against the node's flat `'All'`. An unparseable resource
|
|
143
|
+
* is worse than none: `uip agent refresh` falls back to `resources: []` and drops
|
|
144
|
+
* every other binding with it.
|
|
145
|
+
*/
|
|
146
|
+
export function contextResourceJson(cx, spec) {
|
|
147
|
+
const mode = cx.retrievalMode ?? CONTEXT_INDEX_DEFAULTS.retrievalMode;
|
|
148
|
+
return {
|
|
149
|
+
$resourceType: 'context',
|
|
150
|
+
id: spec.id,
|
|
151
|
+
// Null rather than absent: optional in the schema, but every deployed
|
|
152
|
+
// resource carries it, and a designer round-trip writes it back.
|
|
153
|
+
referenceKey: null,
|
|
154
|
+
name: cx.name,
|
|
155
|
+
description: `Context grounding index "${cx.name}".`,
|
|
156
|
+
contextType: 'index',
|
|
157
|
+
// Required by the schema, and load-bearing: this file carries no `indexId`,
|
|
158
|
+
// so `indexName` + `folderPath` is the whole locator, and `createIndexBinding`
|
|
159
|
+
// leaves the folder off the binding when it is empty. `check` warns when the
|
|
160
|
+
// author omits it — an index outside the deployment folder is then unresolvable.
|
|
161
|
+
folderPath: cx.folderPath ?? '',
|
|
162
|
+
indexName: cx.name,
|
|
163
|
+
settings: {
|
|
164
|
+
retrievalMode: mode,
|
|
165
|
+
// The resource's `query` is VARIANT-tagged where the node's is MODE-tagged,
|
|
166
|
+
// and the two vocabularies do not overlap. `prompt` — what a designer types
|
|
167
|
+
// as guidance — is the model-invoked `dynamic`, and its text is the
|
|
168
|
+
// `description` the model reads when deciding what to search for.
|
|
169
|
+
// `text-builder` — an expression the runtime templates — is the pre-fetched
|
|
170
|
+
// `static`, and its text is the `value`. Confirmed behaviourally: switching
|
|
171
|
+
// the SDK's `query` from a string to an expression turned the same agent's
|
|
172
|
+
// retrieval from a tool call into a pre-fetch.
|
|
173
|
+
query: spec.query.mode === 'text-builder'
|
|
174
|
+
? { variant: 'static', value: String(spec.query.textValue ?? '') }
|
|
175
|
+
: { variant: 'dynamic', description: String(spec.query.promptValue ?? '') },
|
|
176
|
+
// `value: ''`, as the canvas writes it (flow-workbench `mapContextQueryToWire`
|
|
177
|
+
// always sets the value), so a canvas index's resource round-trips unchanged.
|
|
178
|
+
folderPathPrefix: { variant: 'static', value: '' },
|
|
179
|
+
threshold: cx.threshold ?? CONTEXT_INDEX_DEFAULTS.threshold,
|
|
180
|
+
resultCount: cx.resultCount ?? CONTEXT_INDEX_DEFAULTS.resultCount,
|
|
181
|
+
fileExtension: { value: cx.fileExtension ?? ownEntry(CONTEXT_INDEX_DEFAULT_FILE_EXTENSION, mode) ?? 'All' },
|
|
182
|
+
// Mode-specific keys the schema REQUIRES of those two modes and rejects
|
|
183
|
+
// nowhere else, carrying the node's own constants through the enum's
|
|
184
|
+
// spelling (the node says `enabled`; the resource says `Enabled`/`Inline`).
|
|
185
|
+
...(mode === 'deeprag' ? { citationMode: { value: 'Inline' } } : {}),
|
|
186
|
+
...(mode === 'batchtransform'
|
|
187
|
+
? { webSearchGrounding: { value: 'Enabled' }, outputColumns: [] }
|
|
188
|
+
: {}),
|
|
189
|
+
},
|
|
190
|
+
};
|
|
191
|
+
}
|
|
192
|
+
/** A `fieldSchema` object with every declared property marked required. */
|
|
193
|
+
export function requireAll(schema) {
|
|
194
|
+
const names = Object.keys(schema.properties);
|
|
195
|
+
return names.length === 0 ? schema : { ...schema, required: names };
|
|
196
|
+
}
|
|
197
|
+
/**
|
|
198
|
+
* The `resources/<id>/resource.json` a DEPLOYED-RESOURCE tool needs — the tool
|
|
199
|
+
* half of what `contextResourceJson` does for an index, and inert for the same
|
|
200
|
+
* reason (see that function).
|
|
201
|
+
*
|
|
202
|
+
* Shape from a real agent project (`WebResearchBriefingAgent`'s `CountSources`,
|
|
203
|
+
* a `kind: 'process'` tool), cross-checked against the platform schema: this
|
|
204
|
+
* family carries `argumentProperties` and `referenceKey` but no `iconUrl`, and
|
|
205
|
+
* `exampleCalls` is present-and-empty. `location: 'solution'` and an empty
|
|
206
|
+
* `properties.folderPath` are the inline-in-flow pair Studio Web's
|
|
207
|
+
* `mapToolResource` writes and uipcli's `agent validate` enforces — the folder
|
|
208
|
+
* itself lives in the flow's top-level `bindings[]`, which
|
|
209
|
+
* `processResourceBindings` already writes for this tool.
|
|
210
|
+
*
|
|
211
|
+
* Returns `undefined` for every OTHER kind. Which kinds get a file is measured,
|
|
212
|
+
* because `uip agent refresh` strips each resource node and re-derives it FROM
|
|
213
|
+
* the file: where the node is `source`-only the round-trip is lossless, and where
|
|
214
|
+
* the node carries the configuration the strip loses it and the rebuild cannot
|
|
215
|
+
* put it back, so writing the file turns a valid flow INVALID.
|
|
216
|
+
*
|
|
217
|
+
* * `ixp`, `clientside` — measured to fail exactly that way (`ixp` leaves the
|
|
218
|
+
* flow failing validate; `clientside` drops the four inputs its node needs).
|
|
219
|
+
* * `connector` — has its own writer, {@link connectorToolResourceJson}: its
|
|
220
|
+
* node carries the configuration in `inputs.detail`, which the strip keeps,
|
|
221
|
+
* and the file is the one `uip agent refresh` itself derives from it.
|
|
222
|
+
* * `mcp`, `a2a` — need the server's `availableTools` and the remote agent's
|
|
223
|
+
* card, which are tenant reads.
|
|
224
|
+
* * `builtin` — has its own writer, {@link builtinToolResourceJson}: its
|
|
225
|
+
* `toolType` is a fixed per-tool constant, so the file is derivable offline.
|
|
226
|
+
* * `httpRequest` — has its own writer, {@link httpRequestToolResourceJson}:
|
|
227
|
+
* the file carries the request config as static `argumentProperties` and the
|
|
228
|
+
* model-facing `inputSchema`, both derivable from the node (#935).
|
|
229
|
+
*
|
|
230
|
+
* Which `type`/`toolType` values compile writes — and so owns and may prune —
|
|
231
|
+
* is {@link compileOwnsResource}, read off the same tables these writers use.
|
|
232
|
+
*/
|
|
233
|
+
export function toolResourceJson(tr, spec) {
|
|
234
|
+
const common = {
|
|
235
|
+
$resourceType: 'tool',
|
|
236
|
+
id: spec.id,
|
|
237
|
+
name: tr.name,
|
|
238
|
+
// The runtime hands this to the MODEL as the tool's description, so an empty
|
|
239
|
+
// one is a tool the model has to guess at from its name alone.
|
|
240
|
+
description: tr.description ?? '',
|
|
241
|
+
isEnabled: true,
|
|
242
|
+
// `required` matters here where it does not on the agent's own contract: the
|
|
243
|
+
// model reads this schema to build the call, and an argument the process needs
|
|
244
|
+
// but the schema calls optional fails at run time with nothing said at compile
|
|
245
|
+
// time. The corpus fixture lists every declared argument.
|
|
246
|
+
inputSchema: requireAll(fieldSchema(tr.inputs)),
|
|
247
|
+
outputSchema: fieldSchema(tr.returns),
|
|
248
|
+
// No policies: `inlineAgent`'s tool refs carry none, and the field is
|
|
249
|
+
// required-but-emptyable.
|
|
250
|
+
guardrail: { policies: [] },
|
|
251
|
+
settings: {},
|
|
252
|
+
argumentProperties: {},
|
|
253
|
+
};
|
|
254
|
+
const resourceType = ownEntry(TOOL_RESOURCE_TYPE, tr.kind);
|
|
255
|
+
if (resourceType !== undefined) {
|
|
256
|
+
return {
|
|
257
|
+
...common,
|
|
258
|
+
type: resourceType,
|
|
259
|
+
location: 'solution',
|
|
260
|
+
// The deployed resource's own key: what joins this tool to the release the
|
|
261
|
+
// solution declares.
|
|
262
|
+
referenceKey: tr.key,
|
|
263
|
+
// `folderPath` is EMPTY on purpose, and the tenant folder is not lost: for
|
|
264
|
+
// an inline-in-flow agent the folder lives in the flow's own top-level
|
|
265
|
+
// `bindings[]`, which `processResourceBindings` already writes for this
|
|
266
|
+
// tool. uipcli's `agent validate` says so where it enforces the pair —
|
|
267
|
+
// *"Inline-in-flow agents store folder info in the flow's top-level
|
|
268
|
+
// bindings[] rather than in the tool resource — Studio Web emits an empty
|
|
269
|
+
// folderPath here"* — and every `inlineAgent` tool is inline by definition.
|
|
270
|
+
properties: { processName: tr.name, folderPath: '', exampleCalls: [] },
|
|
271
|
+
};
|
|
272
|
+
}
|
|
273
|
+
return undefined;
|
|
274
|
+
}
|
|
275
|
+
/** `inline` → `Inline`: the node's enum spelling to the resource schema's. */
|
|
276
|
+
export const enumValue = (v) => {
|
|
277
|
+
const s = String(v ?? '');
|
|
278
|
+
return s.charAt(0).toUpperCase() + s.slice(1);
|
|
279
|
+
};
|
|
280
|
+
/**
|
|
281
|
+
* The `resources/<id>/resource.json` of a platform BUILT-IN tool (#920) — the
|
|
282
|
+
* file the runtime builds the agent's tool list from. Without it the
|
|
283
|
+
* `tool.builtin.*` node is inert, the same failure #806 fixed for deployed
|
|
284
|
+
* resources: the agent runs with no built-in tool and the model never learns
|
|
285
|
+
* it exists.
|
|
286
|
+
*
|
|
287
|
+
* Derived from the node's own inputs (`tdef.inputDefaults` with the author's
|
|
288
|
+
* values over them), because those are what the file must agree with:
|
|
289
|
+
* `uip agent refresh --inline-in-flow` shell-ifies every resource node down to
|
|
290
|
+
* `{source, detail, itemsDescription}` and Studio Web re-hydrates the rest FROM
|
|
291
|
+
* this file, so a value only on the node does not survive a refresh or a
|
|
292
|
+
* designer round-trip.
|
|
293
|
+
*
|
|
294
|
+
* Shape: the skill's per-tool walkthroughs (`analyze-attachments.md`,
|
|
295
|
+
* `deeprag/impl-json.md`, `batch-transform/impl-json.md`) checked against the
|
|
296
|
+
* platform's `builtInToolResourceStorageSchemaV40`. Two vocabularies differ
|
|
297
|
+
* from the node's, as they do for a context index (`contextResourceJson`): the
|
|
298
|
+
* resource `query` is VARIANT-tagged where the node's is MODE-tagged, and its
|
|
299
|
+
* enums are capitalized (`Inline`, `Enabled`). `analyze-attachments` declares
|
|
300
|
+
* the model-facing call contract (`attachments` + `analysisTask` → `analysis`);
|
|
301
|
+
* `deep-rag` and `batch-transform` declare empty schemas and carry their
|
|
302
|
+
* configuration in `properties.settings`.
|
|
303
|
+
*/
|
|
304
|
+
export function builtinToolResourceJson(tool, label, inputs, spec) {
|
|
305
|
+
const toolType = ownEntry(BUILTIN_TOOL_TYPE, tool);
|
|
306
|
+
if (toolType === undefined)
|
|
307
|
+
return undefined;
|
|
308
|
+
const empty = { type: 'object', properties: {} };
|
|
309
|
+
const common = {
|
|
310
|
+
$resourceType: 'tool',
|
|
311
|
+
id: spec.id,
|
|
312
|
+
// Null, not absent: what identifies a built-in as NOT an external tool.
|
|
313
|
+
referenceKey: null,
|
|
314
|
+
name: label,
|
|
315
|
+
type: INTERNAL_TOOL_RESOURCE_TYPE,
|
|
316
|
+
description: String(inputs.description ?? ''),
|
|
317
|
+
isEnabled: true,
|
|
318
|
+
settings: {},
|
|
319
|
+
guardrail: { policies: [] },
|
|
320
|
+
argumentProperties: {},
|
|
321
|
+
};
|
|
322
|
+
const query = inputs.query?.mode === 'text-builder'
|
|
323
|
+
? { variant: 'static', value: String(inputs.query.textValue ?? '') }
|
|
324
|
+
: { variant: 'dynamic', description: String(inputs.query?.promptValue ?? '') };
|
|
325
|
+
if (toolType === 'analyze-attachments') {
|
|
326
|
+
return {
|
|
327
|
+
...common,
|
|
328
|
+
inputSchema: {
|
|
329
|
+
type: 'object',
|
|
330
|
+
properties: {
|
|
331
|
+
attachments: {
|
|
332
|
+
type: 'array',
|
|
333
|
+
items: { $ref: JOB_ATTACHMENT_REF },
|
|
334
|
+
description: String(inputs.attachmentsDescription ?? ''),
|
|
335
|
+
},
|
|
336
|
+
analysisTask: { type: 'string', description: String(inputs.analysisTaskDescription ?? '') },
|
|
337
|
+
},
|
|
338
|
+
required: ['attachments', 'analysisTask'],
|
|
339
|
+
definitions: { 'job-attachment': jobAttachmentDefinition() },
|
|
340
|
+
},
|
|
341
|
+
outputSchema: {
|
|
342
|
+
type: 'object',
|
|
343
|
+
properties: {
|
|
344
|
+
analysis: {
|
|
345
|
+
type: 'string',
|
|
346
|
+
description: 'Analysis result of the attachments (analysis, summary, extracted information, etc.)',
|
|
347
|
+
},
|
|
348
|
+
},
|
|
349
|
+
required: ['analysis'],
|
|
350
|
+
},
|
|
351
|
+
properties: { toolType, exampleCalls: [] },
|
|
352
|
+
};
|
|
353
|
+
}
|
|
354
|
+
if (toolType === 'deep-rag') {
|
|
355
|
+
return {
|
|
356
|
+
...common,
|
|
357
|
+
inputSchema: empty,
|
|
358
|
+
outputSchema: empty,
|
|
359
|
+
properties: {
|
|
360
|
+
toolType,
|
|
361
|
+
exampleCalls: [],
|
|
362
|
+
settings: {
|
|
363
|
+
contextType: 'attachments',
|
|
364
|
+
query,
|
|
365
|
+
folderPathPrefix: { variant: 'static' },
|
|
366
|
+
fileExtension: { value: String(inputs.fileExtension?.value ?? 'pdf') },
|
|
367
|
+
citationMode: { value: enumValue(inputs.citationMode?.value ?? 'inline') },
|
|
368
|
+
},
|
|
369
|
+
},
|
|
370
|
+
};
|
|
371
|
+
}
|
|
372
|
+
return {
|
|
373
|
+
...common,
|
|
374
|
+
inputSchema: empty,
|
|
375
|
+
outputSchema: empty,
|
|
376
|
+
properties: {
|
|
377
|
+
toolType,
|
|
378
|
+
exampleCalls: [],
|
|
379
|
+
settings: {
|
|
380
|
+
contextType: 'attachments',
|
|
381
|
+
query,
|
|
382
|
+
folderPathPrefix: { variant: 'static' },
|
|
383
|
+
outputColumns: Array.isArray(inputs.outputColumns) ? inputs.outputColumns : [],
|
|
384
|
+
webSearchGrounding: { value: enumValue(inputs.webSearchGrounding?.value ?? 'enabled') },
|
|
385
|
+
},
|
|
386
|
+
},
|
|
387
|
+
};
|
|
388
|
+
}
|
|
389
|
+
/**
|
|
390
|
+
* The six request fields of the HTTP-request tool, in the node's (and the
|
|
391
|
+
* designer's file's) order, with the JSON Schema each takes in the file's
|
|
392
|
+
* model-facing `inputSchema`. `headers`/`params` are name/value rows and
|
|
393
|
+
* `timeout` is a number — the same normalization the node's text-builder
|
|
394
|
+
* values already carry.
|
|
395
|
+
*/
|
|
396
|
+
export const HTTP_REQUEST_FIELDS = [
|
|
397
|
+
['url', { type: 'string' }],
|
|
398
|
+
['method', { type: 'string' }],
|
|
399
|
+
['headers', { type: 'array', items: { type: 'object', properties: { name: { type: 'string' }, value: { type: 'string' } } } }],
|
|
400
|
+
['params', { type: 'array', items: { type: 'object', properties: { name: { type: 'string' }, value: { type: 'string' } } } }],
|
|
401
|
+
['body', { type: 'string' }],
|
|
402
|
+
['timeout', { type: 'number' }],
|
|
403
|
+
];
|
|
404
|
+
/**
|
|
405
|
+
* The `resources/<id>/resource.json` of the built-in HTTP-request tool (#935) —
|
|
406
|
+
* without it the deployed agent is never offered the tool (measured: one LLM
|
|
407
|
+
* call, no tool span), and `uip agent refresh --inline-in-flow` strips the node
|
|
408
|
+
* to `{source}`, so the request config is lost entirely.
|
|
409
|
+
*
|
|
410
|
+
* Derived from the node's own mode-tagged inputs, so the two halves cannot
|
|
411
|
+
* disagree. The rules are the ones Studio Web's own file follows (measured by
|
|
412
|
+
* saving the agent once in the designer; a file built by these rules is
|
|
413
|
+
* JSON-equal to it, and a debug run with only this file showed the tool spans):
|
|
414
|
+
*
|
|
415
|
+
* * `inputSchema` declares all six fields, each with the node's `promptValue`
|
|
416
|
+
* as its description — the text the model reads when it fills the field;
|
|
417
|
+
* * `argumentProperties` pins every field NOT in `prompt` mode as
|
|
418
|
+
* `$['<field>']: {variant: 'static', value: <textValue>}` — a fixed value the
|
|
419
|
+
* model does not get to choose. The SDK writes only `prompt` and
|
|
420
|
+
* `text-builder` fields; a `text-builder` value is the literal.
|
|
421
|
+
*/
|
|
422
|
+
export function httpRequestToolResourceJson(label, inputs, spec) {
|
|
423
|
+
const properties = {};
|
|
424
|
+
const argumentProperties = {};
|
|
425
|
+
for (const [field, schema] of HTTP_REQUEST_FIELDS) {
|
|
426
|
+
const row = (inputs[field] ?? {});
|
|
427
|
+
properties[field] = { ...schema, description: String(row.promptValue ?? '') };
|
|
428
|
+
if (row.mode !== undefined && row.mode !== 'prompt') {
|
|
429
|
+
argumentProperties[`$['${field}']`] = { variant: 'static', value: row.textValue, isSensitive: false };
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
return {
|
|
433
|
+
$resourceType: 'tool',
|
|
434
|
+
id: spec.id,
|
|
435
|
+
canvasNodeId: spec.canvasNodeId,
|
|
436
|
+
name: label,
|
|
437
|
+
type: INTERNAL_TOOL_RESOURCE_TYPE,
|
|
438
|
+
description: String(inputs.description ?? ''),
|
|
439
|
+
isEnabled: true,
|
|
440
|
+
inputSchema: { type: 'object', properties, required: ['url'] },
|
|
441
|
+
outputSchema: { type: 'object', properties: {} },
|
|
442
|
+
settings: {},
|
|
443
|
+
guardrail: { policies: [] },
|
|
444
|
+
argumentProperties,
|
|
445
|
+
properties: { toolType: HTTP_REQUEST_TOOL_TYPE },
|
|
446
|
+
};
|
|
447
|
+
}
|
|
448
|
+
/**
|
|
449
|
+
* The .NET type an Action Center app argument of each field type carries. Studio
|
|
450
|
+
* Web serializes the task payload through `inputSchemaDotnetTypeMapping`, which
|
|
451
|
+
* the platform schema REQUIRES on an `actionCenter` channel; it reads the raw
|
|
452
|
+
* type off the app's action schema, which the SDK does not fetch, so this is
|
|
453
|
+
* the inverse of flow-workbench's `dotNetTypeToJsonSchema` for the scalars and
|
|
454
|
+
* `System.Object` for the two composite types it has no single answer for.
|
|
455
|
+
*/
|
|
456
|
+
export const DOTNET_TYPE = {
|
|
457
|
+
string: 'System.String', number: 'System.Double', integer: 'System.Int32',
|
|
458
|
+
boolean: 'System.Boolean', object: 'System.Object', array: 'System.Object',
|
|
459
|
+
};
|
|
460
|
+
/**
|
|
461
|
+
* The folder an app escalation names: the author's `folderPath`, or
|
|
462
|
+
* `solution_folder` when they gave none — an app that ships in this solution.
|
|
463
|
+
*
|
|
464
|
+
* `solution_folder` is the platform's own spelling for that case: it is the
|
|
465
|
+
* `Folder` that `uip solution resources list` reports for a solution-internal
|
|
466
|
+
* app, and what the agents skill tells authors to copy into
|
|
467
|
+
* `channels[].properties.folderName`. The SDK used to write `""`, which no
|
|
468
|
+
* product writer produces (flow-builder-sdk#967). Measured, the two are the
|
|
469
|
+
* same everywhere it counts: the agent tool's binding generator drops both
|
|
470
|
+
* from the App binding (`resolveFolderPathBindingValue`), `uip agent refresh`
|
|
471
|
+
* plus `uip solution resources refresh` derive byte-identical files from
|
|
472
|
+
* either, and a live debug resolves the app in the debug folder for both. So
|
|
473
|
+
* this changes the vocabulary, not the behaviour.
|
|
474
|
+
*/
|
|
475
|
+
export function escalationAppFolder(app) {
|
|
476
|
+
return app.folderPath ? app.folderPath : 'solution_folder';
|
|
477
|
+
}
|
|
478
|
+
/**
|
|
479
|
+
* The `resources/<id>/resource.json` an inline agent's ESCALATION needs — the
|
|
480
|
+
* escalation half of what `toolResourceJson` does for a tool, and inert without
|
|
481
|
+
* it for the same reason (#921). Measured 2026-10-05 on alpha with `uip maestro
|
|
482
|
+
* flow debug`: the same agent, told to escalate every claim, answered *"could
|
|
483
|
+
* not escalate: no ExpenseEscalation tool available"* with the node alone, and
|
|
484
|
+
* created the Action Center task with this file beside it.
|
|
485
|
+
*
|
|
486
|
+
* Shape: the uipath-agents skill's agent-level escalation resource (it owns the
|
|
487
|
+
* body), checked field by field against the platform's
|
|
488
|
+
* `currentResourceStorageSchema` — which REQUIRES `description` (top level and
|
|
489
|
+
* channel), `isAgentMemoryEnabled`, `governanceProperties`, `properties`,
|
|
490
|
+
* `inputSchemaDotnetTypeMapping` and `channels[].properties.appVersion`.
|
|
491
|
+
*
|
|
492
|
+
* Unlike a tool, an escalation node CARRIES its configuration, and `uip agent
|
|
493
|
+
* refresh --inline-in-flow` strips it to `source` either way. Writing the file
|
|
494
|
+
* does not change that; it is what makes refresh keep the escalation at all
|
|
495
|
+
* (`Resources: 1` and the App binding in `bindings_v2.json`, against
|
|
496
|
+
* `Resources: 0` without it). The compiled `.flow` keeps the node's full inputs,
|
|
497
|
+
* so `flow validate` is unaffected.
|
|
498
|
+
*/
|
|
499
|
+
export function escalationResourceJson(esc, spec) {
|
|
500
|
+
const outcomeMapping = esc.outcomes && esc.outcomes.length
|
|
501
|
+
? Object.fromEntries(esc.outcomes.map((o) => [o, 'continue']))
|
|
502
|
+
: {};
|
|
503
|
+
const common = {
|
|
504
|
+
id: spec.channelId,
|
|
505
|
+
name: 'Channel',
|
|
506
|
+
description: esc.description ?? '',
|
|
507
|
+
outcomeMapping,
|
|
508
|
+
recipients: (esc.recipients ?? []).map((v) => ({ type: 3, value: v })),
|
|
509
|
+
taskTitle: esc.taskTitle ?? '',
|
|
510
|
+
taskTitleV2: {
|
|
511
|
+
type: 'textBuilder',
|
|
512
|
+
tokens: esc.taskTitle ? [{ type: 'simpleText', rawString: esc.taskTitle }] : [],
|
|
513
|
+
},
|
|
514
|
+
labels: esc.labels ?? [],
|
|
515
|
+
argumentProperties: {},
|
|
516
|
+
};
|
|
517
|
+
let channel;
|
|
518
|
+
if (spec.quickFormSchema !== undefined) {
|
|
519
|
+
// The INLINE form: the node's own `schema` (fields + outcomes) is the
|
|
520
|
+
// channel's, and its contract is read off the field directions — what the
|
|
521
|
+
// reviewer is shown (`input`/`inOut`) and what comes back (`output`/`inOut`).
|
|
522
|
+
const fields = esc.fields ?? [];
|
|
523
|
+
const props = (dirs) => Object.fromEntries(fields
|
|
524
|
+
.filter((f) => dirs.includes(f.direction))
|
|
525
|
+
.map((f) => [f.id, formFieldJsonSchema(f.type) ?? { type: 'string' }]));
|
|
526
|
+
channel = {
|
|
527
|
+
...common,
|
|
528
|
+
type: 'actionCenterQuickForm',
|
|
529
|
+
inputSchema: { type: 'object', properties: props(['input', 'inOut']) },
|
|
530
|
+
outputSchema: { type: 'object', properties: props(['output', 'inOut']) },
|
|
531
|
+
properties: {
|
|
532
|
+
schema: spec.quickFormSchema,
|
|
533
|
+
isActionableMessageEnabled: false,
|
|
534
|
+
actionableMessageMetaData: null,
|
|
535
|
+
},
|
|
536
|
+
};
|
|
537
|
+
}
|
|
538
|
+
else {
|
|
539
|
+
const app = esc.app;
|
|
540
|
+
const mapping = (fields) => Object.fromEntries(Object.entries(fields ?? {}).map(([k, t]) => [k, ownEntry(DOTNET_TYPE, t) ?? 'System.Object']));
|
|
541
|
+
channel = {
|
|
542
|
+
...common,
|
|
543
|
+
type: 'actionCenter',
|
|
544
|
+
inputSchema: fieldSchema(app.inputs),
|
|
545
|
+
outputSchema: fieldSchema(app.outputs),
|
|
546
|
+
inputSchemaDotnetTypeMapping: mapping(app.inputs),
|
|
547
|
+
outputSchemaDotnetTypeMapping: mapping(app.outputs),
|
|
548
|
+
properties: {
|
|
549
|
+
// The app's key and name are what `uip agent refresh` turns into the
|
|
550
|
+
// App binding (`name` + `folderPath`) that `uip solution resources
|
|
551
|
+
// refresh` imports; `folderName` is the app's Orchestrator folder.
|
|
552
|
+
resourceKey: app.key,
|
|
553
|
+
appName: app.name,
|
|
554
|
+
folderName: escalationAppFolder(app),
|
|
555
|
+
// The app's deploy version (its action schema's `version`). The SDK does
|
|
556
|
+
// not read the action schema, so this is the platform's own default — the
|
|
557
|
+
// storage migration that introduced the field writes `1` when absent.
|
|
558
|
+
appVersion: app.version ?? 1,
|
|
559
|
+
isActionableMessageEnabled: false,
|
|
560
|
+
actionableMessageMetaData: null,
|
|
561
|
+
},
|
|
562
|
+
};
|
|
563
|
+
}
|
|
564
|
+
return {
|
|
565
|
+
$resourceType: 'escalation',
|
|
566
|
+
id: spec.id,
|
|
567
|
+
name: esc.name,
|
|
568
|
+
description: esc.description ?? '',
|
|
569
|
+
escalationType: 0,
|
|
570
|
+
isAgentMemoryEnabled: false,
|
|
571
|
+
properties: {},
|
|
572
|
+
governanceProperties: { isEscalatedAtRuntime: false },
|
|
573
|
+
isEnabled: true,
|
|
574
|
+
channels: [channel],
|
|
575
|
+
};
|
|
576
|
+
}
|
|
577
|
+
/**
|
|
578
|
+
* The one TARGET handle every tool resource declares: `input`, artifact-typed,
|
|
579
|
+
* fed by the two agent types' `tool` handles. Transcribed from `registry get`
|
|
580
|
+
* (identical across `tool.builtin.*`, `tool.connector.*` and every
|
|
581
|
+
* per-instance kind, 2026-07-31) — the mirror of the agent's own
|
|
582
|
+
* `allowedTargets: [{ nodeType: 'uipath.agent.resource.tool.*' }]`.
|
|
583
|
+
*/
|
|
584
|
+
export function toolInputHandle() {
|
|
585
|
+
return {
|
|
586
|
+
position: 'top',
|
|
587
|
+
handles: [{
|
|
588
|
+
id: 'input', type: 'target', handleType: 'artifact', isDefaultForType: true,
|
|
589
|
+
constraints: {
|
|
590
|
+
maxConnections: 1,
|
|
591
|
+
allowedSources: [
|
|
592
|
+
{ nodeType: 'uipath.agent.autonomous', handleId: 'tool' },
|
|
593
|
+
{ nodeType: 'uipath.agent.conversational', handleId: 'tool' },
|
|
594
|
+
],
|
|
595
|
+
validationMessage: "Only Agent's tool handle can connect here",
|
|
596
|
+
},
|
|
597
|
+
}],
|
|
598
|
+
};
|
|
599
|
+
}
|
|
600
|
+
/** A `{ name: type }` field map as the JSON-schema object the platform stores. */
|
|
601
|
+
export function fieldSchema(fields) {
|
|
602
|
+
return {
|
|
603
|
+
type: 'object',
|
|
604
|
+
properties: Object.fromEntries(Object.entries(fields ?? {}).map(([k, t]) => [k, { type: t }])),
|
|
605
|
+
};
|
|
606
|
+
}
|
|
607
|
+
/**
|
|
608
|
+
* The definitions for the three platform BUILT-IN tools, transcribed from
|
|
609
|
+
* `uip maestro flow registry get uipath.agent.resource.tool.builtin.<name>`
|
|
610
|
+
* (2026-07-31, version 1.1 all three). These are fixed types — no tenant
|
|
611
|
+
* resource behind them — so unlike every other tool kind the def CAN be
|
|
612
|
+
* bundled.
|
|
613
|
+
*
|
|
614
|
+
* One wrinkle carried faithfully rather than smoothed over: the three defs
|
|
615
|
+
* disagree about where the node's `source` identity lives. `analyzefiles`
|
|
616
|
+
* declares `model.source: true` (the resource-family convention);
|
|
617
|
+
* `summarize`/`batchtransform` have NO model and declare `source` as an
|
|
618
|
+
* ordinary input instead. Both spellings satisfy the platform's stable-UUID
|
|
619
|
+
* requirement, so each def ships the registry's own shape.
|
|
620
|
+
*/
|
|
621
|
+
export const BUILTIN_TOOL_DEFS = {
|
|
622
|
+
analyzefiles: {
|
|
623
|
+
nodeType: 'uipath.agent.resource.tool.builtin.analyzefiles',
|
|
624
|
+
version: '1.1',
|
|
625
|
+
category: 'agent.tool.builtin',
|
|
626
|
+
tags: ['agent-tool', 'builtin', 'analyze', 'files', 'attachments'],
|
|
627
|
+
description: 'Extract and analyze content from files',
|
|
628
|
+
sortOrder: 48,
|
|
629
|
+
display: {
|
|
630
|
+
label: 'Analyze Files',
|
|
631
|
+
description: 'Analyze one or more files with an LLM to extract, synthesize, or answer queries about their content.',
|
|
632
|
+
icon: 'paperclip', shape: 'circle',
|
|
633
|
+
},
|
|
634
|
+
handleConfiguration: [toolInputHandle()],
|
|
635
|
+
model: { source: true },
|
|
636
|
+
inputDefinition: {
|
|
637
|
+
type: 'object',
|
|
638
|
+
properties: {
|
|
639
|
+
description: { type: 'string' },
|
|
640
|
+
attachmentsDescription: { type: 'string' },
|
|
641
|
+
itemsDescription: {
|
|
642
|
+
type: 'array',
|
|
643
|
+
items: { type: 'object', properties: { label: { type: 'string' }, value: { type: 'string' } } },
|
|
644
|
+
},
|
|
645
|
+
analysisTaskDescription: { type: 'string' },
|
|
646
|
+
exampleCalls: { type: 'array' },
|
|
647
|
+
},
|
|
648
|
+
},
|
|
649
|
+
inputDefaults: {
|
|
650
|
+
description: 'Analyze one or more files with an LLM to extract, synthesize, or answer queries about their content.',
|
|
651
|
+
attachmentsDescription: 'Array of files, documents, images, or other attachments to process',
|
|
652
|
+
itemsDescription: [],
|
|
653
|
+
analysisTaskDescription: "The task, question, or instruction for processing the files (e.g., 'summarize this document', 'extract key points', 'what is in this image')",
|
|
654
|
+
exampleCalls: [],
|
|
655
|
+
},
|
|
656
|
+
},
|
|
657
|
+
summarize: {
|
|
658
|
+
nodeType: 'uipath.agent.resource.tool.builtin.summarize',
|
|
659
|
+
version: '1.1',
|
|
660
|
+
category: 'agent.tool.builtin',
|
|
661
|
+
tags: ['agent-tool', 'builtin', 'summarize', 'document', 'synthesis', 'citations'],
|
|
662
|
+
description: 'Synthesize across documents with citations',
|
|
663
|
+
sortOrder: 48,
|
|
664
|
+
display: {
|
|
665
|
+
label: 'Summarize',
|
|
666
|
+
description: 'Get comprehensive synthesis across up to 1,000 pages with citations.',
|
|
667
|
+
icon: 'database', shape: 'circle',
|
|
668
|
+
},
|
|
669
|
+
handleConfiguration: [toolInputHandle()],
|
|
670
|
+
inputDefinition: {
|
|
671
|
+
type: 'object',
|
|
672
|
+
properties: {
|
|
673
|
+
description: { type: 'string' },
|
|
674
|
+
source: { type: 'string' },
|
|
675
|
+
query: { type: 'object' },
|
|
676
|
+
fileExtension: { type: 'object', properties: { value: { type: 'string' } } },
|
|
677
|
+
citationMode: { type: 'object', properties: { value: { type: 'string' } } },
|
|
678
|
+
},
|
|
679
|
+
},
|
|
680
|
+
inputDefaults: {
|
|
681
|
+
description: 'Get comprehensive synthesis across up to 1,000 pages with citations.',
|
|
682
|
+
source: '',
|
|
683
|
+
query: { mode: 'prompt', textValue: '', promptValue: '', argumentPath: '' },
|
|
684
|
+
fileExtension: { value: 'pdf' },
|
|
685
|
+
citationMode: { value: 'inline' },
|
|
686
|
+
},
|
|
687
|
+
},
|
|
688
|
+
batchtransform: {
|
|
689
|
+
nodeType: 'uipath.agent.resource.tool.builtin.batchtransform',
|
|
690
|
+
version: '1.1',
|
|
691
|
+
category: 'agent.tool.builtin',
|
|
692
|
+
tags: ['agent-tool', 'builtin', 'batch-transform', 'csv', 'transform'],
|
|
693
|
+
description: 'Process and transform CSV files',
|
|
694
|
+
sortOrder: 48,
|
|
695
|
+
display: {
|
|
696
|
+
label: 'Batch Transform',
|
|
697
|
+
description: 'Process and transform CSV files.',
|
|
698
|
+
icon: 'table', shape: 'circle',
|
|
699
|
+
},
|
|
700
|
+
handleConfiguration: [toolInputHandle()],
|
|
701
|
+
inputDefinition: {
|
|
702
|
+
type: 'object',
|
|
703
|
+
properties: {
|
|
704
|
+
description: { type: 'string' },
|
|
705
|
+
source: { type: 'string' },
|
|
706
|
+
query: { type: 'object' },
|
|
707
|
+
outputColumns: { type: 'array' },
|
|
708
|
+
webSearchGrounding: { type: 'object', properties: { value: { type: 'string' } } },
|
|
709
|
+
},
|
|
710
|
+
},
|
|
711
|
+
inputDefaults: {
|
|
712
|
+
description: 'Process and transform CSV files.',
|
|
713
|
+
source: '',
|
|
714
|
+
query: { mode: 'prompt', textValue: '', promptValue: '', argumentPath: '' },
|
|
715
|
+
outputColumns: [],
|
|
716
|
+
webSearchGrounding: { value: 'enabled' },
|
|
717
|
+
},
|
|
718
|
+
},
|
|
719
|
+
};
|
|
720
|
+
/**
|
|
721
|
+
* The definition for one CONNECTOR-operation tool, derived from the same
|
|
722
|
+
* curated-library entry `connector(…)` resolves — measured equivalence, not
|
|
723
|
+
* hope: the library's `.v1def.json` `model` (bindings + the
|
|
724
|
+
* connectorKey/objectName/method context) is byte-identical to what
|
|
725
|
+
* `registry get uipath.agent.resource.tool.connector.<key>.<op>` returns for
|
|
726
|
+
* the same operation, minus `source: true` (checked 2026-07-31 against
|
|
727
|
+
* `uipath-uipath-airdk/web-search`). What the tool era changes is the frame:
|
|
728
|
+
* the node type moves one namespace over, the category gains the
|
|
729
|
+
* `agent.tool.` prefix over the v1def's own (which carries the connector's
|
|
730
|
+
* numeric id — `connector.196640` → `agent.tool.connector.196640`), the
|
|
731
|
+
* handle becomes the tool artifact handle, and `sortOrder` is the registry's
|
|
732
|
+
* 565.
|
|
733
|
+
*/
|
|
734
|
+
export function connectorToolDef(rc) {
|
|
735
|
+
const v1 = rc.v1def ?? {};
|
|
736
|
+
const suffix = String(rc.nodeType).replace(/^uipath\.connector\./, '');
|
|
737
|
+
return {
|
|
738
|
+
nodeType: `uipath.agent.resource.tool.connector.${suffix}`,
|
|
739
|
+
version: v1.version ?? '1.0.0',
|
|
740
|
+
category: `agent.tool.${v1.category ?? 'connector'}`,
|
|
741
|
+
tags: ['agent-tool', 'connector', 'activity'],
|
|
742
|
+
sortOrder: 565,
|
|
743
|
+
display: { ...jsonClone(v1.display ?? {}), shape: 'circle' },
|
|
744
|
+
handleConfiguration: [toolInputHandle()],
|
|
745
|
+
model: { ...jsonClone(v1.model ?? {}), source: true },
|
|
746
|
+
inputDefinition: { type: 'object', properties: {} },
|
|
747
|
+
inputDefaults: {},
|
|
748
|
+
};
|
|
749
|
+
}
|
|
750
|
+
/** The folder key a connection with no folder binding carries in its resource.json. */
|
|
751
|
+
export const NO_FOLDER_KEY = '00000000-0000-0000-0000-000000000000';
|
|
752
|
+
/**
|
|
753
|
+
* The library's operation description without the `(<Connector Name>) ` prefix
|
|
754
|
+
* the v1def's `display.description` carries — the model reads this text, and
|
|
755
|
+
* the designer's own resource.json has the bare sentence.
|
|
756
|
+
*/
|
|
757
|
+
export function connectorToolDescription(rc) {
|
|
758
|
+
const text = String(rc.v1def?.display?.description ?? rc.entry?.display?.description ?? '');
|
|
759
|
+
const prefix = `(${rc.connectorName}) `;
|
|
760
|
+
return rc.connectorName && text.startsWith(prefix) ? text.slice(prefix.length) : text;
|
|
761
|
+
}
|
|
762
|
+
/**
|
|
763
|
+
* An agent-decided value: `{{prompt: "<what the model is told>"}}`, byte for byte
|
|
764
|
+
* what the product writes — `uip maestro flow node configure` (UiPath/cli
|
|
765
|
+
* `connector-service.ts`, `` `{{prompt: "${f.description || f.displayName || f.name}"}}` ``)
|
|
766
|
+
* and the designer's `PROMPT_WRAPPER` (@uipath/maestro-sdk) both splice the text RAW.
|
|
767
|
+
* The chip format has no escape: the product's reader (`PROMPT_UNWRAPPER`) slices
|
|
768
|
+
* off the fixed `{{prompt:` / `}}` frame and strips one pair of outer quotes, so
|
|
769
|
+
* any text — `"`, `\`, a newline, a `}}` — round-trips. Escaping it here made the
|
|
770
|
+
* model read literal backslashes. FB011 registers this as the one raw chip splice.
|
|
771
|
+
*/
|
|
772
|
+
export function promptChip(f) {
|
|
773
|
+
const text = f.description || f.displayName || f.name;
|
|
774
|
+
return `{{prompt: "${text}"}}`;
|
|
775
|
+
}
|
|
776
|
+
export const isPromptChip = (v) => typeof v === 'string' && v.startsWith('{{prompt');
|
|
777
|
+
/** The text inside `{{prompt: "…"}}`, read the way the product reads it (maestro-sdk PROMPT_UNWRAPPER); `''` when `value` is not a chip. */
|
|
778
|
+
export function chipText(value) {
|
|
779
|
+
if (!(value.startsWith('{{prompt:') && value.endsWith('}}')))
|
|
780
|
+
return '';
|
|
781
|
+
const inner = value.slice(9, -2).trim();
|
|
782
|
+
const quoted = (inner.startsWith('"') && inner.endsWith('"')) || (inner.startsWith("'") && inner.endsWith("'"));
|
|
783
|
+
return quoted ? inner.slice(1, -1) : inner;
|
|
784
|
+
}
|
|
785
|
+
/** A field's enum as `{ name, value }` rows, the platform's spelling; `undefined` when it has none. */
|
|
786
|
+
export function enumRows(raw) {
|
|
787
|
+
if (!Array.isArray(raw) || raw.length === 0)
|
|
788
|
+
return undefined;
|
|
789
|
+
return raw.map((e) => (typeof e === 'string'
|
|
790
|
+
? { name: e, value: e }
|
|
791
|
+
: { name: String(e?.name ?? e?.value ?? ''), value: String(e?.value ?? e?.name ?? '') }));
|
|
792
|
+
}
|
|
793
|
+
/**
|
|
794
|
+
* The `inputs.detail` of a connector-tool node — the same key set a
|
|
795
|
+
* `connector()` step's node carries, with the request buckets filled the way
|
|
796
|
+
* `uip maestro flow node configure` fills them for a `tool.connector.*` node
|
|
797
|
+
* (uipcli `buildAgentToolParamsFromMetadata`): every BODY field is
|
|
798
|
+
* agent-decided (`{{prompt: "<description>"}}`) except a single-value enum,
|
|
799
|
+
* which is its one constant (the model would otherwise guess — `google` for
|
|
800
|
+
* `GoogleCustomSearch`); a QUERY/PATH parameter with a declared default sends
|
|
801
|
+
* it, and otherwise is agent-decided too.
|
|
802
|
+
*/
|
|
803
|
+
export function connectorToolDetail(rc, conn, folder, seed) {
|
|
804
|
+
const params = Array.isArray(rc.entry?.operation?.parameters) ? rc.entry.operation.parameters : [];
|
|
805
|
+
const buckets = { body: {}, query: {}, path: {} };
|
|
806
|
+
for (const f of rc.fields ?? []) {
|
|
807
|
+
const loc = f.location ?? 'body';
|
|
808
|
+
if (loc === 'body') {
|
|
809
|
+
const only = enumRows(f.enum);
|
|
810
|
+
buckets.body[f.name] = only && only.length === 1 ? only[0].value : promptChip(f);
|
|
811
|
+
}
|
|
812
|
+
else if (loc === 'query' || loc === 'path') {
|
|
813
|
+
const dflt = params.find((p) => p?.name === f.name)?.defaultValue;
|
|
814
|
+
buckets[loc][f.name] = dflt !== undefined && dflt !== null && dflt !== '' ? dflt : promptChip(f);
|
|
815
|
+
}
|
|
816
|
+
}
|
|
817
|
+
const parts = rc.multipartParts ?? [];
|
|
818
|
+
return {
|
|
819
|
+
connector: rc.connectorKey,
|
|
820
|
+
method: transportHttpMethod(rc.httpMethod),
|
|
821
|
+
endpoint: rc.endpoint,
|
|
822
|
+
...(rc.objectName ? { objectName: rc.objectName } : {}),
|
|
823
|
+
inputMetadata: parts.length
|
|
824
|
+
? { type: 'multipart', multipart: { bodyFieldName: multipartBodyFieldName(parts) } }
|
|
825
|
+
: {},
|
|
826
|
+
errorState: { hasError: false },
|
|
827
|
+
telemetryData: { connectorKey: rc.connectorKey, connectorName: rc.connectorName },
|
|
828
|
+
configuration: buildConfiguration(rc.entry, rc.version, {}, seed),
|
|
829
|
+
...(rc.activityTypeId ? { uiPathActivityTypeId: rc.activityTypeId } : {}),
|
|
830
|
+
...(conn ? { connectionId: conn, connectionResourceId: conn } : {}),
|
|
831
|
+
...(folder ? { connectionFolderKey: folder } : {}),
|
|
832
|
+
bodyParameters: buckets.body,
|
|
833
|
+
...(Object.keys(buckets.path).length ? { pathParameters: buckets.path } : {}),
|
|
834
|
+
...(Object.keys(buckets.query).length ? { queryParameters: buckets.query } : {}),
|
|
835
|
+
...(parts.length ? { multipartParameters: parts.map((p) => ({ name: p.name, dataType: p.dataType })) } : {}),
|
|
836
|
+
};
|
|
837
|
+
}
|
|
838
|
+
/** Walk (creating) the object chain for a dotted field name; returns the parent schema and the leaf key. */
|
|
839
|
+
export function nestedSlot(root, name) {
|
|
840
|
+
const segments = name.split('.');
|
|
841
|
+
let parent = root;
|
|
842
|
+
for (const seg of segments.slice(0, -1)) {
|
|
843
|
+
let child = parent.properties[seg];
|
|
844
|
+
if (!child || child.type !== 'object') {
|
|
845
|
+
child = { type: 'object', properties: {} };
|
|
846
|
+
parent.properties[seg] = child;
|
|
847
|
+
}
|
|
848
|
+
parent = child;
|
|
849
|
+
}
|
|
850
|
+
return { parent, leaf: segments[segments.length - 1] };
|
|
851
|
+
}
|
|
852
|
+
/**
|
|
853
|
+
* The `resources/<id>/resource.json` of a CONNECTOR tool (#867) — the file the
|
|
854
|
+
* agent runtime registers the tool from, and the one that puts the connection
|
|
855
|
+
* under the solution's Resources.
|
|
856
|
+
*
|
|
857
|
+
* The shape is uipcli's `mapConnectorToolToResource` (the writer behind `uip
|
|
858
|
+
* agent refresh --inline-in-flow`, itself ported from flow-workbench's
|
|
859
|
+
* `agent-connector-tool` mapper), in its metadata-enriched form: field types,
|
|
860
|
+
* enums, `required` and the response fields come from the curated library,
|
|
861
|
+
* which carries the same declarations that command fetches from Integration
|
|
862
|
+
* Service. Where that writer and a Studio Web export differ, this follows the
|
|
863
|
+
* writer: `enum`/`oneOf` only on agent-decided fields (a static field with an
|
|
864
|
+
* enum breaks the runtime's model build), no `additionalProperties`, and
|
|
865
|
+
* array-of-object response fields (`results[*]`) left out.
|
|
866
|
+
*
|
|
867
|
+
* The values come from the node's own `detail` — the same source refresh reads —
|
|
868
|
+
* so a later refresh rewrites this file to itself.
|
|
869
|
+
*/
|
|
870
|
+
export function connectorToolResourceJson(ctx, id) {
|
|
871
|
+
const { rc, detail } = ctx;
|
|
872
|
+
const valueOf = (raw) => raw !== null && typeof raw === 'object' ? JSON.stringify(raw) : String(raw ?? '');
|
|
873
|
+
const bucketOf = { body: 'bodyParameters', query: 'queryParameters', path: 'pathParameters' };
|
|
874
|
+
const fields = [];
|
|
875
|
+
for (const f of rc.fields ?? []) {
|
|
876
|
+
const loc = f.location ?? 'body';
|
|
877
|
+
if (loc === 'multipart')
|
|
878
|
+
continue;
|
|
879
|
+
const value = valueOf(detail[bucketOf[loc]]?.[f.name]);
|
|
880
|
+
fields.push({
|
|
881
|
+
name: f.name,
|
|
882
|
+
fieldLocation: loc,
|
|
883
|
+
type: f.type || 'string',
|
|
884
|
+
...(loc === 'body' && enumRows(f.enum)
|
|
885
|
+
? { enumValues: enumRows(f.enum) } : {}),
|
|
886
|
+
required: f.required === true,
|
|
887
|
+
description: f.description ?? '',
|
|
888
|
+
...(f.displayName ? { displayName: f.displayName } : {}),
|
|
889
|
+
value,
|
|
890
|
+
isPrompt: isPromptChip(value),
|
|
891
|
+
});
|
|
892
|
+
}
|
|
893
|
+
for (const part of (detail.multipartParameters ?? [])) {
|
|
894
|
+
fields.push({
|
|
895
|
+
name: part.name, fieldLocation: 'multipart', type: part.dataType || 'string',
|
|
896
|
+
required: false, description: '', value: '{{prompt}}', isPrompt: true,
|
|
897
|
+
});
|
|
898
|
+
}
|
|
899
|
+
const inputSchema = { type: 'object', properties: {} };
|
|
900
|
+
for (const f of fields) {
|
|
901
|
+
if (f.fieldLocation === 'multipart')
|
|
902
|
+
continue;
|
|
903
|
+
const { parent, leaf } = nestedSlot(inputSchema, f.name);
|
|
904
|
+
const prop = { type: f.type, title: f.displayName ?? leaf, description: f.description };
|
|
905
|
+
if (f.isPrompt && f.enumValues) {
|
|
906
|
+
prop.enum = f.enumValues.map((e) => e.value);
|
|
907
|
+
prop.oneOf = f.enumValues.map((e) => ({ const: e.value, title: e.name }));
|
|
908
|
+
}
|
|
909
|
+
parent.properties[leaf] = prop;
|
|
910
|
+
if (f.required)
|
|
911
|
+
(parent.required ??= []).push(leaf);
|
|
912
|
+
}
|
|
913
|
+
const outputSchema = { type: 'object', properties: {}, definitions: {} };
|
|
914
|
+
for (const f of (rc.entry?.outputSchema?.fields ?? [])) {
|
|
915
|
+
if (typeof f?.name !== 'string' || f.name.includes('[*]'))
|
|
916
|
+
continue;
|
|
917
|
+
const { parent, leaf } = nestedSlot(outputSchema, f.name);
|
|
918
|
+
parent.properties[leaf] = {
|
|
919
|
+
type: f.type || 'string',
|
|
920
|
+
title: f.displayName ?? leaf,
|
|
921
|
+
...(f.description ? { description: f.description } : {}),
|
|
922
|
+
};
|
|
923
|
+
}
|
|
924
|
+
const multipart = detail.inputMetadata?.type === 'multipart';
|
|
925
|
+
return {
|
|
926
|
+
$resourceType: 'tool',
|
|
927
|
+
name: ctx.name,
|
|
928
|
+
description: ctx.description,
|
|
929
|
+
location: 'external',
|
|
930
|
+
type: CONNECTOR_TOOL_RESOURCE_TYPE,
|
|
931
|
+
inputSchema,
|
|
932
|
+
outputSchema,
|
|
933
|
+
settings: {},
|
|
934
|
+
properties: {
|
|
935
|
+
toolPath: rc.endpoint ?? '',
|
|
936
|
+
objectName: rc.objectName ?? '',
|
|
937
|
+
toolDisplayName: ctx.name,
|
|
938
|
+
toolDescription: ctx.description,
|
|
939
|
+
// The operation KEY (`GETBYID` for a get-by-id), not the transport verb:
|
|
940
|
+
// the runtime sends it to Integration Service to pick the operation.
|
|
941
|
+
method: rc.httpMethod,
|
|
942
|
+
connection: {
|
|
943
|
+
id: detail.connectionId ?? '',
|
|
944
|
+
name: rc.connectorName,
|
|
945
|
+
elementInstanceId: 0,
|
|
946
|
+
apiBaseUri: '',
|
|
947
|
+
state: 'enabled',
|
|
948
|
+
isDefault: false,
|
|
949
|
+
connector: { key: rc.connectorKey, name: rc.connectorName, image: '' },
|
|
950
|
+
folder: { key: detail.connectionFolderKey ?? NO_FOLDER_KEY, path: '' },
|
|
951
|
+
},
|
|
952
|
+
parameters: fields.map((f, i) => ({
|
|
953
|
+
name: f.name,
|
|
954
|
+
displayName: f.displayName ?? f.name,
|
|
955
|
+
type: f.type,
|
|
956
|
+
fieldLocation: f.fieldLocation,
|
|
957
|
+
value: f.value,
|
|
958
|
+
// uipcli's fallback: an undescribed agent-decided field keeps the chip's own text.
|
|
959
|
+
description: f.description || (f.isPrompt ? chipText(f.value) : ''),
|
|
960
|
+
position: f.required ? 'primary' : 'secondary',
|
|
961
|
+
sortOrder: i + 1,
|
|
962
|
+
required: f.required,
|
|
963
|
+
fieldVariant: f.isPrompt ? 'dynamic' : 'static',
|
|
964
|
+
isCascading: false,
|
|
965
|
+
dynamic: f.isPrompt,
|
|
966
|
+
enumValues: f.enumValues ?? null,
|
|
967
|
+
loadReferenceOptionsByDefault: null,
|
|
968
|
+
dynamicBehavior: [],
|
|
969
|
+
reference: null,
|
|
970
|
+
})),
|
|
971
|
+
bodyStructure: multipart
|
|
972
|
+
? { contentType: 'multipart', jsonBodySection: detail.inputMetadata.multipart?.bodyFieldName ?? 'body' }
|
|
973
|
+
: { contentType: 'json' },
|
|
974
|
+
},
|
|
975
|
+
guardrail: { policies: [] },
|
|
976
|
+
iconUrl: ctx.iconUrl,
|
|
977
|
+
id,
|
|
978
|
+
isEnabled: true,
|
|
979
|
+
};
|
|
980
|
+
}
|
|
981
|
+
/**
|
|
982
|
+
* The tool kinds that wrap a DEPLOYED tenant resource, and everything that
|
|
983
|
+
* differs between them. Transcribed from `registry get` over one live example
|
|
984
|
+
* of each (2026-07-31): the five agree on the whole frame (`bpmn:ServiceTask`,
|
|
985
|
+
* `version: 'v2'`, `section: 'Published'`, the `<bindings.name>` /
|
|
986
|
+
* `<bindings.folderPath>` context, `source: true`, version `1.0.0`, the tool
|
|
987
|
+
* artifact handle, `tags: ['agent-tool']`) and differ in exactly these rows.
|
|
988
|
+
*/
|
|
989
|
+
export const PROCESS_TOOL_META = {
|
|
990
|
+
process: { ns: 'process', serviceType: 'Orchestrator.StartJob', subType: 'Process', orchestratorType: 'process', category: 'agent.tool.rpa', icon: 'rpa', sortOrder: 550 },
|
|
991
|
+
api: { ns: 'api', serviceType: 'Orchestrator.ExecuteApiWorkflowAsync', subType: 'Api', orchestratorType: 'api', category: 'agent.tool.api', icon: 'api', sortOrder: 555 },
|
|
992
|
+
maestro: { ns: 'processorchestration', serviceType: 'Orchestrator.StartAgenticProcess', subType: 'ProcessOrchestration', orchestratorType: 'processorchestration', category: 'agent.tool.agentic-process', icon: 'agentic-process', sortOrder: 558 },
|
|
993
|
+
flow: { ns: 'flow', serviceType: 'Orchestrator.StartFlowProcess', subType: 'Flow', orchestratorType: 'flow', category: 'agent.tool.flow', icon: 'flow-project', sortOrder: 559 },
|
|
994
|
+
agent: { ns: 'agent', serviceType: 'Orchestrator.StartAgentJob', subType: 'Agent', orchestratorType: 'agent', category: 'agent.tool.agent', icon: 'autonomous-agent', sortOrder: 560 },
|
|
995
|
+
// A published FUNCTION as an agent tool (Phase 5): same shape as its
|
|
996
|
+
// siblings, with the function family's service type and binding subtype
|
|
997
|
+
// (see `publishedFunctionDef`, transcribed from the platform's manifest).
|
|
998
|
+
function: { ns: 'function', serviceType: 'Orchestrator.ExecuteFunctionAsync', subType: 'Function', orchestratorType: 'function', category: 'agent.tool.function', icon: 'function', sortOrder: 557 },
|
|
999
|
+
};
|
|
1000
|
+
/**
|
|
1001
|
+
* The definition for one per-instance tool resource. Like `rpaWorkflowDef`
|
|
1002
|
+
* and its siblings — whose model half this shares verb for verb — the def
|
|
1003
|
+
* cannot be bundled: the node type carries the deployed resource's own key,
|
|
1004
|
+
* and the contract (`inputSchema`/`outputSchema` in `inputDefaults`, which is
|
|
1005
|
+
* where the registry's tenant-synthesized copy puts it) comes from the author
|
|
1006
|
+
* because offline nothing else can know it.
|
|
1007
|
+
*/
|
|
1008
|
+
export function processToolDef(kind, spec) {
|
|
1009
|
+
const meta = PROCESS_TOOL_META[kind];
|
|
1010
|
+
const inputProps = Object.keys(spec.inputs ?? {});
|
|
1011
|
+
return {
|
|
1012
|
+
nodeType: `uipath.agent.resource.tool.${meta.ns}.${spec.key}`,
|
|
1013
|
+
version: '1.0.0',
|
|
1014
|
+
category: meta.category,
|
|
1015
|
+
tags: ['agent-tool'],
|
|
1016
|
+
sortOrder: meta.sortOrder,
|
|
1017
|
+
display: { label: spec.name, icon: meta.icon, shape: 'circle' },
|
|
1018
|
+
handleConfiguration: [toolInputHandle()],
|
|
1019
|
+
model: {
|
|
1020
|
+
type: 'bpmn:ServiceTask',
|
|
1021
|
+
serviceType: meta.serviceType,
|
|
1022
|
+
version: 'v2',
|
|
1023
|
+
section: 'Published',
|
|
1024
|
+
bindings: {
|
|
1025
|
+
resource: 'process',
|
|
1026
|
+
resourceSubType: meta.subType,
|
|
1027
|
+
resourceKey: `${spec.folderPath}.${spec.name}`,
|
|
1028
|
+
orchestratorType: meta.orchestratorType,
|
|
1029
|
+
values: [
|
|
1030
|
+
{ name: 'name', propertyAttribute: 'name', default: spec.name },
|
|
1031
|
+
{ name: 'folderPath', propertyAttribute: 'folderPath', default: spec.folderPath },
|
|
1032
|
+
],
|
|
1033
|
+
},
|
|
1034
|
+
context: [
|
|
1035
|
+
{ name: 'name', type: 'string', value: '<bindings.name>' },
|
|
1036
|
+
{ name: 'folderPath', type: 'string', value: '<bindings.folderPath>' },
|
|
1037
|
+
{ name: '_label', type: 'string', value: spec.name },
|
|
1038
|
+
],
|
|
1039
|
+
source: true,
|
|
1040
|
+
},
|
|
1041
|
+
// The registry's per-tenant copy declares the resource's arguments here
|
|
1042
|
+
// when it can read the deployed resource; offline the author's declaration
|
|
1043
|
+
// is the only source (`{}` when none — the registry's own empty shape).
|
|
1044
|
+
inputDefinition: inputProps.length ? fieldSchema(spec.inputs) : {},
|
|
1045
|
+
inputDefaults: {
|
|
1046
|
+
...Object.fromEntries(inputProps.map((k) => [k, null])),
|
|
1047
|
+
inputSchema: fieldSchema(spec.inputs),
|
|
1048
|
+
outputSchema: fieldSchema(spec.returns),
|
|
1049
|
+
},
|
|
1050
|
+
};
|
|
1051
|
+
}
|
|
1052
|
+
/**
|
|
1053
|
+
* The definition for one published IxP project AS A TOOL.
|
|
1054
|
+
*
|
|
1055
|
+
* The same registry family as `ixpExtractDef`'s one namespace over, and the
|
|
1056
|
+
* comparison that prices it: this def has **no `model` key at all** — which is
|
|
1057
|
+
* not special, it is the shape `tool.builtin.summarize` and
|
|
1058
|
+
* `tool.builtin.batchtransform` already carry. So an IxP tool needs no bindings
|
|
1059
|
+
* and no resource identity beyond the node type; the agent's own resource list
|
|
1060
|
+
* is what points at it. (G16 deferred this family as "`model: null`" — measured
|
|
1061
|
+
* again on 2026-07-31 with a fresh registry cache, the key is absent, and the
|
|
1062
|
+
* only genuinely new thing is the 6-field `inputDefaults` below.)
|
|
1063
|
+
*
|
|
1064
|
+
* Transcribed from `registry get uipath.agent.resource.tool.ixp.<slug>.<projectId>`
|
|
1065
|
+
* (2026-07-31, post-`pull --force`). Note the tail is the project's OWN id here,
|
|
1066
|
+
* where an extraction STEP's type carries the project VERSION id.
|
|
1067
|
+
*/
|
|
1068
|
+
export function ixpToolDef(spec) {
|
|
1069
|
+
const slug = spec.name.toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/^-|-$/g, '');
|
|
1070
|
+
const attachmentDescription = spec.attachment?.description ?? 'File to extract data from.';
|
|
1071
|
+
return {
|
|
1072
|
+
nodeType: `uipath.agent.resource.tool.ixp.${slug}.${spec.projectId}`,
|
|
1073
|
+
version: '1.0.0',
|
|
1074
|
+
category: 'agent.tool.ixp',
|
|
1075
|
+
tags: ['agent-tool', 'ixp', 'document-understanding'],
|
|
1076
|
+
description: '',
|
|
1077
|
+
sortOrder: 575,
|
|
1078
|
+
display: {
|
|
1079
|
+
label: spec.name,
|
|
1080
|
+
description: spec.description ?? '',
|
|
1081
|
+
icon: 'file-text',
|
|
1082
|
+
shape: 'circle',
|
|
1083
|
+
},
|
|
1084
|
+
handleConfiguration: [toolInputHandle()],
|
|
1085
|
+
form: {
|
|
1086
|
+
id: 'ixp-tool-form',
|
|
1087
|
+
title: 'IXP Tool',
|
|
1088
|
+
sections: [{
|
|
1089
|
+
id: 'ixp-settings',
|
|
1090
|
+
title: 'IXP Settings',
|
|
1091
|
+
collapsible: true,
|
|
1092
|
+
defaultExpanded: true,
|
|
1093
|
+
fields: [
|
|
1094
|
+
{ name: 'inputs.name', type: 'text', label: 'Name' },
|
|
1095
|
+
{ name: 'inputs.description', type: 'textarea', label: 'Description', placeholder: 'Enter tool description' },
|
|
1096
|
+
{
|
|
1097
|
+
name: 'inputs.versionTag', type: 'select', label: 'Model',
|
|
1098
|
+
placeholder: 'Select a version tag',
|
|
1099
|
+
dataSource: {
|
|
1100
|
+
type: 'fetch',
|
|
1101
|
+
url: `uipath://ixp/projects/${spec.projectId}/tags`,
|
|
1102
|
+
method: 'GET',
|
|
1103
|
+
transform: 'data.map(tag => ({ label: tag.name, value: tag.name }))',
|
|
1104
|
+
},
|
|
1105
|
+
},
|
|
1106
|
+
{
|
|
1107
|
+
name: 'inputs.attachmentConfig', type: 'custom', component: 'ixp-attachment-editor',
|
|
1108
|
+
label: '', componentProps: { defaultDescription: attachmentDescription },
|
|
1109
|
+
},
|
|
1110
|
+
],
|
|
1111
|
+
}],
|
|
1112
|
+
},
|
|
1113
|
+
// The tool namespace's OWN `inputDefinition` — the same 11-property
|
|
1114
|
+
// catalogue, but with `required: ["fileRef"]` and no `minLength`s, because a
|
|
1115
|
+
// tool has no model or folder to resolve at design time (measured: the
|
|
1116
|
+
// extractor's and the tool's copies are NOT equal, even post-pull) — plus
|
|
1117
|
+
// ONE deliberate widening, three keys.
|
|
1118
|
+
//
|
|
1119
|
+
// The registry's copy declares neither `name` nor `description`, and its own
|
|
1120
|
+
// `form` binds `inputs.name` and `inputs.description` as the tool's first two
|
|
1121
|
+
// fields, so a node that omits them is a tool with no name. `source` is the
|
|
1122
|
+
// third: the platform's validator requires a stable uuid there on every
|
|
1123
|
+
// agent-resource node (MST-9265), and its own model-less builtin tool defs
|
|
1124
|
+
// declare it as an input for exactly that reason. Widening is lesson 42's
|
|
1125
|
+
// other branch, taken here because — unlike the extractor's `description` —
|
|
1126
|
+
// these three are load-bearing rather than inert.
|
|
1127
|
+
inputDefinition: {
|
|
1128
|
+
...jsonClone(IXP_DEF.toolInputDefinition),
|
|
1129
|
+
properties: {
|
|
1130
|
+
...jsonClone(IXP_DEF.toolInputDefinition.properties),
|
|
1131
|
+
source: { type: 'string' },
|
|
1132
|
+
name: { type: 'string' },
|
|
1133
|
+
description: { type: 'string' },
|
|
1134
|
+
},
|
|
1135
|
+
},
|
|
1136
|
+
inputDefaults: {
|
|
1137
|
+
name: spec.name,
|
|
1138
|
+
description: spec.description ?? '',
|
|
1139
|
+
projectName: spec.name,
|
|
1140
|
+
projectId: spec.projectId,
|
|
1141
|
+
versionTag: spec.versionTag ?? '',
|
|
1142
|
+
attachmentConfig: {
|
|
1143
|
+
mode: spec.attachment?.mode ?? 'prompt',
|
|
1144
|
+
description: attachmentDescription,
|
|
1145
|
+
argumentPath: spec.attachment?.argumentPath ?? '',
|
|
1146
|
+
},
|
|
1147
|
+
},
|
|
1148
|
+
};
|
|
1149
|
+
}
|
|
1150
|
+
/**
|
|
1151
|
+
* The definition for an ESCALATION — the `coded-action-app` variant, which is
|
|
1152
|
+
* the one this tenant's registry serves. Transcribed from `registry get
|
|
1153
|
+
* uipath.agent.resource.escalation.coded-action-app` (2026-07-31, v1.1).
|
|
1154
|
+
*
|
|
1155
|
+
* Node-type note, because it is the one place this card had to choose between
|
|
1156
|
+
* eras: the deployed corpus (2026-05-22) carries the BARE
|
|
1157
|
+
* `uipath.agent.resource.escalation`, today's registry resolves only the
|
|
1158
|
+
* suffixed variants, and the agent definition's own handle accepts both
|
|
1159
|
+
* (`allowedTargets: [{escalation}, {escalation.*}]`). New emission follows
|
|
1160
|
+
* what the registry can still answer for.
|
|
1161
|
+
*/
|
|
1162
|
+
export function escalationDef() {
|
|
1163
|
+
return {
|
|
1164
|
+
nodeType: 'uipath.agent.resource.escalation.coded-action-app',
|
|
1165
|
+
version: '1.1',
|
|
1166
|
+
category: 'agent',
|
|
1167
|
+
tags: ['agentic', 'ai', 'agent', 'escalation'],
|
|
1168
|
+
description: 'Escalate to a human for review or action',
|
|
1169
|
+
sortOrder: 5,
|
|
1170
|
+
display: {
|
|
1171
|
+
label: 'Action App Escalation',
|
|
1172
|
+
description: 'Escalation point for human intervention',
|
|
1173
|
+
icon: 'user-round', shape: 'circle',
|
|
1174
|
+
},
|
|
1175
|
+
handleConfiguration: [
|
|
1176
|
+
{
|
|
1177
|
+
position: 'bottom',
|
|
1178
|
+
handles: [{
|
|
1179
|
+
id: 'input', type: 'target', handleType: 'artifact',
|
|
1180
|
+
constraints: {
|
|
1181
|
+
maxConnections: 1,
|
|
1182
|
+
allowedSources: [
|
|
1183
|
+
{ nodeType: 'uipath.agent.autonomous', handleId: 'escalation' },
|
|
1184
|
+
{ nodeType: 'uipath.agent.conversational', handleId: 'escalation' },
|
|
1185
|
+
],
|
|
1186
|
+
validationMessage: "Only Agent's escalation handle can connect here",
|
|
1187
|
+
},
|
|
1188
|
+
}],
|
|
1189
|
+
},
|
|
1190
|
+
],
|
|
1191
|
+
model: { source: true },
|
|
1192
|
+
// ONE deliberate widening over the registry's copy, documented rather than
|
|
1193
|
+
// silent: the registry's inputDefinition declares only {name, description,
|
|
1194
|
+
// type} while its own inputDefaults — and every deployed escalation node in
|
|
1195
|
+
// the corpus — carry six more keys (app, recipients, outcomeMapping,
|
|
1196
|
+
// _additionalProps, _notifications, _appInputs). A def that under-declares
|
|
1197
|
+
// what the node carries makes a validator flag OUR OWN emission — it sets
|
|
1198
|
+
// inputs its type does not declare (measured: v1's deployed escalation
|
|
1199
|
+
// artifact draws the same six warnings). Declaring them keeps
|
|
1200
|
+
// the node checkable, which is the same reason CX's context def carries a
|
|
1201
|
+
// full schema (its comment: "without it a misspelled field reaches the
|
|
1202
|
+
// tenant unremarked").
|
|
1203
|
+
inputDefinition: {
|
|
1204
|
+
type: 'object',
|
|
1205
|
+
properties: {
|
|
1206
|
+
name: { type: 'string', minLength: 1 },
|
|
1207
|
+
description: { type: 'string' },
|
|
1208
|
+
type: { type: 'string' },
|
|
1209
|
+
app: { type: ['object', 'null'] },
|
|
1210
|
+
recipients: { type: 'array' },
|
|
1211
|
+
outcomeMapping: { type: ['object', 'null'] },
|
|
1212
|
+
_additionalProps: { type: 'object' },
|
|
1213
|
+
_notifications: { type: 'boolean' },
|
|
1214
|
+
_appInputs: { type: ['object', 'null'] },
|
|
1215
|
+
},
|
|
1216
|
+
required: ['name'],
|
|
1217
|
+
},
|
|
1218
|
+
inputDefaults: {
|
|
1219
|
+
name: 'Escalation',
|
|
1220
|
+
description: '',
|
|
1221
|
+
type: 'app-task',
|
|
1222
|
+
app: null,
|
|
1223
|
+
recipients: [],
|
|
1224
|
+
outcomeMapping: null,
|
|
1225
|
+
_additionalProps: { taskTitle: '', priority: 'medium', labels: [] },
|
|
1226
|
+
_notifications: false,
|
|
1227
|
+
_appInputs: null,
|
|
1228
|
+
},
|
|
1229
|
+
};
|
|
1230
|
+
}
|
|
1231
|
+
/**
|
|
1232
|
+
* The definition for ONE MCP-server tool resource, mirrored from the platform's
|
|
1233
|
+
* own mapper (`flow-core` `transformMcpServer`, read from the workbench dist —
|
|
1234
|
+
* the registry serves the family per instance, tenant-side). The node type is
|
|
1235
|
+
* minted from the server's name and key with the mapper's own slug rule.
|
|
1236
|
+
*/
|
|
1237
|
+
export function mcpToolDef(spec) {
|
|
1238
|
+
const slug = (v) => v.replace(/[^a-zA-Z0-9.]+/g, '-').toLowerCase();
|
|
1239
|
+
return {
|
|
1240
|
+
nodeType: `uipath.agent.resource.tool.mcp.${slug(spec.name)}.${slug(spec.key)}`,
|
|
1241
|
+
version: '1.0.0',
|
|
1242
|
+
category: 'agent.tool.mcp',
|
|
1243
|
+
tags: ['agent-tool', 'mcp', 'mcp-server'],
|
|
1244
|
+
description: spec.description ?? '',
|
|
1245
|
+
sortOrder: 585,
|
|
1246
|
+
display: { label: spec.name, description: spec.description ?? '', icon: 'mcp', shape: 'circle' },
|
|
1247
|
+
handleConfiguration: [toolInputHandle()],
|
|
1248
|
+
model: { source: true },
|
|
1249
|
+
inputDefaults: {
|
|
1250
|
+
name: spec.name,
|
|
1251
|
+
description: spec.description ?? '',
|
|
1252
|
+
referenceKey: spec.key,
|
|
1253
|
+
folderPath: spec.folderPath ?? '',
|
|
1254
|
+
folderKey: spec.folderKey ?? '',
|
|
1255
|
+
mcpType: spec.mcpType ?? '',
|
|
1256
|
+
serviceName: spec.serviceName ?? '',
|
|
1257
|
+
// The mapper's field name for the canonical server SLUG (historical).
|
|
1258
|
+
serverUrl: spec.slug ?? '',
|
|
1259
|
+
selectedTools: spec.selectedTools ?? [],
|
|
1260
|
+
toolCatalog: spec.selectedTools ?? [],
|
|
1261
|
+
discoveryMode: { type: 'cached', refreshSchemaBeforeCall: true },
|
|
1262
|
+
},
|
|
1263
|
+
};
|
|
1264
|
+
}
|
|
1265
|
+
/**
|
|
1266
|
+
* The definition for ONE remote-A2A tool resource, mirrored from
|
|
1267
|
+
* `transformRemoteA2aResource` — same provenance and slug rule as the MCP def.
|
|
1268
|
+
*/
|
|
1269
|
+
export function a2aToolDef(spec) {
|
|
1270
|
+
const slug = (v) => v.replace(/[^a-zA-Z0-9.]+/g, '-').toLowerCase();
|
|
1271
|
+
return {
|
|
1272
|
+
nodeType: `uipath.agent.resource.tool.a2a.${slug(spec.name)}.${slug(spec.key)}`,
|
|
1273
|
+
version: '1.0.0',
|
|
1274
|
+
category: 'agent.tool.remote-a2a',
|
|
1275
|
+
tags: ['agent-tool', 'remote-a2a', 'a2a', 'agent'],
|
|
1276
|
+
description: spec.description ?? '',
|
|
1277
|
+
sortOrder: 590,
|
|
1278
|
+
display: { label: spec.name, description: spec.description ?? '', icon: 'a2a', shape: 'circle' },
|
|
1279
|
+
handleConfiguration: [toolInputHandle()],
|
|
1280
|
+
model: { source: true },
|
|
1281
|
+
inputDefaults: {
|
|
1282
|
+
name: spec.name,
|
|
1283
|
+
description: spec.description ?? '',
|
|
1284
|
+
slug: spec.slug,
|
|
1285
|
+
cachedAgentCard: spec.cachedAgentCard ?? null,
|
|
1286
|
+
referenceKey: spec.key,
|
|
1287
|
+
resourceType: spec.resourceType ?? null,
|
|
1288
|
+
folderPath: spec.folderPath ?? '',
|
|
1289
|
+
folderKey: spec.folderKey ?? '',
|
|
1290
|
+
resourceFolderPath: spec.resourceFolderPath ?? '',
|
|
1291
|
+
},
|
|
1292
|
+
outputDefinition: {},
|
|
1293
|
+
};
|
|
1294
|
+
}
|
|
1295
|
+
/**
|
|
1296
|
+
* The definition for ONE episodic-memory resource, mirrored from
|
|
1297
|
+
* `transformEpisodicMemory`. The memory's identity rides ON the
|
|
1298
|
+
* inputDefinition (`memoryId`/`folderKey`), verbatim — that is where the
|
|
1299
|
+
* platform's own mapper puts it.
|
|
1300
|
+
*/
|
|
1301
|
+
export function memoryResourceDef(spec) {
|
|
1302
|
+
const slug = (v) => v.replace(/[^a-zA-Z0-9.]+/g, '-').toLowerCase();
|
|
1303
|
+
return {
|
|
1304
|
+
nodeType: `uipath.agent.resource.memory.${slug(spec.name)}.${slug(spec.id)}`,
|
|
1305
|
+
version: '1.0.0',
|
|
1306
|
+
category: 'agent',
|
|
1307
|
+
tags: ['agent-memory', 'memory'],
|
|
1308
|
+
description: spec.description ?? '',
|
|
1309
|
+
sortOrder: 15,
|
|
1310
|
+
display: { label: spec.name, description: spec.description ?? '', icon: 'database', shape: 'circle' },
|
|
1311
|
+
handleConfiguration: [{
|
|
1312
|
+
position: 'bottom',
|
|
1313
|
+
handles: [{
|
|
1314
|
+
id: 'input', type: 'target', handleType: 'artifact',
|
|
1315
|
+
constraints: {
|
|
1316
|
+
maxConnections: 1,
|
|
1317
|
+
allowedSources: [
|
|
1318
|
+
{ nodeType: 'uipath.agent.autonomous', handleId: 'memory' },
|
|
1319
|
+
{ nodeType: 'uipath.agent.conversational', handleId: 'memory' },
|
|
1320
|
+
],
|
|
1321
|
+
validationMessage: "Only Agent's memory handle can connect here",
|
|
1322
|
+
},
|
|
1323
|
+
}],
|
|
1324
|
+
}],
|
|
1325
|
+
model: { source: true },
|
|
1326
|
+
inputDefinition: {
|
|
1327
|
+
memoryId: spec.id,
|
|
1328
|
+
folderKey: spec.folderKey ?? '',
|
|
1329
|
+
type: 'object',
|
|
1330
|
+
properties: {
|
|
1331
|
+
name: { type: 'string' },
|
|
1332
|
+
description: { type: 'string' },
|
|
1333
|
+
dynamicFewShotLearning: { type: 'boolean' },
|
|
1334
|
+
semanticSimilarity: { type: 'number', minimum: 0, maximum: 1 },
|
|
1335
|
+
kValue: { type: 'number' },
|
|
1336
|
+
searchMode: { type: 'string' },
|
|
1337
|
+
},
|
|
1338
|
+
},
|
|
1339
|
+
inputDefaults: {
|
|
1340
|
+
name: spec.name,
|
|
1341
|
+
description: spec.description ?? '',
|
|
1342
|
+
dynamicFewShotLearning: true,
|
|
1343
|
+
semanticSimilarity: 0,
|
|
1344
|
+
kValue: 3,
|
|
1345
|
+
searchMode: 'hybrid',
|
|
1346
|
+
},
|
|
1347
|
+
};
|
|
1348
|
+
}
|
|
1349
|
+
/** True when a definition declares the agent's `memory` artifact handle. */
|
|
1350
|
+
export function declaresMemoryHandle(def) {
|
|
1351
|
+
return (def?.handleConfiguration ?? []).some((g) => (g?.handles ?? []).some((h) => h?.id === 'memory'));
|
|
1352
|
+
}
|
|
1353
|
+
/**
|
|
1354
|
+
* The definition for the INLINE autonomous agent: the bundled pair for
|
|
1355
|
+
* `version` (the newest by default), plus an explicit overlay of the details
|
|
1356
|
+
* the registry payload does not carry.
|
|
1357
|
+
*
|
|
1358
|
+
* This is the one node type in the agent family that is the SAME on every
|
|
1359
|
+
* tenant — it carries its own configuration instead of referencing a resource —
|
|
1360
|
+
* so `uip maestro flow registry get uipath.agent.autonomous` is a direct oracle
|
|
1361
|
+
* and the bundle entry is the registry's definition as last synced
|
|
1362
|
+
* (`sync:definitions` writes it through unchanged; `check:definitions --deep`
|
|
1363
|
+
* reports content that has moved since). Versions and content both come from
|
|
1364
|
+
* there.
|
|
1365
|
+
*
|
|
1366
|
+
* The overlay, and nothing else:
|
|
1367
|
+
*
|
|
1368
|
+
* * `outputDefinition.output.source` is `=result.response`. The registry's
|
|
1369
|
+
* definition declares no `source`; the deployed corpus (29 `uipath.agent.autonomous` nodes, 05-18 → 07-29) has
|
|
1370
|
+
* `=result.response` in 27 of 27 that declare outputs, and the engine's own
|
|
1371
|
+
* inline dispatch returns `{ response: { …declared keys } }`. Without it the
|
|
1372
|
+
* node's `output` is the whole result envelope rather than the declared keys.
|
|
1373
|
+
* It is applied at every version; the registry payload has no `source`.
|
|
1374
|
+
* * The `memory` handle is kept only when the flow attaches memory. It is
|
|
1375
|
+
* LOCAL content (#431) on the bundled 1.4, absent from the registry's 1.4
|
|
1376
|
+
* (`check.ts` AGENT_MEMORY_NOT_AUTHORABLE), so an agent without memory gets
|
|
1377
|
+
* the registry's handle set.
|
|
1378
|
+
*
|
|
1379
|
+
* NO per-instance schema goes on the definition: `definitions[]` is keyed by
|
|
1380
|
+
* node TYPE, and every inline agent in a flow shares this one entry. A flow
|
|
1381
|
+
* with two agents (an analyst and a writer, say) therefore cannot have both
|
|
1382
|
+
* contracts here — whichever was emitted last would win, and every read against
|
|
1383
|
+
* the other resolved to the wrong shape (GA row 7's port: 25 undeclared-field
|
|
1384
|
+
* errors on a correct flow). The authored contract goes on the NODE
|
|
1385
|
+
* (`node.outputs.output.schema`, in the `inlineAgent` arm), where a reader that
|
|
1386
|
+
* refines per node picks it up.
|
|
1387
|
+
*
|
|
1388
|
+
* The bundled 1.2 is the pre-#956 default, kept so a flow this SDK compiled
|
|
1389
|
+
* before — and a decompile of one, which pins `{ version: '1.2' }` — still
|
|
1390
|
+
* compiles. The corpus also carries 1.1 and 1.3; `shared/sample-flows/
|
|
1391
|
+
* inline-agent.flow` says 1.0, which appears in none of the 29.
|
|
1392
|
+
*/
|
|
1393
|
+
export function inlineAgentDef(version, withMemory, resolve) {
|
|
1394
|
+
const bundled = resolve(INLINE_AGENT, version);
|
|
1395
|
+
if (!bundled)
|
|
1396
|
+
return undefined;
|
|
1397
|
+
const def = jsonClone(bundled);
|
|
1398
|
+
const output = def.outputDefinition?.output;
|
|
1399
|
+
if (output)
|
|
1400
|
+
output.source = '=result.response';
|
|
1401
|
+
if (!withMemory) {
|
|
1402
|
+
for (const group of def.handleConfiguration ?? []) {
|
|
1403
|
+
if (Array.isArray(group?.handles))
|
|
1404
|
+
group.handles = group.handles.filter((h) => h?.id !== 'memory');
|
|
1405
|
+
}
|
|
1406
|
+
def.handleConfiguration = (def.handleConfiguration ?? []).filter((g) => g?.handles?.length);
|
|
1407
|
+
}
|
|
1408
|
+
return def;
|
|
1409
|
+
}
|