@uipath/maestro-builder-sdk 6.14.4 → 6.14.5

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.
@@ -36,20 +36,24 @@ function opt(argv, name) {
36
36
  return i >= 0 ? argv[i + 1] : undefined;
37
37
  }
38
38
  /**
39
- * The deployed-resource tool `type` values `compile` writes. NOT every
40
- * `$resourceType: 'tool'`: `uip agent refresh` generates the `integration` ones
41
- * from the flow's connector nodes into the same directory, so pruning by
42
- * `$resourceType` alone would delete the generator's own output.
39
+ * The tool `type` values `compile` writes. NOT every `$resourceType: 'tool'`:
40
+ * the designer writes kinds the SDK cannot derive into the same directory, so
41
+ * pruning by `$resourceType` alone would delete theirs.
42
+ *
43
+ * `integration` (connector tools) is compile's since #867: compile writes the
44
+ * file under the tool node's own `source` id, the same id and content `uip agent
45
+ * refresh --inline-in-flow` derives from that node, so a file under any other id
46
+ * belongs to a tool node the flow no longer has.
43
47
  */
44
48
  const COMPILE_OWNED_TOOL_TYPES = new Set([
45
- 'process', 'api', 'processOrchestration', 'flow', 'agent', 'function',
49
+ 'process', 'api', 'processOrchestration', 'flow', 'agent', 'function', 'integration',
46
50
  ]);
47
51
  /**
48
52
  * Does `compile` own this resource file — i.e. would this run have written it,
49
53
  * so an unwritten one is stale rather than someone else's?
50
54
  *
51
- * Everything it says no to belongs to `uip agent refresh` (connector tools) or to
52
- * the designer (escalation, mcp, and the kinds the SDK cannot derive).
55
+ * Everything it says no to belongs to the designer (escalation, mcp, and the
56
+ * kinds the SDK cannot derive).
53
57
  */
