@uipath/maestro-builder-sdk 6.41.2 → 6.43.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (73) hide show
  1. package/dist/api-index.md +99 -99
  2. package/dist/api-members.md +307 -307
  3. package/dist/check.d.ts +0 -19
  4. package/dist/check.js +40 -1036
  5. package/dist/cli/commands/registry.js +15 -0
  6. package/dist/compile-cli.js +2 -1
  7. package/dist/core/actions.d.ts +1 -713
  8. package/dist/core/actions.js +0 -193
  9. package/dist/core/agent-sidecar.d.ts +186 -0
  10. package/dist/core/agent-sidecar.js +409 -0
  11. package/dist/core/agent-tool-resources.d.ts +62 -0
  12. package/dist/core/agent-tool-resources.js +81 -0
  13. package/dist/core/cli-spelling.d.ts +9 -0
  14. package/dist/core/cli-spelling.js +9 -0
  15. package/dist/{config.js → core/connector-configuration.js} +2 -2
  16. package/dist/core/library-cache.d.ts +0 -9
  17. package/dist/core/library-cache.js +0 -9
  18. package/dist/core/library.d.ts +47 -0
  19. package/dist/core/library.js +135 -34
  20. package/dist/core/list-envelope.d.ts +36 -1
  21. package/dist/core/list-envelope.js +24 -19
  22. package/dist/core/lookup-store.d.ts +4 -3
  23. package/dist/core/lookup-store.js +46 -38
  24. package/dist/core/node-id-placeholder.d.ts +8 -0
  25. package/dist/core/node-id-placeholder.js +10 -0
  26. package/dist/core/read-text.d.ts +26 -0
  27. package/dist/core/read-text.js +43 -0
  28. package/dist/core/ts-literal.d.ts +6 -0
  29. package/dist/core/ts-literal.js +15 -0
  30. package/dist/core/typed-raw-node.js +0 -5
  31. package/dist/decompile.js +34 -822
  32. package/dist/flow-node-registry.js +2 -1
  33. package/dist/flow-node-type.d.ts +130 -6
  34. package/dist/flow-sdk.d.ts +1 -0
  35. package/dist/flow-sdk.js +1 -0
  36. package/dist/nodes/agent/flow/check.d.ts +88 -0
  37. package/dist/nodes/agent/flow/check.js +873 -0
  38. package/dist/nodes/agent/flow/factories.d.ts +134 -0
  39. package/dist/nodes/agent/flow/factories.js +143 -0
  40. package/dist/nodes/agent/flow/family.d.ts +3 -0
  41. package/dist/nodes/agent/flow/family.js +8 -0
  42. package/dist/nodes/agent/flow/index.d.ts +3 -0
  43. package/dist/nodes/agent/flow/index.js +3 -0
  44. package/dist/nodes/agent/flow/inline-agent-lift.d.ts +44 -0
  45. package/dist/nodes/agent/flow/inline-agent-lift.js +572 -0
  46. package/dist/nodes/agent/flow/inline-agent.d.ts +15 -0
  47. package/dist/nodes/agent/flow/inline-agent.js +526 -0
  48. package/dist/nodes/agent/flow/node-type.d.ts +20 -0
  49. package/dist/nodes/agent/flow/node-type.js +422 -0
  50. package/dist/nodes/agent/flow/resources.d.ts +502 -0
  51. package/dist/nodes/agent/flow/resources.js +1409 -0
  52. package/dist/nodes/agent/flow/spec.d.ts +448 -0
  53. package/dist/nodes/agent/flow/spec.js +1 -0
  54. package/dist/nodes/process/flow/check.d.ts +40 -2
  55. package/dist/nodes/process/flow/check.js +156 -0
  56. package/dist/nodes/process/flow/factories.d.ts +58 -3
  57. package/dist/nodes/process/flow/factories.js +59 -2
  58. package/dist/nodes/process/flow/node-type.d.ts +4 -1
  59. package/dist/nodes/process/flow/node-type.js +181 -10
  60. package/dist/nodes/process/flow/spec.d.ts +112 -5
  61. package/dist/registry/describe-cli.d.ts +10 -0
  62. package/dist/registry/describe-cli.js +41 -0
  63. package/dist/registry/library-reader.d.ts +12 -2
  64. package/dist/registry/library-reader.js +17 -16
  65. package/dist/registry/lookup-specs.d.ts +17 -1
  66. package/dist/registry/lookup-specs.js +3 -7
  67. package/dist/registry/path-cli.js +2 -1
  68. package/dist/registry/search-cli.js +2 -1
  69. package/dist/serialize.d.ts +9 -54
  70. package/dist/serialize.js +47 -2763
  71. package/package.json +2 -2
  72. /package/dist/{config.d.ts → core/connector-configuration.d.ts} +0 -0
  73. /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
+ }