@uipath/maestro-builder-sdk 6.16.2 → 6.16.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/check.js CHANGED
@@ -50,9 +50,9 @@ function checkFlow(built, ancestors, opts = {}) {
50
50
  checkTrigger(built.trigger, diags, opts);
51
51
  if (built.trigger?.kind === 'form' && built.inputs.length === 0) {
52
52
  diags.push({
53
- level: 'warning', code: 'FORM_TRIGGER_NO_INPUTS', step: 'trigger',
54
- message: `formTrigger() renders one field per flow input, and this flow declares none — the person `
55
- + `gets an empty form with only a submit button.`,
53
+ level: 'error', code: 'FORM_TRIGGER_NO_INPUTS', step: 'trigger',
54
+ message: `formTrigger() renders one field per flow input, and this flow declares none. `
55
+ + `Publish skips a form with no fields, so the flow would ship with no form.`,
56
56
  suggestion: `.input({ amount: types.number })`,
57
57
  });
58
58
  }
@@ -857,7 +857,22 @@ function checkFlow(built, ancestors, opts = {}) {
857
857
  }
858
858
  }
859
859
  else {
860
- checkTrigger(ep.trigger, diags, opts, `onEvent() entry point "${ep.id}"`);
860
+ checkTrigger(ep.trigger, diags, opts, ep.id);
861
+ }
862
+ if (ep.trigger.kind === 'form' && ep.inputs.length === 0) {
863
+ diags.push({
864
+ level: 'error', code: 'FORM_TRIGGER_NO_INPUTS', step: ep.id,
865
+ message: `.entryPoint("${ep.id}", formTrigger()) renders one field per entry-point input, and it declares none. `
866
+ + `Publish skips a form with no fields, so this root would ship with no form.`,
867
+ suggestion: `.entryPoint("${ep.id}", formTrigger(), { inputs: { amount: types.number } })`,
868
+ });
869
+ }
870
+ if ((ep.trigger.kind === 'conversation' || ep.trigger.kind === 'voice') && ep.inputs.length) {
871
+ diags.push({
872
+ level: 'warning', code: 'TRIGGER_INPUTS_IGNORED', step: ep.id,
873
+ message: `.entryPoint("${ep.id}") declares inputs, but a ${ep.trigger.kind} trigger starts with only what the platform `
874
+ + `supplies (${ep.trigger.kind === 'voice' ? 'callContext' : 'conversationId'}). Its inputs are never passed.`,
875
+ });
861
876
  }
862
877
  for (const inp of ep.inputs) {
863
878
  const collides = built.inputs.some((g) => g.name === inp.name)
@@ -879,6 +894,17 @@ function checkFlow(built, ancestors, opts = {}) {
879
894
  message: 'Entry points join the shared body, but this flow has no shared step. Add one, or end each prefix terminally.',
880
895
  });
881
896
  }
897
+ const conversationRoots = [
898
+ ...(built.trigger?.kind === 'conversation' ? [triggerId] : []),
899
+ ...(built.entryPoints ?? []).filter((ep) => ep.trigger.kind === 'conversation').map((ep) => ep.id),
900
+ ];
901
+ if (conversationRoots.length > 1) {
902
+ diags.push({
903
+ level: 'error', code: 'SINGLE_CONVERSATION_TRIGGER', step: conversationRoots[1],
904
+ message: `Only one conversationTrigger() is allowed per flow, and ${conversationRoots.length} roots use one: `
905
+ + `${conversationRoots.map((id) => `"${id}"`).join(', ')}. Keep one and give the others another trigger.`,
906
+ });
907
+ }
882
908
  checkStepRefs(built, diags);
883
909
  checkArmScopedReads(built, diags);
884
910
  // A fire-and-forget dispatch publishes no output: any read of it would wait
@@ -1731,9 +1757,11 @@ function checkNodeUpdates(built, diags) {
1731
1757
  };
1732
1758
  visit(built.steps);
1733
1759
  }
