@nocobase/plugin-flow-engine 2.1.0-alpha.40 → 2.1.0-alpha.46

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 (105) hide show
  1. package/dist/client/index.js +1 -1
  2. package/dist/externalVersion.js +9 -9
  3. package/dist/node_modules/@ant-design/icons-svg/package.json +1 -1
  4. package/dist/node_modules/acorn/package.json +1 -1
  5. package/dist/node_modules/acorn-jsx/package.json +1 -1
  6. package/dist/node_modules/acorn-walk/package.json +1 -1
  7. package/dist/node_modules/ses/package.json +1 -1
  8. package/dist/node_modules/zod/package.json +1 -1
  9. package/dist/server/flow-surfaces/apply/compiler.js +28 -14
  10. package/dist/server/flow-surfaces/apply/matching.js +2 -0
  11. package/dist/server/flow-surfaces/authoring-validation.d.ts +1 -0
  12. package/dist/server/flow-surfaces/authoring-validation.js +1453 -151
  13. package/dist/server/flow-surfaces/blueprint/compile-blocks.js +21 -3
  14. package/dist/server/flow-surfaces/blueprint/compile-plan.js +9 -9
  15. package/dist/server/flow-surfaces/blueprint/normalize-document.js +5 -1
  16. package/dist/server/flow-surfaces/catalog.js +26 -9
  17. package/dist/server/flow-surfaces/chart-config.js +231 -14
  18. package/dist/server/flow-surfaces/compose-compiler.d.ts +2 -0
  19. package/dist/server/flow-surfaces/compose-compiler.js +2 -0
  20. package/dist/server/flow-surfaces/compose-runtime.js +4 -7
  21. package/dist/server/flow-surfaces/configure-options.js +19 -8
  22. package/dist/server/flow-surfaces/contract-guard.js +40 -6
  23. package/dist/server/flow-surfaces/default-block-actions.js +2 -0
  24. package/dist/server/flow-surfaces/errors.d.ts +15 -0
  25. package/dist/server/flow-surfaces/errors.js +49 -3
  26. package/dist/server/flow-surfaces/event-flow-normalizer.d.ts +19 -0
  27. package/dist/server/flow-surfaces/event-flow-normalizer.js +128 -0
  28. package/dist/server/flow-surfaces/filter-group.d.ts +9 -1
  29. package/dist/server/flow-surfaces/filter-group.js +402 -3
  30. package/dist/server/flow-surfaces/locator.js +16 -2
  31. package/dist/server/flow-surfaces/public-data-surface-default-filter.js +2 -1
  32. package/dist/server/flow-surfaces/route-sync.js +19 -2
  33. package/dist/server/flow-surfaces/runjs-authoring/ast/bindings.d.ts +66 -0
  34. package/dist/server/flow-surfaces/runjs-authoring/ast/bindings.js +661 -0
  35. package/dist/server/flow-surfaces/runjs-authoring/ast/execution.d.ts +20 -0
  36. package/dist/server/flow-surfaces/runjs-authoring/ast/execution.js +275 -0
  37. package/dist/server/flow-surfaces/runjs-authoring/ast/parser.d.ts +16 -0
  38. package/dist/server/flow-surfaces/runjs-authoring/ast/parser.js +130 -0
  39. package/dist/server/flow-surfaces/runjs-authoring/ast/react-values.d.ts +20 -0
  40. package/dist/server/flow-surfaces/runjs-authoring/ast/react-values.js +401 -0
  41. package/dist/server/flow-surfaces/runjs-authoring/ast/request-config.d.ts +21 -0
  42. package/dist/server/flow-surfaces/runjs-authoring/ast/request-config.js +199 -0
  43. package/dist/server/flow-surfaces/runjs-authoring/ast/source.d.ts +70 -0
  44. package/dist/server/flow-surfaces/runjs-authoring/ast/source.js +895 -0
  45. package/dist/server/flow-surfaces/runjs-authoring/ast/static-bindings.d.ts +23 -0
  46. package/dist/server/flow-surfaces/runjs-authoring/ast/static-bindings.js +618 -0
  47. package/dist/server/flow-surfaces/runjs-authoring/ast/static-values.d.ts +196 -0
  48. package/dist/server/flow-surfaces/runjs-authoring/ast/static-values.js +1777 -0
  49. package/dist/server/flow-surfaces/runjs-authoring/ast/walk.d.ts +10 -0
  50. package/dist/server/flow-surfaces/runjs-authoring/ast/walk.js +55 -0
  51. package/dist/server/flow-surfaces/runjs-authoring/collectors.d.ts +12 -0
  52. package/dist/server/flow-surfaces/runjs-authoring/collectors.js +589 -0
  53. package/dist/server/flow-surfaces/runjs-authoring/ctx-libs-member-mismatch-stop/index.js +1 -1
  54. package/dist/server/flow-surfaces/runjs-authoring/index.d.ts +2 -25
  55. package/dist/server/flow-surfaces/runjs-authoring/index.js +5 -7033
  56. package/dist/server/flow-surfaces/runjs-authoring/inspect.d.ts +13 -0
  57. package/dist/server/flow-surfaces/runjs-authoring/inspect.js +149 -0
  58. package/dist/server/flow-surfaces/runjs-authoring/internal-types.d.ts +333 -0
  59. package/dist/server/flow-surfaces/runjs-authoring/internal-types.js +36 -0
  60. package/dist/server/flow-surfaces/runjs-authoring/rules.js +2 -0
  61. package/dist/server/flow-surfaces/runjs-authoring/runtime/constants.d.ts +67 -0
  62. package/dist/server/flow-surfaces/runjs-authoring/runtime/constants.js +757 -0
  63. package/dist/server/flow-surfaces/runjs-authoring/runtime/errors.d.ts +22 -0
  64. package/dist/server/flow-surfaces/runjs-authoring/runtime/errors.js +91 -0
  65. package/dist/server/flow-surfaces/runjs-authoring/runtime/source-budget.d.ts +16 -0
  66. package/dist/server/flow-surfaces/runjs-authoring/runtime/source-budget.js +115 -0
  67. package/dist/server/flow-surfaces/runjs-authoring/runtime/surface.d.ts +19 -0
  68. package/dist/server/flow-surfaces/runjs-authoring/runtime/surface.js +140 -0
  69. package/dist/server/flow-surfaces/runjs-authoring/runtime/types.d.ts +91 -0
  70. package/dist/server/flow-surfaces/runjs-authoring/runtime/types.js +24 -0
  71. package/dist/server/flow-surfaces/runjs-authoring/scan/ctx-api.d.ts +138 -0
  72. package/dist/server/flow-surfaces/runjs-authoring/scan/ctx-api.js +1779 -0
  73. package/dist/server/flow-surfaces/runjs-authoring/scan/filter.d.ts +10 -0
  74. package/dist/server/flow-surfaces/runjs-authoring/scan/filter.js +1583 -0
  75. package/dist/server/flow-surfaces/runjs-authoring/scan/index.d.ts +195 -0
  76. package/dist/server/flow-surfaces/runjs-authoring/scan/index.js +463 -0
  77. package/dist/server/flow-surfaces/runjs-authoring/scan/react-render.d.ts +48 -0
  78. package/dist/server/flow-surfaces/runjs-authoring/scan/react-render.js +379 -0
  79. package/dist/server/flow-surfaces/runjs-authoring/scan/react.d.ts +26 -0
  80. package/dist/server/flow-surfaces/runjs-authoring/scan/react.js +1441 -0
  81. package/dist/server/flow-surfaces/runjs-authoring/scan/resource.d.ts +23 -0
  82. package/dist/server/flow-surfaces/runjs-authoring/scan/resource.js +1427 -0
  83. package/dist/server/flow-surfaces/runjs-authoring/scan/source-patterns.d.ts +91 -0
  84. package/dist/server/flow-surfaces/runjs-authoring/scan/source-patterns.js +889 -0
  85. package/dist/server/flow-surfaces/runjs-authoring/types.d.ts +1 -1
  86. package/dist/server/flow-surfaces/runjs-authoring/unknown-global-stop/index.d.ts +10 -0
  87. package/dist/server/flow-surfaces/runjs-authoring/unknown-global-stop/index.js +40 -0
  88. package/dist/server/flow-surfaces/runjs-authoring/validators/index.d.ts +12 -0
  89. package/dist/server/flow-surfaces/runjs-authoring/validators/index.js +887 -0
  90. package/dist/server/flow-surfaces/service-helpers.d.ts +29 -0
  91. package/dist/server/flow-surfaces/service-helpers.js +105 -0
  92. package/dist/server/flow-surfaces/service-utils.d.ts +17 -3
  93. package/dist/server/flow-surfaces/service-utils.js +14 -5
  94. package/dist/server/flow-surfaces/service.d.ts +74 -15
  95. package/dist/server/flow-surfaces/service.js +1781 -193
  96. package/dist/server/flow-surfaces/template-service-utils.d.ts +1 -0
  97. package/dist/server/flow-surfaces/types.d.ts +3 -0
  98. package/dist/server/repository.d.ts +12 -1
  99. package/dist/server/repository.js +195 -23
  100. package/dist/swagger/flow-surfaces.d.ts +180 -2
  101. package/dist/swagger/flow-surfaces.examples.d.ts +11 -37
  102. package/dist/swagger/flow-surfaces.examples.js +6 -6
  103. package/dist/swagger/flow-surfaces.js +136 -54
  104. package/dist/swagger/index.d.ts +180 -2
  105. package/package.json +2 -2
@@ -42,6 +42,7 @@ module.exports = __toCommonJS(service_exports);
42
42
  var import_crypto = require("crypto");
43
43
  var import_utils = require("@nocobase/utils");
44
44
  var import_lodash = __toESM(require("lodash"));
45
+ var antDesignIconAsn = __toESM(require("@ant-design/icons-svg"));
45
46
  var import_repository = __toESM(require("../repository"));
46
47
  var import_catalog = require("./catalog");
47
48
  var import_action_scope = require("./action-scope");
@@ -80,6 +81,7 @@ var import_placement = require("./placement");
80
81
  var import_route_sync = require("./route-sync");
81
82
  var import_surface_context = require("./surface-context");
82
83
  var import_context = require("./context");
84
+ var import_event_flow_normalizer = require("./event-flow-normalizer");
83
85
  var import_configure_options = require("./configure-options");
84
86
  var import_public_compatibility = require("./public-compatibility");
85
87
  var import_support_matrix = require("./support-matrix");
@@ -102,6 +104,131 @@ var import_template_service_utils = require("./template-service-utils");
102
104
  var import_hidden_popup_contract = require("./hidden-popup-contract");
103
105
  var import_hidden_popup_calendar = require("./hidden-popup-calendar");
104
106
  var import_hidden_popup_kanban = require("./hidden-popup-kanban");
107
+ const FLOW_SURFACE_CHART_REPAIR_HINT = "This is a chart payload shape problem. Keep using chart and repair the current chart block payload using assets.charts.<key>.query/visual plus block.chart, or localized settings.query/settings.visual. Do not change this block type to table, jsBlock, actionPanel, gridCard, or another block type. Do not drop or defer the chart. KPI / summary numbers should use jsBlock; charts are for trends, distributions, rankings, and visual analysis.";
108
+ const FLOW_SURFACE_CHART_REPAIR_STEPS = [
109
+ "Keep the block type as chart.",
110
+ "Define assets.charts.<key>.query and assets.charts.<key>.visual, or fill localized settings.query and settings.visual on the existing chart block.",
111
+ "Reference the asset from the chart block with block.chart = <key> when using assets.charts.",
112
+ "Retry the chart payload instead of replacing the chart with another block type, omitting it, or deferring it."
113
+ ];
114
+ const FLOW_SURFACE_CHART_EXPECTED_SHAPE = {
115
+ settings: {
116
+ query: {
117
+ mode: "builder",
118
+ resource: {
119
+ dataSourceKey: "main",
120
+ collectionName: "employees"
121
+ },
122
+ measures: [
123
+ {
124
+ field: "id",
125
+ aggregation: "count",
126
+ alias: "employeeCount"
127
+ }
128
+ ]
129
+ },
130
+ visual: {
131
+ mode: "basic",
132
+ type: "bar",
133
+ mappings: {
134
+ x: "status",
135
+ y: "employeeCount"
136
+ }
137
+ }
138
+ },
139
+ assets: {
140
+ charts: {
141
+ chartKey: {
142
+ query: "builder/sql query configuration",
143
+ visual: "basic/custom visual configuration"
144
+ }
145
+ }
146
+ },
147
+ block: {
148
+ type: "chart",
149
+ chart: "chartKey"
150
+ }
151
+ };
152
+ const FLOW_SURFACE_CHART_FORBIDDEN_FALLBACKS = [
153
+ "table",
154
+ "list",
155
+ "jsBlock",
156
+ "actionPanel",
157
+ "gridCard",
158
+ "markdown",
159
+ "drop chart",
160
+ "defer chart"
161
+ ];
162
+ const FLOW_SURFACE_CHART_REPAIR_EXAMPLE = {
163
+ settings: {
164
+ query: {
165
+ mode: "builder",
166
+ resource: {
167
+ dataSourceKey: "main",
168
+ collectionName: "<collectionName>"
169
+ },
170
+ measures: [
171
+ {
172
+ field: "id",
173
+ aggregation: "count",
174
+ alias: "recordCount"
175
+ }
176
+ ],
177
+ dimensions: [
178
+ {
179
+ field: "<dimensionField>"
180
+ }
181
+ ]
182
+ },
183
+ visual: {
184
+ mode: "basic",
185
+ type: "bar",
186
+ mappings: {
187
+ x: "<dimensionField>",
188
+ y: "recordCount"
189
+ }
190
+ }
191
+ }
192
+ };
193
+ function withChartRepairMessage(message) {
194
+ return `${message}. ${FLOW_SURFACE_CHART_REPAIR_HINT}`;
195
+ }
196
+ function withFlowSurfaceChartRepairDetails(details = {}) {
197
+ return {
198
+ ...details,
199
+ requiredBlockType: "chart",
200
+ fixStrategy: "repair_same_block_type",
201
+ repairHint: FLOW_SURFACE_CHART_REPAIR_HINT,
202
+ repairSteps: FLOW_SURFACE_CHART_REPAIR_STEPS,
203
+ expectedShape: FLOW_SURFACE_CHART_EXPECTED_SHAPE,
204
+ repairExample: FLOW_SURFACE_CHART_REPAIR_EXAMPLE,
205
+ forbiddenFallbacks: FLOW_SURFACE_CHART_FORBIDDEN_FALLBACKS
206
+ };
207
+ }
208
+ function throwChartRepairBadRequest(message, options = {}) {
209
+ const details = import_lodash.default.isPlainObject(options.details) ? options.details : {};
210
+ (0, import_errors.throwBadRequest)(withChartRepairMessage(message), {
211
+ ...options,
212
+ details: withFlowSurfaceChartRepairDetails(details)
213
+ });
214
+ }
215
+ function isChartConfigureBadRequestError(error) {
216
+ return error instanceof import_errors.FlowSurfaceBadRequestError && String(error.message || "").startsWith("chart ");
217
+ }
218
+ function buildChartConfigureFromSemanticChangesWithRepair(currentConfigure, changes) {
219
+ try {
220
+ return (0, import_chart_config.buildChartConfigureFromSemanticChanges)(currentConfigure, changes);
221
+ } catch (error) {
222
+ if (isChartConfigureBadRequestError(error)) {
223
+ throwChartRepairBadRequest(error.message, error.options);
224
+ }
225
+ throw error;
226
+ }
227
+ }
228
+ function isFlowSurfaceChartRepairError(error) {
229
+ var _a, _b;
230
+ return error instanceof import_errors.FlowSurfaceBadRequestError && ((_b = (_a = error.options) == null ? void 0 : _a.details) == null ? void 0 : _b.repairHint) === FLOW_SURFACE_CHART_REPAIR_HINT;
231
+ }
105
232
  const FORM_BLOCK_USES = /* @__PURE__ */ new Set(["FormBlockModel", "CreateFormModel", "EditFormModel", ...import_approval.APPROVAL_FORM_BLOCK_USES]);
106
233
  const AUTO_SUBMIT_FORM_BLOCK_USES = /* @__PURE__ */ new Set(["CreateFormModel", "EditFormModel"]);
107
234
  const FLOW_SURFACE_DEFAULT_ACTION_SETTINGS_KEYS = /* @__PURE__ */ new Set(["filter"]);
@@ -251,6 +378,17 @@ const JS_ACTION_USES = /* @__PURE__ */ new Set([
251
378
  "JSActionModel"
252
379
  ]);
253
380
  const JS_ITEM_ACTION_USES = /* @__PURE__ */ new Set(["JSItemActionModel"]);