54
58
  function compileOwnsResource(body) {
55
59
  const kind = body?.$resourceType;
@@ -236,7 +240,9 @@ export async function run(argv) {
236
240
  // fail on it. Keyed the way refresh keys it — an index by its name.
237
241
  const named = body?.indexName ?? body?.name;
238
242
  const key = typeof named === 'string' && named !== '' ? named : undefined;
239
- if (key !== undefined) {
243
+ // A connector tool's binding is a `connection` row, and `mergeBindingsV2`
244
+ // re-derives every connection row from the flow below — nothing to retract.
245
+ if (key !== undefined && body?.type !== 'integration') {
240
246
  // Keyed the way refresh keys it: an index by its name, a deployed-resource
241
247
  // tool under `process` whatever its own type says.
242
248
  staleBindings.push({ resource: resourceType === 'context' ? 'index' : 'process', key });
@@ -1806,6 +1806,25 @@ export interface ConnectorToolRef {
1806
1806
  object?: string;
1807
1807
  /** Display label; defaults to the library's own (e.g. `'Create Issue'`). */
1808
1808
  name?: string;
1809
+ /**
1810
+ * What the agent is told the tool does — the runtime hands it to the MODEL.
1811
+ * Defaults to the library's own operation description.
1812
+ */
1813
+ description?: string;
1814
+ /**
1815
+ * The connection's binding NAME in `bindings.json` — the same label
1816
+ * `connector()`'s `connection` option takes (e.g. `'genai'`), resolved to
1817
+ * the tenant connection id at compile. Required: a connector tool with no
1818
+ * connection has nothing to call Integration Service with, and `check`
1819
+ * reports `INLINE_AGENT_TOOL_CONNECTOR_NO_CONNECTION`.
1820
+ */
1821
+ connection?: string;
1822
+ /**
1823
+ * The connection's folder binding NAME in `bindings.json` (a `FolderKey`
1824
+ * entry) — the same label `connector()`'s `folder` option takes. Required
1825
+ * with `connection`.
1826
+ */
1827
+ folder?: string;
1809
1828
  }
1810
1829
  /**
1811
1830
  * The tool kinds that wrap a DEPLOYED tenant resource. `kind` picks the
package/dist/serialize.js CHANGED
@@ -980,8 +980,9 @@ const TOOL_RESOURCE_TYPE = {
980
980
  *
981
981
  * * `ixp`, `clientside` — measured to fail exactly that way (`ixp` leaves the
982
982
  * flow failing validate; `clientside` drops the four inputs its node needs).
983
- * * `connector` — `uip agent refresh` GENERATES it from the flow's connector
984
- * node, reconstructing a whole `properties` block the SDK should not duplicate.
983
+ * * `connector` — has its own writer, {@link connectorToolResourceJson}: its
984
+ * node carries the configuration in `inputs.detail`, which the strip keeps,
985
+ * and the file is the one `uip agent refresh` itself derives from it.
985
986
  * * `mcp`, `a2a` — need the server's `availableTools` and the remote agent's
986
987
  * card, which are tenant reads.
987
988
  * * `builtin`, `httpRequest` — need a `properties.toolType` that maps
@@ -1202,6 +1203,225 @@ function connectorToolDef(rc) {
1202
1203
  inputDefaults: {},
1203
1204
  };
1204
1205
  }
1206
+ /** The folder key a connection with no folder binding carries in its resource.json. */
1207
+ const NO_FOLDER_KEY = '00000000-0000-0000-0000-000000000000';
1208
+ /**
1209
+ * The library's operation description without the `(<Connector Name>) ` prefix
1210
+ * the v1def's `display.description` carries — the model reads this text, and
1211
+ * the designer's own resource.json has the bare sentence.
1212
+ */
1213
+ function connectorToolDescription(rc) {
1214
+ const text = String(rc.v1def?.display?.description ?? rc.entry?.display?.description ?? '');
1215
+ const prefix = `(${rc.connectorName}) `;
1216
+ return rc.connectorName && text.startsWith(prefix) ? text.slice(prefix.length) : text;
1217
+ }
1218
+ /** An agent-decided value: `{{prompt: "<what the model is told>"}}`. */
1219
+ function promptChip(f) {
1220
+ const text = f.description || f.displayName || f.name;
1221
+ return `{{prompt: "${text.replace(/\\/g, '\\\\').replace(/"/g, '\\"')}"}}`;
1222
+ }
1223
+ const isPromptChip = (v) => typeof v === 'string' && v.startsWith('{{prompt');
1224
+ /** The text inside `{{prompt: "…"}}`, unescaped; `''` for a bare `{{prompt}}`. */
1225
+ function chipText(value) {
1226
+ const m = /\{\{\s*prompt:\s*"((?:[^"\\]|\\.)*)"/.exec(value);
1227
+ return m ? m[1].replace(/\\(.)/g, '$1') : '';
1228
+ }
1229
+ /** A field's enum as `{ name, value }` rows, the platform's spelling; `undefined` when it has none. */
1230
+ function enumRows(raw) {
1231
+ if (!Array.isArray(raw) || raw.length === 0)
1232
+ return undefined;
1233
+ return raw.map((e) => (typeof e === 'string'
1234
+ ? { name: e, value: e }
1235
+ : { name: String(e?.name ?? e?.value ?? ''), value: String(e?.value ?? e?.name ?? '') }));
1236
+ }
1237
+ /**
1238
+ * The `inputs.detail` of a connector-tool node — the same key set a
1239
+ * `connector()` step's node carries, with the request buckets filled the way
1240
+ * `uip maestro flow node configure` fills them for a `tool.connector.*` node
1241
+ * (uipcli `buildAgentToolParamsFromMetadata`): every BODY field is
1242
+ * agent-decided (`{{prompt: "<description>"}}`) except a single-value enum,
1243
+ * which is its one constant (the model would otherwise guess — `google` for
1244
+ * `GoogleCustomSearch`); a QUERY/PATH parameter with a declared default sends
1245
+ * it, and otherwise is agent-decided too.
1246
+ */
1247
+ function connectorToolDetail(rc, conn, folder, seed) {
1248
+ const params = Array.isArray(rc.entry?.operation?.parameters) ? rc.entry.operation.parameters : [];
1249
+ const buckets = { body: {}, query: {}, path: {} };
1250
+ for (const f of rc.fields ?? []) {
1251
+ const loc = f.location ?? 'body';
1252
+ if (loc === 'body') {
1253
+ const only = enumRows(f.enum);
1254
+ buckets.body[f.name] = only && only.length === 1 ? only[0].value : promptChip(f);
1255
+ }
1256
+ else if (loc === 'query' || loc === 'path') {
1257
+ const dflt = params.find((p) => p?.name === f.name)?.defaultValue;
1258
+ buckets[loc][f.name] = dflt !== undefined && dflt !== null && dflt !== '' ? dflt : promptChip(f);
1259
+ }
1260
+ }
1261
+ const parts = rc.multipartParts ?? [];
1262
+ return {
1263
+ connector: rc.connectorKey,
1264
+ method: transportHttpMethod(rc.httpMethod),
1265
+ endpoint: rc.endpoint,
1266
+ ...(rc.objectName ? { objectName: rc.objectName } : {}),
1267
+ inputMetadata: parts.length
1268
+ ? { type: 'multipart', multipart: { bodyFieldName: multipartBodyFieldName(parts) } }
1269
+ : {},
1270
+ errorState: { hasError: false },
1271
+ telemetryData: { connectorKey: rc.connectorKey, connectorName: rc.connectorName },
1272
+ configuration: buildConfiguration(rc.entry, rc.version, {}, seed),
1273
+ ...(rc.activityTypeId ? { uiPathActivityTypeId: rc.activityTypeId } : {}),
1274
+ ...(conn ? { connectionId: conn, connectionResourceId: conn } : {}),
1275
+ ...(folder ? { connectionFolderKey: folder } : {}),
1276
+ bodyParameters: buckets.body,
1277
+ ...(Object.keys(buckets.path).length ? { pathParameters: buckets.path } : {}),
1278
+ ...(Object.keys(buckets.query).length ? { queryParameters: buckets.query } : {}),
1279
+ ...(parts.length ? { multipartParameters: parts.map((p) => ({ name: p.name, dataType: p.dataType })) } : {}),
1280
+ };
1281
+ }
1282
+ /** Walk (creating) the object chain for a dotted field name; returns the parent schema and the leaf key. */
1283
+ function nestedSlot(root, name) {
1284
+ const segments = name.split('.');
1285
+ let parent = root;
1286
+ for (const seg of segments.slice(0, -1)) {
1287
+ let child = parent.properties[seg];
1288
+ if (!child || child.type !== 'object') {
1289
+ child = { type: 'object', properties: {} };
1290
+ parent.properties[seg] = child;
1291
+ }
1292
+ parent = child;
1293
+ }
1294
+ return { parent, leaf: segments[segments.length - 1] };
1295
+ }
1296
+ /**
1297
+ * The `resources/<id>/resource.json` of a CONNECTOR tool (#867) — the file the
1298
+ * agent runtime registers the tool from, and the one that puts the connection
1299
+ * under the solution's Resources.
1300
+ *
1301
+ * The shape is uipcli's `mapConnectorToolToResource` (the writer behind `uip
1302
+ * agent refresh --inline-in-flow`, itself ported from flow-workbench's
1303
+ * `agent-connector-tool` mapper), in its metadata-enriched form: field types,
1304
+ * enums, `required` and the response fields come from the curated library,
1305
+ * which carries the same declarations that command fetches from Integration
1306
+ * Service. Where that writer and a Studio Web export differ, this follows the
1307
+ * writer: `enum`/`oneOf` only on agent-decided fields (a static field with an
1308
+ * enum breaks the runtime's model build), no `additionalProperties`, and
1309
+ * array-of-object response fields (`results[*]`) left out.
1310
+ *
1311
+ * The values come from the node's own `detail` — the same source refresh reads —
1312
+ * so a later refresh rewrites this file to itself.
1313
+ */
1314
+ function connectorToolResourceJson(ctx, id) {
1315
+ const { rc, detail } = ctx;
1316
+ const valueOf = (raw) => raw !== null && typeof raw === 'object' ? JSON.stringify(raw) : String(raw ?? '');
1317
+ const bucketOf = { body: 'bodyParameters', query: 'queryParameters', path: 'pathParameters' };
1318
+ const fields = [];
1319
+ for (const f of rc.fields ?? []) {
1320
+ const loc = f.location ?? 'body';
1321
+ if (loc === 'multipart')
1322
+ continue;
1323
+ const value = valueOf(detail[bucketOf[loc]]?.[f.name]);
1324
+ fields.push({
1325
+ name: f.name,
1326
+ fieldLocation: loc,
1327
+ type: f.type || 'string',
1328
+ ...(loc === 'body' && enumRows(f.enum)
1329
+ ? { enumValues: enumRows(f.enum) } : {}),
1330
+ required: f.required === true,
1331
+ description: f.description ?? '',
1332
+ ...(f.displayName ? { displayName: f.displayName } : {}),
1333
+ value,
1334
+ isPrompt: isPromptChip(value),
1335
+ });
1336
+ }
1337
+ for (const part of (detail.multipartParameters ?? [])) {
1338
+ fields.push({
1339
+ name: part.name, fieldLocation: 'multipart', type: part.dataType || 'string',
1340
+ required: false, description: '', value: '{{prompt}}', isPrompt: true,
1341
+ });
1342
+ }
1343
+ const inputSchema = { type: 'object', properties: {} };
1344
+ for (const f of fields) {
1345
+ if (f.fieldLocation === 'multipart')
1346
+ continue;
1347
+ const { parent, leaf } = nestedSlot(inputSchema, f.name);
1348
+ const prop = { type: f.type, title: f.displayName ?? leaf, description: f.description };
1349
+ if (f.isPrompt && f.enumValues) {
1350
+ prop.enum = f.enumValues.map((e) => e.value);
1351
+ prop.oneOf = f.enumValues.map((e) => ({ const: e.value, title: e.name }));
1352
+ }
1353
+ parent.properties[leaf] = prop;
1354
+ if (f.required)
1355
+ (parent.required ??= []).push(leaf);
1356
+ }
1357
+ const outputSchema = { type: 'object', properties: {}, definitions: {} };
1358
+ for (const f of (rc.entry?.outputSchema?.fields ?? [])) {
1359
+ if (typeof f?.name !== 'string' || f.name.includes('[*]'))
1360
+ continue;
1361
+ const { parent, leaf } = nestedSlot(outputSchema, f.name);
1362
+ parent.properties[leaf] = {
1363
+ type: f.type || 'string',
1364
+ title: f.displayName ?? leaf,
1365
+ ...(f.description ? { description: f.description } : {}),
1366
+ };
1367
+ }
1368
+ const multipart = detail.inputMetadata?.type === 'multipart';
1369
+ return {
1370
+ $resourceType: 'tool',
1371
+ name: ctx.name,
1372
+ description: ctx.description,
1373
+ location: 'external',
1374
+ type: 'integration',
1375
+ inputSchema,
1376
+ outputSchema,
1377
+ settings: {},
1378
+ properties: {
1379
+ toolPath: rc.endpoint ?? '',
1380
+ objectName: rc.objectName ?? '',
1381
+ toolDisplayName: ctx.name,
1382
+ toolDescription: ctx.description,
1383
+ // The operation KEY (`GETBYID` for a get-by-id), not the transport verb:
1384
+ // the runtime sends it to Integration Service to pick the operation.
1385
+ method: rc.httpMethod,
1386
+ connection: {
1387
+ id: detail.connectionId ?? '',
1388
+ name: rc.connectorName,
1389
+ elementInstanceId: 0,
1390
+ apiBaseUri: '',
1391
+ state: 'enabled',
1392
+ isDefault: false,
1393
+ connector: { key: rc.connectorKey, name: rc.connectorName, image: '' },
1394
+ folder: { key: detail.connectionFolderKey ?? NO_FOLDER_KEY, path: '' },
1395
+ },
1396
+ parameters: fields.map((f, i) => ({
1397
+ name: f.name,
1398
+ displayName: f.displayName ?? f.name,
1399
+ type: f.type,
1400
+ fieldLocation: f.fieldLocation,
1401
+ value: f.value,
1402
+ // uipcli's fallback: an undescribed agent-decided field keeps the chip's own text.
1403
+ description: f.description || (f.isPrompt ? chipText(f.value) : ''),
1404
+ position: f.required ? 'primary' : 'secondary',
1405
+ sortOrder: i + 1,
1406
+ required: f.required,
1407
+ fieldVariant: f.isPrompt ? 'dynamic' : 'static',
1408
+ isCascading: false,
1409
+ dynamic: f.isPrompt,
1410
+ enumValues: f.enumValues ?? null,
1411
+ loadReferenceOptionsByDefault: null,
1412
+ dynamicBehavior: [],
1413
+ reference: null,
1414
+ })),
1415
+ bodyStructure: multipart
1416
+ ? { contentType: 'multipart', jsonBodySection: detail.inputMetadata.multipart?.bodyFieldName ?? 'body' }
1417
+ : { contentType: 'json' },
1418
+ },
1419
+ guardrail: { policies: [] },
1420
+ iconUrl: ctx.iconUrl,
1421
+ id,
1422
+ isEnabled: true,
1423
+ };
1424
+ }
1205
1425
  /**
1206
1426
  * The tool kinds that wrap a DEPLOYED tenant resource, and everything that
1207
1427
  * differs between them. Transcribed from `registry get` over one live example
@@ -4712,6 +4932,7 @@ export function serialize(built, opts = {}) {
4712
4932
  let tdef;
4713
4933
  let label;
4714
4934
  let extraInputs = {};
4935
+ let connectorTool;
4715
4936
  if (tr.kind === 'builtin') {
4716
4937
  tdef = clone(BUILTIN_TOOL_DEFS[tr.tool]);
4717
4938
  if (!tdef)
@@ -4754,10 +4975,33 @@ export function serialize(built, opts = {}) {
4754
4975
  const rc = opts.library.resolve(tr.connector, tr.operation, tr.version, tr.object);
4755
4976
  tdef = connectorToolDef(rc);
4756
4977
  label = tr.name ?? rc.v1def?.display?.label ?? tr.operation;
4757
- // The connector tool's configuration (connection, folder) is bound at
4758
- // the agent/solution layer, not on the node: the deployed corpus
4759
- // carries `inputs: {}` and a top-level `bindings: []` even for a
4760
- // connector tool, so nothing is emitted here beyond the identity.
4978
+ // The connection rides the same three places it does for a
4979
+ // `connector()` step, because the platform reads it from the same
4980
+ // three (#867):
4981
+ // * the node's `inputs.detail` — what `uip maestro flow node configure`
4982
+ // writes on a `tool.connector.*` node, what `uip agent refresh
4983
+ // --inline-in-flow` builds the tool's resource.json FROM, and what
4984
+ // its shell-ify pass keeps ("where a connector tool's request config
4985
+ // lives"). It is also what `bindings_v2.json` is derived from.
4986
+ // * the top-level `bindings[]` pair the def's `model.context`
4987
+ // placeholders (`<bindings.… connection>` / `<bindings.FolderKey>`)
4988
+ // name.
4989
+ // * the agent's `resources/<id>/resource.json` (below).
4990
+ // Emitting none of them left the tool with no connection anywhere.
4991
+ const where = `inline agent "${step.name}"'s connector tool "${tr.connector}/${tr.operation}"`;
4992
+ const target = { key: tr.connector, action: tr.operation };
4993
+ const conn = resolveBinding(opts, tr.connection, 'connection', where, target);
4994
+ const folder = resolveBinding(opts, tr.folder, 'folder', where, target);
4995
+ const { entries: connBindings } = connectionResourceBindings({ def: rc.v1def, connectorKey: rc.connectorKey, connectionId: conn, folderKey: folder }, resourceBindings);
4996
+ for (const b of connBindings)
4997
+ resourceBindings.set(b.id, b);
4998
+ const detail = connectorToolDetail(rc, conn, folder, `${step.name}Tool`);
4999
+ extraInputs = { detail };
5000
+ connectorTool = {
5001
+ rc, detail, name: label,
5002
+ description: tr.description ?? connectorToolDescription(rc),
5003
+ iconUrl: String(tdef.display?.icon ?? ''),
5004
+ };
4761
5005
  }
4762
5006
  else if (tr.kind === 'mcp') {
4763
5007
  // Per-instance def minted like the ixp family's; the node carries the
@@ -4837,6 +5081,10 @@ export function serialize(built, opts = {}) {
4837
5081
  const tid = uid(scope, `${step.name}Tool`);
4838
5082
  const tnode = makeNode(tdef.nodeType, tid, label, { parentId, def: tdef });
4839
5083
  tnode.display = { ...tnode.display, shape: 'circle' };
5084
+ // `uip agent refresh` reads the tool's description off the node's display
5085
+ // first, so the node carries the one the resource.json does.
5086
+ if (connectorTool)
5087
+ tnode.display.description = connectorTool.description;
4840
5088
  // The resource model lives on the DEFINITION only (CX's rule for
4841
5089
  // context, and what the deployed corpus carries for tools).
4842
5090
  delete tnode.model;
@@ -4855,8 +5103,10 @@ export function serialize(built, opts = {}) {
4855
5103
  addEdge(scope, { node: id, port: 'tool' }, tid, 'input');
4856
5104
  // The other half, for the kinds whose file is DERIVABLE — see
4857
5105
  // `toolResourceJson` for which are not, and why each is left to the
4858
- // generator or the designer rather than guessed.
4859
- const toolResource = toolResourceJson(tr, { id: toolResourceId });
5106
+ // designer rather than guessed.
5107
+ const toolResource = connectorTool
5108
+ ? connectorToolResourceJson(connectorTool, toolResourceId)
5109
+ : toolResourceJson(tr, { id: toolResourceId });
4860
5110
  if (toolResource !== undefined) {
4861
5111
  sidecars.push({
4862
5112
  path: `${source}/resources/${toolResourceId}/resource.json`,
package/lib/README.md CHANGED
@@ -90,6 +90,7 @@ points at the materialized `library-json/`. Until you unpack, only
90
90
  | `uipath-atlassian-jira` | `create-issue` | `fields.project.key`, `fields.summary` |
91
91
  | `uipath-salesforce-slack` | `send-message-to-user` | `channel`, `messageToSend` |
92
92
  | `uipath-microsoft-outlook365` | `send-email` | `message.toRecipients` |
93
+ | `uipath-uipath-airdk` | `web-search` | `query` — the inline-agent connector-tool fixture (#867) |
93
94
 
94
95
  ## Refreshing the shipped library (needs an authenticated `uip`)
95
96
 
@@ -163,6 +163,15 @@
163
163
  "objectName": "send_message_to_user_v2",
164
164
  "path": "uipath-salesforce-slack/send-message-to-user@1.0.0.json"
165
165
  },
166
+ {
167
+ "nodeType": "uipath.connector.uipath-uipath-airdk.web-search",
168
+ "version": "1.0.0",
169
+ "connectorKey": "uipath-uipath-airdk",
170
+ "label": "Web Search",
171
+ "activityType": "Curated",
172
+ "objectName": "v2::webSearch",
173
+ "path": "uipath-uipath-airdk/web-search@1.0.0.json"
174
+ },
166
175
  {
167
176
  "nodeType": "uipath.connector.uipath-uipath-dataservice.query-entity-records",
168
177
  "version": "1.0.0",
@@ -0,0 +1,118 @@
1
+ {
2
+ "schemaVersion": "1",
3
+ "nodeType": "uipath.connector.uipath-uipath-airdk.web-search",
4
+ "version": "1.0.0",
5
+ "category": "connector.196640",
6
+ "tags": [
7
+ "connector",
8
+ "activity"
9
+ ],
10
+ "connector": {
11
+ "key": "uipath-uipath-airdk",
12
+ "name": "UiPath GenAI Activities"
13
+ },
14
+ "operation": {
15
+ "name": "Create",
16
+ "objectName": "v2::webSearch",
17
+ "objectDisplayName": "Web Search",
18
+ "httpMethod": "POST",
19
+ "path": "/v2/webSearch",
20
+ "subType": "standard",
21
+ "supportsStreaming": false,
22
+ "activityType": "Curated",
23
+ "pathTemplate": "/v2/webSearch"
24
+ },
25
+ "display": {
26
+ "label": "Web Search",
27
+ "description": "(UiPath GenAI Activities) Web search executes a search of the public domain using a natural language search query. The search results are available in the output. Web search offers near-real time search results, but certain time sensitive data like stock prices or weather will not consistently be current. There could be a few days lag. Be sure to pass in date/time and/or location as arguments in the query if required.",
28
+ "icon": "https://alpha.uipath.com/bc2ddac5-57bc-40e6-93fe-3b319b60ce36/studio_/typecache/icons/d93eefff0af32b964a4ce6fe3823de1d4c7ed71962c6f878520b57ebf8f4497a.svg",
29
+ "iconBackground": "linear-gradient(225deg, #FAFAFB 0%, #ECEDEF 100%)",
30
+ "iconBackgroundDark": "linear-gradient(225deg, #526069 0%, rgba(50, 60, 66, 0.6) 100%)",
31
+ "operationLabel": "Web Search"
32
+ },
33
+ "runtime": {
34
+ "bpmnType": "bpmn:SendTask",
35
+ "serviceType": "Intsvc.ActivityExecution",
36
+ "activityConfigurationVersion": "v1",
37
+ "requiresConnection": true,
38
+ "requiresFolderKey": true,
39
+ "requiresConnectionForSchema": false
40
+ },
41
+ "inputSchema": {
42
+ "fields": [
43
+ {
44
+ "name": "provider",
45
+ "type": "string",
46
+ "displayName": "Search Engine",
47
+ "description": "The search engine to use",
48
+ "enum": [
49
+ "GoogleCustomSearch"
50
+ ],
51
+ "required": true
52
+ },
53
+ {
54
+ "name": "query",
55
+ "type": "string",
56
+ "displayName": "Search",
57
+ "description": "The natural language query to search the web for",
58
+ "required": true
59
+ },
60
+ {
61
+ "name": "num",
62
+ "type": "integer",
63
+ "displayName": "Number of results",
64
+ "description": "The number of results. Default to 10.",
65
+ "required": false
66
+ },
67
+ {
68
+ "name": "start",
69
+ "type": "integer",
70
+ "displayName": "Start",
71
+ "description": "First result index (pagination).",
72
+ "required": false
73
+ },
74
+ {
75
+ "name": "siteSearch",
76
+ "type": "string",
77
+ "displayName": "Site Search",
78
+ "description": "Restrict or exclude results for a domain.",
79
+ "required": false
80
+ },
81
+ {
82
+ "name": "siteSearchFilter",
83
+ "type": "string",
84
+ "displayName": "Site Search Filter",
85
+ "description": "Either \"i\" (include) or \"e\" (exclude).",
86
+ "required": false
87
+ }
88
+ ]
89
+ },
90
+ "outputSchema": {
91
+ "fields": [
92
+ {
93
+ "name": "formattedResults",
94
+ "type": "string",
95
+ "displayName": "FormattedResults",
96
+ "description": "The search results string containing title, URL, and snippet"
97
+ },
98
+ {
99
+ "name": "results[*].title",
100
+ "type": "string",
101
+ "displayName": "Result title",
102
+ "description": "The title of a search result item."
103
+ },
104
+ {
105
+ "name": "results[*].snippet",
106
+ "type": "string",
107
+ "displayName": "Result snippet",
108
+ "description": "A brief summary or excerpt of a search result item."
109
+ },
110
+ {
111
+ "name": "results[*].url",
112
+ "type": "string",
113
+ "displayName": "Result url",
114
+ "description": "The web address of the search result."
115
+ }
116
+ ]
117
+ }
118
+ }