1734
- function checkTrigger(spec, diags, opts = {}, where) {
1760
+ function checkTrigger(spec, diags, opts = {}, entryPointId) {
1761
+ const where = entryPointId === undefined ? undefined : `onEvent() entry point "${entryPointId}"`;
1762
+ const call = entryPointId === undefined ? '.trigger()' : `.entryPoint("${entryPointId}")`;
1735
1763
  const push = (level) => (code, message, suggestion) => {
1736
- diags.push({ level, code, message, step: 'trigger', ...(suggestion ? { suggestion } : {}) });
1764
+ diags.push({ level, code, message, step: entryPointId ?? 'trigger', ...(suggestion ? { suggestion } : {}) });
1737
1765
  };
1738
1766
  const at = push('error');
1739
1767
  // Types are stripped at run time, so `.trigger({ every: 'R/PT1H' })` (the spec
@@ -1758,7 +1786,7 @@ function checkTrigger(spec, diags, opts = {}, where) {
1758
1786
  if (spec?.kind === 'conversation' || spec?.kind === 'voice')
1759
1787
  return;
1760
1788
  if (spec?.kind !== 'scheduled') {
1761
- at('TRIGGER_UNKNOWN_KIND', `.trigger() was given ${JSON.stringify(spec?.kind)}, which is not a trigger. `
1789
+ at('TRIGGER_UNKNOWN_KIND', `${call} was given ${JSON.stringify(spec?.kind)}, which is not a trigger. `
1762
1790
  + `Wrap it in a factory: scheduled({ every: 'R/PT1H' }) for a schedule, `
1763
1791
  + `onEvent({ connector, event, where, connection, folder }) to start on a connector event, formTrigger() to start from a `
1764
1792
  + `submitted form, or conversationTrigger() to start on a new conversation. Omit .trigger() entirely `
@@ -1407,20 +1407,19 @@ export interface InlineAgentInputs {
1407
1407
  * ```
1408
1408
  *
1409
1409
  * Why a placeholder rather than an interpolated expression: an inline agent is two
1410
- * artifacts, and its prompt exists in THREE dialects. You write one; the compiler
1411
- * emits the other two.
1410
+ * artifacts, and its prompt exists in TWO dialects. You write one; the compiler
1411
+ * emits the other into the `agent.json` sidecar. The `.flow` node carries no copy
1412
+ * of the prompt: it is a shell, so Studio Web reads the sidecar.
1412
1413
  *
1413
1414
  * 1. yours `{{input.claim}}`
1414
- * 2. the NODE's `=js:$vars.prepare.output.claimRef` ← what the platform SCANS
1415
- * 3. the AGENT.JSON `{{input.prepare__output__claimRef}}` ← what the runtime SENDS
1416
- *
1417
- * The third exists because the platform does not send an agent the names you
1418
- * declared: it scans the node's prompts for `$vars.*` references and names each
1419
- * argument after the reference PATH, `__`-joined. For a flow input the two
1420
- * coincide (`$vars.notes` → `notes`); for a step output they do not, and a sidecar
1421
- * still saying `{{input.claim}}` templates against a key that is not there — the
1422
- * literal text reaches the model. See the inline-agent node reference (`website/reference/inline-agent.md`)
1423
- * §"The three prompt DIALECTS".
1415
+ * 2. the AGENT.JSON `{{input.prepare__output__claimRef}}` ← what the runtime SENDS
1416
+ *
1417
+ * The second exists because the platform does not send an agent the names you
1418
+ * declared: it names each argument after the bound reference PATH, `__`-joined.
1419
+ * For a flow input the two coincide (`$vars.notes` → `notes`); for a step output
1420
+ * they do not, and a sidecar still saying `{{input.claim}}` templates against a key
1421
+ * that is not there — the literal text reaches the model. See the inline-agent
1422
+ * node reference (`website/reference/inline-agent.md`) §"The prompt dialects".
1424
1423
  *
1425
1424
  * **The answer's shape is `returns`, not the prompt.** `returns` becomes the node's
1426
1425
  * `agentOutputVariables` and the `agent.json` `outputSchema` — what Studio Web's
@@ -19,7 +19,7 @@
19
19
  */
20
20
  import { reportResult } from './cli-result.js';
21
21
  import { mkdirSync, readFileSync, writeFileSync } from 'node:fs';
22
- import { basename, join, resolve } from 'node:path';
22
+ import { basename, dirname, join, resolve } from 'node:path';
23
23
  import { defaultSourceOut, workDirBeside, writeSource } from './workdir.js';
24
24
  import { decompile } from './decompile.js';
25
25
  import { resolveFlowRefs } from './ref-resolve.js';
@@ -65,6 +65,15 @@ export function run(argv) {
65
65
  // writable band) go to stderr, where the agent running the brownfield loop
66
66
  // reads them before compile refuses.
67
67
  warn: (message) => console.error(`flow-decompile: warning: ${message}`),
68
+ // A shell agent node's prompts live in `<source>/agent.json` beside the .flow.
69
+ readSidecar: (relativePath) => {
70
+ try {
71
+ return readFileSync(join(dirname(resolve(pos)), relativePath), 'utf8');
72
+ }
73
+ catch {
74
+ return undefined;
75
+ }
76
+ },
68
77
  });
69
78
  const out = opt(argv, '-o') ?? defaultSourceOut(`${baseName(flow)}.flow.ts`);
70
79
  writeSource(out, ts);
@@ -133,6 +133,17 @@ export interface DecompileOptions {
133
133
  * `compile` will refuse it). The CLI prints these on stderr.
134
134
  */
135
135
  warn?: (message: string) => void;
136
+ /**
137
+ * Reads a file of the flow's project, by path relative to the `.flow`'s
138
+ * directory; returns `undefined` when it is absent. Used for an agent node
139
+ * that is a SHELL (no prompts on the node, the shape compile writes since
140
+ * #927): its prompts, model and voice settings are read back from
141
+ * `<source>/agent.json`. The sidecar speaks flat input names
142
+ * (`{{input.start__output__notes}}`), so the recovered source declares its
143
+ * inputs under those names — the same flow, not the same spelling. Without a
144
+ * reader, a shell agent decompiles with empty prompts and a warning.
145
+ */
146
+ readSidecar?: (relativePath: string) => string | undefined;
136
147
  }
137
148
  export declare function decompile(flow: FlowFile, options?: DecompileOptions): string;
138
149
  export {};
package/dist/decompile.js CHANGED
@@ -79,6 +79,8 @@ let runStrict = false;
79
79
  /** `DecompileOptions.warn` for the current run, and the messages already sent to it (one line per finding). */
80
80
  let runWarn;
81
81
  let runWarned = new Set();
82
+ /** `DecompileOptions.readSidecar` for the current run. */
83
+ let runReadSidecar;
82
84
  function warnOnce(message) {
83
85
  if (runWarned.has(message))
84
86
  return;
@@ -1037,8 +1039,53 @@ function connectorObjectName(configuration) {
1037
1039
  * arguments reach the model, spelled the platform's way. (Node ids are
1038
1040
  * preserved exactly; only these logical names change.)
1039
1041
  */
1042
+ /**
1043
+ * The agent configuration a SHELL agent node keeps in its sidecar: the prompts,
1044
+ * the model and (voice) the `voice` settings, read from `<source>/agent.json`
1045
+ * in the node's own input names. Empty when the node carries prompts itself (an
1046
+ * embedded node, e.g. a `.flow` saved by Studio Web with the self-contained
1047
+ * flag on), or when no sidecar can be read.
1048
+ */
1049
+ function sidecarAgentInputs(node) {
1050
+ const inputs = (node.inputs ?? {});
1051
+ if (typeof inputs.systemPrompt === 'string' || typeof inputs.userPrompt === 'string')
1052
+ return {};
1053
+ const source = unwrapSource(inputs.source);
1054
+ if (!source)
1055
+ return {};
1056
+ const path = `${source}/agent.json`;
1057
+ const raw = runReadSidecar?.(path);
1058
+ if (raw === undefined) {
1059
+ warnOnce(`agent step "${node.id}" carries no prompts and ${path} was not found beside the .flow, `
1060
+ + 'so its prompts and model decompile empty. Decompile the .flow from inside its project directory.');
1061
+ return {};
1062
+ }
1063
+ let agent;
1064
+ try {
1065
+ agent = JSON.parse(raw);
1066
+ }
1067
+ catch {
1068
+ warnOnce(`agent step "${node.id}": ${path} is not valid JSON, so its prompts and model decompile empty.`);
1069
+ return {};
1070
+ }
1071
+ const messages = (Array.isArray(agent?.messages) ? agent.messages : []);
1072
+ const content = (role) => {
1073
+ const c = messages.find((m) => m.role === role)?.content;
1074
+ return typeof c === 'string' ? c : undefined;
1075
+ };
1076
+ const settings = (agent?.settings ?? {});
1077
+ const system = content('system');
1078
+ const user = content('user');
1079
+ return {
1080
+ ...(system === undefined ? {} : { systemPrompt: system }),
1081
+ ...(user === undefined ? {} : { userPrompt: user }),
1082
+ ...(typeof settings.model === 'string' ? { model: settings.model } : {}),
1083
+ ...(node.type === T.voiceAgent && settings.voice && typeof settings.voice === 'object' ? { voice: settings.voice } : {}),
1084
+ };
1085
+ }
1040
1086
  function emitAgentCluster(node, imp, inputNames, graph) {
1041
- const i = (node.inputs ?? {});
1087
+ // A key on the node wins over the sidecar's copy of it.
1088
+ const i = { ...sidecarAgentInputs(node), ...(node.inputs ?? {}) };
1042
1089
  const plain = (v) => {
1043
1090
  const raw = unwrapSource(v);
1044
1091
  return raw === undefined || raw === '' ? undefined : raw;
@@ -2616,6 +2663,7 @@ export function decompile(flow, options = {}) {
2616
2663
  const importSpecifier = options.importSpecifier ?? './flow-sdk.js';
2617
2664
  runStrict = options.bestEffort === false;
2618
2665
  runWarn = options.warn;
2666
+ runReadSidecar = options.readSidecar;
2619
2667
  runWarned = new Set();
2620
2668
  runDefPairs = new Set((flow.definitions ?? [])
2621
2669
  .filter((d) => typeof d?.nodeType === 'string' && typeof d?.version === 'string')
@@ -661,7 +661,7 @@ export interface FlowLayout {
661
661
  export interface BuiltEntryPoint {
662
662
  /** The trigger node's id — also the root expressions read (`$vars.<id>.output.x`). */
663
663
  id: string;
664
- /** What fires this root: `manual()`, `scheduled(...)`, or `onEvent(...)`. */
664
+ /** What fires this root: any built-in trigger factory, or a custom trigger. */
665
665
  trigger: TriggerSpec;
666
666
  /** Node options for the trigger node — exact `version` selection. */
667
667
  options?: NodeOptions;
@@ -1278,8 +1278,9 @@ declare class FlowBuilder extends StepList {
1278
1278
  *
1279
1279
  * @param id - The root's trigger-node id — unique among entry points, steps,
1280
1280
  * and the default trigger id. Expressions read it as `$vars.<id>.output.*`.
1281
- * @param trigger - What fires this root: `manual()`, `scheduled(...)`, or
1282
- * `onEvent(...)`.
1281
+ * @param trigger - What fires this root: `manual()`, `scheduled(...)`,
1282
+ * `onEvent(...)`, `formTrigger()` (one field per scoped input),
1283
+ * `conversationTrigger()`, or `voiceTrigger()`.
1283
1284
  * @param options - `version` (exact trigger definition version) and `inputs`
1284
1285
  * — this root's scoped inputs, declared like `.input()`.
1285
1286
  * @param prefixFn - Optional sub-builder for the root's prefix steps.
package/dist/flow-sdk.js CHANGED
@@ -993,8 +993,9 @@ class FlowBuilder extends StepList {
993
993
  *
994
994
  * @param id - The root's trigger-node id — unique among entry points, steps,
995
995
  * and the default trigger id. Expressions read it as `$vars.<id>.output.*`.
996
- * @param trigger - What fires this root: `manual()`, `scheduled(...)`, or
997
- * `onEvent(...)`.
996
+ * @param trigger - What fires this root: `manual()`, `scheduled(...)`,
997
+ * `onEvent(...)`, `formTrigger()` (one field per scoped input),
998
+ * `conversationTrigger()`, or `voiceTrigger()`.
998
999
  * @param options - `version` (exact trigger definition version) and `inputs`
999
1000
  * — this root's scoped inputs, declared like `.input()`.
1000
1001
  * @param prefixFn - Optional sub-builder for the root's prefix steps.
package/dist/serialize.js CHANGED
@@ -2204,10 +2204,9 @@ function inlineAgentDef(spec) {
2204
2204
  /**
2205
2205
  * Classify a RENDERED input value (the output of {@link renderValue}).
2206
2206
  *
2207
- * The `=js:` test is the same one `renderInlineAgentPrompt` uses, deliberately:
2208
- * both halves of the emission must agree about what is an expression, and a
2209
- * literal string that happens to begin with `=js:` is already treated as one
2210
- * there.
2207
+ * A rendered value is an expression exactly when it starts with `=js:` — the
2208
+ * same test the rest of the serializer applies, so a literal string that
2209
+ * happens to begin with `=js:` is treated as one everywhere.
2211
2210
  */
2212
2211
  function classifyAgentInput(rendered) {
2213
2212
  if (typeof rendered !== 'string' || !rendered.startsWith('=js:')) {
@@ -2471,40 +2470,6 @@ function promptTokens(content) {
2471
2470
  tokens.push({ type: 'simpleText', rawString: content.slice(last) });
2472
2471
  return tokens.length ? tokens : [{ type: 'simpleText', rawString: content }];
2473
2472
  }
2474
- /**
2475
- * Render an inline agent's prompt for the NODE, which speaks a different dialect
2476
- * from the `agent.json`.
2477
- *
2478
- * The author writes `{{input.<name>}}`, which is what the agent's own template
2479
- * language uses and what goes into `agent.json` verbatim. The node's copy has to
2480
- * carry the FLOW expression instead: flow-v1's `preDeriveAgentInputDefinitions`
2481
- * builds the deployed input list by scanning the node's prompts for `$vars.*`
2482
- * references, and — when its caller prunes — drops any `agentInputVariables` entry no
2483
- * such reference points at. A prompt with no references can therefore deploy an agent
2484
- * that receives nothing, with the authored list sitting in the file unused.
2485
- *
2486
- * So each placeholder becomes the rendered binding of that input, `=js:`-prefixed the
2487
- * way an embedded expression is read. One caveat that comes with that encoding:
2488
- * an embedded run has no terminator, so it is read to
2489
- * end-of-line and then backed off token by token until a prefix evaluates. That
2490
- * resolves `"Notes: =js:$vars.notes and then some"` correctly, but a placeholder
2491
- * followed by more text on the same line is doing more work than one at end of line.
2492
- *
2493
- * A placeholder naming something `inputs` does not declare is left ALONE rather than
2494
- * silently deleted: it reaches the model as literal `{{input.x}}`, which is visible,
2495
- * and `check` names it.
2496
- */
2497
- function renderInlineAgentPrompt(template, inputs, rename) {
2498
- return template.replace(INPUT_PLACEHOLDER, (whole, name) => {
2499
- if (!Object.prototype.hasOwnProperty.call(inputs, name))
2500
- return whole;
2501
- const rendered = renderValue(inputs[name], rename);
2502
- // A literal binding needs no expression at all — inline the value.
2503
- if (typeof rendered === 'string' && rendered.startsWith('=js:'))
2504
- return rendered;
2505
- return String(rendered);
2506
- });
2507
- }
2508
2473
  /**
2509
2474
  * The parts every `uipath.ixp.*` definition shares, bundled verbatim from
2510
2475
  * `uip maestro flow registry get` (2026-07-31, after `registry pull --force`).
@@ -4838,9 +4803,9 @@ export function serialize(built, opts = {}) {
4838
4803
  ? scheduleInputs(built.trigger.inputs, triggerId)
4839
4804
  : built.trigger?.kind === 'form'
4840
4805
  ? {
4841
- // The definition marks the family an entry point (`model.entryPointId:
4842
- // true`); the id is the node's own, the scheduled trigger's convention.
4843
- entryPointId: triggerId,
4806
+ // A GUID: publish keys the form's FormTrigger binding on it and skips
4807
+ // the form when it is not one.
4808
+ entryPointId: stableId(`${built.id}:${triggerId}:entryPoint`),
4844
4809
  schema: { fields: formTriggerFields(built.inputs), outcomes: [] },
4845
4810
  }
4846
4811
  : built.trigger?.kind === 'conversation' || built.trigger?.kind === 'voice'
@@ -5136,14 +5101,18 @@ export function serialize(built, opts = {}) {
5136
5101
  // What the PLATFORM will call each of these — computed once and shared by
5137
5102
  // the descriptor array below and the sidecar. See `agentInputPlan`.
5138
5103
  const plan = agentInputPlan(nodeInputs, scope.rename);
5104
+ // A SHELL node (#927): the prompts and the model live only in the
5105
+ // `<source>/agent.json` sidecar written below, never on the node. Studio
5106
+ // Web decides which copy wins by `hasEmbeddedAgentContent`
5107
+ // (@uipath/flow-schema): any `systemPrompt`/`userPrompt` string on the node
5108
+ // makes the `.flow` authoritative and the sidecar is skipped, so a node copy
5109
+ // shadowed the sidecar and lost the input tokens and the tool configuration
5110
+ // on the first save. With no prompt strings the converter also keeps the
5111
+ // `agentInputVariables` below as written (`canPruneAgentInputs` is false),
5112
+ // and `uip maestro flow debug` reconciles them against the sidecar's
5113
+ // `inputSchema` — the shape the v1 skill and `uip agent refresh` write.
5139
5114
  node.inputs = {
5140
5115
  source,
5141
- // The NODE's dialect: `{{input.x}}` → the flow expression, so flow-v1's
5142
- // input-derivation pre-pass can see the reference. See
5143
- // `renderInlineAgentPrompt`.
5144
- systemPrompt: renderInlineAgentPrompt(s.systemPrompt, nodeInputs, scope.rename),
5145
- userPrompt: renderInlineAgentPrompt(s.userPrompt, nodeInputs, scope.rename),
5146
- model: s.model,
5147
5116
  ...(s.mode === undefined ? {} : { mode: s.mode }),
5148
5117
  ...(s.temperature === undefined ? {} : { temperature: s.temperature }),
5149
5118
  ...(s.maxTokenPerResponse === undefined ? {} : { maxTokenPerResponse: s.maxTokenPerResponse }),
@@ -5154,11 +5123,10 @@ export function serialize(built, opts = {}) {
5154
5123
  // binding expression; `agentOutputVariables` entries are name + type only,
5155
5124
  // and the runtime's structured final call fills exactly these keys.
5156
5125
  //
5157
- // `id` is the FLAT NAME, not the author's — the platform re-derives this
5158
- // array from the node's prompts and keeps a pre-existing entry only when
5159
- // its id already IS a flatName (`deriveAgentInputVariables:506-509`). One
5160
- // entry per flatName, so two logical names bound to the same reference
5161
- // claim one argument rather than two.
5126
+ // `id` is the FLAT NAME, not the author's — the name the runtime's
5127
+ // JobArguments are keyed by, and the key the CLI reconciles against the
5128
+ // sidecar's `inputSchema`. One entry per flatName, so two logical names
5129
+ // bound to the same reference claim one argument rather than two.
5162
5130
  agentInputVariables: plan.refs.map((r) => ({
5163
5131
  id: r.flatName,
5164
5132
  type: 'string',
@@ -5727,10 +5695,11 @@ export function serialize(built, opts = {}) {
5727
5695
  if (v !== undefined)
5728
5696
  settings[key] = v;
5729
5697
  }
5698
+ // A SHELL node, like the inline agent's (#927): the system prompt and the
5699
+ // model live only in the sidecar, so Studio Web reads the sidecar instead
5700
+ // of letting a node copy shadow it.
5730
5701
  node.inputs = {
5731
5702
  source,
5732
- systemPrompt: s.systemPrompt,
5733
- model: s.model,
5734
5703
  // The family's own flag — every instance of this node IS conversational.
5735
5704
  isConversational: true,
5736
5705
  conversationalAgentSettings: settings,
@@ -5797,15 +5766,18 @@ export function serialize(built, opts = {}) {
5797
5766
  delete node.model;
5798
5767
  const nodeInputs = s.inputs ?? {};
5799
5768
  const plan = agentInputPlan(nodeInputs, scope.rename);
5769
+ // A SHELL node (#927): the system prompt and the `voice` settings live only
5770
+ // in the sidecar's `messages` and `settings.voice`. On a voice node either
5771
+ // one makes Studio Web treat the `.flow` as authoritative and skip the
5772
+ // sidecar (`hasEmbeddedAgentContent`). `uip maestro flow debug` builds the
5773
+ // voice agent from the sidecar (`packageInlineAgents`).
5800
5774
  node.inputs = {
5801
5775
  source,
5802
- systemPrompt: renderInlineAgentPrompt(s.systemPrompt, nodeInputs, scope.rename),
5803
5776
  callContext: renderValue(toExpr(s.callContext), scope.rename),
5804
5777
  // The platform's own flag for this family — every voice agent IS
5805
5778
  // conversational, and the definition declares the field.
5806
5779
  isConversational: true,
5807
5780
  ...(s.maxIterations === undefined ? {} : { maxIterations: s.maxIterations }),
5808
- ...(s.voice === undefined ? {} : { voice: { ...s.voice } }),
5809
5781
  ...(plan.refs.length === 0 ? {} : {
5810
5782
  agentInputVariables: plan.refs.map((r) => ({
5811
5783
  id: r.flatName,
@@ -6842,10 +6814,28 @@ export function serialize(built, opts = {}) {
6842
6814
  ...scheduleInputs(epSpec.inputs, ep.id),
6843
6815
  };
6844
6816
  }
6845
- else {
6817
+ else if (epSpec.kind === 'form') {
6818
+ node = makeNode(NODE_TYPE.formTrigger, ep.id, 'Form trigger', { requestedVersion });
6819
+ node.inputs = {
6820
+ entryPointId: stableId(`${built.id}:${ep.id}:entryPoint`),
6821
+ schema: { fields: formTriggerFields(ep.inputs), outcomes: [] },
6822
+ };
6823
+ }
6824
+ else if (epSpec.kind === 'conversation') {
6825
+ node = makeNode(NODE_TYPE.conversationTrigger, ep.id, 'Conversation trigger', { requestedVersion });
6826
+ node.inputs = { entryPointId: stableId(`${built.id}:${ep.id}:entryPoint`) };
6827
+ }
6828
+ else if (epSpec.kind === 'voice') {
6829
+ node = makeNode(NODE_TYPE.voiceTrigger, ep.id, 'Incoming call trigger', { requestedVersion });
6830
+ node.inputs = { entryPointId: stableId(`${built.id}:${ep.id}:entryPoint`) };
6831
+ }
6832
+ else if (epSpec.kind === 'manual') {
6846
6833
  node = makeNode(NODE_TYPE.trigger, ep.id, 'Manual trigger', { requestedVersion });
6847
6834
  node.inputs = { entryPointId: stableId(`${built.id}:${ep.id}:entryPoint`) };
6848
6835
  }
6836
+ else {
6837
+ throw new Error(`.entryPoint("${ep.id}"): unsupported trigger kind ${JSON.stringify(epSpec.kind)}.`);
6838
+ }
6849
6839
  root.nodes.push(node);
6850
6840
  const prefixTails = emit(ep.steps, [{ node: ep.id, port: 'output' }], root);
6851
6841
  for (const t of prefixTails) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@uipath/maestro-builder-sdk",
3
- "version": "6.16.2",
3
+ "version": "6.16.4",
4
4
  "description": "Build UiPath Flow, Case, and BPMN artifacts by writing TypeScript.",
5
5
  "license": "MIT",
6
6
  "homepage": "https://docs.uipath.com/maestro",
@@ -92,5 +92,5 @@
92
92
  "@types/node": "^22.7.0",
93
93
  "esbuild": "^0.28.1"
94
94
  },
95
- "gitref": "f8da41b16f3176129d44eeb3dbd1f6c8be1ecfef"
95
+ "gitref": "eac0393e29eed1fe0992c8923b88964e50483eaf"
96
96
  }