381
+ const JS_POPUP_GUIDANCE_MESSAGE = "should use ctx.openView to open popup";
382
+ const JS_POPUP_GUIDANCE_USES = /* @__PURE__ */ new Set([
383
+ ...JS_ACTION_USES,
384
+ ...JS_ITEM_ACTION_USES,
385
+ "JSColumnModel",
386
+ "JSItemModel",
387
+ "JSFieldModel",
388
+ "JSEditableFieldModel",
389
+ "FormJSFieldItemModel"
390
+ ]);
391
+ const JS_POPUP_GUIDANCE_PUBLIC_KEYS = /* @__PURE__ */ new Set(["js", "jsColumn", "jsItem"]);
254
392
  const POPUP_ACTION_USES = /* @__PURE__ */ new Set([
255
393
  "AddNewActionModel",
256
394
  "ViewActionModel",
@@ -264,6 +402,24 @@ const POPUP_ACTION_USES = /* @__PURE__ */ new Set([
264
402
  "AddChildActionModel",
265
403
  "MailSendActionModel"
266
404
  ]);
405
+ function withJsPopupGuidance(message, value) {
406
+ const normalizedValue = String(value || "").trim();
407
+ if (JS_POPUP_GUIDANCE_USES.has(normalizedValue) || JS_POPUP_GUIDANCE_PUBLIC_KEYS.has(normalizedValue)) {
408
+ return `${message}; ${JS_POPUP_GUIDANCE_MESSAGE}`;
409
+ }
410
+ return message;
411
+ }
412
+ function assertNoJsDeclarativeOpenView(context, changes, use) {
413
+ const normalizedUse = String(use || "").trim();
414
+ if ((0, import_service_utils.hasOwnDefined)(changes, "openView") && JS_POPUP_GUIDANCE_USES.has(normalizedUse)) {
415
+ (0, import_errors.throwBadRequest)(
416
+ withJsPopupGuidance(
417
+ `flowSurfaces configure ${context} '${normalizedUse}' does not support openView`,
418
+ normalizedUse
419
+ )
420
+ );
421
+ }
422
+ }
267
423
  const POPUP_HOST_DEFAULT_RECORD_CONTEXT_ACTION_USES = /* @__PURE__ */ new Set([
268
424
  "ViewActionModel",
269
425
  "EditActionModel",
@@ -457,16 +613,25 @@ const UPDATE_SETTINGS_STEP_PARAM_MIRRORS_BY_USE = {
457
613
  DividerItemModel: DIVIDER_ITEM_STEP_PARAM_MIRRORS
458
614
  };
459
615
  const FLOW_SURFACE_MENU_BINDABLE_OPTION_KEY = "flowSurfaceMenuBindable";
616
+ const ANT_DESIGN_ICON_NAMES = new Set(Object.keys(antDesignIconAsn || {}));
460
617
  const AI_EMPLOYEE_ACTION_USE = "AIEmployeeButtonModel";
461
618
  const AI_EMPLOYEE_OWNER_PLUGIN = "@nocobase/plugin-ai";
462
619
  const AI_EMPLOYEE_PUBLIC_SETTING_KEYS = ["username", "auto", "workContext", "tasks", "style"];
463
620
  const AI_EMPLOYEE_INTERNAL_PROP_KEYS = ["aiEmployee", "context", "auto", "tasks", "style"];
621
+ const AI_EMPLOYEE_TASK_STEP_PARAMS_PATH = ["shortcutSettings", "editTasks", "tasks"];
464
622
  const AI_EMPLOYEE_WORK_CONTEXT_PUBLIC_KEYS = ["type", "uid", "target"];
465
623
  const AI_EMPLOYEE_TASK_PUBLIC_SETTING_KEYS = ["title", "message", "autoSend", "skillSettings", "model", "webSearch"];
624
+ const AI_EMPLOYEE_TASK_PATCH_PUBLIC_SETTING_KEYS = [...AI_EMPLOYEE_TASK_PUBLIC_SETTING_KEYS, "prompt"];
466
625
  const AI_EMPLOYEE_TASK_MESSAGE_PUBLIC_KEYS = ["system", "user", "workContext"];
467
626
  const AI_EMPLOYEE_TASK_MODEL_PUBLIC_KEYS = ["llmService", "model"];
468
627
  const AI_EMPLOYEE_SKILL_SETTINGS_PUBLIC_KEYS = ["skills", "tools", "skillsVersion", "toolsVersion"];
469
628
  const AI_EMPLOYEE_STYLE_PUBLIC_KEYS = ["size", "mask"];
629
+ const AI_EMPLOYEE_WORK_CONTEXT_REPAIR_HINT = 'Use workContext entries like { "target": "self" } or { "uid": "<flow-model-uid>" }. The only supported type is flow-model, so type is optional and defaults to flow-model.';
630
+ const AI_EMPLOYEE_TASK_REPAIR_HINT = "Use task keys title, message, prompt, autoSend, skillSettings, model, and webSearch. Put user instructions in tasks[n].message.user or the prompt alias; do not write raw props, stepParams, or prompt beside message.user.";
631
+ const AI_EMPLOYEE_PROMPT_CONTEXT_MAX_DEPTH = 8;
632
+ const AI_EMPLOYEE_PROMPT_VARIABLE_INVALID = "FLOW_SURFACE_AI_EMPLOYEE_PROMPT_VARIABLE_INVALID";
633
+ const AI_EMPLOYEE_CURRENT_RECORD_PROMPT_VARIABLE = "{{ ctx.record }}";
634
+ const AI_EMPLOYEE_CURRENT_RECORD_PROMPT_VARIABLE_RE = /\{\{\s*ctx\.record\s*\}\}/;
470
635
  const AI_EMPLOYEE_DEFAULT_STYLE = {
471
636
  size: 40,
472
637
  mask: false
@@ -710,10 +875,16 @@ class FlowSurfacesService {
710
875
  );
711
876
  }
712
877
  async loadEnabledPluginPackages(transaction) {
713
- if (!this.db.getCollection("applicationPlugins")) {
878
+ var _a;
879
+ const db = this.db;
880
+ if (!((_a = db == null ? void 0 : db.getCollection) == null ? void 0 : _a.call(db, "applicationPlugins")) || !(db == null ? void 0 : db.getRepository)) {
881
+ return /* @__PURE__ */ new Set();
882
+ }
883
+ const repository = db.getRepository("applicationPlugins");
884
+ if (!(repository == null ? void 0 : repository.find)) {
714
885
  return /* @__PURE__ */ new Set();
715
886
  }
716
- const plugins = await this.db.getRepository("applicationPlugins").find({
887
+ const plugins = await repository.find({
717
888
  fields: ["packageName"],
718
889
  filter: {
719
890
  enabled: true
@@ -721,8 +892,8 @@ class FlowSurfacesService {
721
892
  transaction
722
893
  });
723
894
  const packageNames = plugins.map((plugin) => {
724
- var _a;
725
- return ((_a = plugin == null ? void 0 : plugin.get) == null ? void 0 : _a.call(plugin, "packageName")) || (plugin == null ? void 0 : plugin.packageName);
895
+ var _a2;
896
+ return ((_a2 = plugin == null ? void 0 : plugin.get) == null ? void 0 : _a2.call(plugin, "packageName")) || (plugin == null ? void 0 : plugin.packageName);
726
897
  }).filter((packageName) => typeof packageName === "string" && !!packageName.trim()).map((packageName) => packageName.trim());
727
898
  return new Set(packageNames);
728
899
  }
@@ -762,6 +933,18 @@ class FlowSurfacesService {
762
933
  })
763
934
  );
764
935
  }
936
+ routeParentIdMatches(routeParentId, parentId) {
937
+ if (import_lodash.default.isNil(routeParentId) && import_lodash.default.isNil(parentId)) {
938
+ return true;
939
+ }
940
+ return String(routeParentId ?? "") === String(parentId ?? "");
941
+ }
942
+ async findMenuGroupRoutesByParentIdAndTitle(parentId, title, transaction) {
943
+ const routes = await this.findMenuGroupRoutesByTitle(title, transaction);
944
+ return routes.filter(
945
+ (route) => this.routeParentIdMatches(this.readRouteField(route, "parentId") ?? null, parentId)
946
+ );
947
+ }
765
948
  async findFlowPageRoutesByParentIdAndTitle(parentId, title, transaction) {
766
949
  const normalizedTitle = String(title || "").trim();
767
950
  const normalizedParentId = String(parentId ?? "").trim();
@@ -843,20 +1026,97 @@ class FlowSurfacesService {
843
1026
  ...extras
844
1027
  };
845
1028
  }
1029
+ isValidMenuIconName(value) {
1030
+ const normalized = String(value || "").trim();
1031
+ return !!normalized && ANT_DESIGN_ICON_NAMES.has(normalized);
1032
+ }
1033
+ assertVisibleNavigationIcon(actionName, path, values) {
1034
+ if (values.hideInMenu === true) {
1035
+ return;
1036
+ }
1037
+ const icon = String(values.icon || "").trim();
1038
+ if (!icon) {
1039
+ (0, import_errors.throwBadRequest)(`flowSurfaces ${actionName} ${path}.icon is required when creating a visible menu route`, {
1040
+ ruleId: "navigation-icon-required",
1041
+ path: `${path}.icon`,
1042
+ details: {
1043
+ repairHint: "Pass a valid Ant Design icon name such as AppstoreOutlined, DashboardOutlined, or FolderOpenOutlined."
1044
+ }
1045
+ });
1046
+ }
1047
+ if (!this.isValidMenuIconName(icon)) {
1048
+ (0, import_errors.throwBadRequest)(`flowSurfaces ${actionName} ${path}.icon must be a valid Ant Design icon name`, {
1049
+ ruleId: "navigation-icon-unknown",
1050
+ path: `${path}.icon`,
1051
+ details: {
1052
+ icon
1053
+ }
1054
+ });
1055
+ }
1056
+ }
1057
+ assertVisibleNavigationRouteIconUpdate(actionName, path, route, values) {
1058
+ if (!Object.prototype.hasOwnProperty.call(values, "icon") || this.readRouteField(route, "hideInMenu") === true) {
1059
+ return;
1060
+ }
1061
+ const icon = String(values.icon || "").trim();
1062
+ if (!icon) {
1063
+ (0, import_errors.throwBadRequest)(`flowSurfaces ${actionName} ${path}.icon cannot be empty for a visible menu route`, {
1064
+ ruleId: "navigation-icon-required",
1065
+ path: `${path}.icon`,
1066
+ details: {
1067
+ repairHint: "Pass a valid Ant Design icon name such as AppstoreOutlined, DashboardOutlined, or FolderOpenOutlined."
1068
+ }
1069
+ });
1070
+ }
1071
+ if (!this.isValidMenuIconName(icon)) {
1072
+ (0, import_errors.throwBadRequest)(`flowSurfaces ${actionName} ${path}.icon must be a valid Ant Design icon name`, {
1073
+ ruleId: "navigation-icon-unknown",
1074
+ path: `${path}.icon`,
1075
+ details: {
1076
+ icon
1077
+ }
1078
+ });
1079
+ }
1080
+ }
846
1081
  async createFlowMenuGroup(values, transaction) {
847
1082
  const parentRoute = await this.assertMenuParentIsGroup(values.parentMenuRouteId, transaction);
1083
+ const parentId = this.readRouteField(parentRoute, "id") ?? null;
1084
+ const title = String(values.title || "").trim();
1085
+ const existingGroups = await this.findMenuGroupRoutesByParentIdAndTitle(parentId, title, transaction);
1086
+ if (existingGroups.length === 1) {
1087
+ return this.buildMenuResult(existingGroups[0]);
1088
+ }
1089
+ if (existingGroups.length > 1) {
1090
+ (0, import_errors.throwBadRequest)(
1091
+ `flowSurfaces createMenu group title '${title}' is ambiguous under parentMenuRouteId '${values.parentMenuRouteId ?? "root"}'; use an explicit existing navigation.group.routeId or clean up duplicate menu groups before retrying`,
1092
+ {
1093
+ ruleId: "menu-group-title-ambiguous-under-parent",
1094
+ details: {
1095
+ title,
1096
+ parentMenuRouteId: values.parentMenuRouteId ?? null,
1097
+ matches: existingGroups.map((route2) => ({
1098
+ routeId: this.readRouteField(route2, "id"),
1099
+ parentMenuRouteId: this.readRouteField(route2, "parentId") ?? null,
1100
+ type: this.readRouteField(route2, "type"),
1101
+ schemaUid: this.readRouteField(route2, "schemaUid") ?? null
1102
+ }))
1103
+ }
1104
+ }
1105
+ );
1106
+ }
1107
+ this.assertVisibleNavigationIcon("createMenu", "values", values);
848
1108
  const schemaUid = values.schemaUid || (0, import_utils.uid)();
849
1109
  const desktopRoutes = this.db.getRepository("desktopRoutes");
850
1110
  await desktopRoutes.create({
851
1111
  values: {
852
1112
  type: "group",
853
1113
  sort: this.allocateRouteSortValue(),
854
- title: values.title,
1114
+ title,
855
1115
  icon: values.icon,
856
1116
  tooltip: values.tooltip,
857
1117
  schemaUid,
858
1118
  hideInMenu: !!values.hideInMenu,
859
- parentId: this.readRouteField(parentRoute, "id") ?? null
1119
+ parentId
860
1120
  },
861
1121
  transaction
862
1122
  });
@@ -869,6 +1129,7 @@ class FlowSurfacesService {
869
1129
  async createFlowMenuItem(values, transaction) {
870
1130
  var _a;
871
1131
  const parentRoute = await this.assertMenuParentIsGroup(values.parentMenuRouteId, transaction);
1132
+ this.assertVisibleNavigationIcon("createMenu", "values", values);
872
1133
  const pageSchemaUid = values.pageSchemaUid || (0, import_utils.uid)();
873
1134
  const pageUid = values.pageUid || (0, import_utils.uid)();
874
1135
  const tabSchemaUid = values.tabSchemaUid || (0, import_utils.uid)();
@@ -903,7 +1164,7 @@ class FlowSurfacesService {
903
1164
  parentId: null,
904
1165
  options: {
905
1166
  documentTitle: values.tabDocumentTitle,
906
- flowRegistry: values.tabFlowRegistry || {}
1167
+ flowRegistry: this.normalizeEventFlowRegistry("createMenu", values.tabFlowRegistry || {})
907
1168
  }
908
1169
  }
909
1170
  ]
@@ -2596,6 +2857,10 @@ class FlowSurfacesService {
2596
2857
  ),
2597
2858
  options.transaction
2598
2859
  );
2860
+ if (!(rawNode == null ? void 0 : rawNode.uid)) {
2861
+ const resolvedUid = String((resolved == null ? void 0 : resolved.uid) || "").trim() || String(target.uid || target.tabSchemaUid || target.pageSchemaUid || target.routeId || "").trim() || "unknown";
2862
+ (0, import_errors.throwBadRequest)(`flowSurfaces:get target '${resolvedUid}' could not resolve a readable surface tree`);
2863
+ }
2599
2864
  const publicNode = this.stripInternalSurfaceMetaFromNodeTree(import_lodash.default.cloneDeep(rawNode));
2600
2865
  return this.buildSurfaceReadPayload(target, resolved, publicNode, options);
2601
2866
  }
@@ -2630,7 +2895,7 @@ class FlowSurfacesService {
2630
2895
  use: "RootPageTabModel",
2631
2896
  props: import_lodash.default.omit(import_lodash.default.cloneDeep((currentNode == null ? void 0 : currentNode.props) || {}), ["route"]),
2632
2897
  decoratorProps: import_lodash.default.cloneDeep((currentNode == null ? void 0 : currentNode.decoratorProps) || {}),
2633
- flowRegistry: import_lodash.default.cloneDeep((currentNode == null ? void 0 : currentNode.flowRegistry) || {}),
2898
+ flowRegistry: this.getEventFlowRegistry(currentNode),
2634
2899
  stepParams: import_lodash.default.cloneDeep((values == null ? void 0 : values.stepParams) || (currentNode == null ? void 0 : currentNode.stepParams) || {})
2635
2900
  }),
2636
2901
  { transaction: options == null ? void 0 : options.transaction }
@@ -2767,13 +3032,13 @@ class FlowSurfacesService {
2767
3032
  if (!groupTitle) {
2768
3033
  return document;
2769
3034
  }
2770
- const matchedRoutes = await this.findMenuGroupRoutesByTitle(groupTitle, transaction);
3035
+ const matchedRoutes = await this.findMenuGroupRoutesByParentIdAndTitle(null, groupTitle, transaction);
2771
3036
  if (!matchedRoutes.length) {
2772
3037
  return document;
2773
3038
  }
2774
3039
  if (matchedRoutes.length > 1) {
2775
3040
  (0, import_errors.throwBadRequest)(
2776
- `flowSurfaces applyBlueprint navigation.group.title '${groupTitle}' matches ${matchedRoutes.length} existing menu groups; pass navigation.group.routeId explicitly`
3041
+ `flowSurfaces applyBlueprint navigation.group.title '${groupTitle}' matches ${matchedRoutes.length} existing root menu groups; pass navigation.group.routeId explicitly`
2777
3042
  );
2778
3043
  }
2779
3044
  const routeId = this.readRouteField(matchedRoutes[0], "id");
@@ -2843,6 +3108,225 @@ class FlowSurfacesService {
2843
3108
  getCollection: (dataSourceKey, collectionName) => this.getCollection(dataSourceKey || "main", collectionName || "")
2844
3109
  });
2845
3110
  }
3111
+ async prevalidateApplyBlueprintChartAssets(document, transaction) {
3112
+ var _a;
3113
+ const chartAssets = import_lodash.default.isPlainObject((_a = document.assets) == null ? void 0 : _a.charts) ? document.assets.charts : {};
3114
+ for (const [chartKey, chartAsset] of Object.entries(chartAssets)) {
3115
+ if (!import_lodash.default.isPlainObject(chartAsset)) {
3116
+ continue;
3117
+ }
3118
+ (0, import_service_utils.assertSupportedSimpleChanges)("chart", chartAsset, (0, import_configure_options.getConfigureOptionKeysForUse)("ChartBlockModel"));
3119
+ const nextConfigure = buildChartConfigureFromSemanticChangesWithRepair(void 0, chartAsset);
3120
+ await this.validateChartConfigureForRuntime(
3121
+ `applyBlueprint assets.charts.${chartKey}`,
3122
+ nextConfigure,
3123
+ transaction
3124
+ );
3125
+ }
3126
+ }
3127
+ composeChartBlockHasInlineConfig(block) {
3128
+ const settings = import_lodash.default.isPlainObject(block.settings) ? block.settings : {};
3129
+ return ["configure", "query", "visual", "events"].some(
3130
+ (key) => Object.prototype.hasOwnProperty.call(settings, key)
3131
+ );
3132
+ }
3133
+ prepareComposeChartAssetNestedPopupBlocks(input, path, chartAssets) {
3134
+ if (!import_lodash.default.isPlainObject(input)) {
3135
+ return { value: input, didResolveChartAsset: false };
3136
+ }
3137
+ let didResolveChartAsset = false;
3138
+ let nextInput = input;
3139
+ const ensureNextInput = () => {
3140
+ if (nextInput === input) {
3141
+ nextInput = { ...input };
3142
+ }
3143
+ return nextInput;
3144
+ };
3145
+ const preparePopupBlocks = (key) => {
3146
+ const popup = input[key];
3147
+ if (!import_lodash.default.isPlainObject(popup) || !Array.isArray(popup.blocks)) {
3148
+ return;
3149
+ }
3150
+ const prepared = this.prepareComposeChartAssetBlockList(popup.blocks, `${path}.${key}.blocks`, chartAssets);
3151
+ if (!prepared.didResolveChartAsset) {
3152
+ return;
3153
+ }
3154
+ ensureNextInput()[key] = {
3155
+ ...popup,
3156
+ blocks: prepared.blocks
3157
+ };
3158
+ didResolveChartAsset = true;
3159
+ };
3160
+ preparePopupBlocks("popup");
3161
+ preparePopupBlocks("openView");
3162
+ return {
3163
+ value: nextInput,
3164
+ didResolveChartAsset
3165
+ };
3166
+ }
3167
+ prepareComposeChartAssetBlockList(blocks, path, chartAssets) {
3168
+ const rawBlocks = import_lodash.default.castArray(blocks || []);
3169
+ if (!rawBlocks.length) {
3170
+ return { blocks: rawBlocks, didResolveChartAsset: false };
3171
+ }
3172
+ let didResolveChartAsset = false;
3173
+ const hiddenPopupKeys = [
3174
+ "quickCreatePopup",
3175
+ "eventPopup",
3176
+ "cardPopup",
3177
+ "quickCreatePopupSettings",
3178
+ "eventPopupSettings",
3179
+ "cardPopupSettings"
3180
+ ];
3181
+ const nextBlocks = rawBlocks.map((block, index) => {
3182
+ if (!import_lodash.default.isPlainObject(block)) {
3183
+ return block;
3184
+ }
3185
+ const blockPath = `${path}[${index}]`;
3186
+ let nextBlock = block;
3187
+ const ensureNextBlock = () => {
3188
+ if (nextBlock === block) {
3189
+ nextBlock = { ...block };
3190
+ }
3191
+ return nextBlock;
3192
+ };
3193
+ if (Array.isArray(block.blocks)) {
3194
+ const prepared = this.prepareComposeChartAssetBlockList(block.blocks, `${blockPath}.blocks`, chartAssets);
3195
+ if (prepared.didResolveChartAsset) {
3196
+ ensureNextBlock().blocks = prepared.blocks;
3197
+ didResolveChartAsset = true;
3198
+ }
3199
+ }
3200
+ for (const slot of ["actions", "recordActions", "fields"]) {
3201
+ if (!Array.isArray(block[slot])) {
3202
+ continue;
3203
+ }
3204
+ let didPrepareSlot = false;
3205
+ const nextItems = block[slot].map((item, itemIndex) => {
3206
+ const prepared = this.prepareComposeChartAssetNestedPopupBlocks(
3207
+ item,
3208
+ `${blockPath}.${slot}[${itemIndex}]`,
3209
+ chartAssets
3210
+ );
3211
+ didPrepareSlot = didPrepareSlot || prepared.didResolveChartAsset;
3212
+ return prepared.value;
3213
+ });
3214
+ if (didPrepareSlot) {
3215
+ ensureNextBlock()[slot] = nextItems;
3216
+ didResolveChartAsset = true;
3217
+ }
3218
+ }
3219
+ if (Array.isArray(block.fieldGroups)) {
3220
+ let didPrepareFieldGroups = false;
3221
+ const nextFieldGroups = block.fieldGroups.map((group, groupIndex) => {
3222
+ if (!import_lodash.default.isPlainObject(group) || !Array.isArray(group.fields)) {
3223
+ return group;
3224
+ }
3225
+ let didPrepareGroupFields = false;
3226
+ const nextFields = group.fields.map((field, fieldIndex) => {
3227
+ const prepared = this.prepareComposeChartAssetNestedPopupBlocks(
3228
+ field,
3229
+ `${blockPath}.fieldGroups[${groupIndex}].fields[${fieldIndex}]`,
3230
+ chartAssets
3231
+ );
3232
+ didPrepareGroupFields = didPrepareGroupFields || prepared.didResolveChartAsset;
3233
+ return prepared.value;
3234
+ });
3235
+ if (!didPrepareGroupFields) {
3236
+ return group;
3237
+ }
3238
+ didPrepareFieldGroups = true;
3239
+ return {
3240
+ ...group,
3241
+ fields: nextFields
3242
+ };
3243
+ });
3244
+ if (didPrepareFieldGroups) {
3245
+ ensureNextBlock().fieldGroups = nextFieldGroups;
3246
+ didResolveChartAsset = true;
3247
+ }
3248
+ }
3249
+ if (import_lodash.default.isPlainObject(block.popup) && Array.isArray(block.popup.blocks)) {
3250
+ const prepared = this.prepareComposeChartAssetBlockList(
3251
+ block.popup.blocks,
3252
+ `${blockPath}.popup.blocks`,
3253
+ chartAssets
3254
+ );
3255
+ if (prepared.didResolveChartAsset) {
3256
+ ensureNextBlock().popup = {
3257
+ ...block.popup,
3258
+ blocks: prepared.blocks
3259
+ };
3260
+ didResolveChartAsset = true;
3261
+ }
3262
+ }
3263
+ if (import_lodash.default.isPlainObject(block.settings)) {
3264
+ for (const key of hiddenPopupKeys) {
3265
+ const popup = block.settings[key];
3266
+ if (!import_lodash.default.isPlainObject(popup) || !Array.isArray(popup.blocks)) {
3267
+ continue;
3268
+ }
3269
+ const prepared = this.prepareComposeChartAssetBlockList(
3270
+ popup.blocks,
3271
+ `${blockPath}.settings.${key}.blocks`,
3272
+ chartAssets
3273
+ );
3274
+ if (!prepared.didResolveChartAsset) {
3275
+ continue;
3276
+ }
3277
+ ensureNextBlock().settings = {
3278
+ ...nextBlock.settings || {},
3279
+ [key]: {
3280
+ ...popup,
3281
+ blocks: prepared.blocks
3282
+ }
3283
+ };
3284
+ didResolveChartAsset = true;
3285
+ }
3286
+ }
3287
+ if (String(nextBlock.type || "").trim() !== "chart" || !Object.prototype.hasOwnProperty.call(nextBlock, "chart") || this.composeChartBlockHasInlineConfig(nextBlock)) {
3288
+ return nextBlock;
3289
+ }
3290
+ const chartKey = String(nextBlock.chart || "").trim();
3291
+ if (!chartKey) {
3292
+ throwChartRepairBadRequest(`${blockPath}.chart must reference one key from assets.charts`, {
3293
+ path: `${blockPath}.chart`,
3294
+ ruleId: "chart-block-asset-reference-required"
3295
+ });
3296
+ }
3297
+ const chartAsset = chartAssets[chartKey];
3298
+ if (!import_lodash.default.isPlainObject(chartAsset)) {
3299
+ throwChartRepairBadRequest(`${blockPath}.chart references missing chart asset '${chartKey}'`, {
3300
+ path: `${blockPath}.chart`,
3301
+ ruleId: "chart-block-asset-reference-missing",
3302
+ details: {
3303
+ chartKey
3304
+ }
3305
+ });
3306
+ }
3307
+ if (!import_lodash.default.isUndefined(nextBlock.settings) && !import_lodash.default.isPlainObject(nextBlock.settings)) {
3308
+ return nextBlock;
3309
+ }
3310
+ didResolveChartAsset = true;
3311
+ return {
3312
+ ...nextBlock,
3313
+ settings: import_lodash.default.merge({}, import_lodash.default.cloneDeep(nextBlock.settings || {}), import_lodash.default.cloneDeep(chartAsset))
3314
+ };
3315
+ });
3316
+ return {
3317
+ blocks: nextBlocks,
3318
+ didResolveChartAsset
3319
+ };
3320
+ }
3321
+ prepareComposeChartAssetSettings(values) {
3322
+ var _a;
3323
+ const chartAssets = import_lodash.default.isPlainObject((_a = values == null ? void 0 : values.assets) == null ? void 0 : _a.charts) ? values.assets.charts : {};
3324
+ const prepared = this.prepareComposeChartAssetBlockList(values == null ? void 0 : values.blocks, "$.blocks", chartAssets);
3325
+ return prepared.didResolveChartAsset ? {
3326
+ ...values,
3327
+ blocks: prepared.blocks
3328
+ } : values;
3329
+ }
2846
3330
  getApplyBlueprintKanbanBlockResourceObject(block) {
2847
3331
  return import_lodash.default.isPlainObject(block == null ? void 0 : block.resource) ? block.resource : {};
2848
3332
  }
@@ -3231,25 +3715,38 @@ class FlowSurfacesService {
3231
3715
  }
3232
3716
  if (!options.transaction) {
3233
3717
  const createdKanbanSortFields = [];
3234
- try {
3235
- return await this.transaction(
3236
- (transaction) => this.applyBlueprintWithTransaction(
3237
- values,
3238
- {
3239
- ...options,
3240
- transaction
3241
- },
3242
- createdKanbanSortFields
3243
- )
3244
- );
3245
- } catch (error) {
3246
- await this.cleanupApplyBlueprintKanbanSortFields(createdKanbanSortFields);
3247
- throw error;
3248
- }
3718
+ const mutationResult = await this.applyBlueprintMutationWithoutExternalTransaction(
3719
+ values,
3720
+ options,
3721
+ createdKanbanSortFields
3722
+ );
3723
+ const surface = await this.get(mutationResult.pageLocator, { currentRoles: options.currentRoles });
3724
+ return this.buildApplyBlueprintResponse(mutationResult.mode, mutationResult.pageLocator, surface);
3249
3725
  }
3250
3726
  }
3251
- async applyBlueprintWithTransaction(values, options = {}, createdKanbanSortFields) {
3252
- var _a;
3727
+ async applyBlueprintMutationWithoutExternalTransaction(values, options = {}, createdKanbanSortFields) {
3728
+ try {
3729
+ await this.assertApplyBlueprintAuthoringPayload(values, options);
3730
+ const document = (0, import_blueprint.prepareFlowSurfaceApplyBlueprintDocument)(values);
3731
+ await this.prevalidateApplyBlueprintChartAssets(document);
3732
+ return await this.transaction(
3733
+ (transaction) => this.applyBlueprintWithTransaction(
3734
+ values,
3735
+ {
3736
+ ...options,
3737
+ transaction,
3738
+ skipAuthoringValidation: true
3739
+ },
3740
+ createdKanbanSortFields,
3741
+ { readSurface: false }
3742
+ )
3743
+ );
3744
+ } catch (error) {
3745
+ await this.cleanupApplyBlueprintKanbanSortFields(createdKanbanSortFields);
3746
+ throw error;
3747
+ }
3748
+ }
3749
+ async assertApplyBlueprintAuthoringPayload(values, options = {}) {
3253
3750
  const enabledPackages = await this.resolveEnabledPluginPackages(options);
3254
3751
  await (0, import_authoring_validation.assertFlowSurfaceAuthoringPayload)("applyBlueprint", values, {
3255
3752
  transaction: options.transaction,
@@ -3257,6 +3754,11 @@ class FlowSurfacesService {
3257
3754
  findMenuGroupRoutesByTitle: (title, transaction) => this.findMenuGroupRoutesByTitle(title, transaction),
3258
3755
  getCollection: (dataSourceKey, collectionName) => this.getCollection(dataSourceKey || "main", collectionName || "")
3259
3756
  });
3757
+ }
3758
+ async applyBlueprintWithTransaction(values, options = {}, createdKanbanSortFields, resultOptions = {}) {
3759
+ if (options.skipAuthoringValidation !== true) {
3760
+ await this.assertApplyBlueprintAuthoringPayload(values, options);
3761
+ }
3260
3762
  const prepared = await this.prepareApplyBlueprintRequest(values, options.transaction, createdKanbanSortFields);
3261
3763
  const popupTemplateAliasSession = this.createPopupTemplateAliasSession();
3262
3764
  const popupTemplateTreeCache = /* @__PURE__ */ new Map();
@@ -3277,10 +3779,27 @@ class FlowSurfacesService {
3277
3779
  options
3278
3780
  );
3279
3781
  const pageLocator = (0, import_blueprint.resolveApplyBlueprintPageLocator)(prepared, result);
3782
+ await this.ensureSurfaceTableDefaultActionIntegrity(pageLocator, {
3783
+ ...options,
3784
+ enabledPackages: await this.resolveEnabledPluginPackages(options),
3785
+ popupTemplateAliasSession,
3786
+ popupTemplateTreeCache
3787
+ });
3788
+ if (resultOptions.readSurface === false) {
3789
+ return {
3790
+ version: "1",
3791
+ mode: prepared.document.mode,
3792
+ pageLocator
3793
+ };
3794
+ }
3280
3795
  const surface = await this.get(pageLocator, options);
3796
+ return this.buildApplyBlueprintResponse(prepared.document.mode, pageLocator, surface);
3797
+ }
3798
+ buildApplyBlueprintResponse(mode, pageLocator, surface) {
3799
+ var _a;
3281
3800
  return {
3282
3801
  version: "1",
3283
- mode: prepared.document.mode,
3802
+ mode,
3284
3803
  target: (0, import_service_utils.buildDefinedPayload)({
3285
3804
  pageSchemaUid: pageLocator.pageSchemaUid,
3286
3805
  pageUid: (_a = surface == null ? void 0 : surface.target) == null ? void 0 : _a.uid
@@ -3942,8 +4461,8 @@ class FlowSurfacesService {
3942
4461
  stepParams: (0, import_template_service_utils.buildFlowTemplateReferenceBlockStepParams)(template, templateTargetUid, init)
3943
4462
  };
3944
4463
  }
3945
- buildPopupTemplateReferenceOpenView(template, templateTargetUid) {
3946
- return this.buildPopupTemplateOpenView(template, "reference", templateTargetUid);
4464
+ buildPopupTemplateReferenceOpenView(template, templateTargetUid, options = {}) {
4465
+ return this.buildPopupTemplateOpenView(template, "reference", templateTargetUid, options);
3947
4466
  }
3948
4467
  shouldHydrateDetachedPopupTemplateBlockResourceContext(blockNode, context) {
3949
4468
  if (!this.isCollectionBlockUse(blockNode == null ? void 0 : blockNode.use) || !context.associationName || !context.sourceId) {
@@ -4293,15 +4812,35 @@ class FlowSurfacesService {
4293
4812
  }
4294
4813
  const nextStepParams = import_lodash.default.cloneDeep(sourceNode.stepParams || {});
4295
4814
  const currentGroup = import_lodash.default.isPlainObject(nextStepParams[openViewStep.flowKey]) ? import_lodash.default.cloneDeep(nextStepParams[openViewStep.flowKey]) : {};
4815
+ const popupProfile = await this.resolvePopupBlockProfile(sourceNode.uid, null, sourceNode, transaction).catch(
4816
+ () => null
4817
+ );
4818
+ const useRuntimeRecordFilterByTk = (popupProfile == null ? void 0 : popupProfile.hasCurrentRecord) === true;
4819
+ const filterTargetKey = useRuntimeRecordFilterByTk ? await this.resolveOpenViewRuntimeRecordFilterTargetKey(openViewStep.openView, template, {
4820
+ transaction,
4821
+ popupTemplateHostUid: sourceNode.uid,
4822
+ popupActionContext: {
4823
+ hasCurrentRecord: true
4824
+ }
4825
+ }) : void 0;
4826
+ const currentOpenView = import_lodash.default.omit(openViewStep.openView || {}, [
4827
+ "popupTemplateUid",
4828
+ "popupTemplateMode",
4829
+ "popupTemplateContext",
4830
+ "popupTemplateHasFilterByTk",
4831
+ "popupTemplateHasSourceId"
4832
+ ]);
4833
+ if (useRuntimeRecordFilterByTk && this.shouldOmitRuntimeRecordFilterByTk(currentOpenView.filterByTk, {
4834
+ filterTargetKey
4835
+ })) {
4836
+ delete currentOpenView.filterByTk;
4837
+ }
4296
4838
  let nextOpenView = {
4297
- ...import_lodash.default.omit(openViewStep.openView || {}, [
4298
- "popupTemplateUid",
4299
- "popupTemplateMode",
4300
- "popupTemplateContext",
4301
- "popupTemplateHasFilterByTk",
4302
- "popupTemplateHasSourceId"
4303
- ]),
4304
- ...this.buildPopupTemplateReferenceOpenView(template, templateTargetUid)
4839
+ ...currentOpenView,
4840
+ ...this.buildPopupTemplateReferenceOpenView(template, templateTargetUid, {
4841
+ filterTargetKey,
4842
+ useRuntimeRecordFilterByTk
4843
+ })
4305
4844
  };
4306
4845
  if (String(sourceNode.use || "").trim() === "AddChildActionModel") {
4307
4846
  nextOpenView = await this.normalizeAddChildOpenViewForAction(sourceNode, nextOpenView, transaction);
@@ -4862,12 +5401,13 @@ class FlowSurfacesService {
4862
5401
  async compose(values, options = {}) {
4863
5402
  var _a, _b, _c;
4864
5403
  const enabledPackages = await this.resolveEnabledPluginPackages(options);
4865
- const target = await this.prepareWriteTarget("compose", values == null ? void 0 : values.target, values, options);
5404
+ const composeValues = this.prepareComposeChartAssetSettings(values);
5405
+ const target = await this.prepareWriteTarget("compose", composeValues == null ? void 0 : composeValues.target, composeValues, options);
4866
5406
  const authoringContext = await this.buildTargetAuthoringContext({
4867
5407
  target,
4868
5408
  transaction: options.transaction
4869
5409
  });
4870
- await (0, import_authoring_validation.assertFlowSurfaceAuthoringPayload)("compose", values, {
5410
+ await (0, import_authoring_validation.assertFlowSurfaceAuthoringPayload)("compose", composeValues, {
4871
5411
  transaction: options.transaction,
4872
5412
  enabledPackages,
4873
5413
  skipGeneratedLayoutSingleColumnErrors: options.skipGeneratedLayoutSingleColumnErrors === true,
@@ -4880,12 +5420,17 @@ class FlowSurfacesService {
4880
5420
  ...options,
4881
5421
  popupTemplateTreeCache
4882
5422
  };
4883
- const mode = this.assertComposeMode(values == null ? void 0 : values.mode);
4884
- const popupDefaultsMetadata = this.buildPopupDefaultsMetadata(values == null ? void 0 : values.defaults);
4885
- const normalizedBlocks = this.normalizeComposeBlocks(values == null ? void 0 : values.blocks, enabledPackages, popupDefaultsMetadata, {
4886
- dataSourceKey: authoringContext.currentDataSourceKey,
4887
- collectionName: authoringContext.currentCollectionName
4888
- });
5423
+ const mode = this.assertComposeMode(composeValues == null ? void 0 : composeValues.mode);
5424
+ const popupDefaultsMetadata = this.buildPopupDefaultsMetadata(composeValues == null ? void 0 : composeValues.defaults);
5425
+ const normalizedBlocks = this.normalizeComposeBlocks(
5426
+ composeValues == null ? void 0 : composeValues.blocks,
5427
+ enabledPackages,
5428
+ popupDefaultsMetadata,
5429
+ {
5430
+ dataSourceKey: authoringContext.currentDataSourceKey,
5431
+ collectionName: authoringContext.currentCollectionName
5432
+ }
5433
+ );
4889
5434
  this.validateComposePopupTemplateAliases(normalizedBlocks, popupTemplateAliasSession);
4890
5435
  const blockParent = await this.surfaceContext.resolveBlockParent(target, options.transaction);
4891
5436
  const gridUid = blockParent.parentUid;
@@ -4911,7 +5456,7 @@ class FlowSurfacesService {
4911
5456
  mode,
4912
5457
  normalizedBlocks,
4913
5458
  existingItemUids: existingItems.map((item) => item.uid),
4914
- layout: values.layout
5459
+ layout: composeValues.layout
4915
5460
  });
4916
5461
  const generatedDefaultFilterByComposeBlockUid = /* @__PURE__ */ new Map();
4917
5462
  const result = await (0, import_compose_runtime.executeComposeRuntime)(plan, {
@@ -4946,7 +5491,7 @@ class FlowSurfacesService {
4946
5491
  {
4947
5492
  ...payload,
4948
5493
  ...Object.keys(hiddenPopupSettings).length ? { settings: hiddenPopupSettings } : {},
4949
- ...values.defaults ? { defaults: values.defaults } : {}
5494
+ ...composeValues.defaults ? { defaults: composeValues.defaults } : {}
4950
5495
  },
4951
5496
  {
4952
5497
  ...runtimeOptions,
@@ -5043,6 +5588,13 @@ class FlowSurfacesService {
5043
5588
  });
5044
5589
  for (const [blockIndex, block] of result.blocks.entries()) {
5045
5590
  const blockSpec = normalizedBlocks.find((item) => item.key === block.key);
5591
+ if ((blockSpec == null ? void 0 : blockSpec.type) === "table") {
5592
+ await this.ensureTableDefaultActionIntegrity(block.uid, {
5593
+ ...runtimeOptions,
5594
+ enabledPackages,
5595
+ popupTemplateAliasSession
5596
+ });
5597
+ }
5046
5598
  if ((blockSpec == null ? void 0 : blockSpec.type) === "table") {
5047
5599
  const appliedTreeTableDefaults = await this.applyTreeTableCreatedBlockDefaults(
5048
5600
  {
@@ -5176,7 +5728,7 @@ class FlowSurfacesService {
5176
5728
  if ((current == null ? void 0 : current.use) === "DividerItemModel") {
5177
5729
  return this.configureDividerItem(target, changes, options);
5178
5730
  }
5179
- return this.configureJSItem(target, changes, options);
5731
+ return this.configureJSItem(target, changes, { ...options, currentUse: (current == null ? void 0 : current.use) || "JSItemModel" });
5180
5732
  }
5181
5733
  if ((0, import_service_utils.isFieldNodeUse)(current == null ? void 0 : current.use)) {
5182
5734
  return this.configureFieldNode(target, changes, configureOptions);
@@ -5251,6 +5803,7 @@ class FlowSurfacesService {
5251
5803
  if (!this.isBindableMenuRoutePendingInitialization(route, structure)) {
5252
5804
  (0, import_errors.throwBadRequest)(`flowSurfaces createPage does not allow re-initializing menu route '${routeId}'`);
5253
5805
  }
5806
+ this.assertVisibleNavigationRouteIconUpdate("createPage", "values", route, values);
5254
5807
  const existingPage = structure.pageModel;
5255
5808
  const enableTabs = !!values.enableTabs;
5256
5809
  const displayTitle = values.displayTitle !== false;
@@ -5275,7 +5828,7 @@ class FlowSurfacesService {
5275
5828
  parentId: routeId,
5276
5829
  options: {
5277
5830
  documentTitle: values.tabDocumentTitle,
5278
- flowRegistry: values.tabFlowRegistry || {}
5831
+ flowRegistry: this.normalizeEventFlowRegistry("createPage", values.tabFlowRegistry || {})
5279
5832
  }
5280
5833
  },
5281
5834
  transaction
@@ -5294,7 +5847,10 @@ class FlowSurfacesService {
5294
5847
  options: {
5295
5848
  ...this.readRouteOptions(tabRoute),
5296
5849
  documentTitle: values.tabDocumentTitle ?? this.readRouteOptions(tabRoute).documentTitle,
5297
- flowRegistry: values.tabFlowRegistry || this.readRouteOptions(tabRoute).flowRegistry || {}
5850
+ flowRegistry: this.normalizeEventFlowRegistry(
5851
+ "createPage",
5852
+ values.tabFlowRegistry || this.readRouteOptions(tabRoute).flowRegistry || {}
5853
+ )
5298
5854
  }
5299
5855
  },
5300
5856
  transaction
@@ -5436,7 +5992,7 @@ class FlowSurfacesService {
5436
5992
  hidden: !pageRoute.get("enableTabs"),
5437
5993
  options: {
5438
5994
  documentTitle: values.documentTitle,
5439
- flowRegistry: values.flowRegistry || {}
5995
+ flowRegistry: this.normalizeEventFlowRegistry("addTab", values.flowRegistry || {})
5440
5996
  }
5441
5997
  },
5442
5998
  transaction: options.transaction
@@ -5484,7 +6040,7 @@ class FlowSurfacesService {
5484
6040
  )
5485
6041
  }
5486
6042
  } : void 0,
5487
- flowRegistry: !import_lodash.default.isUndefined(values.flowRegistry) ? values.flowRegistry : void 0
6043
+ flowRegistry: !import_lodash.default.isUndefined(values.flowRegistry) ? this.normalizeEventFlowRegistry("updateTab", values.flowRegistry) : void 0
5488
6044
  });
5489
6045
  await this.routeSync.persistTabSettings(target, current, nextPayload, options.transaction);
5490
6046
  return {
@@ -5600,7 +6156,7 @@ class FlowSurfacesService {
5600
6156
  title: values.title,
5601
6157
  icon: values.icon,
5602
6158
  documentTitle: values.documentTitle,
5603
- flowRegistry: values.flowRegistry
6159
+ flowRegistry: this.normalizeEventFlowRegistry("addPopupTab", values.flowRegistry)
5604
6160
  });
5605
6161
  await this.repository.upsertModel(
5606
6162
  {
@@ -5655,7 +6211,7 @@ class FlowSurfacesService {
5655
6211
  }
5656
6212
  }
5657
6213
  } : void 0,
5658
- flowRegistry: !import_lodash.default.isUndefined(values.flowRegistry) ? values.flowRegistry : void 0
6214
+ flowRegistry: !import_lodash.default.isUndefined(values.flowRegistry) ? this.normalizeEventFlowRegistry("updatePopupTab", values.flowRegistry) : void 0
5659
6215
  });
5660
6216
  if (Object.keys(nextPayload).length === 1) {
5661
6217
  return { uid: popupTab.uid };
@@ -5856,7 +6412,7 @@ class FlowSurfacesService {
5856
6412
  use: "ReferenceFormGridModel",
5857
6413
  props: currentGrid.props,
5858
6414
  decoratorProps: currentGrid.decoratorProps,
5859
- flowRegistry: currentGrid.flowRegistry,
6415
+ flowRegistry: this.getEventFlowRegistry(currentGrid),
5860
6416
  sortIndex: currentGrid.sortIndex,
5861
6417
  parentId: blockUid,
5862
6418
  subKey: "grid",
@@ -6102,7 +6658,8 @@ class FlowSurfacesService {
6102
6658
  if ((0, import_service_utils.hasOwnDefined)(normalizedSettings, "defaultFilter")) {
6103
6659
  normalizedSettings.defaultFilter = (0, import_filter_group.normalizeFlowSurfaceFilterGroupValue)(
6104
6660
  normalizedSettings.defaultFilter,
6105
- `flowSurfaces ${actionName} defaultActionSettings.filter.defaultFilter expects FilterGroup like ${import_filter_group.FLOW_SURFACE_FILTER_GROUP_EXAMPLE}`
6661
+ `flowSurfaces ${actionName} defaultActionSettings.filter.defaultFilter expects FilterGroup like ${import_filter_group.FLOW_SURFACE_FILTER_GROUP_EXAMPLE}`,
6662
+ { strictDateValues: true }
6106
6663
  );
6107
6664
  normalizedSettings.defaultFilter = this.normalizeEffectivePublicDataSurfaceDefaultFilter(
6108
6665
  normalizedSettings.defaultFilter,
@@ -6330,7 +6887,7 @@ class FlowSurfacesService {
6330
6887
  key: String((values == null ? void 0 : values.key) || catalogItem.key || (values == null ? void 0 : values.type) || (values == null ? void 0 : values.use) || "addBlock_inline").trim() || "addBlock_inline",
6331
6888
  type: catalogItem.key || values.type,
6332
6889
  fields: values.fields,
6333
- fieldsLayout: values.fieldsLayout
6890
+ ...Object.prototype.hasOwnProperty.call(values || {}, "fieldsLayout") ? { fieldsLayout: values.fieldsLayout } : {}
6334
6891
  },
6335
6892
  0,
6336
6893
  enabledPackages,
@@ -6524,6 +7081,8 @@ class FlowSurfacesService {
6524
7081
  ...fieldSpec.popupSize ? { popupSize: fieldSpec.popupSize } : {},
6525
7082
  ...!import_lodash.default.isUndefined(fieldSpec.pageSize) ? { pageSize: fieldSpec.pageSize } : {},
6526
7083
  ...!import_lodash.default.isUndefined(fieldSpec.showIndex) ? { showIndex: fieldSpec.showIndex } : {},
7084
+ ...fieldSpec.defaultTargetUid ? { defaultTargetUid: fieldSpec.defaultTargetUid } : {},
7085
+ ...fieldSpec.targetBlockUid ? { targetBlockUid: fieldSpec.targetBlockUid } : {},
6527
7086
  ...fieldSpec.popup ? { popup: fieldSpec.popup } : {},
6528
7087
  ...fieldSpec.__autoPopupForRelationField ? { __autoPopupForRelationField: true } : {},
6529
7088
  ...fieldSpec[import_defaults.FLOW_SURFACE_APPLY_BLUEPRINT_POPUP_DEFAULTS_KEY] ? {
@@ -6598,6 +7157,13 @@ class FlowSurfacesService {
6598
7157
  enabledPackages
6599
7158
  }
6600
7159
  );
7160
+ if (catalogItem.use === "TableBlockModel" && !options.skipDefaultBlockActions) {
7161
+ await this.ensureTableDefaultActionIntegrity(created, {
7162
+ ...options,
7163
+ enabledPackages,
7164
+ popupTemplateTreeCache: options.popupTemplateTreeCache
7165
+ });
7166
+ }
6601
7167
  if (!options.deferAutoLayout && (initialGrid == null ? void 0 : initialGrid.uid)) {
6602
7168
  const finalGrid = await this.repository.findModelById(parentUid, {
6603
7169
  transaction: options.transaction,
@@ -6665,7 +7231,9 @@ class FlowSurfacesService {
6665
7231
  (0, import_errors.throwBadRequest)("flowSurfaces fieldType is only supported for relation fields");
6666
7232
  }
6667
7233
  if (inlinePopup) {
6668
- (0, import_errors.throwBadRequest)(`flowSurfaces addField type '${values.type}' does not support popup`);
7234
+ (0, import_errors.throwBadRequest)(
7235
+ withJsPopupGuidance(`flowSurfaces addField type '${values.type}' does not support popup`, values.type)
7236
+ );
6669
7237
  }
6670
7238
  if (isFilterFormItem) {
6671
7239
  (0, import_errors.throwBadRequest)(`flowSurfaces addField type '${values.type}' is not allowed under filter-form`);
@@ -6860,9 +7428,18 @@ class FlowSurfacesService {
6860
7428
  }
6861
7429
  }
6862
7430
  if (inlinePopup && !this.isPopupFieldHostUse(boundFieldCapability.fieldUse)) {
6863
- (0, import_errors.throwBadRequest)(`flowSurfaces addField field '${boundFieldCapability.fieldUse}' does not support popup`);
7431
+ (0, import_errors.throwBadRequest)(
7432
+ withJsPopupGuidance(
7433
+ `flowSurfaces addField field '${boundFieldCapability.fieldUse}' does not support popup`,
7434
+ boundFieldCapability.fieldUse
7435
+ )
7436
+ );
6864
7437
  }
6865
- if (values.__autoPopupForRelationField === true && !inlinePopup && this.isPopupFieldHostUse(boundFieldCapability.fieldUse) && ((0, import_service_helpers.isAssociationField)(resolvedField.field) || !!normalizedFieldBinding.associationPathName) && !this.peekInlineFieldSettingsOpenView(inlineSettings, boundFieldCapability.wrapperUse)) {
7438
+ const inlineSettingsOpenView = this.peekInlineFieldSettingsOpenView(
7439
+ inlineSettings,
7440
+ boundFieldCapability.wrapperUse
7441
+ );
7442
+ if (values.__autoPopupForRelationField === true && !inlinePopup && this.isPopupFieldHostUse(boundFieldCapability.fieldUse) && ((0, import_service_helpers.isAssociationField)(resolvedField.field) || !!normalizedFieldBinding.associationPathName) && !inlineSettingsOpenView) {
6866
7443
  const popupDefaultsMetadata = values[import_defaults.FLOW_SURFACE_APPLY_BLUEPRINT_POPUP_DEFAULTS_KEY];
6867
7444
  inlinePopup = this.normalizeInlinePopup("addField", {
6868
7445
  tryTemplate: true,
@@ -6872,9 +7449,19 @@ class FlowSurfacesService {
6872
7449
  } : {}
6873
7450
  });
6874
7451
  }
6875
- if (inlinePopup && this.isExternalPopupOpenView(
6876
- this.peekInlineFieldSettingsOpenView(inlineSettings, boundFieldCapability.wrapperUse)
6877
- )) {
7452
+ const hasExternalInlineSettingsOpenView = this.isExternalPopupOpenView(inlineSettingsOpenView);
7453
+ if (!inlinePopup && this.isPopupFieldHostUse(boundFieldCapability.fieldUse) && !hasExternalInlineSettingsOpenView && (this.isInlineFieldClickToOpenEnabled(inlineSettings, boundFieldCapability.wrapperUse) || import_lodash.default.isPlainObject(inlineSettingsOpenView))) {
7454
+ const popupDefaultsMetadata = values[import_defaults.FLOW_SURFACE_APPLY_BLUEPRINT_POPUP_DEFAULTS_KEY];
7455
+ inlinePopup = this.normalizeInlinePopup("addField", {
7456
+ tryTemplate: true,
7457
+ defaultType: "view",
7458
+ ...import_lodash.default.isPlainObject(inlineSettingsOpenView) ? { openView: import_lodash.default.cloneDeep(inlineSettingsOpenView) } : {},
7459
+ ...popupDefaultsMetadata ? {
7460
+ [import_defaults.FLOW_SURFACE_APPLY_BLUEPRINT_POPUP_DEFAULTS_KEY]: popupDefaultsMetadata
7461
+ } : {}
7462
+ });
7463
+ }
7464
+ if (inlinePopup && hasExternalInlineSettingsOpenView) {
6878
7465
  (0, import_errors.throwBadRequest)(`flowSurfaces addField popup cannot be combined with external openView.uid`);
6879
7466
  }
6880
7467
  const defaultFieldState = (0, import_service_utils.buildDefaultFieldState)(
@@ -7013,7 +7600,12 @@ class FlowSurfacesService {
7013
7600
  );
7014
7601
  }
7015
7602
  if (inlinePopup && !POPUP_ACTION_USES.has(actionCatalogItem.use)) {
7016
- (0, import_errors.throwBadRequest)(`flowSurfaces addAction type '${actionCatalogItem.key}' does not support popup`);
7603
+ (0, import_errors.throwBadRequest)(
7604
+ withJsPopupGuidance(
7605
+ `flowSurfaces addAction type '${actionCatalogItem.key}' does not support popup`,
7606
+ actionCatalogItem.key
7607
+ )
7608
+ );
7017
7609
  }
7018
7610
  await this.assertApprovalActionSingleton(container.parentUid, actionCatalogItem.use, options.transaction);
7019
7611
  (0, import_action_scope.assertRequestedActionScope)({
@@ -7041,21 +7633,43 @@ class FlowSurfacesService {
7041
7633
  return result2;
7042
7634
  }
7043
7635
  const resourceContext = container.ownerUid ? await this.locator.resolveCollectionContext(container.ownerUid, options.transaction).catch(() => null) : null;
7044
- const aiEmployeeProps = this.isAIEmployeeActionUse(actionCatalogItem.use) ? await this.normalizeAIEmployeeActionPublicSettings("addAction", inlineSettings || {}, {
7636
+ const aiEmployeeSettingsPayload = this.isAIEmployeeActionUse(actionCatalogItem.use) ? await this.normalizeAIEmployeeActionPublicSettings("addAction", inlineSettings || {}, {
7045
7637
  transaction: options.transaction,
7046
7638
  enabledPackages,
7047
7639
  requireUsername: true,
7048
7640
  currentRoles: options.currentRoles,
7049
- selfUid: container.ownerUid
7641
+ selfUid: container.ownerUid,
7642
+ promptContext: {
7643
+ kind: "container",
7644
+ ownerUid: container.ownerUid,
7645
+ scope: resolvedScope
7646
+ }
7050
7647
  }) : void 0;
7648
+ const actionSettingsPayload = {
7649
+ props: values.props,
7650
+ stepParams: values.stepParams
7651
+ };
7652
+ if (aiEmployeeSettingsPayload) {
7653
+ this.mergeAIEmployeeActionSettingsPayload(
7654
+ { use: actionCatalogItem.use },
7655
+ actionSettingsPayload,
7656
+ aiEmployeeSettingsPayload
7657
+ );
7658
+ await this.assertNoDuplicateAIEmployeeAction(
7659
+ "addAction",
7660
+ container.parentUid,
7661
+ actionSettingsPayload,
7662
+ options.transaction
7663
+ );
7664
+ }
7051
7665
  const action = (0, import_builder.buildActionTree)({
7052
7666
  use: actionCatalogItem.use,
7053
7667
  containerUse: container.ownerUse,
7054
7668
  resourceInit: values.resourceInit || (resourceContext == null ? void 0 : resourceContext.resourceInit),
7055
- props: aiEmployeeProps || values.props,
7669
+ props: actionSettingsPayload.props,
7056
7670
  decoratorProps: values.decoratorProps,
7057
- stepParams: values.stepParams,
7058
- flowRegistry: values.flowRegistry
7671
+ stepParams: actionSettingsPayload.stepParams,
7672
+ flowRegistry: this.normalizeEventFlowRegistry("addAction", values.flowRegistry)
7059
7673
  });
7060
7674
  this.contractGuard.validateNodeTreeAgainstContract(action);
7061
7675
  const created = await this.repository.upsertModel(
@@ -7109,8 +7723,13 @@ class FlowSurfacesService {
7109
7723
  );
7110
7724
  const resolvedScope = actionCatalogItem.scope;
7111
7725
  if (inlinePopup && !POPUP_ACTION_USES.has(actionCatalogItem.use)) {
7112
- (0, import_errors.throwBadRequest)(`flowSurfaces addRecordAction type '${actionCatalogItem.key}' does not support popup`);
7113
- }
7726
+ (0, import_errors.throwBadRequest)(
7727
+ withJsPopupGuidance(
7728
+ `flowSurfaces addRecordAction type '${actionCatalogItem.key}' does not support popup`,
7729
+ actionCatalogItem.key
7730
+ )
7731
+ );
7732
+ }
7114
7733
  if (this.isAddChildCatalogItem(actionCatalogItem)) {
7115
7734
  await this.assertAddChildSupportedForOwnerNode(container.ownerNode, "addRecordAction", options.transaction);
7116
7735
  }
@@ -7141,21 +7760,43 @@ class FlowSurfacesService {
7141
7760
  }
7142
7761
  const resourceContext = container.ownerUid ? await this.locator.resolveCollectionContext(container.ownerUid, options.transaction).catch(() => null) : null;
7143
7762
  const resourceInit = this.isAddChildCatalogItem(actionCatalogItem) ? await this.resolveAddChildResourceInitForOwnerNode(container.ownerNode, options.transaction) : values.resourceInit || (resourceContext == null ? void 0 : resourceContext.resourceInit);
7144
- const aiEmployeeProps = this.isAIEmployeeActionUse(actionCatalogItem.use) ? await this.normalizeAIEmployeeActionPublicSettings("addRecordAction", inlineSettings || {}, {
7763
+ const aiEmployeeSettingsPayload = this.isAIEmployeeActionUse(actionCatalogItem.use) ? await this.normalizeAIEmployeeActionPublicSettings("addRecordAction", inlineSettings || {}, {
7145
7764
  transaction: options.transaction,
7146
7765
  enabledPackages,
7147
7766
  requireUsername: true,
7148
7767
  currentRoles: options.currentRoles,
7149
- selfUid: container.ownerUid
7768
+ selfUid: container.ownerUid,
7769
+ promptContext: {
7770
+ kind: "container",
7771
+ ownerUid: container.ownerUid,
7772
+ scope: resolvedScope
7773
+ }
7150
7774
  }) : void 0;
7775
+ const actionSettingsPayload = {
7776
+ props: values.props,
7777
+ stepParams: values.stepParams
7778
+ };
7779
+ if (aiEmployeeSettingsPayload) {
7780
+ this.mergeAIEmployeeActionSettingsPayload(
7781
+ { use: actionCatalogItem.use },
7782
+ actionSettingsPayload,
7783
+ aiEmployeeSettingsPayload
7784
+ );
7785
+ await this.assertNoDuplicateAIEmployeeAction(
7786
+ "addRecordAction",
7787
+ materializedContainer.parentUid,
7788
+ actionSettingsPayload,
7789
+ options.transaction
7790
+ );
7791
+ }
7151
7792
  const action = (0, import_builder.buildActionTree)({
7152
7793
  use: actionCatalogItem.use,
7153
7794
  containerUse: container.containerUse,
7154
7795
  resourceInit,
7155
- props: aiEmployeeProps || values.props,
7796
+ props: actionSettingsPayload.props,
7156
7797
  decoratorProps: values.decoratorProps,
7157
- stepParams: values.stepParams,
7158
- flowRegistry: values.flowRegistry
7798
+ stepParams: actionSettingsPayload.stepParams,
7799
+ flowRegistry: this.normalizeEventFlowRegistry("addRecordAction", values.flowRegistry)
7159
7800
  });
7160
7801
  this.contractGuard.validateNodeTreeAgainstContract(action);
7161
7802
  const created = await this.repository.upsertModel(
@@ -7207,7 +7848,8 @@ class FlowSurfacesService {
7207
7848
  },
7208
7849
  {
7209
7850
  ...options,
7210
- preserveSingleScopeDataBlockTitle
7851
+ preserveSingleScopeDataBlockTitle,
7852
+ skipAuthoringValidation: true
7211
7853
  }
7212
7854
  )
7213
7855
  });
@@ -8083,6 +8725,17 @@ class FlowSurfacesService {
8083
8725
  const { fieldChanges } = (0, import_service_utils.splitComposeFieldChanges)(settings, wrapperUse);
8084
8726
  return fieldChanges.openView;
8085
8727
  }
8728
+ isInlineFieldClickToOpenEnabled(settings, wrapperUse) {
8729
+ if (!settings || !Object.keys(settings).length) {
8730
+ return false;
8731
+ }
8732
+ const { fieldChanges } = (0, import_service_utils.splitComposeFieldChanges)(settings, wrapperUse);
8733
+ if (!Object.prototype.hasOwnProperty.call(fieldChanges, "clickToOpen")) {
8734
+ return false;
8735
+ }
8736
+ const value = fieldChanges.clickToOpen;
8737
+ return value === true || import_lodash.default.isPlainObject(value) && value.clickToOpen === true;
8738
+ }
8086
8739
  async assertOpenViewUidTarget(actionName, uid2, options = {}) {
8087
8740
  const targetNode = await this.repository.findModelById(uid2, {
8088
8741
  transaction: options.transaction,
@@ -8613,15 +9266,71 @@ class FlowSurfacesService {
8613
9266
  sourceId: String(template.sourceId || "").trim() || void 0
8614
9267
  };
8615
9268
  }
8616
- buildPopupTemplateOpenView(template, mode, uidValue) {
9269
+ shouldUseRuntimeRecordFilterByTkForOpenView(openView, options = {}) {
9270
+ var _a;
9271
+ return !!String((openView == null ? void 0 : openView.popupTemplateUid) || "").trim() && ((_a = options.popupActionContext) == null ? void 0 : _a.hasCurrentRecord) === true;
9272
+ }
9273
+ resolveOpenViewFilterTargetKey(openView, template) {
9274
+ const dataSourceKey = String((openView == null ? void 0 : openView.dataSourceKey) || (template == null ? void 0 : template.dataSourceKey) || "main").trim() || "main";
9275
+ const collectionName = String((openView == null ? void 0 : openView.collectionName) || (template == null ? void 0 : template.collectionName) || "").trim();
9276
+ const collection = collectionName ? this.getCollection(dataSourceKey, collectionName) : null;
9277
+ return this.getCollectionFilterTargetKey(collection);
9278
+ }
9279
+ async resolvePopupHostCurrentRecordFilterTargetKey(hostUid, transaction) {
9280
+ var _a, _b, _c;
9281
+ const normalizedHostUid = String(hostUid || "").trim();
9282
+ if (!normalizedHostUid) {
9283
+ return void 0;
9284
+ }
9285
+ const hostNode = await this.repository.findModelById(normalizedHostUid, {
9286
+ transaction,
9287
+ includeAsyncNode: true
9288
+ }).catch(() => null);
9289
+ if (!(hostNode == null ? void 0 : hostNode.uid)) {
9290
+ return void 0;
9291
+ }
9292
+ const hostContext = await this.resolvePopupHostProfileContext(hostNode, transaction).catch(() => null);
9293
+ const ownerResourceInit = ((_a = hostContext == null ? void 0 : hostContext.resourceContext) == null ? void 0 : _a.resourceInit) || {};
9294
+ const dataSourceKey = String(((_b = hostContext == null ? void 0 : hostContext.associationContext) == null ? void 0 : _b.dataSourceKey) || ownerResourceInit.dataSourceKey || "main").trim() || "main";
9295
+ const collectionName = String(
9296
+ ((_c = hostContext == null ? void 0 : hostContext.associationContext) == null ? void 0 : _c.collectionName) || ownerResourceInit.collectionName || ""
9297
+ ).trim();
9298
+ const collection = collectionName ? this.getCollection(dataSourceKey, collectionName) : null;
9299
+ return collection ? this.getCollectionFilterTargetKey(collection) : void 0;
9300
+ }
9301
+ async resolveOpenViewRuntimeRecordFilterTargetKey(openView, template, options = {}) {
9302
+ var _a;
9303
+ const contextKey = String(((_a = options.popupActionContext) == null ? void 0 : _a.currentRecordFilterTargetKey) || "").trim();
9304
+ if (contextKey) {
9305
+ return contextKey;
9306
+ }
9307
+ const hostKey = await this.resolvePopupHostCurrentRecordFilterTargetKey(
9308
+ options.popupTemplateHostUid,
9309
+ options.transaction
9310
+ );
9311
+ return hostKey || this.resolveOpenViewFilterTargetKey(openView, template);
9312
+ }
9313
+ shouldOmitRuntimeRecordFilterByTk(value, options = {}) {
9314
+ const normalized = this.normalizeFlowContextTemplateValue(value);
9315
+ if (!normalized) {
9316
+ return false;
9317
+ }
9318
+ if (normalized === "{{ctx.view.inputArgs.filterByTk}}") {
9319
+ return true;
9320
+ }
9321
+ const filterTargetKey = String(options.filterTargetKey || "").trim();
9322
+ return !!filterTargetKey && normalized === `{{ctx.record.${filterTargetKey}}}`;
9323
+ }
9324
+ buildPopupTemplateOpenView(template, mode, uidValue, options = {}) {
8617
9325
  const popupTemplateHasFilterByTk = !!String(template.filterByTk || "").trim();
8618
9326
  const popupTemplateHasSourceId = !!String(template.sourceId || "").trim() || !!String(template.associationName || "").trim();
9327
+ const omitRuntimeRecordFilterByTk = options.useRuntimeRecordFilterByTk === true && this.shouldOmitRuntimeRecordFilterByTk(template.filterByTk, { filterTargetKey: options.filterTargetKey });
8619
9328
  const base = (0, import_service_utils.buildDefinedPayload)({
8620
9329
  uid: uidValue,
8621
9330
  dataSourceKey: template.dataSourceKey,
8622
9331
  collectionName: template.collectionName,
8623
9332
  associationName: template.associationName,
8624
- ...popupTemplateHasFilterByTk ? { filterByTk: template.filterByTk } : {},
9333
+ ...popupTemplateHasFilterByTk && !omitRuntimeRecordFilterByTk ? { filterByTk: template.filterByTk } : {},
8625
9334
  ...popupTemplateHasSourceId && template.sourceId ? { sourceId: template.sourceId } : {},
8626
9335
  popupTemplateHasFilterByTk,
8627
9336
  popupTemplateHasSourceId,
@@ -8694,7 +9403,7 @@ class FlowSurfacesService {
8694
9403
  }
8695
9404
  }
8696
9405
  async normalizeOpenView(actionName, openView, options = {}) {
8697
- var _a;
9406
+ var _a, _b, _c, _d;
8698
9407
  if (import_lodash.default.isUndefined(openView) || import_lodash.default.isNull(openView)) {
8699
9408
  return openView;
8700
9409
  }
@@ -8761,17 +9470,39 @@ class FlowSurfacesService {
8761
9470
  if (templateRef.mode === "copy") {
8762
9471
  await this.ensurePopupSurface(resolvedUid, options.transaction);
8763
9472
  }
8764
- let nextTemplateOpenView = this.buildPopupTemplateOpenView(template, templateRef.mode, resolvedUid);
9473
+ let runtimeRecordFilterTargetKey;
9474
+ if (((_b = options.popupActionContext) == null ? void 0 : _b.hasCurrentRecord) === true) {
9475
+ runtimeRecordFilterTargetKey = await this.resolveOpenViewRuntimeRecordFilterTargetKey(
9476
+ normalizedOpenView,
9477
+ template,
9478
+ options
9479
+ );
9480
+ }
9481
+ const filterTargetKey = runtimeRecordFilterTargetKey || this.resolveOpenViewFilterTargetKey(normalizedOpenView, template);
9482
+ let nextTemplateOpenView = this.buildPopupTemplateOpenView(template, templateRef.mode, resolvedUid, {
9483
+ filterTargetKey,
9484
+ useRuntimeRecordFilterByTk: ((_c = options.popupActionContext) == null ? void 0 : _c.hasCurrentRecord) === true
9485
+ });
8765
9486
  Object.keys(normalizedOpenView).forEach((key) => {
8766
9487
  if (key === "template" || key === "popupTemplateUid" || key === "popupTemplateMode" || key === "popupTemplateContext" || key === "popupTemplateHasFilterByTk" || key === "popupTemplateHasSourceId") {
8767
9488
  delete normalizedOpenView[key];
8768
9489
  }
8769
9490
  });
9491
+ if (((_d = options.popupActionContext) == null ? void 0 : _d.hasCurrentRecord) === true && this.shouldOmitRuntimeRecordFilterByTk(normalizedOpenView.filterByTk, {
9492
+ filterTargetKey
9493
+ })) {
9494
+ delete normalizedOpenView.filterByTk;
9495
+ }
8770
9496
  if (String(options.popupTemplateHostUse || "").trim() === "AddChildActionModel") {
8771
9497
  nextTemplateOpenView = import_lodash.default.omit(nextTemplateOpenView, ["sourceId"]);
8772
9498
  }
8773
9499
  Object.assign(normalizedOpenView, nextTemplateOpenView);
8774
9500
  }
9501
+ if (this.shouldUseRuntimeRecordFilterByTkForOpenView(normalizedOpenView, options) && this.shouldOmitRuntimeRecordFilterByTk(normalizedOpenView.filterByTk, {
9502
+ filterTargetKey: await this.resolveOpenViewRuntimeRecordFilterTargetKey(normalizedOpenView, void 0, options)
9503
+ })) {
9504
+ delete normalizedOpenView.filterByTk;
9505
+ }
8775
9506
  if (!import_lodash.default.isUndefined(normalizedOpenView.uid)) {
8776
9507
  const normalizedUid = String(normalizedOpenView.uid || "").trim();
8777
9508
  if (!normalizedUid) {
@@ -8812,7 +9543,8 @@ class FlowSurfacesService {
8812
9543
  return void 0;
8813
9544
  }
8814
9545
  return {
8815
- hasCurrentRecord: true
9546
+ hasCurrentRecord: true,
9547
+ currentRecordFilterTargetKey: await this.resolvePopupHostCurrentRecordFilterTargetKey(current.uid, transaction)
8816
9548
  };
8817
9549
  }
8818
9550
  unsetPayloadPathAndPruneEmptyParents(payload, path) {
@@ -8970,7 +9702,12 @@ class FlowSurfacesService {
8970
9702
  return {};
8971
9703
  }
8972
9704
  if (input.popup && !this.isPopupFieldHostUse(fieldNode.use)) {
8973
- (0, import_errors.throwBadRequest)(`flowSurfaces ${actionName} field '${fieldNode.use}' does not support popup`);
9705
+ (0, import_errors.throwBadRequest)(
9706
+ withJsPopupGuidance(
9707
+ `flowSurfaces ${actionName} field '${fieldNode.use}' does not support popup`,
9708
+ fieldNode.use
9709
+ )
9710
+ );
8974
9711
  }
8975
9712
  let openView = this.resolvePopupHostOpenView(fieldNode);
8976
9713
  if (input.popup && this.isExternalPopupOpenView(openView, fieldHostUid)) {
@@ -9296,6 +10033,20 @@ class FlowSurfacesService {
9296
10033
  isEmptyInlinePopupPayload(popup) {
9297
10034
  return import_lodash.default.isPlainObject(popup) && Object.keys(popup).length === 0;
9298
10035
  }
10036
+ isInlineFieldPopupDefaultShell(popup) {
10037
+ if (!import_lodash.default.isPlainObject(popup)) {
10038
+ return false;
10039
+ }
10040
+ const shellOnlyKeys = /* @__PURE__ */ new Set([
10041
+ "title",
10042
+ "mode",
10043
+ "openView",
10044
+ "tryTemplate",
10045
+ "defaultType",
10046
+ import_defaults.FLOW_SURFACE_APPLY_BLUEPRINT_POPUP_DEFAULTS_KEY
10047
+ ]);
10048
+ return Object.keys(popup).every((key) => shellOnlyKeys.has(key));
10049
+ }
9299
10050
  shouldAutoCompleteDefaultFieldPopup(fieldNode, popup, wrapperNode) {
9300
10051
  if (!import_lodash.default.isPlainObject(popup)) {
9301
10052
  return false;
@@ -9310,7 +10061,7 @@ class FlowSurfacesService {
9310
10061
  if (!String(fieldContext.collectionName || "").trim()) {
9311
10062
  return false;
9312
10063
  }
9313
- return popup.tryTemplate === true || !import_lodash.default.isUndefined(popup.defaultType) || Object.keys(popup).length === 0;
10064
+ return popup.tryTemplate === true || !import_lodash.default.isUndefined(popup.defaultType) || this.isInlineFieldPopupDefaultShell(popup);
9314
10065
  }
9315
10066
  isInlineFieldPopupBlockMissingResource(block) {
9316
10067
  if (!import_lodash.default.isPlainObject(block) || !import_lodash.default.isUndefined(block.template)) {
@@ -10372,14 +11123,21 @@ class FlowSurfacesService {
10372
11123
  actionNode,
10373
11124
  options.transaction
10374
11125
  ).catch(() => null);
10375
- const filterTargetKey = (popupProfile == null ? void 0 : popupProfile.currentCollection) ? this.getCollectionFilterTargetKey(popupProfile.currentCollection) : null;
11126
+ const useRuntimeRecordFilterByTk = this.isReferencedPopupTemplateOpenView(currentOpenView, actionNode.uid);
11127
+ const filterTargetKey = useRuntimeRecordFilterByTk ? await this.resolveOpenViewRuntimeRecordFilterTargetKey(currentOpenView, void 0, {
11128
+ transaction: options.transaction,
11129
+ popupTemplateHostUid: actionNode.uid,
11130
+ popupActionContext: {
11131
+ hasCurrentRecord: true
11132
+ }
11133
+ }) : (popupProfile == null ? void 0 : popupProfile.currentCollection) ? this.getCollectionFilterTargetKey(popupProfile.currentCollection) : null;
10376
11134
  const defaultFilterByTk = this.resolvePopupCurrentRecordResourceFilterByTk(popupProfile);
10377
11135
  const currentFilterByTk = import_lodash.default.isString(currentOpenView.filterByTk) ? currentOpenView.filterByTk.trim() : "";
10378
- const preserveCustomFilterByTk = currentFilterByTk && currentFilterByTk !== "{{ctx.view.inputArgs.filterByTk}}" && currentFilterByTk !== (filterTargetKey ? `{{ctx.record.${filterTargetKey}}}` : "");
11136
+ const preserveCustomFilterByTk = currentFilterByTk && !this.shouldOmitRuntimeRecordFilterByTk(currentOpenView.filterByTk, { filterTargetKey: filterTargetKey || "" });
10379
11137
  const nextOpenView = (0, import_service_utils.buildDefinedPayload)({
10380
- ...currentOpenView,
11138
+ ...useRuntimeRecordFilterByTk && !preserveCustomFilterByTk ? import_lodash.default.omit(currentOpenView, ["filterByTk"]) : currentOpenView,
10381
11139
  title: openViewTitle,
10382
- filterByTk: preserveCustomFilterByTk ? currentOpenView.filterByTk : defaultFilterByTk || currentOpenView.filterByTk
11140
+ filterByTk: useRuntimeRecordFilterByTk ? preserveCustomFilterByTk ? currentOpenView.filterByTk : void 0 : preserveCustomFilterByTk ? currentOpenView.filterByTk : defaultFilterByTk || currentOpenView.filterByTk
10383
11141
  });
10384
11142
  if (import_lodash.default.isEqual(nextOpenView, currentOpenView)) {
10385
11143
  return;
@@ -10641,7 +11399,7 @@ class FlowSurfacesService {
10641
11399
  }
10642
11400
  if (this.isAIEmployeeActionUse(current == null ? void 0 : current.use) && this.hasAIEmployeePublicSettings(normalizedValues)) {
10643
11401
  const enabledPackages = await this.resolveEnabledPluginPackages(options);
10644
- const aiEmployeeProps = await this.normalizeAIEmployeeActionPublicSettings(
11402
+ const aiEmployeeSettingsPayload = await this.normalizeAIEmployeeActionPublicSettings(
10645
11403
  "updateSettings",
10646
11404
  import_lodash.default.pick(normalizedValues, AI_EMPLOYEE_PUBLIC_SETTING_KEYS),
10647
11405
  {
@@ -10649,13 +11407,17 @@ class FlowSurfacesService {
10649
11407
  enabledPackages,
10650
11408
  current,
10651
11409
  currentRoles: options.currentRoles,
10652
- selfUid: await this.resolveAIEmployeeActionSelfUid(current, options.transaction)
11410
+ selfUid: await this.resolveAIEmployeeActionSelfUid(current, options.transaction),
11411
+ promptContext: {
11412
+ kind: "target",
11413
+ targetUid: (current == null ? void 0 : current.uid) || writeTarget.uid
11414
+ }
10653
11415
  }
10654
11416
  );
10655
11417
  AI_EMPLOYEE_PUBLIC_SETTING_KEYS.forEach((key) => {
10656
11418
  delete normalizedValues[key];
10657
11419
  });
10658
- normalizedValues.props = import_lodash.default.merge({}, normalizedValues.props || {}, aiEmployeeProps);
11420
+ this.mergeAIEmployeeActionSettingsPayload(current, normalizedValues, aiEmployeeSettingsPayload);
10659
11421
  }
10660
11422
  assertNoFlowSurfaceIdTitleFieldSettings(normalizedValues, {
10661
11423
  action: "updateSettings",
@@ -10675,6 +11437,7 @@ class FlowSurfacesService {
10675
11437
  }
10676
11438
  if (domain === "flowRegistry") {
10677
11439
  this.assertNoTreeConnectFieldsFlowRegistry(current, normalizedValues[domain], "updateSettings");
11440
+ normalizedValues[domain] = this.normalizeEventFlowRegistry("updateSettings", normalizedValues[domain]);
10678
11441
  }
10679
11442
  if (!contract.editableDomains.includes(domain)) {
10680
11443
  (0, import_errors.throwBadRequest)(`flowSurfaces updateSettings domain '${domain}' is not editable`);
@@ -10691,6 +11454,9 @@ class FlowSurfacesService {
10691
11454
  current.use
10692
11455
  );
10693
11456
  });
11457
+ if (!import_lodash.default.isUndefined(nextPayload.flowRegistry)) {
11458
+ nextPayload.flowRegistry = this.normalizeEventFlowRegistry("updateSettings", nextPayload.flowRegistry);
11459
+ }
10694
11460
  this.replaceExplicitPopupStepParamSubtreesForUpdateSettings(
10695
11461
  current,
10696
11462
  normalizedValues,
@@ -10703,6 +11469,14 @@ class FlowSurfacesService {
10703
11469
  nextPayload,
10704
11470
  options.replaceRecordHistorySettings === true
10705
11471
  );
11472
+ this.syncAIEmployeeTaskStepParamsForUpdateSettings(current, nextPayload);
11473
+ await this.normalizeAIEmployeeStepParamTasksForUpdateSettings(current, nextPayload, writeTarget, options);
11474
+ await this.assertAIEmployeeStepParamTaskPromptVariablesAllowedForUpdateSettings(
11475
+ current,
11476
+ nextPayload,
11477
+ writeTarget,
11478
+ options
11479
+ );
10706
11480
  this.syncCalendarPopupPropsForUpdateSettings(current, normalizedValues, nextPayload);
10707
11481
  this.syncKanbanPopupPropsForUpdateSettings(current, normalizedValues, nextPayload);
10708
11482
  this.syncDefaultSortingForUpdateSettings(current, normalizedValues, nextPayload);
@@ -10749,7 +11523,7 @@ class FlowSurfacesService {
10749
11523
  props: nextPayload.props ?? current.props,
10750
11524
  decoratorProps: nextPayload.decoratorProps ?? current.decoratorProps,
10751
11525
  stepParams: nextPayload.stepParams ?? current.stepParams,
10752
- flowRegistry: nextPayload.flowRegistry ?? current.flowRegistry
11526
+ flowRegistry: nextPayload.flowRegistry ?? this.getEventFlowRegistry(current)
10753
11527
  };
10754
11528
  const shouldValidateFlowRegistry = !import_lodash.default.isUndefined(nextPayload.flowRegistry) || !import_lodash.default.isUndefined(nextPayload.stepParams);
10755
11529
  assertNoFlowSurfaceIdTitleFieldSettings(import_lodash.default.pick(effectiveNode, ["props", "stepParams"]), {
@@ -11174,12 +11948,12 @@ class FlowSurfacesService {
11174
11948
  "flowSurfaces updateSettings filter action values 'props.defaultFilterValue/filterValue' and 'stepParams.filterSettings.defaultFilter.defaultFilter' must match"
11175
11949
  );
11176
11950
  }
11177
- const filterValue = this.normalizeEffectivePublicDataSurfaceDefaultFilter(
11178
- hasStepDefaultFilter ? nextStepFilter : nextPropFilter,
11179
- {
11180
- requiredFieldCount: options.requiredFieldCount
11181
- }
11951
+ const normalizedFilterValue = this.normalizeFilterActionDefaultFilterValue(
11952
+ hasStepDefaultFilter ? nextStepFilter : nextPropFilter
11182
11953
  );
11954
+ const filterValue = this.normalizeEffectivePublicDataSurfaceDefaultFilter(normalizedFilterValue, {
11955
+ requiredFieldCount: options.requiredFieldCount
11956
+ });
11183
11957
  if (!hasPropsFilterableFieldNames && !hasStepFilterableFieldNames) {
11184
11958
  const filterableFieldNames = (0, import_public_data_surface_default_filter.resolveFlowSurfaceDefaultFilterFieldNames)(filterValue);
11185
11959
  if (filterableFieldNames.length) {
@@ -11590,7 +12364,12 @@ class FlowSurfacesService {
11590
12364
  }
11591
12365
  if (!((_c = sqlPreview.queryOutputs) == null ? void 0 : _c.length)) {
11592
12366
  (0, import_errors.throwBadRequest)(
11593
- "chart visual.mode='basic' requires previewable SQL query outputs; write query first, then read flowSurfaces:context(path='chart'), or use visual.mode='custom' after browser verification"
12367
+ withChartRepairMessage(
12368
+ "chart visual.mode='basic' requires previewable SQL query outputs; write query first, then read flowSurfaces:context(path='chart'), or use visual.mode='custom' after browser verification"
12369
+ ),
12370
+ {
12371
+ details: withFlowSurfaceChartRepairDetails()
12372
+ }
11594
12373
  );
11595
12374
  }
11596
12375
  const supportedOutputs = new Set(
@@ -11599,7 +12378,14 @@ class FlowSurfacesService {
11599
12378
  for (const mappingField of (0, import_chart_config.getChartVisualMappingAliases)(state.visual)) {
11600
12379
  if (!supportedOutputs.has(mappingField)) {
11601
12380
  (0, import_errors.throwBadRequest)(
11602
- `chart visual mappings only support SQL query output fields: ${Array.from(supportedOutputs).join(", ")}`
12381
+ withChartRepairMessage(
12382
+ `chart visual mappings only support SQL query output fields: ${Array.from(supportedOutputs).join(", ")}`
12383
+ ),
12384
+ {
12385
+ details: withFlowSurfaceChartRepairDetails({
12386
+ supportedOutputs: Array.from(supportedOutputs)
12387
+ })
12388
+ }
11603
12389
  );
11604
12390
  }
11605
12391
  }
@@ -11611,7 +12397,7 @@ class FlowSurfacesService {
11611
12397
  return String(input || "").trim();
11612
12398
  }
11613
12399
  validateBuilderChartFieldsForRuntime(actionName, configure) {
11614
- var _a, _b;
12400
+ var _a, _b, _c, _d, _e, _f, _g;
11615
12401
  const resourceInit = (0, import_chart_config.getChartBuilderResourceInit)(configure);
11616
12402
  if (!(resourceInit == null ? void 0 : resourceInit.collectionName)) {
11617
12403
  return;
@@ -11629,10 +12415,12 @@ class FlowSurfacesService {
11629
12415
  const selections = [
11630
12416
  ...import_lodash.default.castArray(state.query.measures || []).map((selection, index) => ({
11631
12417
  selection,
12418
+ kind: "measure",
11632
12419
  path: `chart query.measures[${index}].field`
11633
12420
  })),
11634
12421
  ...import_lodash.default.castArray(state.query.dimensions || []).map((selection, index) => ({
11635
12422
  selection,
12423
+ kind: "dimension",
11636
12424
  path: `chart query.dimensions[${index}].field`
11637
12425
  }))
11638
12426
  ];
@@ -11641,20 +12429,153 @@ class FlowSurfacesService {
11641
12429
  if (!fieldPath) {
11642
12430
  continue;
11643
12431
  }
12432
+ const fieldPathParts = fieldPath.split(".").filter(Boolean);
12433
+ const isCountMeasureSelection = item.kind === "measure" && String(((_c = item.selection) == null ? void 0 : _c.aggregation) || "").trim() === "count" && !((_d = item.selection) == null ? void 0 : _d.distinct);
12434
+ if (fieldPathParts.length > 1 && !isCountMeasureSelection) {
12435
+ const directAssociationPath = fieldPathParts[0];
12436
+ const directAssociationField = (0, import_service_helpers.resolveFieldFromCollection)(collection, directAssociationPath);
12437
+ const directAssociationTargetCollection = directAssociationField && (0, import_service_helpers.isAssociationField)(directAssociationField) ? (0, import_service_helpers.resolveFieldTargetCollection)(
12438
+ directAssociationField,
12439
+ dataSourceKey,
12440
+ (resolvedDataSourceKey, targetCollection) => this.getCollection(resolvedDataSourceKey, targetCollection)
12441
+ ) : null;
12442
+ const invalidDirectSubfield = directAssociationTargetCollection ? (0, import_service_helpers.getInvalidChartBuilderRelationDirectSubfieldDetails)({
12443
+ associationPathName: directAssociationPath,
12444
+ selectedSubfieldPath: fieldPathParts.slice(1).join("."),
12445
+ targetCollection: directAssociationTargetCollection
12446
+ }) : null;
12447
+ if (invalidDirectSubfield) {
12448
+ (0, import_errors.throwBadRequest)(
12449
+ withChartRepairMessage(
12450
+ `flowSurfaces ${actionName} ${item.path} '${fieldPath}' must reference a direct scalar child field under relation '${invalidDirectSubfield.associationPath}'. ${(0, import_service_helpers.formatChartBuilderSupportedRelationSubfields)(
12451
+ invalidDirectSubfield.associationPath,
12452
+ invalidDirectSubfield.supportedFields
12453
+ )}`
12454
+ ),
12455
+ {
12456
+ path: item.path,
12457
+ ruleId: "chart-builder-query-relation-direct-subfield-required",
12458
+ details: withFlowSurfaceChartRepairDetails({
12459
+ fieldPath,
12460
+ dataSourceKey,
12461
+ collectionName,
12462
+ ...invalidDirectSubfield
12463
+ })
12464
+ }
12465
+ );
12466
+ }
12467
+ }
11644
12468
  const parsed = this.parseFieldPath(collection, fieldPath, void 0, dataSourceKey, collectionName);
12469
+ if (parsed.associationPathName) {
12470
+ const associationField = parsed.associationField;
12471
+ const associationTargetCollection = associationField && (0, import_service_helpers.isAssociationField)(associationField) ? (0, import_service_helpers.resolveFieldTargetCollection)(
12472
+ associationField,
12473
+ dataSourceKey,
12474
+ (resolvedDataSourceKey, targetCollection) => this.getCollection(resolvedDataSourceKey, targetCollection)
12475
+ ) : null;
12476
+ if (!associationField || !(0, import_service_helpers.isAssociationField)(associationField) || !associationTargetCollection) {
12477
+ (0, import_errors.throwBadRequest)(
12478
+ withChartRepairMessage(
12479
+ `flowSurfaces ${actionName} ${item.path} '${fieldPath}' uses invalid association path '${parsed.associationPathName}' for builder charts`
12480
+ ),
12481
+ {
12482
+ path: item.path,
12483
+ ruleId: "chart-builder-query-association-path-invalid",
12484
+ details: withFlowSurfaceChartRepairDetails({
12485
+ fieldPath,
12486
+ associationPath: parsed.associationPathName,
12487
+ dataSourceKey,
12488
+ collectionName
12489
+ })
12490
+ }
12491
+ );
12492
+ }
12493
+ }
11645
12494
  const field = (0, import_service_helpers.resolveFieldFromCollection)(parsed.leafCollection, parsed.leafFieldPath);
12495
+ const leafModelAttributes = (0, import_service_helpers.getCollectionModelAttributes)(parsed.leafCollection);
12496
+ const hasLeafModelAttribute = Object.prototype.hasOwnProperty.call(leafModelAttributes, parsed.leafFieldPath);
12497
+ const isCountMeasureRelationSubfield = item.kind === "measure" && String(((_e = item.selection) == null ? void 0 : _e.aggregation) || "").trim() === "count" && !((_f = item.selection) == null ? void 0 : _f.distinct) && parsed.associationField && (0, import_service_helpers.isAssociationField)(parsed.associationField);
12498
+ const unsupportedRelationSubfield = parsed.associationField && (0, import_service_helpers.isAssociationField)(parsed.associationField) ? (0, import_service_helpers.getUnsupportedChartBuilderRelationSubfieldDetails)({
12499
+ associationPathName: parsed.associationPathName,
12500
+ leafFieldName: parsed.leafFieldPath,
12501
+ leafField: field,
12502
+ targetCollection: parsed.leafCollection
12503
+ }) : null;
11646
12504
  if (!field) {
11647
- if (this.collectionHasConcreteField(parsed.leafCollection, parsed.leafFieldPath)) {
11648
- continue;
12505
+ const hasConcreteField = hasLeafModelAttribute || this.collectionHasConcreteField(parsed.leafCollection, parsed.leafFieldPath);
12506
+ if (!hasConcreteField) {
12507
+ (0, import_errors.throwBadRequest)(
12508
+ withChartRepairMessage(
12509
+ `flowSurfaces ${actionName} ${item.path} '${fieldPath}' does not exist on collection '${dataSourceKey}.${collectionName}'`
12510
+ ),
12511
+ {
12512
+ details: withFlowSurfaceChartRepairDetails({
12513
+ fieldPath,
12514
+ dataSourceKey,
12515
+ collectionName
12516
+ })
12517
+ }
12518
+ );
11649
12519
  }
12520
+ }
12521
+ if (!fieldPath.includes(".") && field && (0, import_service_helpers.isAssociationField)(field)) {
12522
+ const suggestion = this.resolveBuilderChartAssociationSubfieldSuggestion(fieldPath, field, dataSourceKey);
11650
12523
  (0, import_errors.throwBadRequest)(
11651
- `flowSurfaces ${actionName} ${item.path} '${fieldPath}' does not exist on collection '${dataSourceKey}.${collectionName}'`
12524
+ withChartRepairMessage(
12525
+ `flowSurfaces ${actionName} ${item.path} '${fieldPath}' references an association field directly; use scalar subfield '${suggestion.suggestedFieldPath}' for builder charts`
12526
+ ),
12527
+ {
12528
+ details: withFlowSurfaceChartRepairDetails({
12529
+ fieldPath,
12530
+ dataSourceKey,
12531
+ collectionName,
12532
+ ...suggestion
12533
+ })
12534
+ }
11652
12535
  );
11653
12536
  }
11654
- if (!fieldPath.includes(".") && (0, import_service_helpers.isAssociationField)(field)) {
11655
- const suggestion = this.resolveBuilderChartAssociationSubfieldSuggestion(fieldPath, field, dataSourceKey);
12537
+ if (isCountMeasureRelationSubfield) {
12538
+ (0, import_errors.throwBadRequest)(
12539
+ withChartRepairMessage(
12540
+ `flowSurfaces ${actionName} ${item.path} '${fieldPath}' counts a relation subfield; count a scalar base field such as 'id' and keep '${fieldPath}' as a dimension`
12541
+ ),
12542
+ {
12543
+ path: item.path,
12544
+ ruleId: "chart-builder-query-count-measure-relation-subfield",
12545
+ details: withFlowSurfaceChartRepairDetails({
12546
+ fieldPath,
12547
+ dataSourceKey,
12548
+ collectionName,
12549
+ suggestedMeasure: {
12550
+ field: "id",
12551
+ aggregation: "count",
12552
+ alias: String(((_g = item.selection) == null ? void 0 : _g.alias) || "").trim() || "recordCount"
12553
+ },
12554
+ suggestedDimension: {
12555
+ field: fieldPath
12556
+ }
12557
+ })
12558
+ }
12559
+ );
12560
+ }
12561
+ if (unsupportedRelationSubfield) {
11656
12562
  (0, import_errors.throwBadRequest)(
11657
- `flowSurfaces ${actionName} ${item.path} '${fieldPath}' references an association field directly; use scalar subfield '${suggestion.suggestedFieldPath}' for builder charts`
12563
+ withChartRepairMessage(
12564
+ `flowSurfaces ${actionName} ${item.path} '${fieldPath}' references relation subfield '${fieldPath}', but current chart builder SQL generation cannot query relation subfield '${unsupportedRelationSubfield.leafFieldName}' because its database column is '${unsupportedRelationSubfield.columnName}'. ${(0, import_service_helpers.formatChartBuilderSupportedRelationSubfields)(
12565
+ unsupportedRelationSubfield.associationPath,
12566
+ unsupportedRelationSubfield.supportedFields
12567
+ )}`
12568
+ ),
12569
+ {
12570
+ path: item.path,
12571
+ ruleId: "chart-builder-query-relation-subfield-column-unsupported",
12572
+ details: withFlowSurfaceChartRepairDetails({
12573
+ fieldPath,
12574
+ dataSourceKey,
12575
+ collectionName,
12576
+ ...unsupportedRelationSubfield
12577
+ })
12578
+ }
11658
12579
  );
11659
12580
  }
11660
12581
  }
@@ -11679,6 +12600,48 @@ class FlowSurfacesService {
11679
12600
  suggestedFieldPath: `${fieldPath}.<field>`
11680
12601
  };
11681
12602
  }
12603
+ async validateChartConfigureForRuntime(actionName, configure, transaction) {
12604
+ var _a, _b, _c;
12605
+ if (!import_lodash.default.isPlainObject(configure)) {
12606
+ return;
12607
+ }
12608
+ this.validateBuilderChartFieldsForRuntime(actionName, configure);
12609
+ const state = (0, import_chart_config.deriveChartSemanticState)(configure);
12610
+ if (((_a = state.query) == null ? void 0 : _a.mode) !== "sql") {
12611
+ return;
12612
+ }
12613
+ const sqlPreview = await this.resolveSqlChartPreview(state.query, transaction);
12614
+ if (((_b = state.visual) == null ? void 0 : _b.mode) !== "basic") {
12615
+ return;
12616
+ }
12617
+ if (!((_c = sqlPreview.queryOutputs) == null ? void 0 : _c.length)) {
12618
+ (0, import_errors.throwBadRequest)(
12619
+ withChartRepairMessage(
12620
+ "chart visual.mode='basic' requires previewable SQL query outputs; write query first, then read flowSurfaces:context(path='chart'), or use visual.mode='custom' after browser verification"
12621
+ ),
12622
+ {
12623
+ details: withFlowSurfaceChartRepairDetails()
12624
+ }
12625
+ );
12626
+ }
12627
+ const supportedOutputs = new Set(
12628
+ sqlPreview.queryOutputs.map((output) => String((output == null ? void 0 : output.alias) || "").trim()).filter(Boolean)
12629
+ );
12630
+ for (const mappingField of (0, import_chart_config.getChartVisualMappingAliases)(state.visual)) {
12631
+ if (!supportedOutputs.has(mappingField)) {
12632
+ (0, import_errors.throwBadRequest)(
12633
+ withChartRepairMessage(
12634
+ `chart visual mappings only support SQL query output fields: ${Array.from(supportedOutputs).join(", ")}`
12635
+ ),
12636
+ {
12637
+ details: withFlowSurfaceChartRepairDetails({
12638
+ supportedOutputs: Array.from(supportedOutputs)
12639
+ })
12640
+ }
12641
+ );
12642
+ }
12643
+ }
12644
+ }
11682
12645
  stripChartSqlForInspection(sql) {
11683
12646
  return sql.replace(/\/\*[\s\S]*?\*\//g, " ").replace(/--[^\r\n]*/g, " ").replace(/'(?:''|[^'])*'/g, "''").replace(/"(?:[""]|[^"])*"/g, '""');
11684
12647
  }
@@ -11686,16 +12649,16 @@ class FlowSurfacesService {
11686
12649
  const normalized = (typeof sql === "string" ? sql : "").trim();
11687
12650
  const inspected = this.stripChartSqlForInspection(normalized).replace(/;+\s*$/, "").trim();
11688
12651
  if (!inspected) {
11689
- (0, import_errors.throwBadRequest)("chart query.sql cannot be empty");
12652
+ throwChartRepairBadRequest("chart query.sql cannot be empty");
11690
12653
  }
11691
12654
  if (inspected.includes(";")) {
11692
- (0, import_errors.throwBadRequest)("chart query.sql must be a single read-only SELECT statement");
12655
+ throwChartRepairBadRequest("chart query.sql must be a single read-only SELECT statement");
11693
12656
  }
11694
12657
  if (!/^(with|select)\b/i.test(inspected)) {
11695
- (0, import_errors.throwBadRequest)("chart query.sql must start with SELECT or WITH");
12658
+ throwChartRepairBadRequest("chart query.sql must start with SELECT or WITH");
11696
12659
  }
11697
12660
  if (/\b(insert|update|delete|drop|alter|truncate|create|replace|grant|revoke|merge|call|execute)\b/i.test(inspected)) {
11698
- (0, import_errors.throwBadRequest)("chart query.sql must be read-only");
12661
+ throwChartRepairBadRequest("chart query.sql must be read-only");
11699
12662
  }
11700
12663
  return normalized.replace(/;+\s*$/, "").trim();
11701
12664
  }
@@ -11713,7 +12676,7 @@ class FlowSurfacesService {
11713
12676
  if ((_a = db == null ? void 0 : db.sequelize) == null ? void 0 : _a.query) {
11714
12677
  return db.sequelize.query(sql, { bind, transaction });
11715
12678
  }
11716
- (0, import_errors.throwBadRequest)("chart SQL preview is unavailable for the target data source");
12679
+ throwChartRepairBadRequest("chart SQL preview is unavailable for the target data source");
11717
12680
  }
11718
12681
  extractSqlChartPreviewAliases(metadata) {
11719
12682
  const pickName = (field) => {
@@ -11751,7 +12714,13 @@ class FlowSurfacesService {
11751
12714
  async resolveSqlChartPreview(query, _transaction) {
11752
12715
  var _a, _b;
11753
12716
  const normalizedSql = this.normalizeReadOnlyChartSql(query == null ? void 0 : query.sql);
11754
- const transformed = await (0, import_utils.transformSQL)(normalizedSql);
12717
+ let transformed;
12718
+ try {
12719
+ transformed = await (0, import_utils.transformSQL)(normalizedSql);
12720
+ } catch (error) {
12721
+ const message = (error == null ? void 0 : error.message) || String(error);
12722
+ throwChartRepairBadRequest(`chart query.sql is invalid: ${message}`);
12723
+ }
11755
12724
  const riskyHints = [];
11756
12725
  const hasRuntimeContext = Object.keys((transformed == null ? void 0 : transformed.bind) || {}).length > 0 || Object.keys((transformed == null ? void 0 : transformed.liquidContext) || {}).length > 0;
11757
12726
  if (hasRuntimeContext) {
@@ -11807,7 +12776,7 @@ class FlowSurfacesService {
11807
12776
  }
11808
12777
  const outputAliases = Object.keys(firstRow);
11809
12778
  if (!outputAliases.length) {
11810
- (0, import_errors.throwBadRequest)("chart query.sql must expose at least one output column");
12779
+ throwChartRepairBadRequest("chart query.sql must expose at least one output column");
11811
12780
  }
11812
12781
  return {
11813
12782
  queryOutputs: outputAliases.map((alias) => ({
@@ -11818,8 +12787,11 @@ class FlowSurfacesService {
11818
12787
  riskyHints
11819
12788
  };
11820
12789
  } catch (error) {
12790
+ if (isFlowSurfaceChartRepairError(error)) {
12791
+ throw error;
12792
+ }
11821
12793
  const message = (error == null ? void 0 : error.message) || String(error);
11822
- (0, import_errors.throwBadRequest)(`chart query.sql is invalid: ${message}`);
12794
+ throwChartRepairBadRequest(`chart query.sql is invalid: ${message}`);
11823
12795
  }
11824
12796
  }
11825
12797
  syncChartCardRuntimeStepParamsForUpdateSettings(current, nextPayload, replacementCardSettings) {
@@ -11876,6 +12848,8 @@ class FlowSurfacesService {
11876
12848
  options
11877
12849
  );
11878
12850
  const flowRegistry = this.getEventFlowRegistry(current);
12851
+ const directEvents = import_lodash.default.cloneDeep(((_a = contract.eventCapabilities) == null ? void 0 : _a.direct) || []);
12852
+ const objectEvents = import_lodash.default.uniq([...import_lodash.default.cloneDeep(((_b = contract.eventCapabilities) == null ? void 0 : _b.object) || []), ...directEvents]);
11879
12853
  return {
11880
12854
  target: {
11881
12855
  uid: target.uid,
@@ -11884,8 +12858,8 @@ class FlowSurfacesService {
11884
12858
  },
11885
12859
  flowRegistry,
11886
12860
  events: {
11887
- direct: import_lodash.default.cloneDeep(((_a = contract.eventCapabilities) == null ? void 0 : _a.direct) || []),
11888
- object: import_lodash.default.cloneDeep(((_b = contract.eventCapabilities) == null ? void 0 : _b.object) || [])
12861
+ direct: directEvents,
12862
+ object: objectEvents
11889
12863
  },
11890
12864
  phases: {
11891
12865
  supported: ["beforeAllFlows", "afterAllFlows", "beforeFlow", "afterFlow", "beforeStep", "afterStep"],
@@ -11920,13 +12894,12 @@ class FlowSurfacesService {
11920
12894
  if (phase !== "beforeAllFlows") {
11921
12895
  (0, import_errors.throwBadRequest)(`flowSurfaces addEventFlow only supports phase 'beforeAllFlows'`);
11922
12896
  }
11923
- const flow = this.normalizeAddEventFlowInput(key, values, phase);
11924
- const nextFlowRegistry = {
12897
+ const nextFlowRegistry = this.normalizeEventFlowRegistry("addEventFlow", {
11925
12898
  ...flowRegistry,
11926
- [key]: flow
11927
- };
12899
+ [key]: this.normalizeAddEventFlowInput(key, values, phase)
12900
+ });
11928
12901
  await this.persistEventFlowRegistry("addEventFlow", target, current, nextFlowRegistry, options);
11929
- return this.buildEventFlowWriteResult(target, key, flow, nextFlowRegistry);
12902
+ return this.buildEventFlowWriteResult(target, key, nextFlowRegistry[key], nextFlowRegistry);
11930
12903
  }
11931
12904
  async setEventFlow(values, options = {}) {
11932
12905
  var _a;
@@ -11940,13 +12913,12 @@ class FlowSurfacesService {
11940
12913
  const key = this.normalizeEventFlowKey("setEventFlow", (values == null ? void 0 : values.key) ?? ((_a = values == null ? void 0 : values.flow) == null ? void 0 : _a.key));
11941
12914
  this.assertEventFlowFingerprint("setEventFlow", values == null ? void 0 : values.expectedFingerprint, flowRegistry);
11942
12915
  const flowInput = import_lodash.default.isPlainObject(values == null ? void 0 : values.flow) ? values.flow : values;
11943
- const flow = this.normalizeEventFlowObject("setEventFlow", key, flowInput);
11944
- const nextFlowRegistry = {
12916
+ const nextFlowRegistry = this.normalizeEventFlowRegistry("setEventFlow", {
11945
12917
  ...flowRegistry,
11946
- [key]: flow
11947
- };
12918
+ [key]: this.normalizeEventFlowObject("setEventFlow", key, flowInput)
12919
+ });
11948
12920
  await this.persistEventFlowRegistry("setEventFlow", target, current, nextFlowRegistry, options);
11949
- return this.buildEventFlowWriteResult(target, key, flow, nextFlowRegistry);
12921
+ return this.buildEventFlowWriteResult(target, key, nextFlowRegistry[key], nextFlowRegistry);
11950
12922
  }
11951
12923
  async removeEventFlow(values, options = {}) {
11952
12924
  (0, import_payload_shape.validateFlowSurfacePayloadShape)("removeEventFlow", values, "values");
@@ -11961,14 +12933,14 @@ class FlowSurfacesService {
11961
12933
  (0, import_errors.throwBadRequest)(`flowSurfaces removeEventFlow flow '${key}' does not exist`);
11962
12934
  }
11963
12935
  this.assertEventFlowFingerprint("removeEventFlow", values == null ? void 0 : values.expectedFingerprint, flowRegistry);
11964
- const nextFlowRegistry = import_lodash.default.omit(flowRegistry, [key]);
12936
+ const nextFlowRegistry = this.normalizeEventFlowRegistry("removeEventFlow", import_lodash.default.omit(flowRegistry, [key]));
11965
12937
  await this.persistEventFlowRegistry("removeEventFlow", target, current, nextFlowRegistry, options);
11966
12938
  return this.buildEventFlowWriteResult(target, key, void 0, nextFlowRegistry);
11967
12939
  }
11968
12940
  async setEventFlows(values, options = {}) {
11969
12941
  (0, import_payload_shape.validateFlowSurfacePayloadShape)("setEventFlows", values, "values");
11970
12942
  const { target, current } = await this.resolveEventFlowTarget("setEventFlows", values == null ? void 0 : values.target, values, options);
11971
- const flows = values.flowRegistry || values.flows || {};
12943
+ const flows = this.normalizeEventFlowRegistry("setEventFlows", values.flowRegistry || values.flows || {});
11972
12944
  await this.persistEventFlowRegistry("setEventFlows", target, current, flows, options);
11973
12945
  return {
11974
12946
  uid: target.uid,
@@ -11994,7 +12966,10 @@ class FlowSurfacesService {
11994
12966
  };
11995
12967
  }
11996
12968
  getEventFlowRegistry(node) {
11997
- return import_lodash.default.isPlainObject(node == null ? void 0 : node.flowRegistry) ? import_lodash.default.cloneDeep(node.flowRegistry) : {};
12969
+ return import_lodash.default.isPlainObject(node == null ? void 0 : node.flowRegistry) ? this.normalizeEventFlowRegistry("getEventFlowRegistry", node.flowRegistry) : {};
12970
+ }
12971
+ normalizeEventFlowRegistry(actionName, flowRegistry) {
12972
+ return (0, import_event_flow_normalizer.normalizeFlowSurfaceEventFlowRegistry)(actionName, flowRegistry);
11998
12973
  }
11999
12974
  buildEventFlowFingerprint(flowRegistry) {
12000
12975
  return this.buildSurfaceFingerprint({
@@ -12032,7 +13007,9 @@ class FlowSurfacesService {
12032
13007
  normalizeAddEventFlowInput(key, values, phase) {
12033
13008
  var _a, _b, _c;
12034
13009
  const flow = import_lodash.default.isPlainObject(values.flow) ? import_lodash.default.cloneDeep(values.flow) : {};
12035
- const eventName = String(values.eventName ?? ((_a = flow == null ? void 0 : flow.on) == null ? void 0 : _a.eventName) ?? "").trim();
13010
+ const eventName = String(
13011
+ values.eventName ?? (typeof flow.on === "string" ? flow.on : (_a = flow == null ? void 0 : flow.on) == null ? void 0 : _a.eventName) ?? ""
13012
+ ).trim();
12036
13013
  if (!eventName) {
12037
13014
  (0, import_errors.throwBadRequest)(`flowSurfaces addEventFlow requires eventName`);
12038
13015
  }
@@ -12055,25 +13032,7 @@ class FlowSurfacesService {
12055
13032
  });
12056
13033
  }
12057
13034
  normalizeEventFlowObject(actionName, key, flowInput) {
12058
- if (!import_lodash.default.isPlainObject(flowInput)) {
12059
- (0, import_errors.throwBadRequest)(`flowSurfaces ${actionName} flow '${key}' must be an object`);
12060
- }
12061
- const flow = import_lodash.default.cloneDeep(flowInput);
12062
- flow.key = key;
12063
- if (import_lodash.default.isPlainObject(flow.on)) {
12064
- const eventName = String(flow.on.eventName || "").trim();
12065
- if (eventName) {
12066
- flow.on.eventName = eventName;
12067
- }
12068
- const phase = String(flow.on.phase || "").trim();
12069
- if (phase) {
12070
- flow.on.phase = phase;
12071
- }
12072
- }
12073
- if (import_lodash.default.isUndefined(flow.steps)) {
12074
- flow.steps = {};
12075
- }
12076
- return flow;
13035
+ return (0, import_event_flow_normalizer.normalizeFlowSurfaceEventFlow)(actionName, key, flowInput);
12077
13036
  }
12078
13037
  async persistEventFlowRegistry(actionName, target, current, flowRegistry, options = {}) {
12079
13038
  this.assertNoTreeConnectFieldsFlowRegistry(current, flowRegistry, actionName);
@@ -13827,6 +14786,97 @@ class FlowSurfacesService {
13827
14786
  }
13828
14787
  }
13829
14788
  }
14789
+ hasActionWithPublicType(actions, type) {
14790
+ return actions.some((action) => import_catalog.ACTION_KEY_BY_USE.get(String((action == null ? void 0 : action.use) || "").trim()) === type);
14791
+ }
14792
+ async ensureTableDefaultActionIntegrity(tableUid, options = {}) {
14793
+ var _a, _b, _c, _d;
14794
+ let table = await this.repository.findModelById(tableUid, {
14795
+ transaction: options.transaction,
14796
+ includeAsyncNode: true
14797
+ });
14798
+ if ((table == null ? void 0 : table.use) !== "TableBlockModel" || this.resolveTreeTableCreationContext(table)) {
14799
+ return;
14800
+ }
14801
+ const descriptors = (0, import_default_block_actions.getFlowSurfaceDefaultBlockActions)({ blockType: "table" });
14802
+ const blockActionDescriptors = descriptors.filter((descriptor) => descriptor.scope === "actions");
14803
+ const recordActionDescriptors = descriptors.filter((descriptor) => descriptor.scope === "recordActions");
14804
+ let blockActions = import_lodash.default.castArray(((_a = table == null ? void 0 : table.subModels) == null ? void 0 : _a.actions) || []);
14805
+ for (const descriptor of blockActionDescriptors) {
14806
+ if (this.hasActionWithPublicType(blockActions, descriptor.type)) {
14807
+ continue;
14808
+ }
14809
+ await this.addAction(
14810
+ {
14811
+ target: { uid: tableUid },
14812
+ type: descriptor.type,
14813
+ ...descriptor.popup ? { popup: import_lodash.default.cloneDeep(descriptor.popup) } : {}
14814
+ },
14815
+ options
14816
+ );
14817
+ table = await this.repository.findModelById(tableUid, {
14818
+ transaction: options.transaction,
14819
+ includeAsyncNode: true
14820
+ });
14821
+ blockActions = import_lodash.default.castArray(((_b = table == null ? void 0 : table.subModels) == null ? void 0 : _b.actions) || []);
14822
+ }
14823
+ const actionsColumnUid = await this.ensureTableActionsColumn(tableUid, options.transaction);
14824
+ let actionsColumn = await this.repository.findModelById(actionsColumnUid, {
14825
+ transaction: options.transaction,
14826
+ includeAsyncNode: true
14827
+ });
14828
+ let recordActions = import_lodash.default.castArray(((_c = actionsColumn == null ? void 0 : actionsColumn.subModels) == null ? void 0 : _c.actions) || []);
14829
+ for (const descriptor of recordActionDescriptors) {
14830
+ if (this.hasActionWithPublicType(recordActions, descriptor.type)) {
14831
+ continue;
14832
+ }
14833
+ await this.addRecordAction(
14834
+ {
14835
+ target: { uid: tableUid },
14836
+ type: descriptor.type,
14837
+ ...descriptor.popup ? { popup: import_lodash.default.cloneDeep(descriptor.popup) } : {}
14838
+ },
14839
+ options
14840
+ );
14841
+ actionsColumn = await this.repository.findModelById(actionsColumnUid, {
14842
+ transaction: options.transaction,
14843
+ includeAsyncNode: true
14844
+ });
14845
+ recordActions = import_lodash.default.castArray(((_d = actionsColumn == null ? void 0 : actionsColumn.subModels) == null ? void 0 : _d.actions) || []);
14846
+ }
14847
+ const missingRecordActions = recordActionDescriptors.map((descriptor) => descriptor.type).filter((type) => !this.hasActionWithPublicType(recordActions, type));
14848
+ if (missingRecordActions.length) {
14849
+ (0, import_errors.throwBadRequest)(`flowSurfaces table '${tableUid}' is missing required default record actions`, {
14850
+ ruleId: "table-record-actions-required",
14851
+ details: {
14852
+ tableUid,
14853
+ missingRecordActions,
14854
+ repairHint: "Put row actions under recordActions, or omit/pass an empty recordActions array so Flow Surfaces can create the default View/Edit/Delete row actions."
14855
+ }
14856
+ });
14857
+ }
14858
+ }
14859
+ collectNodeTreeDescendants(node, predicate, bucket = []) {
14860
+ if (!node || typeof node !== "object") {
14861
+ return bucket;
14862
+ }
14863
+ if (predicate(node)) {
14864
+ bucket.push(node);
14865
+ }
14866
+ Object.values(import_lodash.default.isPlainObject(node.subModels) ? node.subModels : {}).forEach((subModel) => {
14867
+ import_lodash.default.castArray(subModel).forEach((child) => this.collectNodeTreeDescendants(child, predicate, bucket));
14868
+ });
14869
+ return bucket;
14870
+ }
14871
+ async ensureSurfaceTableDefaultActionIntegrity(pageLocator, options = {}) {
14872
+ const surface = await this.get(pageLocator, options);
14873
+ const tables = this.collectNodeTreeDescendants(surface == null ? void 0 : surface.tree, (item) => (item == null ? void 0 : item.use) === "TableBlockModel");
14874
+ for (const table of tables) {
14875
+ if (table == null ? void 0 : table.uid) {
14876
+ await this.ensureTableDefaultActionIntegrity(table.uid, options);
14877
+ }
14878
+ }
14879
+ }
13830
14880
  async resolveReusableSingletonAction(input) {
13831
14881
  var _a, _b, _c;
13832
14882
  if (AUTO_SUBMIT_FORM_BLOCK_USES.has(input.ownerUse || "") && input.actionUse === "FormSubmitActionModel") {
@@ -13990,8 +15040,10 @@ class FlowSurfacesService {
13990
15040
  if (blockCatalogItem) {
13991
15041
  this.validateComposeActionGroups(blockCatalogItem.use, actions, recordActions, enabledPackages);
13992
15042
  }
13993
- const actionKeys = new Set(actions.map((item) => item.key).filter(Boolean));
13994
- const recordActionKeys = new Set(recordActions.map((item) => item.key).filter(Boolean));
15043
+ const actionKeys = new Set(actions.map((item) => String(item.key || "").trim()).filter(Boolean));
15044
+ const recordActionKeys = new Set(
15045
+ recordActions.map((item) => String(item.key || "").trim()).filter(Boolean)
15046
+ );
13995
15047
  const blockDescriptor = this.describeComposeBlock({
13996
15048
  index: index + 1,
13997
15049
  key,
@@ -14422,6 +15474,7 @@ class FlowSurfacesService {
14422
15474
  ...(0, import_service_utils.hasDefinedValue)(changes, [
14423
15475
  "pageSize",
14424
15476
  "density",
15477
+ "enableRowSelection",
14425
15478
  "showRowNumbers",
14426
15479
  "sorting",
14427
15480
  "dataScope",
@@ -14435,6 +15488,7 @@ class FlowSurfacesService {
14435
15488
  ...(0, import_service_utils.hasOwnDefined)(changes, "pageSize") ? { pageSize: { pageSize: changes.pageSize } } : {},
14436
15489
  ...(0, import_service_utils.hasOwnDefined)(changes, "density") ? { tableDensity: { size: changes.density } } : {},
14437
15490
  ...(0, import_service_utils.hasOwnDefined)(changes, "quickEdit") ? { quickEdit: { editable: changes.quickEdit } } : {},
15491
+ ...(0, import_service_utils.hasOwnDefined)(changes, "enableRowSelection") ? { enableRowSelection: { enableRowSelection: changes.enableRowSelection } } : {},
14438
15492
  ...(0, import_service_utils.hasOwnDefined)(changes, "showRowNumbers") ? { showRowNumbers: { showIndex: changes.showRowNumbers } } : {},
14439
15493
  ...(0, import_service_utils.hasOwnDefined)(changes, "sorting") ? {
14440
15494
  defaultSorting: {
@@ -15370,7 +16424,10 @@ class FlowSurfacesService {
15370
16424
  const shouldLoadCurrent = shouldUpdateConfigure || shouldUpdateCardSettings;
15371
16425
  const resolved = shouldLoadCurrent ? await this.locator.resolve(target, options) : null;
15372
16426
  const current = resolved ? await this.loadResolvedNode(resolved, options.transaction) : null;
15373
- let nextConfigure = shouldUpdateConfigure ? (0, import_chart_config.buildChartConfigureFromSemanticChanges)(import_lodash.default.get(current, ["stepParams", "chartSettings", "configure"]), changes) : void 0;
16427
+ let nextConfigure = shouldUpdateConfigure ? buildChartConfigureFromSemanticChangesWithRepair(
16428
+ import_lodash.default.get(current, ["stepParams", "chartSettings", "configure"]),
16429
+ changes
16430
+ ) : void 0;
15374
16431
  if (shouldUpdateConfigure) {
15375
16432
  nextConfigure = await this.stripBasicSqlVisualWhenPreviewUnavailable(nextConfigure, changes, options.transaction);
15376
16433
  }
@@ -15498,18 +16555,18 @@ class FlowSurfacesService {
15498
16555
  return String(raw || "").trim();
15499
16556
  }
15500
16557
  collectionHasConcreteField(collection, fieldName) {
15501
- var _a, _b, _c, _d, _e, _f;
16558
+ var _a, _b, _c;
15502
16559
  const normalized = String(fieldName || "").trim();
15503
16560
  if (!normalized) {
15504
16561
  return false;
15505
16562
  }
15506
- const modelAttributes = (typeof ((_a = collection == null ? void 0 : collection.model) == null ? void 0 : _a.getAttributes) === "function" ? collection.model.getAttributes() : null) || ((_b = collection == null ? void 0 : collection.model) == null ? void 0 : _b.rawAttributes) || ((_c = collection == null ? void 0 : collection.model) == null ? void 0 : _c.attributes) || {};
16563
+ const modelAttributes = (0, import_service_helpers.getCollectionModelAttributes)(collection);
15507
16564
  const primaryKeyAttributes = import_lodash.default.castArray(
15508
- ((_d = collection == null ? void 0 : collection.model) == null ? void 0 : _d.primaryKeyAttributes) || ((_e = collection == null ? void 0 : collection.model) == null ? void 0 : _e.primaryKeyAttribute) || []
16565
+ ((_a = collection == null ? void 0 : collection.model) == null ? void 0 : _a.primaryKeyAttributes) || ((_b = collection == null ? void 0 : collection.model) == null ? void 0 : _b.primaryKeyAttribute) || []
15509
16566
  );
15510
16567
  const modelAttribute = modelAttributes == null ? void 0 : modelAttributes[normalized];
15511
16568
  const isModelPrimaryKey = primaryKeyAttributes.includes(normalized) || !!(modelAttribute == null ? void 0 : modelAttribute.primaryKey);
15512
- return !!((0, import_service_helpers.resolveFieldFromCollection)(collection, normalized) || ((_f = collection == null ? void 0 : collection.getField) == null ? void 0 : _f.call(collection, normalized)) || (0, import_service_helpers.getCollectionFields)(collection).some((field) => (0, import_service_helpers.getFieldName)(field) === normalized) || isModelPrimaryKey);
16569
+ return !!((0, import_service_helpers.resolveFieldFromCollection)(collection, normalized) || ((_c = collection == null ? void 0 : collection.getField) == null ? void 0 : _c.call(collection, normalized)) || (0, import_service_helpers.getCollectionFields)(collection).some((field) => (0, import_service_helpers.getFieldName)(field) === normalized) || isModelPrimaryKey);
15513
16570
  }
15514
16571
  assertInitialBlockResourceCompatible(actionName, blockUse, resourceInit, popupProfile) {
15515
16572
  if (blockUse === "CommentsBlockModel") {
@@ -15812,6 +16869,7 @@ class FlowSurfacesService {
15812
16869
  );
15813
16870
  }
15814
16871
  async configureJSColumn(target, changes, options) {
16872
+ assertNoJsDeclarativeOpenView("jsColumn", changes, "JSColumnModel");
15815
16873
  const allowedKeys = (0, import_configure_options.getConfigureOptionKeysForUse)("JSColumnModel");
15816
16874
  (0, import_service_utils.assertSupportedSimpleChanges)("jsColumn", changes, allowedKeys);
15817
16875
  return this.updateSettings(
@@ -15845,6 +16903,8 @@ class FlowSurfacesService {
15845
16903
  );
15846
16904
  }
15847
16905
  async configureJSItem(target, changes, options) {
16906
+ const { currentUse = "JSItemModel", ...updateOptions } = options;
16907
+ assertNoJsDeclarativeOpenView("jsItem", changes, currentUse);
15848
16908
  const allowedKeys = (0, import_configure_options.getConfigureOptionKeysForUse)("JSItemModel");
15849
16909
  (0, import_service_utils.assertSupportedSimpleChanges)("jsItem", changes, allowedKeys);
15850
16910
  return this.updateSettings(
@@ -15869,7 +16929,7 @@ class FlowSurfacesService {
15869
16929
  }
15870
16930
  } : void 0
15871
16931
  },
15872
- options
16932
+ updateOptions
15873
16933
  );
15874
16934
  }
15875
16935
  async configureDividerItem(target, changes, options) {
@@ -15920,6 +16980,7 @@ class FlowSurfacesService {
15920
16980
  transaction: options.transaction,
15921
16981
  includeAsyncNode: true
15922
16982
  }) : null;
16983
+ assertNoJsDeclarativeOpenView("field", changes, innerField == null ? void 0 : innerField.use);
15923
16984
  const currentFieldInit = ((_e = (_d = current == null ? void 0 : current.stepParams) == null ? void 0 : _d.fieldSettings) == null ? void 0 : _e.init) || ((_g = (_f = innerField == null ? void 0 : innerField.stepParams) == null ? void 0 : _f.fieldSettings) == null ? void 0 : _g.init) || {};
15924
16985
  const bindingChange = (0, import_service_utils.hasDefinedValue)(wrapperChanges, ["fieldPath", "associationPathName"]);
15925
16986
  const normalizedBinding = bindingChange ? this.normalizeDisplayFieldBinding({
@@ -16131,6 +17192,8 @@ class FlowSurfacesService {
16131
17192
  const enabledPackages = await this.resolveEnabledPluginPackages(options);
16132
17193
  const resolved = await this.locator.resolve(target, options);
16133
17194
  const current = await this.loadResolvedNode(resolved, options.transaction);
17195
+ const isJsFieldNode = ["JSFieldModel", "JSEditableFieldModel"].includes((current == null ? void 0 : current.use) || "");
17196
+ assertNoJsDeclarativeOpenView("field", changes, current == null ? void 0 : current.use);
16134
17197
  (0, import_service_utils.assertSupportedSimpleChanges)(
16135
17198
  "field",
16136
17199
  changes,
@@ -16141,7 +17204,6 @@ class FlowSurfacesService {
16141
17204
  transaction: options.transaction,
16142
17205
  includeAsyncNode: true
16143
17206
  }) : null;
16144
- const isJsFieldNode = ["JSFieldModel", "JSEditableFieldModel"].includes((current == null ? void 0 : current.use) || "");
16145
17207
  if ((0, import_service_utils.hasDefinedValue)(changes, ["code", "version"]) && !isJsFieldNode) {
16146
17208
  (0, import_errors.throwBadRequest)(`flowSurfaces configure field '${current == null ? void 0 : current.use}' does not support code/version`);
16147
17209
  }
@@ -16315,7 +17377,11 @@ class FlowSurfacesService {
16315
17377
  items: []
16316
17378
  };
16317
17379
  }
16318
- return import_lodash.default.cloneDeep(value);
17380
+ return (0, import_filter_group.normalizeFlowSurfaceFilterGroupValue)(
17381
+ value,
17382
+ `flowSurfaces configure action defaultFilter expects FilterGroup like ${import_filter_group.FLOW_SURFACE_FILTER_GROUP_EXAMPLE}`,
17383
+ { strictDateValues: true }
17384
+ );
16319
17385
  }
16320
17386
  normalizeActionAssignValues(actionName, value) {
16321
17387
  if (!import_lodash.default.isPlainObject(value)) {
@@ -16346,6 +17412,93 @@ class FlowSurfacesService {
16346
17412
  isAIEmployeeActionUse(use) {
16347
17413
  return String(use || "").trim() === AI_EMPLOYEE_ACTION_USE;
16348
17414
  }
17415
+ stableSerializeAIEmployeeValue(value) {
17416
+ if (Array.isArray(value)) {
17417
+ return `[${value.map((item) => this.stableSerializeAIEmployeeValue(item)).join(",")}]`;
17418
+ }
17419
+ if (import_lodash.default.isPlainObject(value)) {
17420
+ return `{${Object.keys(value).sort().map((key) => `${JSON.stringify(key)}:${this.stableSerializeAIEmployeeValue(value[key])}`).join(",")}}`;
17421
+ }
17422
+ return JSON.stringify(value);
17423
+ }
17424
+ buildAIEmployeeActionDedupIdentity(values) {
17425
+ var _a, _b, _c, _d, _e, _f;
17426
+ const username = String(((_b = (_a = values == null ? void 0 : values.props) == null ? void 0 : _a.aiEmployee) == null ? void 0 : _b.username) || "").trim();
17427
+ if (!username) {
17428
+ return "";
17429
+ }
17430
+ return this.stableSerializeAIEmployeeValue({
17431
+ username,
17432
+ auto: typeof ((_c = values == null ? void 0 : values.props) == null ? void 0 : _c.auto) === "boolean" ? values.props.auto : false,
17433
+ workContext: this.normalizeAIEmployeeWorkContextForDedupIdentity((_e = (_d = values == null ? void 0 : values.props) == null ? void 0 : _d.context) == null ? void 0 : _e.workContext),
17434
+ tasks: this.normalizeAIEmployeeTasksForDedupIdentity(
17435
+ import_lodash.default.get(values, ["stepParams", ...AI_EMPLOYEE_TASK_STEP_PARAMS_PATH])
17436
+ ),
17437
+ style: {
17438
+ ...AI_EMPLOYEE_DEFAULT_STYLE,
17439
+ ...import_lodash.default.isPlainObject((_f = values == null ? void 0 : values.props) == null ? void 0 : _f.style) ? import_lodash.default.pick(values.props.style, AI_EMPLOYEE_STYLE_PUBLIC_KEYS) : {}
17440
+ }
17441
+ });
17442
+ }
17443
+ normalizeAIEmployeeWorkContextForDedupIdentity(value) {
17444
+ return import_lodash.default.castArray(value || []).map(
17445
+ (item) => import_lodash.default.isPlainObject(item) ? import_lodash.default.pick(item, AI_EMPLOYEE_WORK_CONTEXT_PUBLIC_KEYS) : item
17446
+ );
17447
+ }
17448
+ normalizeAIEmployeeTasksForDedupIdentity(value) {
17449
+ return import_lodash.default.castArray(value || []).map((task) => {
17450
+ if (!import_lodash.default.isPlainObject(task)) {
17451
+ return task;
17452
+ }
17453
+ const output = import_lodash.default.pick(task, AI_EMPLOYEE_TASK_PUBLIC_SETTING_KEYS);
17454
+ if (import_lodash.default.isPlainObject(output.message)) {
17455
+ output.message = import_lodash.default.pick(output.message, AI_EMPLOYEE_TASK_MESSAGE_PUBLIC_KEYS);
17456
+ if (Object.prototype.hasOwnProperty.call(output.message, "workContext")) {
17457
+ output.message.workContext = this.normalizeAIEmployeeWorkContextForDedupIdentity(output.message.workContext);
17458
+ }
17459
+ }
17460
+ if (import_lodash.default.isPlainObject(output.model)) {
17461
+ output.model = import_lodash.default.pick(output.model, AI_EMPLOYEE_TASK_MODEL_PUBLIC_KEYS);
17462
+ }
17463
+ if (import_lodash.default.isPlainObject(output.skillSettings)) {
17464
+ output.skillSettings = import_lodash.default.pick(output.skillSettings, AI_EMPLOYEE_SKILL_SETTINGS_PUBLIC_KEYS);
17465
+ }
17466
+ return output;
17467
+ });
17468
+ }
17469
+ async assertNoDuplicateAIEmployeeAction(actionName, parentUid, values, transaction) {
17470
+ var _a;
17471
+ const identity = this.buildAIEmployeeActionDedupIdentity(values);
17472
+ if (!identity) {
17473
+ return;
17474
+ }
17475
+ const parentNode = await this.repository.findModelById(parentUid, {
17476
+ transaction,
17477
+ includeAsyncNode: true
17478
+ });
17479
+ const duplicate = import_lodash.default.castArray(((_a = parentNode == null ? void 0 : parentNode.subModels) == null ? void 0 : _a.actions) || []).find((action) => {
17480
+ if (!this.isAIEmployeeActionUse(action == null ? void 0 : action.use)) {
17481
+ return false;
17482
+ }
17483
+ return this.buildAIEmployeeActionDedupIdentity({
17484
+ props: action.props,
17485
+ stepParams: action.stepParams
17486
+ }) === identity;
17487
+ });
17488
+ if (!(duplicate == null ? void 0 : duplicate.uid)) {
17489
+ return;
17490
+ }
17491
+ (0, import_errors.throwBadRequest)(
17492
+ `flowSurfaces ${actionName} duplicates an existing AI employee action in the same action container; reuse or update the existing button instead of appending another identical AI employee action`,
17493
+ {
17494
+ ruleId: "duplicate-ai-employee-action",
17495
+ details: {
17496
+ existingUid: duplicate.uid,
17497
+ repairHint: "Remove the duplicate aiEmployee action, or change username, task title/key, or workContext if this is a genuinely different AI employee action."
17498
+ }
17499
+ }
17500
+ );
17501
+ }
16349
17502
  hasAIEmployeePublicSettings(settings) {
16350
17503
  return import_lodash.default.isPlainObject(settings) && AI_EMPLOYEE_PUBLIC_SETTING_KEYS.some((key) => Object.prototype.hasOwnProperty.call(settings, key));
16351
17504
  }
@@ -16353,16 +17506,31 @@ class FlowSurfacesService {
16353
17506
  const unsupportedKeys = Object.keys(settings || {}).filter((key) => !AI_EMPLOYEE_PUBLIC_SETTING_KEYS.includes(key));
16354
17507
  if (unsupportedKeys.length) {
16355
17508
  (0, import_errors.throwBadRequest)(
16356
- `flowSurfaces ${actionName} AI employee settings do not support keys: ${unsupportedKeys.join(", ")}`
17509
+ `flowSurfaces ${actionName} AI employee settings do not support keys: ${unsupportedKeys.join(
17510
+ ", "
17511
+ )}; allowed keys: ${AI_EMPLOYEE_PUBLIC_SETTING_KEYS.join(
17512
+ ", "
17513
+ )}; repairHint: use top-level username, auto, workContext, tasks, or style instead of raw props/stepParams`
16357
17514
  );
16358
17515
  }
16359
17516
  }
17517
+ aiEmployeeNestedRepairHint(path) {
17518
+ if (path.includes("workContext")) {
17519
+ return AI_EMPLOYEE_WORK_CONTEXT_REPAIR_HINT;
17520
+ }
17521
+ if (path.includes("tasks")) {
17522
+ return AI_EMPLOYEE_TASK_REPAIR_HINT;
17523
+ }
17524
+ return "Use only the documented public AI employee settings keys for this nested object.";
17525
+ }
16360
17526
  assertOnlyAIEmployeeNestedPublicSettings(actionName, path, value, allowedKeys) {
16361
17527
  const unsupportedKeys = Object.keys(value || {}).filter((key) => !allowedKeys.includes(key));
16362
17528
  if (!unsupportedKeys.length) {
16363
17529
  return;
16364
17530
  }
16365
- (0, import_errors.throwBadRequest)(`flowSurfaces ${actionName} ${path} does not support key '${unsupportedKeys[0]}'`);
17531
+ (0, import_errors.throwBadRequest)(
17532
+ `flowSurfaces ${actionName} ${path} does not support key '${unsupportedKeys[0]}'; allowed keys: ${allowedKeys.join(", ")}; repairHint: ${this.aiEmployeeNestedRepairHint(path)}`
17533
+ );
16366
17534
  }
16367
17535
  assertNoAIEmployeeInternalPropSettings(actionName, values) {
16368
17536
  const props = values == null ? void 0 : values.props;
@@ -16381,6 +17549,67 @@ class FlowSurfacesService {
16381
17549
  )}; use top-level username, auto, workContext, tasks or style instead`
16382
17550
  );
16383
17551
  }
17552
+ readAIEmployeePersistedTasks(current) {
17553
+ var _a;
17554
+ const stepTasks = import_lodash.default.get(current, ["stepParams", ...AI_EMPLOYEE_TASK_STEP_PARAMS_PATH]);
17555
+ if (Array.isArray(stepTasks)) {
17556
+ return stepTasks;
17557
+ }
17558
+ const legacyPropsTasks = (_a = current == null ? void 0 : current.props) == null ? void 0 : _a.tasks;
17559
+ return Array.isArray(legacyPropsTasks) ? legacyPropsTasks : [];
17560
+ }
17561
+ buildAIEmployeeTaskStepParams(tasks) {
17562
+ const stepParams = {};
17563
+ import_lodash.default.set(stepParams, AI_EMPLOYEE_TASK_STEP_PARAMS_PATH, import_lodash.default.cloneDeep(tasks));
17564
+ return stepParams;
17565
+ }
17566
+ mergeAIEmployeeActionSettingsPayload(current, values, payload) {
17567
+ if (Object.keys(payload.props).length) {
17568
+ values.props = import_lodash.default.merge({}, values.props || {}, payload.props);
17569
+ }
17570
+ if (Object.keys(payload.stepParams).length) {
17571
+ values.stepParams = import_lodash.default.mergeWith({}, values.stepParams || {}, payload.stepParams, (_currentValue, nextValue) => {
17572
+ if (Array.isArray(nextValue)) {
17573
+ return import_lodash.default.cloneDeep(nextValue);
17574
+ }
17575
+ return void 0;
17576
+ });
17577
+ }
17578
+ if (import_lodash.default.has(payload.stepParams, AI_EMPLOYEE_TASK_STEP_PARAMS_PATH) && this.isAIEmployeeActionUse(current == null ? void 0 : current.use)) {
17579
+ if (import_lodash.default.isPlainObject(values.props)) {
17580
+ delete values.props.tasks;
17581
+ }
17582
+ }
17583
+ }
17584
+ syncAIEmployeeTaskStepParamsForUpdateSettings(current, nextPayload) {
17585
+ var _a;
17586
+ if (!this.isAIEmployeeActionUse(current == null ? void 0 : current.use)) {
17587
+ return;
17588
+ }
17589
+ const legacyPropsTasks = (_a = current == null ? void 0 : current.props) == null ? void 0 : _a.tasks;
17590
+ const hasLegacyPropsTasks = Array.isArray(legacyPropsTasks);
17591
+ const hasNextStepTasks = import_lodash.default.has(nextPayload, ["stepParams", ...AI_EMPLOYEE_TASK_STEP_PARAMS_PATH]);
17592
+ const currentStepTasks = import_lodash.default.get(current, ["stepParams", ...AI_EMPLOYEE_TASK_STEP_PARAMS_PATH]);
17593
+ if (!hasNextStepTasks && hasLegacyPropsTasks && !Array.isArray(currentStepTasks)) {
17594
+ nextPayload.stepParams = import_lodash.default.mergeWith(
17595
+ {},
17596
+ (current == null ? void 0 : current.stepParams) || {},
17597
+ this.buildAIEmployeeTaskStepParams(legacyPropsTasks),
17598
+ (_currentValue, nextValue) => {
17599
+ if (Array.isArray(nextValue)) {
17600
+ return import_lodash.default.cloneDeep(nextValue);
17601
+ }
17602
+ return void 0;
17603
+ }
17604
+ );
17605
+ }
17606
+ if (!hasLegacyPropsTasks && !import_lodash.default.has(nextPayload, ["props", "tasks"])) {
17607
+ return;
17608
+ }
17609
+ const nextProps = import_lodash.default.cloneDeep(nextPayload.props ?? (current == null ? void 0 : current.props) ?? {});
17610
+ delete nextProps.tasks;
17611
+ nextPayload.props = nextProps;
17612
+ }
16384
17613
  assertAIEmployeePluginEnabled(actionName, enabledPackages) {
16385
17614
  if (!enabledPackages.has(AI_EMPLOYEE_OWNER_PLUGIN)) {
16386
17615
  (0, import_errors.throwBadRequest)(
@@ -16464,8 +17693,13 @@ class FlowSurfacesService {
16464
17693
  return username;
16465
17694
  }
16466
17695
  assertAIEmployeeWorkContextType(actionName, itemPath, item) {
17696
+ if (!Object.prototype.hasOwnProperty.call(item, "type") || import_lodash.default.isUndefined(item.type) || item.type === null) {
17697
+ return;
17698
+ }
16467
17699
  if (String(item.type || "").trim() !== "flow-model") {
16468
- (0, import_errors.throwBadRequest)(`flowSurfaces ${actionName} ${itemPath}.type must be 'flow-model'`);
17700
+ (0, import_errors.throwBadRequest)(
17701
+ `flowSurfaces ${actionName} ${itemPath}.type only supports 'flow-model'; omit type to use the default flow-model context. repairHint: ${AI_EMPLOYEE_WORK_CONTEXT_REPAIR_HINT}`
17702
+ );
16469
17703
  }
16470
17704
  }
16471
17705
  normalizeAIEmployeeWorkContext(actionName, value, options) {
@@ -16574,8 +17808,290 @@ class FlowSurfacesService {
16574
17808
  if (Object.prototype.hasOwnProperty.call(next, "toolsVersion") && typeof next.toolsVersion !== "number") {
16575
17809
  (0, import_errors.throwBadRequest)(`flowSurfaces ${actionName} ${path}.toolsVersion must be a number`);
16576
17810
  }
17811
+ const hasSkills = Object.prototype.hasOwnProperty.call(next, "skills");
17812
+ const hasTools = Object.prototype.hasOwnProperty.call(next, "tools");
17813
+ const hasSkillsVersion = Object.prototype.hasOwnProperty.call(next, "skillsVersion");
17814
+ const hasToolsVersion = Object.prototype.hasOwnProperty.call(next, "toolsVersion");
17815
+ if (hasSkills && hasTools && !hasSkillsVersion && !hasToolsVersion && Array.isArray(next.skills) && Array.isArray(next.tools) && next.skills.length === 0 && next.tools.length === 0) {
17816
+ return null;
17817
+ }
17818
+ if (hasSkills && !hasSkillsVersion) {
17819
+ next.skillsVersion = 2;
17820
+ }
17821
+ if (hasTools && !hasToolsVersion) {
17822
+ next.toolsVersion = 2;
17823
+ }
16577
17824
  return next;
16578
17825
  }
17826
+ collectAIEmployeePromptVariables(value) {
17827
+ if (!value.includes("{{")) {
17828
+ return [];
17829
+ }
17830
+ return [...value.matchAll(/\{\{\s*([\s\S]+?)\s*\}\}/g)].map(([raw, inner]) => ({
17831
+ raw,
17832
+ inner: String(inner || "").trim()
17833
+ }));
17834
+ }
17835
+ hasAIEmployeePromptVariables(tasks) {
17836
+ return import_lodash.default.castArray(tasks || []).some((task) => {
17837
+ const message = task == null ? void 0 : task.message;
17838
+ return typeof (message == null ? void 0 : message.system) === "string" && this.collectAIEmployeePromptVariables(message.system).length > 0 || typeof (message == null ? void 0 : message.user) === "string" && this.collectAIEmployeePromptVariables(message.user).length > 0;
17839
+ });
17840
+ }
17841
+ collectAIEmployeeContextPathEntries(context) {
17842
+ const result = /* @__PURE__ */ new Map();
17843
+ const visit = (prefix, info) => {
17844
+ if (!prefix || !info) {
17845
+ return;
17846
+ }
17847
+ result.set(prefix, info);
17848
+ if (info.dynamicProperties) {
17849
+ result.set(`${prefix}.*`, info.dynamicProperties);
17850
+ }
17851
+ for (const [childKey, childInfo] of Object.entries(info.properties || {})) {
17852
+ visit(`${prefix}.${childKey}`, childInfo);
17853
+ }
17854
+ };
17855
+ for (const [key, info] of Object.entries((context == null ? void 0 : context.vars) || {})) {
17856
+ visit(key, info);
17857
+ }
17858
+ return result;
17859
+ }
17860
+ isAIEmployeeContextInfoPathAllowed(info, segments) {
17861
+ var _a;
17862
+ if (!info) {
17863
+ return false;
17864
+ }
17865
+ if (!segments.length) {
17866
+ return true;
17867
+ }
17868
+ const [segment, ...rest] = segments;
17869
+ const staticChild = (_a = info.properties) == null ? void 0 : _a[segment];
17870
+ if (staticChild) {
17871
+ return this.isAIEmployeeContextInfoPathAllowed(staticChild, rest);
17872
+ }
17873
+ if (info.dynamicProperties) {
17874
+ return this.isAIEmployeeContextInfoPathAllowed(info.dynamicProperties, rest);
17875
+ }
17876
+ return false;
17877
+ }
17878
+ canResolveAIEmployeeContextPath(path, semantic) {
17879
+ var _a;
17880
+ return !!((_a = (0, import_context.buildFlowSurfaceContextResponse)({
17881
+ semantic,
17882
+ path,
17883
+ maxDepth: 1
17884
+ }).vars) == null ? void 0 : _a[path]);
17885
+ }
17886
+ isAIEmployeeContextPathAllowed(path, validation) {
17887
+ if (validation.contextPaths.has(path)) {
17888
+ return true;
17889
+ }
17890
+ for (const [allowedPath, info] of validation.contextPaths) {
17891
+ if (!allowedPath.endsWith(".*")) {
17892
+ continue;
17893
+ }
17894
+ const prefix = allowedPath.slice(0, -2);
17895
+ if (!path.startsWith(`${prefix}.`)) {
17896
+ continue;
17897
+ }
17898
+ const dynamicSegments = path.slice(prefix.length + 1).split(".").filter(Boolean);
17899
+ if (dynamicSegments.length && this.isAIEmployeeContextInfoPathAllowed(info, dynamicSegments.slice(1))) {
17900
+ return true;
17901
+ }
17902
+ }
17903
+ return this.canResolveAIEmployeeContextPath(path, validation.semantic);
17904
+ }
17905
+ extractAIEmployeePromptContextPath(expression) {
17906
+ const normalized = String(expression || "").trim();
17907
+ if (normalized === "ctx") {
17908
+ return "";
17909
+ }
17910
+ if (!normalized.startsWith("ctx.")) {
17911
+ return null;
17912
+ }
17913
+ const path = normalized.slice(4).trim();
17914
+ return (0, import_context.isBareFlowContextPath)(path) ? path : null;
17915
+ }
17916
+ buildAIEmployeePromptVariableHint(targetUid) {
17917
+ return `Call flowSurfaces:context${targetUid ? ` with target.uid "${targetUid}"` : ""} to inspect available ctx paths before writing prompt variables.`;
17918
+ }
17919
+ throwAIEmployeePromptVariableInvalid(input) {
17920
+ (0, import_errors.throwBadRequest)(
17921
+ `flowSurfaces ${input.actionName} ${input.fieldPath} variable "${input.variable}" is not allowed: ${input.reason}. Use a concrete ctx path such as "{{ ctx.record.id }}" only when that path is available. ${this.buildAIEmployeePromptVariableHint(
17922
+ input.targetUid
17923
+ )}`,
17924
+ AI_EMPLOYEE_PROMPT_VARIABLE_INVALID,
17925
+ {
17926
+ path: input.fieldPath,
17927
+ details: (0, import_service_utils.buildDefinedPayload)({
17928
+ variable: input.variable,
17929
+ contextPath: input.path,
17930
+ targetUid: input.targetUid
17931
+ })
17932
+ }
17933
+ );
17934
+ }
17935
+ assertAIEmployeePromptVariablesAllowed(actionName, fieldPath, value, validation) {
17936
+ for (const variable of this.collectAIEmployeePromptVariables(value)) {
17937
+ const path = this.extractAIEmployeePromptContextPath(variable.inner);
17938
+ if (path === "") {
17939
+ this.throwAIEmployeePromptVariableInvalid({
17940
+ actionName,
17941
+ fieldPath,
17942
+ variable: variable.raw,
17943
+ targetUid: validation.targetUid,
17944
+ reason: "the whole ctx object is not a valid prompt variable; use a concrete ctx path"
17945
+ });
17946
+ }
17947
+ if (!path) {
17948
+ this.throwAIEmployeePromptVariableInvalid({
17949
+ actionName,
17950
+ fieldPath,
17951
+ variable: variable.raw,
17952
+ targetUid: validation.targetUid,
17953
+ reason: 'only simple ctx dot paths like "{{ ctx.record.id }}" are supported'
17954
+ });
17955
+ }
17956
+ if (!this.isAIEmployeeContextPathAllowed(path, validation)) {
17957
+ this.throwAIEmployeePromptVariableInvalid({
17958
+ actionName,
17959
+ fieldPath,
17960
+ variable: variable.raw,
17961
+ targetUid: validation.targetUid,
17962
+ path,
17963
+ reason: `path "${path}" is not available in the current Flow Surface context`
17964
+ });
17965
+ }
17966
+ }
17967
+ }
17968
+ assertAIEmployeeTaskPromptVariablesAllowed(actionName, tasks, validation, path = "settings.tasks") {
17969
+ if (!validation) {
17970
+ return;
17971
+ }
17972
+ tasks.forEach((task, index) => {
17973
+ const message = task == null ? void 0 : task.message;
17974
+ if (typeof (message == null ? void 0 : message.system) === "string") {
17975
+ this.assertAIEmployeePromptVariablesAllowed(
17976
+ actionName,
17977
+ `${path}[${index}].message.system`,
17978
+ message.system,
17979
+ validation
17980
+ );
17981
+ }
17982
+ if (typeof (message == null ? void 0 : message.user) === "string") {
17983
+ this.assertAIEmployeePromptVariablesAllowed(
17984
+ actionName,
17985
+ `${path}[${index}].message.user`,
17986
+ message.user,
17987
+ validation
17988
+ );
17989
+ }
17990
+ });
17991
+ }
17992
+ appendAIEmployeeCurrentRecordPromptVariable(value) {
17993
+ if (AI_EMPLOYEE_CURRENT_RECORD_PROMPT_VARIABLE_RE.test(value)) {
17994
+ return value;
17995
+ }
17996
+ return value ? `${value}
17997
+ ${AI_EMPLOYEE_CURRENT_RECORD_PROMPT_VARIABLE}` : AI_EMPLOYEE_CURRENT_RECORD_PROMPT_VARIABLE;
17998
+ }
17999
+ async resolveAIEmployeePromptActionTargetUid(context, transaction) {
18000
+ if (!context) {
18001
+ return void 0;
18002
+ }
18003
+ if (context.kind === "target") {
18004
+ return String(context.targetUid || "").trim() || void 0;
18005
+ }
18006
+ return String(context.ownerUid || "").trim() || void 0;
18007
+ }
18008
+ async buildAIEmployeePromptValidationContext(actionName, tasks, options) {
18009
+ var _a, _b;
18010
+ if (!this.hasAIEmployeePromptVariables(tasks)) {
18011
+ return void 0;
18012
+ }
18013
+ const targetUid = await this.resolveAIEmployeePromptActionTargetUid(options.promptContext, options.transaction);
18014
+ if (!targetUid) {
18015
+ (0, import_errors.throwBadRequest)(
18016
+ `flowSurfaces ${actionName} AI employee prompt variables require a resolvable Flow Surface context. ${this.buildAIEmployeePromptVariableHint()}`,
18017
+ AI_EMPLOYEE_PROMPT_VARIABLE_INVALID
18018
+ );
18019
+ }
18020
+ const target = { uid: targetUid };
18021
+ const resolved = await this.locator.resolve(target, { transaction: options.transaction });
18022
+ let semantic = await this.resolveContextSemantic(((_a = resolved == null ? void 0 : resolved.node) == null ? void 0 : _a.uid) || target.uid, resolved, options.transaction);
18023
+ if (((_b = options.promptContext) == null ? void 0 : _b.kind) === "container" && options.promptContext.scope === "record") {
18024
+ semantic = {
18025
+ ...semantic,
18026
+ recordCollection: semantic.recordCollection || semantic.collection || await this.resolveContextOwnerCollection(targetUid, options.transaction).catch(() => null)
18027
+ };
18028
+ }
18029
+ const context = (0, import_context.buildFlowSurfaceContextResponse)({
18030
+ semantic,
18031
+ maxDepth: AI_EMPLOYEE_PROMPT_CONTEXT_MAX_DEPTH
18032
+ });
18033
+ return {
18034
+ targetUid,
18035
+ semantic,
18036
+ contextPaths: this.collectAIEmployeeContextPathEntries(context)
18037
+ };
18038
+ }
18039
+ appendAIEmployeeCurrentRecordPromptVariableToTasks(tasks, validation) {
18040
+ if (!tasks.length || !validation || !this.isAIEmployeeContextPathAllowed("record", validation)) {
18041
+ return tasks;
18042
+ }
18043
+ return tasks.map((task) => {
18044
+ var _a;
18045
+ if (typeof ((_a = task == null ? void 0 : task.message) == null ? void 0 : _a.user) !== "string") {
18046
+ return task;
18047
+ }
18048
+ const next = import_lodash.default.cloneDeep(task);
18049
+ next.message.user = this.appendAIEmployeeCurrentRecordPromptVariable(next.message.user);
18050
+ return next;
18051
+ });
18052
+ }
18053
+ async assertAIEmployeeStepParamTaskPromptVariablesAllowedForUpdateSettings(current, nextPayload, writeTarget, options) {
18054
+ if (!this.isAIEmployeeActionUse(current == null ? void 0 : current.use) || !import_lodash.default.has(nextPayload, ["stepParams", ...AI_EMPLOYEE_TASK_STEP_PARAMS_PATH])) {
18055
+ return;
18056
+ }
18057
+ const tasks = import_lodash.default.get(nextPayload, ["stepParams", ...AI_EMPLOYEE_TASK_STEP_PARAMS_PATH]);
18058
+ if (!Array.isArray(tasks)) {
18059
+ return;
18060
+ }
18061
+ const actionName = options.openViewActionName || "updateSettings";
18062
+ this.assertAIEmployeeTaskPromptVariablesAllowed(
18063
+ actionName,
18064
+ tasks,
18065
+ await this.buildAIEmployeePromptValidationContext(actionName, tasks, {
18066
+ transaction: options.transaction,
18067
+ promptContext: {
18068
+ kind: "target",
18069
+ targetUid: (current == null ? void 0 : current.uid) || writeTarget.uid
18070
+ }
18071
+ }),
18072
+ "stepParams.shortcutSettings.editTasks.tasks"
18073
+ );
18074
+ }
18075
+ async normalizeAIEmployeeStepParamTasksForUpdateSettings(current, nextPayload, writeTarget, options) {
18076
+ if (!this.isAIEmployeeActionUse(current == null ? void 0 : current.use) || !import_lodash.default.has(nextPayload, ["stepParams", ...AI_EMPLOYEE_TASK_STEP_PARAMS_PATH])) {
18077
+ return;
18078
+ }
18079
+ const tasks = import_lodash.default.get(nextPayload, ["stepParams", ...AI_EMPLOYEE_TASK_STEP_PARAMS_PATH]);
18080
+ if (!Array.isArray(tasks)) {
18081
+ return;
18082
+ }
18083
+ const actionName = options.openViewActionName || "updateSettings";
18084
+ const normalizedTasks = this.normalizeAIEmployeeTasks(
18085
+ actionName,
18086
+ tasks,
18087
+ this.readAIEmployeePersistedTasks(current),
18088
+ {
18089
+ selfUid: await this.resolveAIEmployeeActionSelfUid(current || { uid: writeTarget.uid }, options.transaction),
18090
+ path: "stepParams.shortcutSettings.editTasks.tasks"
18091
+ }
18092
+ );
18093
+ import_lodash.default.set(nextPayload, ["stepParams", ...AI_EMPLOYEE_TASK_STEP_PARAMS_PATH], normalizedTasks);
18094
+ }
16579
18095
  normalizeAIEmployeeTaskMessage(actionName, path, value, existing, options) {
16580
18096
  if (!import_lodash.default.isPlainObject(value)) {
16581
18097
  (0, import_errors.throwBadRequest)(`flowSurfaces ${actionName} ${path} must be an object`);
@@ -16616,9 +18132,18 @@ class FlowSurfacesService {
16616
18132
  (0, import_errors.throwBadRequest)(`flowSurfaces ${actionName} ${options.path} must be an object`);
16617
18133
  }
16618
18134
  const next = import_lodash.default.pick(import_lodash.default.isPlainObject(existing) ? import_lodash.default.cloneDeep(existing) : {}, AI_EMPLOYEE_TASK_PUBLIC_SETTING_KEYS);
18135
+ if (Object.prototype.hasOwnProperty.call(patch, "prompt") && import_lodash.default.isPlainObject(patch.message) && Object.prototype.hasOwnProperty.call(patch.message, "user")) {
18136
+ (0, import_errors.throwBadRequest)(
18137
+ `flowSurfaces ${actionName} ${options.path} cannot set both prompt and message.user; prompt is an alias for message.user. repairHint: keep only one of ${options.path}.prompt or ${options.path}.message.user`
18138
+ );
18139
+ }
16619
18140
  Object.entries(patch).forEach(([key, value]) => {
16620
- if (!AI_EMPLOYEE_TASK_PUBLIC_SETTING_KEYS.includes(key)) {
16621
- (0, import_errors.throwBadRequest)(`flowSurfaces ${actionName} ${options.path} does not support key '${key}'`);
18141
+ if (!AI_EMPLOYEE_TASK_PATCH_PUBLIC_SETTING_KEYS.includes(key)) {
18142
+ (0, import_errors.throwBadRequest)(
18143
+ `flowSurfaces ${actionName} ${options.path} does not support key '${key}'; allowed keys: ${AI_EMPLOYEE_TASK_PATCH_PUBLIC_SETTING_KEYS.join(
18144
+ ", "
18145
+ )}; repairHint: ${AI_EMPLOYEE_TASK_REPAIR_HINT}`
18146
+ );
16622
18147
  }
16623
18148
  if (key === "message") {
16624
18149
  next.message = this.normalizeAIEmployeeTaskMessage(actionName, `${options.path}.message`, value, next.message, {
@@ -16627,6 +18152,18 @@ class FlowSurfacesService {
16627
18152
  });
16628
18153
  return;
16629
18154
  }
18155
+ if (key === "prompt") {
18156
+ if (typeof value !== "string") {
18157
+ (0, import_errors.throwBadRequest)(
18158
+ `flowSurfaces ${actionName} ${options.path}.prompt must be a string; repairHint: ${AI_EMPLOYEE_TASK_REPAIR_HINT}`
18159
+ );
18160
+ }
18161
+ next.message = {
18162
+ ...import_lodash.default.isPlainObject(next.message) ? import_lodash.default.cloneDeep(next.message) : {},
18163
+ user: value
18164
+ };
18165
+ return;
18166
+ }
16630
18167
  if (key === "title") {
16631
18168
  if (typeof value !== "string") {
16632
18169
  (0, import_errors.throwBadRequest)(`flowSurfaces ${actionName} ${options.path}.title must be a string`);
@@ -16661,6 +18198,14 @@ class FlowSurfacesService {
16661
18198
  if (!import_lodash.default.isPlainObject(next.message)) {
16662
18199
  next.message = {};
16663
18200
  }
18201
+ if (import_lodash.default.isPlainObject(next.skillSettings)) {
18202
+ if (Array.isArray(next.skillSettings.skills) && !Object.prototype.hasOwnProperty.call(next.skillSettings, "skillsVersion")) {
18203
+ next.skillSettings.skillsVersion = 2;
18204
+ }
18205
+ if (Array.isArray(next.skillSettings.tools) && !Object.prototype.hasOwnProperty.call(next.skillSettings, "toolsVersion")) {
18206
+ next.skillSettings.toolsVersion = 2;
18207
+ }
18208
+ }
16664
18209
  return next;
16665
18210
  }
16666
18211
  normalizeAIEmployeeTasks(actionName, value, currentTasks, options) {
@@ -16723,6 +18268,7 @@ class FlowSurfacesService {
16723
18268
  });
16724
18269
  }
16725
18270
  const props = {};
18271
+ const stepParams = {};
16726
18272
  if (effectiveUsername && (hasUsername || options.requireUsername)) {
16727
18273
  props.aiEmployee = {
16728
18274
  ...import_lodash.default.isPlainObject(currentProps.aiEmployee) ? import_lodash.default.cloneDeep(currentProps.aiEmployee) : {},
@@ -16748,11 +18294,36 @@ class FlowSurfacesService {
16748
18294
  };
16749
18295
  }
16750
18296
  if (Object.prototype.hasOwnProperty.call(settings, "tasks")) {
16751
- props.tasks = this.normalizeAIEmployeeTasks(actionName, settings.tasks, currentProps.tasks, {
16752
- selfUid: options.selfUid,
16753
- keyMap: options.keyMap,
16754
- path: "settings.tasks"
18297
+ const normalizedTasks = this.normalizeAIEmployeeTasks(
18298
+ actionName,
18299
+ settings.tasks,
18300
+ this.readAIEmployeePersistedTasks(options.current),
18301
+ {
18302
+ selfUid: options.selfUid,
18303
+ keyMap: options.keyMap,
18304
+ path: "settings.tasks"
18305
+ }
18306
+ );
18307
+ const validation = options.promptContext && normalizedTasks.length ? await this.buildAIEmployeePromptValidationContext(
18308
+ actionName,
18309
+ [
18310
+ {
18311
+ message: {
18312
+ user: AI_EMPLOYEE_CURRENT_RECORD_PROMPT_VARIABLE
18313
+ }
18314
+ }
18315
+ ],
18316
+ {
18317
+ transaction: options.transaction,
18318
+ promptContext: options.promptContext
18319
+ }
18320
+ ) : await this.buildAIEmployeePromptValidationContext(actionName, normalizedTasks, {
18321
+ transaction: options.transaction,
18322
+ promptContext: options.promptContext
16755
18323
  });
18324
+ const tasks = this.appendAIEmployeeCurrentRecordPromptVariableToTasks(normalizedTasks, validation);
18325
+ this.assertAIEmployeeTaskPromptVariablesAllowed(actionName, tasks, validation);
18326
+ import_lodash.default.merge(stepParams, this.buildAIEmployeeTaskStepParams(tasks));
16756
18327
  }
16757
18328
  if (Object.prototype.hasOwnProperty.call(settings, "style") || options.requireUsername) {
16758
18329
  const style = Object.prototype.hasOwnProperty.call(settings, "style") ? settings.style : {};
@@ -16780,7 +18351,10 @@ class FlowSurfacesService {
16780
18351
  import_lodash.default.isPlainObject(style) ? import_lodash.default.pick(import_lodash.default.cloneDeep(style), AI_EMPLOYEE_STYLE_PUBLIC_KEYS) : {}
16781
18352
  );
16782
18353
  }
16783
- return props;
18354
+ return {
18355
+ props,
18356
+ stepParams
18357
+ };
16784
18358
  }
16785
18359
  async resolveAIEmployeeActionSelfUid(actionNode, transaction) {
16786
18360
  const parentUid = String((actionNode == null ? void 0 : actionNode.parentId) || "").trim() || ((actionNode == null ? void 0 : actionNode.uid) ? await this.locator.findParentUid(actionNode.uid, transaction).catch(() => "") : "");
@@ -16804,27 +18378,39 @@ class FlowSurfacesService {
16804
18378
  }) : null);
16805
18379
  changes = await this.normalizeActionPanelActionChanges(changes, options);
16806
18380
  const allowedKeys = (0, import_configure_options.getConfigureOptionKeysForUse)(use);
18381
+ if ((0, import_service_utils.hasOwnDefined)(changes, "openView") && !allowedKeys.includes("openView") && JS_POPUP_GUIDANCE_USES.has(use)) {
18382
+ (0, import_errors.throwBadRequest)(withJsPopupGuidance(`flowSurfaces configure action '${use}' does not support openView`, use));
18383
+ }
16807
18384
  (0, import_service_utils.assertSupportedSimpleChanges)("action", changes, allowedKeys);
16808
18385
  if (this.isAIEmployeeActionUse(use)) {
16809
- const props2 = await this.normalizeAIEmployeeActionPublicSettings("configure action", changes, {
16810
- transaction: options.transaction,
16811
- enabledPackages: options.enabledPackages || await this.resolveEnabledPluginPackages(options),
16812
- current: currentNode,
16813
- currentRoles: options.currentRoles,
16814
- selfUid: await this.resolveAIEmployeeActionSelfUid(currentNode, options.transaction)
16815
- });
16816
- return this.updateSettings(
16817
- {
16818
- target,
16819
- props: props2
16820
- },
18386
+ const aiEmployeeSettingsPayload = await this.normalizeAIEmployeeActionPublicSettings(
18387
+ "configure action",
18388
+ changes,
16821
18389
  {
16822
- ...options,
16823
- openViewActionName: options.openViewActionName || "configure action",
16824
- popupTemplateHostUid: target.uid,
16825
- allowAIEmployeeInternalProps: true
18390
+ transaction: options.transaction,
18391
+ enabledPackages: options.enabledPackages || await this.resolveEnabledPluginPackages(options),
18392
+ current: currentNode,
18393
+ currentRoles: options.currentRoles,
18394
+ selfUid: await this.resolveAIEmployeeActionSelfUid(currentNode, options.transaction),
18395
+ promptContext: {
18396
+ kind: "target",
18397
+ targetUid: (currentNode == null ? void 0 : currentNode.uid) || target.uid
18398
+ }
16826
18399
  }
16827
18400
  );
18401
+ const settingsValues = { target };
18402
+ if (Object.keys(aiEmployeeSettingsPayload.props).length) {
18403
+ settingsValues.props = aiEmployeeSettingsPayload.props;
18404
+ }
18405
+ if (Object.keys(aiEmployeeSettingsPayload.stepParams).length) {
18406
+ settingsValues.stepParams = aiEmployeeSettingsPayload.stepParams;
18407
+ }
18408
+ return this.updateSettings(settingsValues, {
18409
+ ...options,
18410
+ openViewActionName: options.openViewActionName || "configure action",
18411
+ popupTemplateHostUid: target.uid,
18412
+ allowAIEmployeeInternalProps: true
18413
+ });
16828
18414
  }
16829
18415
  const normalizedDefaultFilter = (0, import_service_utils.hasOwnDefined)(changes, "defaultFilter") ? this.normalizeFilterActionDefaultFilterValue(changes.defaultFilter) : void 0;
16830
18416
  const stepParams = {};
@@ -16888,7 +18474,7 @@ class FlowSurfacesService {
16888
18474
  openView: import_lodash.default.cloneDeep(changes.openView)
16889
18475
  };
16890
18476
  } else if (!POPUP_ACTION_USES.has(use)) {
16891
- (0, import_errors.throwBadRequest)(`flowSurfaces configure action '${use}' does not support openView`);
18477
+ (0, import_errors.throwBadRequest)(withJsPopupGuidance(`flowSurfaces configure action '${use}' does not support openView`, use));
16892
18478
  } else {
16893
18479
  let openView = import_lodash.default.cloneDeep(changes.openView);
16894
18480
  if (use === "AddChildActionModel") {
@@ -18117,7 +19703,8 @@ class FlowSurfacesService {
18117
19703
  }
18118
19704
  resolveRecordActionContextOwner(ancestors) {
18119
19705
  const currentNode = ancestors[0];
18120
- if (!String((currentNode == null ? void 0 : currentNode.use) || "").endsWith("ActionModel")) {
19706
+ const currentUse = String((currentNode == null ? void 0 : currentNode.use) || "").trim();
19707
+ if (!currentUse.endsWith("ActionModel") && !import_node_use_sets.ACTION_BUTTON_USES.has(currentUse)) {
18121
19708
  return null;
18122
19709
  }
18123
19710
  return ancestors.slice(1).find((node) => (0, import_action_scope.getActionContainerScope)(node == null ? void 0 : node.use) === "record") || null;
@@ -18368,11 +19955,12 @@ class FlowSurfacesService {
18368
19955
  };
18369
19956
  }
18370
19957
  getCollection(dataSourceKey, collectionName) {
18371
- var _a, _b, _c, _d, _e;
19958
+ var _a, _b, _c, _d, _e, _f, _g, _h;
18372
19959
  const normalizedDataSourceKey = dataSourceKey || "main";
18373
- const dataSourceManager = this.plugin.app.dataSourceManager;
18374
- const dataSource = (_a = dataSourceManager == null ? void 0 : dataSourceManager.get) == null ? void 0 : _a.call(dataSourceManager, normalizedDataSourceKey);
18375
- return ((_c = (_b = dataSource == null ? void 0 : dataSource.collectionManager) == null ? void 0 : _b.getCollection) == null ? void 0 : _c.call(_b, collectionName)) || ((_e = (_d = this.plugin.app.db) == null ? void 0 : _d.getCollection) == null ? void 0 : _e.call(_d, collectionName));
19960
+ const app = (_a = this.plugin) == null ? void 0 : _a.app;
19961
+ const dataSourceManager = app == null ? void 0 : app.dataSourceManager;
19962
+ const dataSource = (_b = dataSourceManager == null ? void 0 : dataSourceManager.get) == null ? void 0 : _b.call(dataSourceManager, normalizedDataSourceKey);
19963
+ return ((_d = (_c = dataSource == null ? void 0 : dataSource.collectionManager) == null ? void 0 : _c.getCollection) == null ? void 0 : _d.call(_c, collectionName)) || ((_f = (_e = app == null ? void 0 : app.db) == null ? void 0 : _e.getCollection) == null ? void 0 : _f.call(_e, collectionName)) || ((_h = (_g = this.db) == null ? void 0 : _g.getCollection) == null ? void 0 : _h.call(_g, collectionName));
18376
19964
  }
18377
19965
  normalizeKanbanFieldNameInput(value, context, options = {}) {
18378
19966
  if (import_lodash.default.isUndefined(value)) {
@@ -20443,7 +22031,7 @@ class FlowSurfacesService {
20443
22031
  (value) => !import_lodash.default.isUndefined(value)
20444
22032
  ),
20445
22033
  decoratorProps: import_lodash.default.cloneDeep(innerField.decoratorProps || {}),
20446
- flowRegistry: import_lodash.default.cloneDeep(innerField.flowRegistry || {}),
22034
+ flowRegistry: this.getEventFlowRegistry(innerField),
20447
22035
  stepParams: import_lodash.default.merge({}, innerField.stepParams || {}, {
20448
22036
  fieldBinding: {
20449
22037
  use: normalizedTargetUse