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

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 (32) hide show
  1. package/dist/externalVersion.js +9 -9
  2. package/dist/node_modules/@ant-design/icons-svg/package.json +1 -1
  3. package/dist/node_modules/acorn/package.json +1 -1
  4. package/dist/node_modules/acorn-jsx/package.json +1 -1
  5. package/dist/node_modules/acorn-walk/package.json +1 -1
  6. package/dist/node_modules/ses/package.json +1 -1
  7. package/dist/node_modules/zod/package.json +1 -1
  8. package/dist/server/flow-surfaces/apply/compiler.js +18 -3
  9. package/dist/server/flow-surfaces/apply/matching.js +2 -0
  10. package/dist/server/flow-surfaces/authoring-validation.js +621 -66
  11. package/dist/server/flow-surfaces/blueprint/compile-blocks.js +21 -3
  12. package/dist/server/flow-surfaces/blueprint/compile-plan.js +9 -9
  13. package/dist/server/flow-surfaces/catalog.js +17 -4
  14. package/dist/server/flow-surfaces/chart-config.js +14 -1
  15. package/dist/server/flow-surfaces/compose-compiler.d.ts +2 -0
  16. package/dist/server/flow-surfaces/compose-compiler.js +2 -0
  17. package/dist/server/flow-surfaces/compose-runtime.js +4 -7
  18. package/dist/server/flow-surfaces/configure-options.js +19 -8
  19. package/dist/server/flow-surfaces/locator.js +16 -2
  20. package/dist/server/flow-surfaces/runjs-authoring/ctx-libs-member-mismatch-stop/index.js +1 -1
  21. package/dist/server/flow-surfaces/runjs-authoring/index.js +6224 -2119
  22. package/dist/server/flow-surfaces/service-utils.d.ts +2 -0
  23. package/dist/server/flow-surfaces/service-utils.js +8 -0
  24. package/dist/server/flow-surfaces/service.d.ts +67 -14
  25. package/dist/server/flow-surfaces/service.js +1316 -122
  26. package/dist/server/flow-surfaces/template-service-utils.d.ts +1 -0
  27. package/dist/swagger/flow-surfaces.d.ts +5 -2
  28. package/dist/swagger/flow-surfaces.examples.d.ts +11 -37
  29. package/dist/swagger/flow-surfaces.examples.js +6 -6
  30. package/dist/swagger/flow-surfaces.js +6 -3
  31. package/dist/swagger/index.d.ts +5 -2
  32. 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");
@@ -102,6 +103,56 @@ var import_template_service_utils = require("./template-service-utils");
102
103
  var import_hidden_popup_contract = require("./hidden-popup-contract");
103
104
  var import_hidden_popup_calendar = require("./hidden-popup-calendar");
104
105
  var import_hidden_popup_kanban = require("./hidden-popup-kanban");
106
+ const FLOW_SURFACE_CHART_REPAIR_HINT = "This is a chart payload shape problem. 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.";
107
+ const FLOW_SURFACE_CHART_REPAIR_STEPS = [
108
+ "Keep the block type as chart.",
109
+ "Define assets.charts.<key>.query and assets.charts.<key>.visual, or repair the localized settings.query/settings.visual on the existing chart block.",
110
+ "Reference the asset from the chart block with block.chart = <key> when using assets.charts.",
111
+ "Retry the chart payload instead of replacing the chart with another block type, omitting it, or deferring it."
112
+ ];
113
+ const FLOW_SURFACE_CHART_EXPECTED_SHAPE = {
114
+ assets: {
115
+ charts: {
116
+ chartKey: {
117
+ query: "builder/sql query configuration",
118
+ visual: "basic/custom visual configuration"
119
+ }
120
+ }
121
+ },
122
+ block: {
123
+ type: "chart",
124
+ chart: "chartKey"
125
+ }
126
+ };
127
+ const FLOW_SURFACE_CHART_FORBIDDEN_FALLBACKS = [
128
+ "table",
129
+ "jsBlock",
130
+ "actionPanel",
131
+ "gridCard",
132
+ "drop chart",
133
+ "defer chart"
134
+ ];
135
+ function withChartRepairMessage(message) {
136
+ return `${message}. ${FLOW_SURFACE_CHART_REPAIR_HINT}`;
137
+ }
138
+ function withFlowSurfaceChartRepairDetails(details = {}) {
139
+ return {
140
+ ...details,
141
+ repairHint: FLOW_SURFACE_CHART_REPAIR_HINT,
142
+ repairSteps: FLOW_SURFACE_CHART_REPAIR_STEPS,
143
+ expectedShape: FLOW_SURFACE_CHART_EXPECTED_SHAPE,
144
+ forbiddenFallbacks: FLOW_SURFACE_CHART_FORBIDDEN_FALLBACKS
145
+ };
146
+ }
147
+ function throwChartRepairBadRequest(message, details = {}) {
148
+ (0, import_errors.throwBadRequest)(withChartRepairMessage(message), {
149
+ details: withFlowSurfaceChartRepairDetails(details)
150
+ });
151
+ }
152
+ function isFlowSurfaceChartRepairError(error) {
153
+ var _a, _b;
154
+ 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;
155
+ }
105
156
  const FORM_BLOCK_USES = /* @__PURE__ */ new Set(["FormBlockModel", "CreateFormModel", "EditFormModel", ...import_approval.APPROVAL_FORM_BLOCK_USES]);
106
157
  const AUTO_SUBMIT_FORM_BLOCK_USES = /* @__PURE__ */ new Set(["CreateFormModel", "EditFormModel"]);
107
158
  const FLOW_SURFACE_DEFAULT_ACTION_SETTINGS_KEYS = /* @__PURE__ */ new Set(["filter"]);
@@ -251,6 +302,17 @@ const JS_ACTION_USES = /* @__PURE__ */ new Set([
251
302
  "JSActionModel"
252
303
  ]);
253
304
  const JS_ITEM_ACTION_USES = /* @__PURE__ */ new Set(["JSItemActionModel"]);
305
+ const JS_POPUP_GUIDANCE_MESSAGE = "should use ctx.openView to open popup";
306
+ const JS_POPUP_GUIDANCE_USES = /* @__PURE__ */ new Set([
307
+ ...JS_ACTION_USES,
308
+ ...JS_ITEM_ACTION_USES,
309
+ "JSColumnModel",
310
+ "JSItemModel",
311
+ "JSFieldModel",
312
+ "JSEditableFieldModel",
313
+ "FormJSFieldItemModel"
314
+ ]);
315
+ const JS_POPUP_GUIDANCE_PUBLIC_KEYS = /* @__PURE__ */ new Set(["js", "jsColumn", "jsItem"]);
254
316
  const POPUP_ACTION_USES = /* @__PURE__ */ new Set([
255
317
  "AddNewActionModel",
256
318
  "ViewActionModel",
@@ -264,6 +326,24 @@ const POPUP_ACTION_USES = /* @__PURE__ */ new Set([
264
326
  "AddChildActionModel",
265
327
  "MailSendActionModel"
266
328
  ]);
329
+ function withJsPopupGuidance(message, value) {
330
+ const normalizedValue = String(value || "").trim();
331
+ if (JS_POPUP_GUIDANCE_USES.has(normalizedValue) || JS_POPUP_GUIDANCE_PUBLIC_KEYS.has(normalizedValue)) {
332
+ return `${message}; ${JS_POPUP_GUIDANCE_MESSAGE}`;
333
+ }
334
+ return message;
335
+ }
336
+ function assertNoJsDeclarativeOpenView(context, changes, use) {
337
+ const normalizedUse = String(use || "").trim();
338
+ if ((0, import_service_utils.hasOwnDefined)(changes, "openView") && JS_POPUP_GUIDANCE_USES.has(normalizedUse)) {
339
+ (0, import_errors.throwBadRequest)(
340
+ withJsPopupGuidance(
341
+ `flowSurfaces configure ${context} '${normalizedUse}' does not support openView`,
342
+ normalizedUse
343
+ )
344
+ );
345
+ }
346
+ }
267
347
  const POPUP_HOST_DEFAULT_RECORD_CONTEXT_ACTION_USES = /* @__PURE__ */ new Set([
268
348
  "ViewActionModel",
269
349
  "EditActionModel",
@@ -457,16 +537,25 @@ const UPDATE_SETTINGS_STEP_PARAM_MIRRORS_BY_USE = {
457
537
  DividerItemModel: DIVIDER_ITEM_STEP_PARAM_MIRRORS
458
538
  };
459
539
  const FLOW_SURFACE_MENU_BINDABLE_OPTION_KEY = "flowSurfaceMenuBindable";
540
+ const ANT_DESIGN_ICON_NAMES = new Set(Object.keys(antDesignIconAsn || {}));
460
541
  const AI_EMPLOYEE_ACTION_USE = "AIEmployeeButtonModel";
461
542
  const AI_EMPLOYEE_OWNER_PLUGIN = "@nocobase/plugin-ai";
462
543
  const AI_EMPLOYEE_PUBLIC_SETTING_KEYS = ["username", "auto", "workContext", "tasks", "style"];
463
544
  const AI_EMPLOYEE_INTERNAL_PROP_KEYS = ["aiEmployee", "context", "auto", "tasks", "style"];
545
+ const AI_EMPLOYEE_TASK_STEP_PARAMS_PATH = ["shortcutSettings", "editTasks", "tasks"];
464
546
  const AI_EMPLOYEE_WORK_CONTEXT_PUBLIC_KEYS = ["type", "uid", "target"];
465
547
  const AI_EMPLOYEE_TASK_PUBLIC_SETTING_KEYS = ["title", "message", "autoSend", "skillSettings", "model", "webSearch"];
548
+ const AI_EMPLOYEE_TASK_PATCH_PUBLIC_SETTING_KEYS = [...AI_EMPLOYEE_TASK_PUBLIC_SETTING_KEYS, "prompt"];
466
549
  const AI_EMPLOYEE_TASK_MESSAGE_PUBLIC_KEYS = ["system", "user", "workContext"];
467
550
  const AI_EMPLOYEE_TASK_MODEL_PUBLIC_KEYS = ["llmService", "model"];
468
551
  const AI_EMPLOYEE_SKILL_SETTINGS_PUBLIC_KEYS = ["skills", "tools", "skillsVersion", "toolsVersion"];
469
552
  const AI_EMPLOYEE_STYLE_PUBLIC_KEYS = ["size", "mask"];
553
+ 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.';
554
+ 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.";
555
+ const AI_EMPLOYEE_PROMPT_CONTEXT_MAX_DEPTH = 8;
556
+ const AI_EMPLOYEE_PROMPT_VARIABLE_INVALID = "FLOW_SURFACE_AI_EMPLOYEE_PROMPT_VARIABLE_INVALID";
557
+ const AI_EMPLOYEE_CURRENT_RECORD_PROMPT_VARIABLE = "{{ ctx.record }}";
558
+ const AI_EMPLOYEE_CURRENT_RECORD_PROMPT_VARIABLE_RE = /\{\{\s*ctx\.record\s*\}\}/;
470
559
  const AI_EMPLOYEE_DEFAULT_STYLE = {
471
560
  size: 40,
472
561
  mask: false
@@ -710,10 +799,16 @@ class FlowSurfacesService {
710
799
  );
711
800
  }
712
801
  async loadEnabledPluginPackages(transaction) {
713
- if (!this.db.getCollection("applicationPlugins")) {
802
+ var _a;
803
+ const db = this.db;
804
+ if (!((_a = db == null ? void 0 : db.getCollection) == null ? void 0 : _a.call(db, "applicationPlugins")) || !(db == null ? void 0 : db.getRepository)) {
805
+ return /* @__PURE__ */ new Set();
806
+ }
807
+ const repository = db.getRepository("applicationPlugins");
808
+ if (!(repository == null ? void 0 : repository.find)) {
714
809
  return /* @__PURE__ */ new Set();
715
810
  }
716
- const plugins = await this.db.getRepository("applicationPlugins").find({
811
+ const plugins = await repository.find({
717
812
  fields: ["packageName"],
718
813
  filter: {
719
814
  enabled: true
@@ -721,8 +816,8 @@ class FlowSurfacesService {
721
816
  transaction
722
817
  });
723
818
  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);
819
+ var _a2;
820
+ return ((_a2 = plugin == null ? void 0 : plugin.get) == null ? void 0 : _a2.call(plugin, "packageName")) || (plugin == null ? void 0 : plugin.packageName);
726
821
  }).filter((packageName) => typeof packageName === "string" && !!packageName.trim()).map((packageName) => packageName.trim());
727
822
  return new Set(packageNames);
728
823
  }
@@ -762,6 +857,18 @@ class FlowSurfacesService {
762
857
  })
763
858
  );
764
859
  }
860
+ routeParentIdMatches(routeParentId, parentId) {
861
+ if (import_lodash.default.isNil(routeParentId) && import_lodash.default.isNil(parentId)) {
862
+ return true;
863
+ }
864
+ return String(routeParentId ?? "") === String(parentId ?? "");
865
+ }
866
+ async findMenuGroupRoutesByParentIdAndTitle(parentId, title, transaction) {
867
+ const routes = await this.findMenuGroupRoutesByTitle(title, transaction);
868
+ return routes.filter(
869
+ (route) => this.routeParentIdMatches(this.readRouteField(route, "parentId") ?? null, parentId)
870
+ );
871
+ }
765
872
  async findFlowPageRoutesByParentIdAndTitle(parentId, title, transaction) {
766
873
  const normalizedTitle = String(title || "").trim();
767
874
  const normalizedParentId = String(parentId ?? "").trim();
@@ -843,20 +950,97 @@ class FlowSurfacesService {
843
950
  ...extras
844
951
  };
845
952
  }
953
+ isValidMenuIconName(value) {
954
+ const normalized = String(value || "").trim();
955
+ return !!normalized && ANT_DESIGN_ICON_NAMES.has(normalized);
956
+ }
957
+ assertVisibleNavigationIcon(actionName, path, values) {
958
+ if (values.hideInMenu === true) {
959
+ return;
960
+ }
961
+ const icon = String(values.icon || "").trim();
962
+ if (!icon) {
963
+ (0, import_errors.throwBadRequest)(`flowSurfaces ${actionName} ${path}.icon is required when creating a visible menu route`, {
964
+ ruleId: "navigation-icon-required",
965
+ path: `${path}.icon`,
966
+ details: {
967
+ repairHint: "Pass a valid Ant Design icon name such as AppstoreOutlined, DashboardOutlined, or FolderOpenOutlined."
968
+ }
969
+ });
970
+ }
971
+ if (!this.isValidMenuIconName(icon)) {
972
+ (0, import_errors.throwBadRequest)(`flowSurfaces ${actionName} ${path}.icon must be a valid Ant Design icon name`, {
973
+ ruleId: "navigation-icon-unknown",
974
+ path: `${path}.icon`,
975
+ details: {
976
+ icon
977
+ }
978
+ });
979
+ }
980
+ }
981
+ assertVisibleNavigationRouteIconUpdate(actionName, path, route, values) {
982
+ if (!Object.prototype.hasOwnProperty.call(values, "icon") || this.readRouteField(route, "hideInMenu") === true) {
983
+ return;
984
+ }
985
+ const icon = String(values.icon || "").trim();
986
+ if (!icon) {
987
+ (0, import_errors.throwBadRequest)(`flowSurfaces ${actionName} ${path}.icon cannot be empty for a visible menu route`, {
988
+ ruleId: "navigation-icon-required",
989
+ path: `${path}.icon`,
990
+ details: {
991
+ repairHint: "Pass a valid Ant Design icon name such as AppstoreOutlined, DashboardOutlined, or FolderOpenOutlined."
992
+ }
993
+ });
994
+ }
995
+ if (!this.isValidMenuIconName(icon)) {
996
+ (0, import_errors.throwBadRequest)(`flowSurfaces ${actionName} ${path}.icon must be a valid Ant Design icon name`, {
997
+ ruleId: "navigation-icon-unknown",
998
+ path: `${path}.icon`,
999
+ details: {
1000
+ icon
1001
+ }
1002
+ });
1003
+ }
1004
+ }
846
1005
  async createFlowMenuGroup(values, transaction) {
847
1006
  const parentRoute = await this.assertMenuParentIsGroup(values.parentMenuRouteId, transaction);
1007
+ const parentId = this.readRouteField(parentRoute, "id") ?? null;
1008
+ const title = String(values.title || "").trim();
1009
+ const existingGroups = await this.findMenuGroupRoutesByParentIdAndTitle(parentId, title, transaction);
1010
+ if (existingGroups.length === 1) {
1011
+ return this.buildMenuResult(existingGroups[0]);
1012
+ }
1013
+ if (existingGroups.length > 1) {
1014
+ (0, import_errors.throwBadRequest)(
1015
+ `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`,
1016
+ {
1017
+ ruleId: "menu-group-title-ambiguous-under-parent",
1018
+ details: {
1019
+ title,
1020
+ parentMenuRouteId: values.parentMenuRouteId ?? null,
1021
+ matches: existingGroups.map((route2) => ({
1022
+ routeId: this.readRouteField(route2, "id"),
1023
+ parentMenuRouteId: this.readRouteField(route2, "parentId") ?? null,
1024
+ type: this.readRouteField(route2, "type"),
1025
+ schemaUid: this.readRouteField(route2, "schemaUid") ?? null
1026
+ }))
1027
+ }
1028
+ }
1029
+ );
1030
+ }
1031
+ this.assertVisibleNavigationIcon("createMenu", "values", values);
848
1032
  const schemaUid = values.schemaUid || (0, import_utils.uid)();
849
1033
  const desktopRoutes = this.db.getRepository("desktopRoutes");
850
1034
  await desktopRoutes.create({
851
1035
  values: {
852
1036
  type: "group",
853
1037
  sort: this.allocateRouteSortValue(),
854
- title: values.title,
1038
+ title,
855
1039
  icon: values.icon,
856
1040
  tooltip: values.tooltip,
857
1041
  schemaUid,
858
1042
  hideInMenu: !!values.hideInMenu,
859
- parentId: this.readRouteField(parentRoute, "id") ?? null
1043
+ parentId
860
1044
  },
861
1045
  transaction
862
1046
  });
@@ -869,6 +1053,7 @@ class FlowSurfacesService {
869
1053
  async createFlowMenuItem(values, transaction) {
870
1054
  var _a;
871
1055
  const parentRoute = await this.assertMenuParentIsGroup(values.parentMenuRouteId, transaction);
1056
+ this.assertVisibleNavigationIcon("createMenu", "values", values);
872
1057
  const pageSchemaUid = values.pageSchemaUid || (0, import_utils.uid)();
873
1058
  const pageUid = values.pageUid || (0, import_utils.uid)();
874
1059
  const tabSchemaUid = values.tabSchemaUid || (0, import_utils.uid)();
@@ -2596,6 +2781,10 @@ class FlowSurfacesService {
2596
2781
  ),
2597
2782
  options.transaction
2598
2783
  );
2784
+ if (!(rawNode == null ? void 0 : rawNode.uid)) {
2785
+ const resolvedUid = String((resolved == null ? void 0 : resolved.uid) || "").trim() || String(target.uid || target.tabSchemaUid || target.pageSchemaUid || target.routeId || "").trim() || "unknown";
2786
+ (0, import_errors.throwBadRequest)(`flowSurfaces:get target '${resolvedUid}' could not resolve a readable surface tree`);
2787
+ }
2599
2788
  const publicNode = this.stripInternalSurfaceMetaFromNodeTree(import_lodash.default.cloneDeep(rawNode));
2600
2789
  return this.buildSurfaceReadPayload(target, resolved, publicNode, options);
2601
2790
  }
@@ -2767,13 +2956,13 @@ class FlowSurfacesService {
2767
2956
  if (!groupTitle) {
2768
2957
  return document;
2769
2958
  }
2770
- const matchedRoutes = await this.findMenuGroupRoutesByTitle(groupTitle, transaction);
2959
+ const matchedRoutes = await this.findMenuGroupRoutesByParentIdAndTitle(null, groupTitle, transaction);
2771
2960
  if (!matchedRoutes.length) {
2772
2961
  return document;
2773
2962
  }
2774
2963
  if (matchedRoutes.length > 1) {
2775
2964
  (0, import_errors.throwBadRequest)(
2776
- `flowSurfaces applyBlueprint navigation.group.title '${groupTitle}' matches ${matchedRoutes.length} existing menu groups; pass navigation.group.routeId explicitly`
2965
+ `flowSurfaces applyBlueprint navigation.group.title '${groupTitle}' matches ${matchedRoutes.length} existing root menu groups; pass navigation.group.routeId explicitly`
2777
2966
  );
2778
2967
  }
2779
2968
  const routeId = this.readRouteField(matchedRoutes[0], "id");
@@ -2843,6 +3032,22 @@ class FlowSurfacesService {
2843
3032
  getCollection: (dataSourceKey, collectionName) => this.getCollection(dataSourceKey || "main", collectionName || "")
2844
3033
  });
2845
3034
  }
3035
+ async prevalidateApplyBlueprintChartAssets(document, transaction) {
3036
+ var _a;
3037
+ const chartAssets = import_lodash.default.isPlainObject((_a = document.assets) == null ? void 0 : _a.charts) ? document.assets.charts : {};
3038
+ for (const [chartKey, chartAsset] of Object.entries(chartAssets)) {
3039
+ if (!import_lodash.default.isPlainObject(chartAsset)) {
3040
+ continue;
3041
+ }
3042
+ (0, import_service_utils.assertSupportedSimpleChanges)("chart", chartAsset, (0, import_configure_options.getConfigureOptionKeysForUse)("ChartBlockModel"));
3043
+ const nextConfigure = (0, import_chart_config.buildChartConfigureFromSemanticChanges)(void 0, chartAsset);
3044
+ await this.validateChartConfigureForRuntime(
3045
+ `applyBlueprint assets.charts.${chartKey}`,
3046
+ nextConfigure,
3047
+ transaction
3048
+ );
3049
+ }
3050
+ }
2846
3051
  getApplyBlueprintKanbanBlockResourceObject(block) {
2847
3052
  return import_lodash.default.isPlainObject(block == null ? void 0 : block.resource) ? block.resource : {};
2848
3053
  }
@@ -3231,25 +3436,48 @@ class FlowSurfacesService {
3231
3436
  }
3232
3437
  if (!options.transaction) {
3233
3438
  const createdKanbanSortFields = [];
3234
- try {
3235
- return await this.transaction(
3236
- (transaction) => this.applyBlueprintWithTransaction(
3237
- values,
3238
- {
3239
- ...options,
3240
- transaction
3241
- },
3242
- createdKanbanSortFields
3243
- )
3439
+ const mutationResult = await this.applyBlueprintMutationWithoutExternalTransaction(
3440
+ values,
3441
+ options,
3442
+ createdKanbanSortFields
3443
+ );
3444
+ const surface = await this.get(mutationResult.pageLocator, { currentRoles: options.currentRoles });
3445
+ return this.buildApplyBlueprintResponse(mutationResult.mode, mutationResult.pageLocator, surface);
3446
+ }
3447
+ }
3448
+ async applyBlueprintMutationWithoutExternalTransaction(values, options = {}, createdKanbanSortFields) {
3449
+ try {
3450
+ await this.assertApplyBlueprintAuthoringPayload(values, options);
3451
+ const document = (0, import_blueprint.prepareFlowSurfaceApplyBlueprintDocument)(values);
3452
+ await this.prevalidateApplyBlueprintChartAssets(document);
3453
+ if (document.mode === "create") {
3454
+ return await this.applyBlueprintWithTransaction(
3455
+ values,
3456
+ { ...options, skipAuthoringValidation: true },
3457
+ createdKanbanSortFields,
3458
+ {
3459
+ readSurface: false
3460
+ }
3244
3461
  );
3245
- } catch (error) {
3246
- await this.cleanupApplyBlueprintKanbanSortFields(createdKanbanSortFields);
3247
- throw error;
3248
3462
  }
3463
+ return await this.transaction(
3464
+ (transaction) => this.applyBlueprintWithTransaction(
3465
+ values,
3466
+ {
3467
+ ...options,
3468
+ transaction,
3469
+ skipAuthoringValidation: true
3470
+ },
3471
+ createdKanbanSortFields,
3472
+ { readSurface: false }
3473
+ )
3474
+ );
3475
+ } catch (error) {
3476
+ await this.cleanupApplyBlueprintKanbanSortFields(createdKanbanSortFields);
3477
+ throw error;
3249
3478
  }
3250
3479
  }
3251
- async applyBlueprintWithTransaction(values, options = {}, createdKanbanSortFields) {
3252
- var _a;
3480
+ async assertApplyBlueprintAuthoringPayload(values, options = {}) {
3253
3481
  const enabledPackages = await this.resolveEnabledPluginPackages(options);
3254
3482
  await (0, import_authoring_validation.assertFlowSurfaceAuthoringPayload)("applyBlueprint", values, {
3255
3483
  transaction: options.transaction,
@@ -3257,6 +3485,11 @@ class FlowSurfacesService {
3257
3485
  findMenuGroupRoutesByTitle: (title, transaction) => this.findMenuGroupRoutesByTitle(title, transaction),
3258
3486
  getCollection: (dataSourceKey, collectionName) => this.getCollection(dataSourceKey || "main", collectionName || "")
3259
3487
  });
3488
+ }
3489
+ async applyBlueprintWithTransaction(values, options = {}, createdKanbanSortFields, resultOptions = {}) {
3490
+ if (options.skipAuthoringValidation !== true) {
3491
+ await this.assertApplyBlueprintAuthoringPayload(values, options);
3492
+ }
3260
3493
  const prepared = await this.prepareApplyBlueprintRequest(values, options.transaction, createdKanbanSortFields);
3261
3494
  const popupTemplateAliasSession = this.createPopupTemplateAliasSession();
3262
3495
  const popupTemplateTreeCache = /* @__PURE__ */ new Map();
@@ -3277,10 +3510,27 @@ class FlowSurfacesService {
3277
3510
  options
3278
3511
  );
3279
3512
  const pageLocator = (0, import_blueprint.resolveApplyBlueprintPageLocator)(prepared, result);
3513
+ await this.ensureSurfaceTableDefaultActionIntegrity(pageLocator, {
3514
+ ...options,
3515
+ enabledPackages: await this.resolveEnabledPluginPackages(options),
3516
+ popupTemplateAliasSession,
3517
+ popupTemplateTreeCache
3518
+ });
3519
+ if (resultOptions.readSurface === false) {
3520
+ return {
3521
+ version: "1",
3522
+ mode: prepared.document.mode,
3523
+ pageLocator
3524
+ };
3525
+ }
3280
3526
  const surface = await this.get(pageLocator, options);
3527
+ return this.buildApplyBlueprintResponse(prepared.document.mode, pageLocator, surface);
3528
+ }
3529
+ buildApplyBlueprintResponse(mode, pageLocator, surface) {
3530
+ var _a;
3281
3531
  return {
3282
3532
  version: "1",
3283
- mode: prepared.document.mode,
3533
+ mode,
3284
3534
  target: (0, import_service_utils.buildDefinedPayload)({
3285
3535
  pageSchemaUid: pageLocator.pageSchemaUid,
3286
3536
  pageUid: (_a = surface == null ? void 0 : surface.target) == null ? void 0 : _a.uid
@@ -3942,8 +4192,8 @@ class FlowSurfacesService {
3942
4192
  stepParams: (0, import_template_service_utils.buildFlowTemplateReferenceBlockStepParams)(template, templateTargetUid, init)
3943
4193
  };
3944
4194
  }
3945
- buildPopupTemplateReferenceOpenView(template, templateTargetUid) {
3946
- return this.buildPopupTemplateOpenView(template, "reference", templateTargetUid);
4195
+ buildPopupTemplateReferenceOpenView(template, templateTargetUid, options = {}) {
4196
+ return this.buildPopupTemplateOpenView(template, "reference", templateTargetUid, options);
3947
4197
  }
3948
4198
  shouldHydrateDetachedPopupTemplateBlockResourceContext(blockNode, context) {
3949
4199
  if (!this.isCollectionBlockUse(blockNode == null ? void 0 : blockNode.use) || !context.associationName || !context.sourceId) {
@@ -4293,15 +4543,35 @@ class FlowSurfacesService {
4293
4543
  }
4294
4544
  const nextStepParams = import_lodash.default.cloneDeep(sourceNode.stepParams || {});
4295
4545
  const currentGroup = import_lodash.default.isPlainObject(nextStepParams[openViewStep.flowKey]) ? import_lodash.default.cloneDeep(nextStepParams[openViewStep.flowKey]) : {};
4546
+ const popupProfile = await this.resolvePopupBlockProfile(sourceNode.uid, null, sourceNode, transaction).catch(
4547
+ () => null
4548
+ );
4549
+ const useRuntimeRecordFilterByTk = (popupProfile == null ? void 0 : popupProfile.hasCurrentRecord) === true;
4550
+ const filterTargetKey = useRuntimeRecordFilterByTk ? await this.resolveOpenViewRuntimeRecordFilterTargetKey(openViewStep.openView, template, {
4551
+ transaction,
4552
+ popupTemplateHostUid: sourceNode.uid,
4553
+ popupActionContext: {
4554
+ hasCurrentRecord: true
4555
+ }
4556
+ }) : void 0;
4557
+ const currentOpenView = import_lodash.default.omit(openViewStep.openView || {}, [
4558
+ "popupTemplateUid",
4559
+ "popupTemplateMode",
4560
+ "popupTemplateContext",
4561
+ "popupTemplateHasFilterByTk",
4562
+ "popupTemplateHasSourceId"
4563
+ ]);
4564
+ if (useRuntimeRecordFilterByTk && this.shouldOmitRuntimeRecordFilterByTk(currentOpenView.filterByTk, {
4565
+ filterTargetKey
4566
+ })) {
4567
+ delete currentOpenView.filterByTk;
4568
+ }
4296
4569
  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)
4570
+ ...currentOpenView,
4571
+ ...this.buildPopupTemplateReferenceOpenView(template, templateTargetUid, {
4572
+ filterTargetKey,
4573
+ useRuntimeRecordFilterByTk
4574
+ })
4305
4575
  };
4306
4576
  if (String(sourceNode.use || "").trim() === "AddChildActionModel") {
4307
4577
  nextOpenView = await this.normalizeAddChildOpenViewForAction(sourceNode, nextOpenView, transaction);
@@ -5043,6 +5313,13 @@ class FlowSurfacesService {
5043
5313
  });
5044
5314
  for (const [blockIndex, block] of result.blocks.entries()) {
5045
5315
  const blockSpec = normalizedBlocks.find((item) => item.key === block.key);
5316
+ if ((blockSpec == null ? void 0 : blockSpec.type) === "table") {
5317
+ await this.ensureTableDefaultActionIntegrity(block.uid, {
5318
+ ...runtimeOptions,
5319
+ enabledPackages,
5320
+ popupTemplateAliasSession
5321
+ });
5322
+ }
5046
5323
  if ((blockSpec == null ? void 0 : blockSpec.type) === "table") {
5047
5324
  const appliedTreeTableDefaults = await this.applyTreeTableCreatedBlockDefaults(
5048
5325
  {
@@ -5176,7 +5453,7 @@ class FlowSurfacesService {
5176
5453
  if ((current == null ? void 0 : current.use) === "DividerItemModel") {
5177
5454
  return this.configureDividerItem(target, changes, options);
5178
5455
  }
5179
- return this.configureJSItem(target, changes, options);
5456
+ return this.configureJSItem(target, changes, { ...options, currentUse: (current == null ? void 0 : current.use) || "JSItemModel" });
5180
5457
  }
5181
5458
  if ((0, import_service_utils.isFieldNodeUse)(current == null ? void 0 : current.use)) {
5182
5459
  return this.configureFieldNode(target, changes, configureOptions);
@@ -5251,6 +5528,7 @@ class FlowSurfacesService {
5251
5528
  if (!this.isBindableMenuRoutePendingInitialization(route, structure)) {
5252
5529
  (0, import_errors.throwBadRequest)(`flowSurfaces createPage does not allow re-initializing menu route '${routeId}'`);
5253
5530
  }
5531
+ this.assertVisibleNavigationRouteIconUpdate("createPage", "values", route, values);
5254
5532
  const existingPage = structure.pageModel;
5255
5533
  const enableTabs = !!values.enableTabs;
5256
5534
  const displayTitle = values.displayTitle !== false;
@@ -6330,7 +6608,7 @@ class FlowSurfacesService {
6330
6608
  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
6609
  type: catalogItem.key || values.type,
6332
6610
  fields: values.fields,
6333
- fieldsLayout: values.fieldsLayout
6611
+ ...Object.prototype.hasOwnProperty.call(values || {}, "fieldsLayout") ? { fieldsLayout: values.fieldsLayout } : {}
6334
6612
  },
6335
6613
  0,
6336
6614
  enabledPackages,
@@ -6524,6 +6802,8 @@ class FlowSurfacesService {
6524
6802
  ...fieldSpec.popupSize ? { popupSize: fieldSpec.popupSize } : {},
6525
6803
  ...!import_lodash.default.isUndefined(fieldSpec.pageSize) ? { pageSize: fieldSpec.pageSize } : {},
6526
6804
  ...!import_lodash.default.isUndefined(fieldSpec.showIndex) ? { showIndex: fieldSpec.showIndex } : {},
6805
+ ...fieldSpec.defaultTargetUid ? { defaultTargetUid: fieldSpec.defaultTargetUid } : {},
6806
+ ...fieldSpec.targetBlockUid ? { targetBlockUid: fieldSpec.targetBlockUid } : {},
6527
6807
  ...fieldSpec.popup ? { popup: fieldSpec.popup } : {},
6528
6808
  ...fieldSpec.__autoPopupForRelationField ? { __autoPopupForRelationField: true } : {},
6529
6809
  ...fieldSpec[import_defaults.FLOW_SURFACE_APPLY_BLUEPRINT_POPUP_DEFAULTS_KEY] ? {
@@ -6598,6 +6878,13 @@ class FlowSurfacesService {
6598
6878
  enabledPackages
6599
6879
  }
6600
6880
  );
6881
+ if (catalogItem.use === "TableBlockModel" && !options.skipDefaultBlockActions) {
6882
+ await this.ensureTableDefaultActionIntegrity(created, {
6883
+ ...options,
6884
+ enabledPackages,
6885
+ popupTemplateTreeCache: options.popupTemplateTreeCache
6886
+ });
6887
+ }
6601
6888
  if (!options.deferAutoLayout && (initialGrid == null ? void 0 : initialGrid.uid)) {
6602
6889
  const finalGrid = await this.repository.findModelById(parentUid, {
6603
6890
  transaction: options.transaction,
@@ -6665,7 +6952,9 @@ class FlowSurfacesService {
6665
6952
  (0, import_errors.throwBadRequest)("flowSurfaces fieldType is only supported for relation fields");
6666
6953
  }
6667
6954
  if (inlinePopup) {
6668
- (0, import_errors.throwBadRequest)(`flowSurfaces addField type '${values.type}' does not support popup`);
6955
+ (0, import_errors.throwBadRequest)(
6956
+ withJsPopupGuidance(`flowSurfaces addField type '${values.type}' does not support popup`, values.type)
6957
+ );
6669
6958
  }
6670
6959
  if (isFilterFormItem) {
6671
6960
  (0, import_errors.throwBadRequest)(`flowSurfaces addField type '${values.type}' is not allowed under filter-form`);
@@ -6860,9 +7149,18 @@ class FlowSurfacesService {
6860
7149
  }
6861
7150
  }
6862
7151
  if (inlinePopup && !this.isPopupFieldHostUse(boundFieldCapability.fieldUse)) {
6863
- (0, import_errors.throwBadRequest)(`flowSurfaces addField field '${boundFieldCapability.fieldUse}' does not support popup`);
7152
+ (0, import_errors.throwBadRequest)(
7153
+ withJsPopupGuidance(
7154
+ `flowSurfaces addField field '${boundFieldCapability.fieldUse}' does not support popup`,
7155
+ boundFieldCapability.fieldUse
7156
+ )
7157
+ );
6864
7158
  }
6865
- if (values.__autoPopupForRelationField === true && !inlinePopup && this.isPopupFieldHostUse(boundFieldCapability.fieldUse) && ((0, import_service_helpers.isAssociationField)(resolvedField.field) || !!normalizedFieldBinding.associationPathName) && !this.peekInlineFieldSettingsOpenView(inlineSettings, boundFieldCapability.wrapperUse)) {
7159
+ const inlineSettingsOpenView = this.peekInlineFieldSettingsOpenView(
7160
+ inlineSettings,
7161
+ boundFieldCapability.wrapperUse
7162
+ );
7163
+ if (values.__autoPopupForRelationField === true && !inlinePopup && this.isPopupFieldHostUse(boundFieldCapability.fieldUse) && ((0, import_service_helpers.isAssociationField)(resolvedField.field) || !!normalizedFieldBinding.associationPathName) && !inlineSettingsOpenView) {
6866
7164
  const popupDefaultsMetadata = values[import_defaults.FLOW_SURFACE_APPLY_BLUEPRINT_POPUP_DEFAULTS_KEY];
6867
7165
  inlinePopup = this.normalizeInlinePopup("addField", {
6868
7166
  tryTemplate: true,
@@ -6872,9 +7170,19 @@ class FlowSurfacesService {
6872
7170
  } : {}
6873
7171
  });
6874
7172
  }
6875
- if (inlinePopup && this.isExternalPopupOpenView(
6876
- this.peekInlineFieldSettingsOpenView(inlineSettings, boundFieldCapability.wrapperUse)
6877
- )) {
7173
+ const hasExternalInlineSettingsOpenView = this.isExternalPopupOpenView(inlineSettingsOpenView);
7174
+ if (!inlinePopup && this.isPopupFieldHostUse(boundFieldCapability.fieldUse) && !hasExternalInlineSettingsOpenView && (this.isInlineFieldClickToOpenEnabled(inlineSettings, boundFieldCapability.wrapperUse) || import_lodash.default.isPlainObject(inlineSettingsOpenView))) {
7175
+ const popupDefaultsMetadata = values[import_defaults.FLOW_SURFACE_APPLY_BLUEPRINT_POPUP_DEFAULTS_KEY];
7176
+ inlinePopup = this.normalizeInlinePopup("addField", {
7177
+ tryTemplate: true,
7178
+ defaultType: "view",
7179
+ ...import_lodash.default.isPlainObject(inlineSettingsOpenView) ? { openView: import_lodash.default.cloneDeep(inlineSettingsOpenView) } : {},
7180
+ ...popupDefaultsMetadata ? {
7181
+ [import_defaults.FLOW_SURFACE_APPLY_BLUEPRINT_POPUP_DEFAULTS_KEY]: popupDefaultsMetadata
7182
+ } : {}
7183
+ });
7184
+ }
7185
+ if (inlinePopup && hasExternalInlineSettingsOpenView) {
6878
7186
  (0, import_errors.throwBadRequest)(`flowSurfaces addField popup cannot be combined with external openView.uid`);
6879
7187
  }
6880
7188
  const defaultFieldState = (0, import_service_utils.buildDefaultFieldState)(
@@ -7013,7 +7321,12 @@ class FlowSurfacesService {
7013
7321
  );
7014
7322
  }
7015
7323
  if (inlinePopup && !POPUP_ACTION_USES.has(actionCatalogItem.use)) {
7016
- (0, import_errors.throwBadRequest)(`flowSurfaces addAction type '${actionCatalogItem.key}' does not support popup`);
7324
+ (0, import_errors.throwBadRequest)(
7325
+ withJsPopupGuidance(
7326
+ `flowSurfaces addAction type '${actionCatalogItem.key}' does not support popup`,
7327
+ actionCatalogItem.key
7328
+ )
7329
+ );
7017
7330
  }
7018
7331
  await this.assertApprovalActionSingleton(container.parentUid, actionCatalogItem.use, options.transaction);
7019
7332
  (0, import_action_scope.assertRequestedActionScope)({
@@ -7041,20 +7354,42 @@ class FlowSurfacesService {
7041
7354
  return result2;
7042
7355
  }
7043
7356
  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 || {}, {
7357
+ const aiEmployeeSettingsPayload = this.isAIEmployeeActionUse(actionCatalogItem.use) ? await this.normalizeAIEmployeeActionPublicSettings("addAction", inlineSettings || {}, {
7045
7358
  transaction: options.transaction,
7046
7359
  enabledPackages,
7047
7360
  requireUsername: true,
7048
7361
  currentRoles: options.currentRoles,
7049
- selfUid: container.ownerUid
7362
+ selfUid: container.ownerUid,
7363
+ promptContext: {
7364
+ kind: "container",
7365
+ ownerUid: container.ownerUid,
7366
+ scope: resolvedScope
7367
+ }
7050
7368
  }) : void 0;
7369
+ const actionSettingsPayload = {
7370
+ props: values.props,
7371
+ stepParams: values.stepParams
7372
+ };
7373
+ if (aiEmployeeSettingsPayload) {
7374
+ this.mergeAIEmployeeActionSettingsPayload(
7375
+ { use: actionCatalogItem.use },
7376
+ actionSettingsPayload,
7377
+ aiEmployeeSettingsPayload
7378
+ );
7379
+ await this.assertNoDuplicateAIEmployeeAction(
7380
+ "addAction",
7381
+ container.parentUid,
7382
+ actionSettingsPayload,
7383
+ options.transaction
7384
+ );
7385
+ }
7051
7386
  const action = (0, import_builder.buildActionTree)({
7052
7387
  use: actionCatalogItem.use,
7053
7388
  containerUse: container.ownerUse,
7054
7389
  resourceInit: values.resourceInit || (resourceContext == null ? void 0 : resourceContext.resourceInit),
7055
- props: aiEmployeeProps || values.props,
7390
+ props: actionSettingsPayload.props,
7056
7391
  decoratorProps: values.decoratorProps,
7057
- stepParams: values.stepParams,
7392
+ stepParams: actionSettingsPayload.stepParams,
7058
7393
  flowRegistry: values.flowRegistry
7059
7394
  });
7060
7395
  this.contractGuard.validateNodeTreeAgainstContract(action);
@@ -7109,7 +7444,12 @@ class FlowSurfacesService {
7109
7444
  );
7110
7445
  const resolvedScope = actionCatalogItem.scope;
7111
7446
  if (inlinePopup && !POPUP_ACTION_USES.has(actionCatalogItem.use)) {
7112
- (0, import_errors.throwBadRequest)(`flowSurfaces addRecordAction type '${actionCatalogItem.key}' does not support popup`);
7447
+ (0, import_errors.throwBadRequest)(
7448
+ withJsPopupGuidance(
7449
+ `flowSurfaces addRecordAction type '${actionCatalogItem.key}' does not support popup`,
7450
+ actionCatalogItem.key
7451
+ )
7452
+ );
7113
7453
  }
7114
7454
  if (this.isAddChildCatalogItem(actionCatalogItem)) {
7115
7455
  await this.assertAddChildSupportedForOwnerNode(container.ownerNode, "addRecordAction", options.transaction);
@@ -7141,20 +7481,42 @@ class FlowSurfacesService {
7141
7481
  }
7142
7482
  const resourceContext = container.ownerUid ? await this.locator.resolveCollectionContext(container.ownerUid, options.transaction).catch(() => null) : null;
7143
7483
  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 || {}, {
7484
+ const aiEmployeeSettingsPayload = this.isAIEmployeeActionUse(actionCatalogItem.use) ? await this.normalizeAIEmployeeActionPublicSettings("addRecordAction", inlineSettings || {}, {
7145
7485
  transaction: options.transaction,
7146
7486
  enabledPackages,
7147
7487
  requireUsername: true,
7148
7488
  currentRoles: options.currentRoles,
7149
- selfUid: container.ownerUid
7489
+ selfUid: container.ownerUid,
7490
+ promptContext: {
7491
+ kind: "container",
7492
+ ownerUid: container.ownerUid,
7493
+ scope: resolvedScope
7494
+ }
7150
7495
  }) : void 0;
7496
+ const actionSettingsPayload = {
7497
+ props: values.props,
7498
+ stepParams: values.stepParams
7499
+ };
7500
+ if (aiEmployeeSettingsPayload) {
7501
+ this.mergeAIEmployeeActionSettingsPayload(
7502
+ { use: actionCatalogItem.use },
7503
+ actionSettingsPayload,
7504
+ aiEmployeeSettingsPayload
7505
+ );
7506
+ await this.assertNoDuplicateAIEmployeeAction(
7507
+ "addRecordAction",
7508
+ materializedContainer.parentUid,
7509
+ actionSettingsPayload,
7510
+ options.transaction
7511
+ );
7512
+ }
7151
7513
  const action = (0, import_builder.buildActionTree)({
7152
7514
  use: actionCatalogItem.use,
7153
7515
  containerUse: container.containerUse,
7154
7516
  resourceInit,
7155
- props: aiEmployeeProps || values.props,
7517
+ props: actionSettingsPayload.props,
7156
7518
  decoratorProps: values.decoratorProps,
7157
- stepParams: values.stepParams,
7519
+ stepParams: actionSettingsPayload.stepParams,
7158
7520
  flowRegistry: values.flowRegistry
7159
7521
  });
7160
7522
  this.contractGuard.validateNodeTreeAgainstContract(action);
@@ -8083,6 +8445,17 @@ class FlowSurfacesService {
8083
8445
  const { fieldChanges } = (0, import_service_utils.splitComposeFieldChanges)(settings, wrapperUse);
8084
8446
  return fieldChanges.openView;
8085
8447
  }
8448
+ isInlineFieldClickToOpenEnabled(settings, wrapperUse) {
8449
+ if (!settings || !Object.keys(settings).length) {
8450
+ return false;
8451
+ }
8452
+ const { fieldChanges } = (0, import_service_utils.splitComposeFieldChanges)(settings, wrapperUse);
8453
+ if (!Object.prototype.hasOwnProperty.call(fieldChanges, "clickToOpen")) {
8454
+ return false;
8455
+ }
8456
+ const value = fieldChanges.clickToOpen;
8457
+ return value === true || import_lodash.default.isPlainObject(value) && value.clickToOpen === true;
8458
+ }
8086
8459
  async assertOpenViewUidTarget(actionName, uid2, options = {}) {
8087
8460
  const targetNode = await this.repository.findModelById(uid2, {
8088
8461
  transaction: options.transaction,
@@ -8613,15 +8986,71 @@ class FlowSurfacesService {
8613
8986
  sourceId: String(template.sourceId || "").trim() || void 0
8614
8987
  };
8615
8988
  }
8616
- buildPopupTemplateOpenView(template, mode, uidValue) {
8989
+ shouldUseRuntimeRecordFilterByTkForOpenView(openView, options = {}) {
8990
+ var _a;
8991
+ return !!String((openView == null ? void 0 : openView.popupTemplateUid) || "").trim() && ((_a = options.popupActionContext) == null ? void 0 : _a.hasCurrentRecord) === true;
8992
+ }
8993
+ resolveOpenViewFilterTargetKey(openView, template) {
8994
+ const dataSourceKey = String((openView == null ? void 0 : openView.dataSourceKey) || (template == null ? void 0 : template.dataSourceKey) || "main").trim() || "main";
8995
+ const collectionName = String((openView == null ? void 0 : openView.collectionName) || (template == null ? void 0 : template.collectionName) || "").trim();
8996
+ const collection = collectionName ? this.getCollection(dataSourceKey, collectionName) : null;
8997
+ return this.getCollectionFilterTargetKey(collection);
8998
+ }
8999
+ async resolvePopupHostCurrentRecordFilterTargetKey(hostUid, transaction) {
9000
+ var _a, _b, _c;
9001
+ const normalizedHostUid = String(hostUid || "").trim();
9002
+ if (!normalizedHostUid) {
9003
+ return void 0;
9004
+ }
9005
+ const hostNode = await this.repository.findModelById(normalizedHostUid, {
9006
+ transaction,
9007
+ includeAsyncNode: true
9008
+ }).catch(() => null);
9009
+ if (!(hostNode == null ? void 0 : hostNode.uid)) {
9010
+ return void 0;
9011
+ }
9012
+ const hostContext = await this.resolvePopupHostProfileContext(hostNode, transaction).catch(() => null);
9013
+ const ownerResourceInit = ((_a = hostContext == null ? void 0 : hostContext.resourceContext) == null ? void 0 : _a.resourceInit) || {};
9014
+ const dataSourceKey = String(((_b = hostContext == null ? void 0 : hostContext.associationContext) == null ? void 0 : _b.dataSourceKey) || ownerResourceInit.dataSourceKey || "main").trim() || "main";
9015
+ const collectionName = String(
9016
+ ((_c = hostContext == null ? void 0 : hostContext.associationContext) == null ? void 0 : _c.collectionName) || ownerResourceInit.collectionName || ""
9017
+ ).trim();
9018
+ const collection = collectionName ? this.getCollection(dataSourceKey, collectionName) : null;
9019
+ return collection ? this.getCollectionFilterTargetKey(collection) : void 0;
9020
+ }
9021
+ async resolveOpenViewRuntimeRecordFilterTargetKey(openView, template, options = {}) {
9022
+ var _a;
9023
+ const contextKey = String(((_a = options.popupActionContext) == null ? void 0 : _a.currentRecordFilterTargetKey) || "").trim();
9024
+ if (contextKey) {
9025
+ return contextKey;
9026
+ }
9027
+ const hostKey = await this.resolvePopupHostCurrentRecordFilterTargetKey(
9028
+ options.popupTemplateHostUid,
9029
+ options.transaction
9030
+ );
9031
+ return hostKey || this.resolveOpenViewFilterTargetKey(openView, template);
9032
+ }
9033
+ shouldOmitRuntimeRecordFilterByTk(value, options = {}) {
9034
+ const normalized = this.normalizeFlowContextTemplateValue(value);
9035
+ if (!normalized) {
9036
+ return false;
9037
+ }
9038
+ if (normalized === "{{ctx.view.inputArgs.filterByTk}}") {
9039
+ return true;
9040
+ }
9041
+ const filterTargetKey = String(options.filterTargetKey || "").trim();
9042
+ return !!filterTargetKey && normalized === `{{ctx.record.${filterTargetKey}}}`;
9043
+ }
9044
+ buildPopupTemplateOpenView(template, mode, uidValue, options = {}) {
8617
9045
  const popupTemplateHasFilterByTk = !!String(template.filterByTk || "").trim();
8618
9046
  const popupTemplateHasSourceId = !!String(template.sourceId || "").trim() || !!String(template.associationName || "").trim();
9047
+ const omitRuntimeRecordFilterByTk = options.useRuntimeRecordFilterByTk === true && this.shouldOmitRuntimeRecordFilterByTk(template.filterByTk, { filterTargetKey: options.filterTargetKey });
8619
9048
  const base = (0, import_service_utils.buildDefinedPayload)({
8620
9049
  uid: uidValue,
8621
9050
  dataSourceKey: template.dataSourceKey,
8622
9051
  collectionName: template.collectionName,
8623
9052
  associationName: template.associationName,
8624
- ...popupTemplateHasFilterByTk ? { filterByTk: template.filterByTk } : {},
9053
+ ...popupTemplateHasFilterByTk && !omitRuntimeRecordFilterByTk ? { filterByTk: template.filterByTk } : {},
8625
9054
  ...popupTemplateHasSourceId && template.sourceId ? { sourceId: template.sourceId } : {},
8626
9055
  popupTemplateHasFilterByTk,
8627
9056
  popupTemplateHasSourceId,
@@ -8694,7 +9123,7 @@ class FlowSurfacesService {
8694
9123
  }
8695
9124
  }
8696
9125
  async normalizeOpenView(actionName, openView, options = {}) {
8697
- var _a;
9126
+ var _a, _b, _c, _d;
8698
9127
  if (import_lodash.default.isUndefined(openView) || import_lodash.default.isNull(openView)) {
8699
9128
  return openView;
8700
9129
  }
@@ -8761,17 +9190,39 @@ class FlowSurfacesService {
8761
9190
  if (templateRef.mode === "copy") {
8762
9191
  await this.ensurePopupSurface(resolvedUid, options.transaction);
8763
9192
  }
8764
- let nextTemplateOpenView = this.buildPopupTemplateOpenView(template, templateRef.mode, resolvedUid);
9193
+ let runtimeRecordFilterTargetKey;
9194
+ if (((_b = options.popupActionContext) == null ? void 0 : _b.hasCurrentRecord) === true) {
9195
+ runtimeRecordFilterTargetKey = await this.resolveOpenViewRuntimeRecordFilterTargetKey(
9196
+ normalizedOpenView,
9197
+ template,
9198
+ options
9199
+ );
9200
+ }
9201
+ const filterTargetKey = runtimeRecordFilterTargetKey || this.resolveOpenViewFilterTargetKey(normalizedOpenView, template);
9202
+ let nextTemplateOpenView = this.buildPopupTemplateOpenView(template, templateRef.mode, resolvedUid, {
9203
+ filterTargetKey,
9204
+ useRuntimeRecordFilterByTk: ((_c = options.popupActionContext) == null ? void 0 : _c.hasCurrentRecord) === true
9205
+ });
8765
9206
  Object.keys(normalizedOpenView).forEach((key) => {
8766
9207
  if (key === "template" || key === "popupTemplateUid" || key === "popupTemplateMode" || key === "popupTemplateContext" || key === "popupTemplateHasFilterByTk" || key === "popupTemplateHasSourceId") {
8767
9208
  delete normalizedOpenView[key];
8768
9209
  }
8769
9210
  });
9211
+ if (((_d = options.popupActionContext) == null ? void 0 : _d.hasCurrentRecord) === true && this.shouldOmitRuntimeRecordFilterByTk(normalizedOpenView.filterByTk, {
9212
+ filterTargetKey
9213
+ })) {
9214
+ delete normalizedOpenView.filterByTk;
9215
+ }
8770
9216
  if (String(options.popupTemplateHostUse || "").trim() === "AddChildActionModel") {
8771
9217
  nextTemplateOpenView = import_lodash.default.omit(nextTemplateOpenView, ["sourceId"]);
8772
9218
  }
8773
9219
  Object.assign(normalizedOpenView, nextTemplateOpenView);
8774
9220
  }
9221
+ if (this.shouldUseRuntimeRecordFilterByTkForOpenView(normalizedOpenView, options) && this.shouldOmitRuntimeRecordFilterByTk(normalizedOpenView.filterByTk, {
9222
+ filterTargetKey: await this.resolveOpenViewRuntimeRecordFilterTargetKey(normalizedOpenView, void 0, options)
9223
+ })) {
9224
+ delete normalizedOpenView.filterByTk;
9225
+ }
8775
9226
  if (!import_lodash.default.isUndefined(normalizedOpenView.uid)) {
8776
9227
  const normalizedUid = String(normalizedOpenView.uid || "").trim();
8777
9228
  if (!normalizedUid) {
@@ -8812,7 +9263,8 @@ class FlowSurfacesService {
8812
9263
  return void 0;
8813
9264
  }
8814
9265
  return {
8815
- hasCurrentRecord: true
9266
+ hasCurrentRecord: true,
9267
+ currentRecordFilterTargetKey: await this.resolvePopupHostCurrentRecordFilterTargetKey(current.uid, transaction)
8816
9268
  };
8817
9269
  }
8818
9270
  unsetPayloadPathAndPruneEmptyParents(payload, path) {
@@ -8970,7 +9422,12 @@ class FlowSurfacesService {
8970
9422
  return {};
8971
9423
  }
8972
9424
  if (input.popup && !this.isPopupFieldHostUse(fieldNode.use)) {
8973
- (0, import_errors.throwBadRequest)(`flowSurfaces ${actionName} field '${fieldNode.use}' does not support popup`);
9425
+ (0, import_errors.throwBadRequest)(
9426
+ withJsPopupGuidance(
9427
+ `flowSurfaces ${actionName} field '${fieldNode.use}' does not support popup`,
9428
+ fieldNode.use
9429
+ )
9430
+ );
8974
9431
  }
8975
9432
  let openView = this.resolvePopupHostOpenView(fieldNode);
8976
9433
  if (input.popup && this.isExternalPopupOpenView(openView, fieldHostUid)) {
@@ -9296,6 +9753,20 @@ class FlowSurfacesService {
9296
9753
  isEmptyInlinePopupPayload(popup) {
9297
9754
  return import_lodash.default.isPlainObject(popup) && Object.keys(popup).length === 0;
9298
9755
  }
9756
+ isInlineFieldPopupDefaultShell(popup) {
9757
+ if (!import_lodash.default.isPlainObject(popup)) {
9758
+ return false;
9759
+ }
9760
+ const shellOnlyKeys = /* @__PURE__ */ new Set([
9761
+ "title",
9762
+ "mode",
9763
+ "openView",
9764
+ "tryTemplate",
9765
+ "defaultType",
9766
+ import_defaults.FLOW_SURFACE_APPLY_BLUEPRINT_POPUP_DEFAULTS_KEY
9767
+ ]);
9768
+ return Object.keys(popup).every((key) => shellOnlyKeys.has(key));
9769
+ }
9299
9770
  shouldAutoCompleteDefaultFieldPopup(fieldNode, popup, wrapperNode) {
9300
9771
  if (!import_lodash.default.isPlainObject(popup)) {
9301
9772
  return false;
@@ -9310,7 +9781,7 @@ class FlowSurfacesService {
9310
9781
  if (!String(fieldContext.collectionName || "").trim()) {
9311
9782
  return false;
9312
9783
  }
9313
- return popup.tryTemplate === true || !import_lodash.default.isUndefined(popup.defaultType) || Object.keys(popup).length === 0;
9784
+ return popup.tryTemplate === true || !import_lodash.default.isUndefined(popup.defaultType) || this.isInlineFieldPopupDefaultShell(popup);
9314
9785
  }
9315
9786
  isInlineFieldPopupBlockMissingResource(block) {
9316
9787
  if (!import_lodash.default.isPlainObject(block) || !import_lodash.default.isUndefined(block.template)) {
@@ -10372,14 +10843,21 @@ class FlowSurfacesService {
10372
10843
  actionNode,
10373
10844
  options.transaction
10374
10845
  ).catch(() => null);
10375
- const filterTargetKey = (popupProfile == null ? void 0 : popupProfile.currentCollection) ? this.getCollectionFilterTargetKey(popupProfile.currentCollection) : null;
10846
+ const useRuntimeRecordFilterByTk = this.isReferencedPopupTemplateOpenView(currentOpenView, actionNode.uid);
10847
+ const filterTargetKey = useRuntimeRecordFilterByTk ? await this.resolveOpenViewRuntimeRecordFilterTargetKey(currentOpenView, void 0, {
10848
+ transaction: options.transaction,
10849
+ popupTemplateHostUid: actionNode.uid,
10850
+ popupActionContext: {
10851
+ hasCurrentRecord: true
10852
+ }
10853
+ }) : (popupProfile == null ? void 0 : popupProfile.currentCollection) ? this.getCollectionFilterTargetKey(popupProfile.currentCollection) : null;
10376
10854
  const defaultFilterByTk = this.resolvePopupCurrentRecordResourceFilterByTk(popupProfile);
10377
10855
  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}}}` : "");
10856
+ const preserveCustomFilterByTk = currentFilterByTk && !this.shouldOmitRuntimeRecordFilterByTk(currentOpenView.filterByTk, { filterTargetKey: filterTargetKey || "" });
10379
10857
  const nextOpenView = (0, import_service_utils.buildDefinedPayload)({
10380
- ...currentOpenView,
10858
+ ...useRuntimeRecordFilterByTk && !preserveCustomFilterByTk ? import_lodash.default.omit(currentOpenView, ["filterByTk"]) : currentOpenView,
10381
10859
  title: openViewTitle,
10382
- filterByTk: preserveCustomFilterByTk ? currentOpenView.filterByTk : defaultFilterByTk || currentOpenView.filterByTk
10860
+ filterByTk: useRuntimeRecordFilterByTk ? preserveCustomFilterByTk ? currentOpenView.filterByTk : void 0 : preserveCustomFilterByTk ? currentOpenView.filterByTk : defaultFilterByTk || currentOpenView.filterByTk
10383
10861
  });
10384
10862
  if (import_lodash.default.isEqual(nextOpenView, currentOpenView)) {
10385
10863
  return;
@@ -10641,7 +11119,7 @@ class FlowSurfacesService {
10641
11119
  }
10642
11120
  if (this.isAIEmployeeActionUse(current == null ? void 0 : current.use) && this.hasAIEmployeePublicSettings(normalizedValues)) {
10643
11121
  const enabledPackages = await this.resolveEnabledPluginPackages(options);
10644
- const aiEmployeeProps = await this.normalizeAIEmployeeActionPublicSettings(
11122
+ const aiEmployeeSettingsPayload = await this.normalizeAIEmployeeActionPublicSettings(
10645
11123
  "updateSettings",
10646
11124
  import_lodash.default.pick(normalizedValues, AI_EMPLOYEE_PUBLIC_SETTING_KEYS),
10647
11125
  {
@@ -10649,13 +11127,17 @@ class FlowSurfacesService {
10649
11127
  enabledPackages,
10650
11128
  current,
10651
11129
  currentRoles: options.currentRoles,
10652
- selfUid: await this.resolveAIEmployeeActionSelfUid(current, options.transaction)
11130
+ selfUid: await this.resolveAIEmployeeActionSelfUid(current, options.transaction),
11131
+ promptContext: {
11132
+ kind: "target",
11133
+ targetUid: (current == null ? void 0 : current.uid) || writeTarget.uid
11134
+ }
10653
11135
  }
10654
11136
  );
10655
11137
  AI_EMPLOYEE_PUBLIC_SETTING_KEYS.forEach((key) => {
10656
11138
  delete normalizedValues[key];
10657
11139
  });
10658
- normalizedValues.props = import_lodash.default.merge({}, normalizedValues.props || {}, aiEmployeeProps);
11140
+ this.mergeAIEmployeeActionSettingsPayload(current, normalizedValues, aiEmployeeSettingsPayload);
10659
11141
  }
10660
11142
  assertNoFlowSurfaceIdTitleFieldSettings(normalizedValues, {
10661
11143
  action: "updateSettings",
@@ -10703,6 +11185,14 @@ class FlowSurfacesService {
10703
11185
  nextPayload,
10704
11186
  options.replaceRecordHistorySettings === true
10705
11187
  );
11188
+ this.syncAIEmployeeTaskStepParamsForUpdateSettings(current, nextPayload);
11189
+ await this.normalizeAIEmployeeStepParamTasksForUpdateSettings(current, nextPayload, writeTarget, options);
11190
+ await this.assertAIEmployeeStepParamTaskPromptVariablesAllowedForUpdateSettings(
11191
+ current,
11192
+ nextPayload,
11193
+ writeTarget,
11194
+ options
11195
+ );
10706
11196
  this.syncCalendarPopupPropsForUpdateSettings(current, normalizedValues, nextPayload);
10707
11197
  this.syncKanbanPopupPropsForUpdateSettings(current, normalizedValues, nextPayload);
10708
11198
  this.syncDefaultSortingForUpdateSettings(current, normalizedValues, nextPayload);
@@ -11590,7 +12080,12 @@ class FlowSurfacesService {
11590
12080
  }
11591
12081
  if (!((_c = sqlPreview.queryOutputs) == null ? void 0 : _c.length)) {
11592
12082
  (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"
12083
+ withChartRepairMessage(
12084
+ "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"
12085
+ ),
12086
+ {
12087
+ details: withFlowSurfaceChartRepairDetails()
12088
+ }
11594
12089
  );
11595
12090
  }
11596
12091
  const supportedOutputs = new Set(
@@ -11599,7 +12094,14 @@ class FlowSurfacesService {
11599
12094
  for (const mappingField of (0, import_chart_config.getChartVisualMappingAliases)(state.visual)) {
11600
12095
  if (!supportedOutputs.has(mappingField)) {
11601
12096
  (0, import_errors.throwBadRequest)(
11602
- `chart visual mappings only support SQL query output fields: ${Array.from(supportedOutputs).join(", ")}`
12097
+ withChartRepairMessage(
12098
+ `chart visual mappings only support SQL query output fields: ${Array.from(supportedOutputs).join(", ")}`
12099
+ ),
12100
+ {
12101
+ details: withFlowSurfaceChartRepairDetails({
12102
+ supportedOutputs: Array.from(supportedOutputs)
12103
+ })
12104
+ }
11603
12105
  );
11604
12106
  }
11605
12107
  }
@@ -11648,13 +12150,32 @@ class FlowSurfacesService {
11648
12150
  continue;
11649
12151
  }
11650
12152
  (0, import_errors.throwBadRequest)(
11651
- `flowSurfaces ${actionName} ${item.path} '${fieldPath}' does not exist on collection '${dataSourceKey}.${collectionName}'`
12153
+ withChartRepairMessage(
12154
+ `flowSurfaces ${actionName} ${item.path} '${fieldPath}' does not exist on collection '${dataSourceKey}.${collectionName}'`
12155
+ ),
12156
+ {
12157
+ details: withFlowSurfaceChartRepairDetails({
12158
+ fieldPath,
12159
+ dataSourceKey,
12160
+ collectionName
12161
+ })
12162
+ }
11652
12163
  );
11653
12164
  }
11654
12165
  if (!fieldPath.includes(".") && (0, import_service_helpers.isAssociationField)(field)) {
11655
12166
  const suggestion = this.resolveBuilderChartAssociationSubfieldSuggestion(fieldPath, field, dataSourceKey);
11656
12167
  (0, import_errors.throwBadRequest)(
11657
- `flowSurfaces ${actionName} ${item.path} '${fieldPath}' references an association field directly; use scalar subfield '${suggestion.suggestedFieldPath}' for builder charts`
12168
+ withChartRepairMessage(
12169
+ `flowSurfaces ${actionName} ${item.path} '${fieldPath}' references an association field directly; use scalar subfield '${suggestion.suggestedFieldPath}' for builder charts`
12170
+ ),
12171
+ {
12172
+ details: withFlowSurfaceChartRepairDetails({
12173
+ fieldPath,
12174
+ dataSourceKey,
12175
+ collectionName,
12176
+ ...suggestion
12177
+ })
12178
+ }
11658
12179
  );
11659
12180
  }
11660
12181
  }
@@ -11679,23 +12200,65 @@ class FlowSurfacesService {
11679
12200
  suggestedFieldPath: `${fieldPath}.<field>`
11680
12201
  };
11681
12202
  }
11682
- stripChartSqlForInspection(sql) {
11683
- return sql.replace(/\/\*[\s\S]*?\*\//g, " ").replace(/--[^\r\n]*/g, " ").replace(/'(?:''|[^'])*'/g, "''").replace(/"(?:[""]|[^"])*"/g, '""');
11684
- }
11685
- normalizeReadOnlyChartSql(sql) {
11686
- const normalized = (typeof sql === "string" ? sql : "").trim();
11687
- const inspected = this.stripChartSqlForInspection(normalized).replace(/;+\s*$/, "").trim();
11688
- if (!inspected) {
11689
- (0, import_errors.throwBadRequest)("chart query.sql cannot be empty");
11690
- }
12203
+ async validateChartConfigureForRuntime(actionName, configure, transaction) {
12204
+ var _a, _b, _c;
12205
+ if (!import_lodash.default.isPlainObject(configure)) {
12206
+ return;
12207
+ }
12208
+ this.validateBuilderChartFieldsForRuntime(actionName, configure);
12209
+ const state = (0, import_chart_config.deriveChartSemanticState)(configure);
12210
+ if (((_a = state.query) == null ? void 0 : _a.mode) !== "sql") {
12211
+ return;
12212
+ }
12213
+ const sqlPreview = await this.resolveSqlChartPreview(state.query, transaction);
12214
+ if (((_b = state.visual) == null ? void 0 : _b.mode) !== "basic") {
12215
+ return;
12216
+ }
12217
+ if (!((_c = sqlPreview.queryOutputs) == null ? void 0 : _c.length)) {
12218
+ (0, import_errors.throwBadRequest)(
12219
+ withChartRepairMessage(
12220
+ "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"
12221
+ ),
12222
+ {
12223
+ details: withFlowSurfaceChartRepairDetails()
12224
+ }
12225
+ );
12226
+ }
12227
+ const supportedOutputs = new Set(
12228
+ sqlPreview.queryOutputs.map((output) => String((output == null ? void 0 : output.alias) || "").trim()).filter(Boolean)
12229
+ );
12230
+ for (const mappingField of (0, import_chart_config.getChartVisualMappingAliases)(state.visual)) {
12231
+ if (!supportedOutputs.has(mappingField)) {
12232
+ (0, import_errors.throwBadRequest)(
12233
+ withChartRepairMessage(
12234
+ `chart visual mappings only support SQL query output fields: ${Array.from(supportedOutputs).join(", ")}`
12235
+ ),
12236
+ {
12237
+ details: withFlowSurfaceChartRepairDetails({
12238
+ supportedOutputs: Array.from(supportedOutputs)
12239
+ })
12240
+ }
12241
+ );
12242
+ }
12243
+ }
12244
+ }
12245
+ stripChartSqlForInspection(sql) {
12246
+ return sql.replace(/\/\*[\s\S]*?\*\//g, " ").replace(/--[^\r\n]*/g, " ").replace(/'(?:''|[^'])*'/g, "''").replace(/"(?:[""]|[^"])*"/g, '""');
12247
+ }
12248
+ normalizeReadOnlyChartSql(sql) {
12249
+ const normalized = (typeof sql === "string" ? sql : "").trim();
12250
+ const inspected = this.stripChartSqlForInspection(normalized).replace(/;+\s*$/, "").trim();
12251
+ if (!inspected) {
12252
+ throwChartRepairBadRequest("chart query.sql cannot be empty");
12253
+ }
11691
12254
  if (inspected.includes(";")) {
11692
- (0, import_errors.throwBadRequest)("chart query.sql must be a single read-only SELECT statement");
12255
+ throwChartRepairBadRequest("chart query.sql must be a single read-only SELECT statement");
11693
12256
  }
11694
12257
  if (!/^(with|select)\b/i.test(inspected)) {
11695
- (0, import_errors.throwBadRequest)("chart query.sql must start with SELECT or WITH");
12258
+ throwChartRepairBadRequest("chart query.sql must start with SELECT or WITH");
11696
12259
  }
11697
12260
  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");
12261
+ throwChartRepairBadRequest("chart query.sql must be read-only");
11699
12262
  }
11700
12263
  return normalized.replace(/;+\s*$/, "").trim();
11701
12264
  }
@@ -11713,7 +12276,7 @@ class FlowSurfacesService {
11713
12276
  if ((_a = db == null ? void 0 : db.sequelize) == null ? void 0 : _a.query) {
11714
12277
  return db.sequelize.query(sql, { bind, transaction });
11715
12278
  }
11716
- (0, import_errors.throwBadRequest)("chart SQL preview is unavailable for the target data source");
12279
+ throwChartRepairBadRequest("chart SQL preview is unavailable for the target data source");
11717
12280
  }
11718
12281
  extractSqlChartPreviewAliases(metadata) {
11719
12282
  const pickName = (field) => {
@@ -11751,7 +12314,13 @@ class FlowSurfacesService {
11751
12314
  async resolveSqlChartPreview(query, _transaction) {
11752
12315
  var _a, _b;
11753
12316
  const normalizedSql = this.normalizeReadOnlyChartSql(query == null ? void 0 : query.sql);
11754
- const transformed = await (0, import_utils.transformSQL)(normalizedSql);
12317
+ let transformed;
12318
+ try {
12319
+ transformed = await (0, import_utils.transformSQL)(normalizedSql);
12320
+ } catch (error) {
12321
+ const message = (error == null ? void 0 : error.message) || String(error);
12322
+ throwChartRepairBadRequest(`chart query.sql is invalid: ${message}`);
12323
+ }
11755
12324
  const riskyHints = [];
11756
12325
  const hasRuntimeContext = Object.keys((transformed == null ? void 0 : transformed.bind) || {}).length > 0 || Object.keys((transformed == null ? void 0 : transformed.liquidContext) || {}).length > 0;
11757
12326
  if (hasRuntimeContext) {
@@ -11807,7 +12376,7 @@ class FlowSurfacesService {
11807
12376
  }
11808
12377
  const outputAliases = Object.keys(firstRow);
11809
12378
  if (!outputAliases.length) {
11810
- (0, import_errors.throwBadRequest)("chart query.sql must expose at least one output column");
12379
+ throwChartRepairBadRequest("chart query.sql must expose at least one output column");
11811
12380
  }
11812
12381
  return {
11813
12382
  queryOutputs: outputAliases.map((alias) => ({
@@ -11818,8 +12387,11 @@ class FlowSurfacesService {
11818
12387
  riskyHints
11819
12388
  };
11820
12389
  } catch (error) {
12390
+ if (isFlowSurfaceChartRepairError(error)) {
12391
+ throw error;
12392
+ }
11821
12393
  const message = (error == null ? void 0 : error.message) || String(error);
11822
- (0, import_errors.throwBadRequest)(`chart query.sql is invalid: ${message}`);
12394
+ throwChartRepairBadRequest(`chart query.sql is invalid: ${message}`);
11823
12395
  }
11824
12396
  }
11825
12397
  syncChartCardRuntimeStepParamsForUpdateSettings(current, nextPayload, replacementCardSettings) {
@@ -13827,6 +14399,97 @@ class FlowSurfacesService {
13827
14399
  }
13828
14400
  }
13829
14401
  }
14402
+ hasActionWithPublicType(actions, type) {
14403
+ return actions.some((action) => import_catalog.ACTION_KEY_BY_USE.get(String((action == null ? void 0 : action.use) || "").trim()) === type);
14404
+ }
14405
+ async ensureTableDefaultActionIntegrity(tableUid, options = {}) {
14406
+ var _a, _b, _c, _d;
14407
+ let table = await this.repository.findModelById(tableUid, {
14408
+ transaction: options.transaction,
14409
+ includeAsyncNode: true
14410
+ });
14411
+ if ((table == null ? void 0 : table.use) !== "TableBlockModel" || this.resolveTreeTableCreationContext(table)) {
14412
+ return;
14413
+ }
14414
+ const descriptors = (0, import_default_block_actions.getFlowSurfaceDefaultBlockActions)({ blockType: "table" });
14415
+ const blockActionDescriptors = descriptors.filter((descriptor) => descriptor.scope === "actions");
14416
+ const recordActionDescriptors = descriptors.filter((descriptor) => descriptor.scope === "recordActions");
14417
+ let blockActions = import_lodash.default.castArray(((_a = table == null ? void 0 : table.subModels) == null ? void 0 : _a.actions) || []);
14418
+ for (const descriptor of blockActionDescriptors) {
14419
+ if (this.hasActionWithPublicType(blockActions, descriptor.type)) {
14420
+ continue;
14421
+ }
14422
+ await this.addAction(
14423
+ {
14424
+ target: { uid: tableUid },
14425
+ type: descriptor.type,
14426
+ ...descriptor.popup ? { popup: import_lodash.default.cloneDeep(descriptor.popup) } : {}
14427
+ },
14428
+ options
14429
+ );
14430
+ table = await this.repository.findModelById(tableUid, {
14431
+ transaction: options.transaction,
14432
+ includeAsyncNode: true
14433
+ });
14434
+ blockActions = import_lodash.default.castArray(((_b = table == null ? void 0 : table.subModels) == null ? void 0 : _b.actions) || []);
14435
+ }
14436
+ const actionsColumnUid = await this.ensureTableActionsColumn(tableUid, options.transaction);
14437
+ let actionsColumn = await this.repository.findModelById(actionsColumnUid, {
14438
+ transaction: options.transaction,
14439
+ includeAsyncNode: true
14440
+ });
14441
+ let recordActions = import_lodash.default.castArray(((_c = actionsColumn == null ? void 0 : actionsColumn.subModels) == null ? void 0 : _c.actions) || []);
14442
+ for (const descriptor of recordActionDescriptors) {
14443
+ if (this.hasActionWithPublicType(recordActions, descriptor.type)) {
14444
+ continue;
14445
+ }
14446
+ await this.addRecordAction(
14447
+ {
14448
+ target: { uid: tableUid },
14449
+ type: descriptor.type,
14450
+ ...descriptor.popup ? { popup: import_lodash.default.cloneDeep(descriptor.popup) } : {}
14451
+ },
14452
+ options
14453
+ );
14454
+ actionsColumn = await this.repository.findModelById(actionsColumnUid, {
14455
+ transaction: options.transaction,
14456
+ includeAsyncNode: true
14457
+ });
14458
+ recordActions = import_lodash.default.castArray(((_d = actionsColumn == null ? void 0 : actionsColumn.subModels) == null ? void 0 : _d.actions) || []);
14459
+ }
14460
+ const missingRecordActions = recordActionDescriptors.map((descriptor) => descriptor.type).filter((type) => !this.hasActionWithPublicType(recordActions, type));
14461
+ if (missingRecordActions.length) {
14462
+ (0, import_errors.throwBadRequest)(`flowSurfaces table '${tableUid}' is missing required default record actions`, {
14463
+ ruleId: "table-record-actions-required",
14464
+ details: {
14465
+ tableUid,
14466
+ missingRecordActions,
14467
+ 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."
14468
+ }
14469
+ });
14470
+ }
14471
+ }
14472
+ collectNodeTreeDescendants(node, predicate, bucket = []) {
14473
+ if (!node || typeof node !== "object") {
14474
+ return bucket;
14475
+ }
14476
+ if (predicate(node)) {
14477
+ bucket.push(node);
14478
+ }
14479
+ Object.values(import_lodash.default.isPlainObject(node.subModels) ? node.subModels : {}).forEach((subModel) => {
14480
+ import_lodash.default.castArray(subModel).forEach((child) => this.collectNodeTreeDescendants(child, predicate, bucket));
14481
+ });
14482
+ return bucket;
14483
+ }
14484
+ async ensureSurfaceTableDefaultActionIntegrity(pageLocator, options = {}) {
14485
+ const surface = await this.get(pageLocator, options);
14486
+ const tables = this.collectNodeTreeDescendants(surface == null ? void 0 : surface.tree, (item) => (item == null ? void 0 : item.use) === "TableBlockModel");
14487
+ for (const table of tables) {
14488
+ if (table == null ? void 0 : table.uid) {
14489
+ await this.ensureTableDefaultActionIntegrity(table.uid, options);
14490
+ }
14491
+ }
14492
+ }
13830
14493
  async resolveReusableSingletonAction(input) {
13831
14494
  var _a, _b, _c;
13832
14495
  if (AUTO_SUBMIT_FORM_BLOCK_USES.has(input.ownerUse || "") && input.actionUse === "FormSubmitActionModel") {
@@ -13990,8 +14653,10 @@ class FlowSurfacesService {
13990
14653
  if (blockCatalogItem) {
13991
14654
  this.validateComposeActionGroups(blockCatalogItem.use, actions, recordActions, enabledPackages);
13992
14655
  }
13993
- const actionKeys = new Set(actions.map((item) => item.key).filter(Boolean));
13994
- const recordActionKeys = new Set(recordActions.map((item) => item.key).filter(Boolean));
14656
+ const actionKeys = new Set(actions.map((item) => String(item.key || "").trim()).filter(Boolean));
14657
+ const recordActionKeys = new Set(
14658
+ recordActions.map((item) => String(item.key || "").trim()).filter(Boolean)
14659
+ );
13995
14660
  const blockDescriptor = this.describeComposeBlock({
13996
14661
  index: index + 1,
13997
14662
  key,
@@ -14422,6 +15087,7 @@ class FlowSurfacesService {
14422
15087
  ...(0, import_service_utils.hasDefinedValue)(changes, [
14423
15088
  "pageSize",
14424
15089
  "density",
15090
+ "enableRowSelection",
14425
15091
  "showRowNumbers",
14426
15092
  "sorting",
14427
15093
  "dataScope",
@@ -14435,6 +15101,7 @@ class FlowSurfacesService {
14435
15101
  ...(0, import_service_utils.hasOwnDefined)(changes, "pageSize") ? { pageSize: { pageSize: changes.pageSize } } : {},
14436
15102
  ...(0, import_service_utils.hasOwnDefined)(changes, "density") ? { tableDensity: { size: changes.density } } : {},
14437
15103
  ...(0, import_service_utils.hasOwnDefined)(changes, "quickEdit") ? { quickEdit: { editable: changes.quickEdit } } : {},
15104
+ ...(0, import_service_utils.hasOwnDefined)(changes, "enableRowSelection") ? { enableRowSelection: { enableRowSelection: changes.enableRowSelection } } : {},
14438
15105
  ...(0, import_service_utils.hasOwnDefined)(changes, "showRowNumbers") ? { showRowNumbers: { showIndex: changes.showRowNumbers } } : {},
14439
15106
  ...(0, import_service_utils.hasOwnDefined)(changes, "sorting") ? {
14440
15107
  defaultSorting: {
@@ -15812,6 +16479,7 @@ class FlowSurfacesService {
15812
16479
  );
15813
16480
  }
15814
16481
  async configureJSColumn(target, changes, options) {
16482
+ assertNoJsDeclarativeOpenView("jsColumn", changes, "JSColumnModel");
15815
16483
  const allowedKeys = (0, import_configure_options.getConfigureOptionKeysForUse)("JSColumnModel");
15816
16484
  (0, import_service_utils.assertSupportedSimpleChanges)("jsColumn", changes, allowedKeys);
15817
16485
  return this.updateSettings(
@@ -15845,6 +16513,8 @@ class FlowSurfacesService {
15845
16513
  );
15846
16514
  }
15847
16515
  async configureJSItem(target, changes, options) {
16516
+ const { currentUse = "JSItemModel", ...updateOptions } = options;
16517
+ assertNoJsDeclarativeOpenView("jsItem", changes, currentUse);
15848
16518
  const allowedKeys = (0, import_configure_options.getConfigureOptionKeysForUse)("JSItemModel");
15849
16519
  (0, import_service_utils.assertSupportedSimpleChanges)("jsItem", changes, allowedKeys);
15850
16520
  return this.updateSettings(
@@ -15869,7 +16539,7 @@ class FlowSurfacesService {
15869
16539
  }
15870
16540
  } : void 0
15871
16541
  },
15872
- options
16542
+ updateOptions
15873
16543
  );
15874
16544
  }
15875
16545
  async configureDividerItem(target, changes, options) {
@@ -15920,6 +16590,7 @@ class FlowSurfacesService {
15920
16590
  transaction: options.transaction,
15921
16591
  includeAsyncNode: true
15922
16592
  }) : null;
16593
+ assertNoJsDeclarativeOpenView("field", changes, innerField == null ? void 0 : innerField.use);
15923
16594
  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
16595
  const bindingChange = (0, import_service_utils.hasDefinedValue)(wrapperChanges, ["fieldPath", "associationPathName"]);
15925
16596
  const normalizedBinding = bindingChange ? this.normalizeDisplayFieldBinding({
@@ -16131,6 +16802,8 @@ class FlowSurfacesService {
16131
16802
  const enabledPackages = await this.resolveEnabledPluginPackages(options);
16132
16803
  const resolved = await this.locator.resolve(target, options);
16133
16804
  const current = await this.loadResolvedNode(resolved, options.transaction);
16805
+ const isJsFieldNode = ["JSFieldModel", "JSEditableFieldModel"].includes((current == null ? void 0 : current.use) || "");
16806
+ assertNoJsDeclarativeOpenView("field", changes, current == null ? void 0 : current.use);
16134
16807
  (0, import_service_utils.assertSupportedSimpleChanges)(
16135
16808
  "field",
16136
16809
  changes,
@@ -16141,7 +16814,6 @@ class FlowSurfacesService {
16141
16814
  transaction: options.transaction,
16142
16815
  includeAsyncNode: true
16143
16816
  }) : null;
16144
- const isJsFieldNode = ["JSFieldModel", "JSEditableFieldModel"].includes((current == null ? void 0 : current.use) || "");
16145
16817
  if ((0, import_service_utils.hasDefinedValue)(changes, ["code", "version"]) && !isJsFieldNode) {
16146
16818
  (0, import_errors.throwBadRequest)(`flowSurfaces configure field '${current == null ? void 0 : current.use}' does not support code/version`);
16147
16819
  }
@@ -16346,6 +17018,93 @@ class FlowSurfacesService {
16346
17018
  isAIEmployeeActionUse(use) {
16347
17019
  return String(use || "").trim() === AI_EMPLOYEE_ACTION_USE;
16348
17020
  }
17021
+ stableSerializeAIEmployeeValue(value) {
17022
+ if (Array.isArray(value)) {
17023
+ return `[${value.map((item) => this.stableSerializeAIEmployeeValue(item)).join(",")}]`;
17024
+ }
17025
+ if (import_lodash.default.isPlainObject(value)) {
17026
+ return `{${Object.keys(value).sort().map((key) => `${JSON.stringify(key)}:${this.stableSerializeAIEmployeeValue(value[key])}`).join(",")}}`;
17027
+ }
17028
+ return JSON.stringify(value);
17029
+ }
17030
+ buildAIEmployeeActionDedupIdentity(values) {
17031
+ var _a, _b, _c, _d, _e, _f;
17032
+ const username = String(((_b = (_a = values == null ? void 0 : values.props) == null ? void 0 : _a.aiEmployee) == null ? void 0 : _b.username) || "").trim();
17033
+ if (!username) {
17034
+ return "";
17035
+ }
17036
+ return this.stableSerializeAIEmployeeValue({
17037
+ username,
17038
+ auto: typeof ((_c = values == null ? void 0 : values.props) == null ? void 0 : _c.auto) === "boolean" ? values.props.auto : false,
17039
+ workContext: this.normalizeAIEmployeeWorkContextForDedupIdentity((_e = (_d = values == null ? void 0 : values.props) == null ? void 0 : _d.context) == null ? void 0 : _e.workContext),
17040
+ tasks: this.normalizeAIEmployeeTasksForDedupIdentity(
17041
+ import_lodash.default.get(values, ["stepParams", ...AI_EMPLOYEE_TASK_STEP_PARAMS_PATH])
17042
+ ),
17043
+ style: {
17044
+ ...AI_EMPLOYEE_DEFAULT_STYLE,
17045
+ ...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) : {}
17046
+ }
17047
+ });
17048
+ }
17049
+ normalizeAIEmployeeWorkContextForDedupIdentity(value) {
17050
+ return import_lodash.default.castArray(value || []).map(
17051
+ (item) => import_lodash.default.isPlainObject(item) ? import_lodash.default.pick(item, AI_EMPLOYEE_WORK_CONTEXT_PUBLIC_KEYS) : item
17052
+ );
17053
+ }
17054
+ normalizeAIEmployeeTasksForDedupIdentity(value) {
17055
+ return import_lodash.default.castArray(value || []).map((task) => {
17056
+ if (!import_lodash.default.isPlainObject(task)) {
17057
+ return task;
17058
+ }
17059
+ const output = import_lodash.default.pick(task, AI_EMPLOYEE_TASK_PUBLIC_SETTING_KEYS);
17060
+ if (import_lodash.default.isPlainObject(output.message)) {
17061
+ output.message = import_lodash.default.pick(output.message, AI_EMPLOYEE_TASK_MESSAGE_PUBLIC_KEYS);
17062
+ if (Object.prototype.hasOwnProperty.call(output.message, "workContext")) {
17063
+ output.message.workContext = this.normalizeAIEmployeeWorkContextForDedupIdentity(output.message.workContext);
17064
+ }
17065
+ }
17066
+ if (import_lodash.default.isPlainObject(output.model)) {
17067
+ output.model = import_lodash.default.pick(output.model, AI_EMPLOYEE_TASK_MODEL_PUBLIC_KEYS);
17068
+ }
17069
+ if (import_lodash.default.isPlainObject(output.skillSettings)) {
17070
+ output.skillSettings = import_lodash.default.pick(output.skillSettings, AI_EMPLOYEE_SKILL_SETTINGS_PUBLIC_KEYS);
17071
+ }
17072
+ return output;
17073
+ });
17074
+ }
17075
+ async assertNoDuplicateAIEmployeeAction(actionName, parentUid, values, transaction) {
17076
+ var _a;
17077
+ const identity = this.buildAIEmployeeActionDedupIdentity(values);
17078
+ if (!identity) {
17079
+ return;
17080
+ }
17081
+ const parentNode = await this.repository.findModelById(parentUid, {
17082
+ transaction,
17083
+ includeAsyncNode: true
17084
+ });
17085
+ const duplicate = import_lodash.default.castArray(((_a = parentNode == null ? void 0 : parentNode.subModels) == null ? void 0 : _a.actions) || []).find((action) => {
17086
+ if (!this.isAIEmployeeActionUse(action == null ? void 0 : action.use)) {
17087
+ return false;
17088
+ }
17089
+ return this.buildAIEmployeeActionDedupIdentity({
17090
+ props: action.props,
17091
+ stepParams: action.stepParams
17092
+ }) === identity;
17093
+ });
17094
+ if (!(duplicate == null ? void 0 : duplicate.uid)) {
17095
+ return;
17096
+ }
17097
+ (0, import_errors.throwBadRequest)(
17098
+ `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`,
17099
+ {
17100
+ ruleId: "duplicate-ai-employee-action",
17101
+ details: {
17102
+ existingUid: duplicate.uid,
17103
+ repairHint: "Remove the duplicate aiEmployee action, or change username, task title/key, or workContext if this is a genuinely different AI employee action."
17104
+ }
17105
+ }
17106
+ );
17107
+ }
16349
17108
  hasAIEmployeePublicSettings(settings) {
16350
17109
  return import_lodash.default.isPlainObject(settings) && AI_EMPLOYEE_PUBLIC_SETTING_KEYS.some((key) => Object.prototype.hasOwnProperty.call(settings, key));
16351
17110
  }
@@ -16353,16 +17112,31 @@ class FlowSurfacesService {
16353
17112
  const unsupportedKeys = Object.keys(settings || {}).filter((key) => !AI_EMPLOYEE_PUBLIC_SETTING_KEYS.includes(key));
16354
17113
  if (unsupportedKeys.length) {
16355
17114
  (0, import_errors.throwBadRequest)(
16356
- `flowSurfaces ${actionName} AI employee settings do not support keys: ${unsupportedKeys.join(", ")}`
17115
+ `flowSurfaces ${actionName} AI employee settings do not support keys: ${unsupportedKeys.join(
17116
+ ", "
17117
+ )}; allowed keys: ${AI_EMPLOYEE_PUBLIC_SETTING_KEYS.join(
17118
+ ", "
17119
+ )}; repairHint: use top-level username, auto, workContext, tasks, or style instead of raw props/stepParams`
16357
17120
  );
16358
17121
  }
16359
17122
  }
17123
+ aiEmployeeNestedRepairHint(path) {
17124
+ if (path.includes("workContext")) {
17125
+ return AI_EMPLOYEE_WORK_CONTEXT_REPAIR_HINT;
17126
+ }
17127
+ if (path.includes("tasks")) {
17128
+ return AI_EMPLOYEE_TASK_REPAIR_HINT;
17129
+ }
17130
+ return "Use only the documented public AI employee settings keys for this nested object.";
17131
+ }
16360
17132
  assertOnlyAIEmployeeNestedPublicSettings(actionName, path, value, allowedKeys) {
16361
17133
  const unsupportedKeys = Object.keys(value || {}).filter((key) => !allowedKeys.includes(key));
16362
17134
  if (!unsupportedKeys.length) {
16363
17135
  return;
16364
17136
  }
16365
- (0, import_errors.throwBadRequest)(`flowSurfaces ${actionName} ${path} does not support key '${unsupportedKeys[0]}'`);
17137
+ (0, import_errors.throwBadRequest)(
17138
+ `flowSurfaces ${actionName} ${path} does not support key '${unsupportedKeys[0]}'; allowed keys: ${allowedKeys.join(", ")}; repairHint: ${this.aiEmployeeNestedRepairHint(path)}`
17139
+ );
16366
17140
  }
16367
17141
  assertNoAIEmployeeInternalPropSettings(actionName, values) {
16368
17142
  const props = values == null ? void 0 : values.props;
@@ -16381,6 +17155,67 @@ class FlowSurfacesService {
16381
17155
  )}; use top-level username, auto, workContext, tasks or style instead`
16382
17156
  );
16383
17157
  }
17158
+ readAIEmployeePersistedTasks(current) {
17159
+ var _a;
17160
+ const stepTasks = import_lodash.default.get(current, ["stepParams", ...AI_EMPLOYEE_TASK_STEP_PARAMS_PATH]);
17161
+ if (Array.isArray(stepTasks)) {
17162
+ return stepTasks;
17163
+ }
17164
+ const legacyPropsTasks = (_a = current == null ? void 0 : current.props) == null ? void 0 : _a.tasks;
17165
+ return Array.isArray(legacyPropsTasks) ? legacyPropsTasks : [];
17166
+ }
17167
+ buildAIEmployeeTaskStepParams(tasks) {
17168
+ const stepParams = {};
17169
+ import_lodash.default.set(stepParams, AI_EMPLOYEE_TASK_STEP_PARAMS_PATH, import_lodash.default.cloneDeep(tasks));
17170
+ return stepParams;
17171
+ }
17172
+ mergeAIEmployeeActionSettingsPayload(current, values, payload) {
17173
+ if (Object.keys(payload.props).length) {
17174
+ values.props = import_lodash.default.merge({}, values.props || {}, payload.props);
17175
+ }
17176
+ if (Object.keys(payload.stepParams).length) {
17177
+ values.stepParams = import_lodash.default.mergeWith({}, values.stepParams || {}, payload.stepParams, (_currentValue, nextValue) => {
17178
+ if (Array.isArray(nextValue)) {
17179
+ return import_lodash.default.cloneDeep(nextValue);
17180
+ }
17181
+ return void 0;
17182
+ });
17183
+ }
17184
+ if (import_lodash.default.has(payload.stepParams, AI_EMPLOYEE_TASK_STEP_PARAMS_PATH) && this.isAIEmployeeActionUse(current == null ? void 0 : current.use)) {
17185
+ if (import_lodash.default.isPlainObject(values.props)) {
17186
+ delete values.props.tasks;
17187
+ }
17188
+ }
17189
+ }
17190
+ syncAIEmployeeTaskStepParamsForUpdateSettings(current, nextPayload) {
17191
+ var _a;
17192
+ if (!this.isAIEmployeeActionUse(current == null ? void 0 : current.use)) {
17193
+ return;
17194
+ }
17195
+ const legacyPropsTasks = (_a = current == null ? void 0 : current.props) == null ? void 0 : _a.tasks;
17196
+ const hasLegacyPropsTasks = Array.isArray(legacyPropsTasks);
17197
+ const hasNextStepTasks = import_lodash.default.has(nextPayload, ["stepParams", ...AI_EMPLOYEE_TASK_STEP_PARAMS_PATH]);
17198
+ const currentStepTasks = import_lodash.default.get(current, ["stepParams", ...AI_EMPLOYEE_TASK_STEP_PARAMS_PATH]);
17199
+ if (!hasNextStepTasks && hasLegacyPropsTasks && !Array.isArray(currentStepTasks)) {
17200
+ nextPayload.stepParams = import_lodash.default.mergeWith(
17201
+ {},
17202
+ (current == null ? void 0 : current.stepParams) || {},
17203
+ this.buildAIEmployeeTaskStepParams(legacyPropsTasks),
17204
+ (_currentValue, nextValue) => {
17205
+ if (Array.isArray(nextValue)) {
17206
+ return import_lodash.default.cloneDeep(nextValue);
17207
+ }
17208
+ return void 0;
17209
+ }
17210
+ );
17211
+ }
17212
+ if (!hasLegacyPropsTasks && !import_lodash.default.has(nextPayload, ["props", "tasks"])) {
17213
+ return;
17214
+ }
17215
+ const nextProps = import_lodash.default.cloneDeep(nextPayload.props ?? (current == null ? void 0 : current.props) ?? {});
17216
+ delete nextProps.tasks;
17217
+ nextPayload.props = nextProps;
17218
+ }
16384
17219
  assertAIEmployeePluginEnabled(actionName, enabledPackages) {
16385
17220
  if (!enabledPackages.has(AI_EMPLOYEE_OWNER_PLUGIN)) {
16386
17221
  (0, import_errors.throwBadRequest)(
@@ -16464,8 +17299,13 @@ class FlowSurfacesService {
16464
17299
  return username;
16465
17300
  }
16466
17301
  assertAIEmployeeWorkContextType(actionName, itemPath, item) {
17302
+ if (!Object.prototype.hasOwnProperty.call(item, "type") || import_lodash.default.isUndefined(item.type) || item.type === null) {
17303
+ return;
17304
+ }
16467
17305
  if (String(item.type || "").trim() !== "flow-model") {
16468
- (0, import_errors.throwBadRequest)(`flowSurfaces ${actionName} ${itemPath}.type must be 'flow-model'`);
17306
+ (0, import_errors.throwBadRequest)(
17307
+ `flowSurfaces ${actionName} ${itemPath}.type only supports 'flow-model'; omit type to use the default flow-model context. repairHint: ${AI_EMPLOYEE_WORK_CONTEXT_REPAIR_HINT}`
17308
+ );
16469
17309
  }
16470
17310
  }
16471
17311
  normalizeAIEmployeeWorkContext(actionName, value, options) {
@@ -16574,8 +17414,290 @@ class FlowSurfacesService {
16574
17414
  if (Object.prototype.hasOwnProperty.call(next, "toolsVersion") && typeof next.toolsVersion !== "number") {
16575
17415
  (0, import_errors.throwBadRequest)(`flowSurfaces ${actionName} ${path}.toolsVersion must be a number`);
16576
17416
  }
17417
+ const hasSkills = Object.prototype.hasOwnProperty.call(next, "skills");
17418
+ const hasTools = Object.prototype.hasOwnProperty.call(next, "tools");
17419
+ const hasSkillsVersion = Object.prototype.hasOwnProperty.call(next, "skillsVersion");
17420
+ const hasToolsVersion = Object.prototype.hasOwnProperty.call(next, "toolsVersion");
17421
+ if (hasSkills && hasTools && !hasSkillsVersion && !hasToolsVersion && Array.isArray(next.skills) && Array.isArray(next.tools) && next.skills.length === 0 && next.tools.length === 0) {
17422
+ return null;
17423
+ }
17424
+ if (hasSkills && !hasSkillsVersion) {
17425
+ next.skillsVersion = 2;
17426
+ }
17427
+ if (hasTools && !hasToolsVersion) {
17428
+ next.toolsVersion = 2;
17429
+ }
16577
17430
  return next;
16578
17431
  }
17432
+ collectAIEmployeePromptVariables(value) {
17433
+ if (!value.includes("{{")) {
17434
+ return [];
17435
+ }
17436
+ return [...value.matchAll(/\{\{\s*([\s\S]+?)\s*\}\}/g)].map(([raw, inner]) => ({
17437
+ raw,
17438
+ inner: String(inner || "").trim()
17439
+ }));
17440
+ }
17441
+ hasAIEmployeePromptVariables(tasks) {
17442
+ return import_lodash.default.castArray(tasks || []).some((task) => {
17443
+ const message = task == null ? void 0 : task.message;
17444
+ 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;
17445
+ });
17446
+ }
17447
+ collectAIEmployeeContextPathEntries(context) {
17448
+ const result = /* @__PURE__ */ new Map();
17449
+ const visit = (prefix, info) => {
17450
+ if (!prefix || !info) {
17451
+ return;
17452
+ }
17453
+ result.set(prefix, info);
17454
+ if (info.dynamicProperties) {
17455
+ result.set(`${prefix}.*`, info.dynamicProperties);
17456
+ }
17457
+ for (const [childKey, childInfo] of Object.entries(info.properties || {})) {
17458
+ visit(`${prefix}.${childKey}`, childInfo);
17459
+ }
17460
+ };
17461
+ for (const [key, info] of Object.entries((context == null ? void 0 : context.vars) || {})) {
17462
+ visit(key, info);
17463
+ }
17464
+ return result;
17465
+ }
17466
+ isAIEmployeeContextInfoPathAllowed(info, segments) {
17467
+ var _a;
17468
+ if (!info) {
17469
+ return false;
17470
+ }
17471
+ if (!segments.length) {
17472
+ return true;
17473
+ }
17474
+ const [segment, ...rest] = segments;
17475
+ const staticChild = (_a = info.properties) == null ? void 0 : _a[segment];
17476
+ if (staticChild) {
17477
+ return this.isAIEmployeeContextInfoPathAllowed(staticChild, rest);
17478
+ }
17479
+ if (info.dynamicProperties) {
17480
+ return this.isAIEmployeeContextInfoPathAllowed(info.dynamicProperties, rest);
17481
+ }
17482
+ return false;
17483
+ }
17484
+ canResolveAIEmployeeContextPath(path, semantic) {
17485
+ var _a;
17486
+ return !!((_a = (0, import_context.buildFlowSurfaceContextResponse)({
17487
+ semantic,
17488
+ path,
17489
+ maxDepth: 1
17490
+ }).vars) == null ? void 0 : _a[path]);
17491
+ }
17492
+ isAIEmployeeContextPathAllowed(path, validation) {
17493
+ if (validation.contextPaths.has(path)) {
17494
+ return true;
17495
+ }
17496
+ for (const [allowedPath, info] of validation.contextPaths) {
17497
+ if (!allowedPath.endsWith(".*")) {
17498
+ continue;
17499
+ }
17500
+ const prefix = allowedPath.slice(0, -2);
17501
+ if (!path.startsWith(`${prefix}.`)) {
17502
+ continue;
17503
+ }
17504
+ const dynamicSegments = path.slice(prefix.length + 1).split(".").filter(Boolean);
17505
+ if (dynamicSegments.length && this.isAIEmployeeContextInfoPathAllowed(info, dynamicSegments.slice(1))) {
17506
+ return true;
17507
+ }
17508
+ }
17509
+ return this.canResolveAIEmployeeContextPath(path, validation.semantic);
17510
+ }
17511
+ extractAIEmployeePromptContextPath(expression) {
17512
+ const normalized = String(expression || "").trim();
17513
+ if (normalized === "ctx") {
17514
+ return "";
17515
+ }
17516
+ if (!normalized.startsWith("ctx.")) {
17517
+ return null;
17518
+ }
17519
+ const path = normalized.slice(4).trim();
17520
+ return (0, import_context.isBareFlowContextPath)(path) ? path : null;
17521
+ }
17522
+ buildAIEmployeePromptVariableHint(targetUid) {
17523
+ return `Call flowSurfaces:context${targetUid ? ` with target.uid "${targetUid}"` : ""} to inspect available ctx paths before writing prompt variables.`;
17524
+ }
17525
+ throwAIEmployeePromptVariableInvalid(input) {
17526
+ (0, import_errors.throwBadRequest)(
17527
+ `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(
17528
+ input.targetUid
17529
+ )}`,
17530
+ AI_EMPLOYEE_PROMPT_VARIABLE_INVALID,
17531
+ {
17532
+ path: input.fieldPath,
17533
+ details: (0, import_service_utils.buildDefinedPayload)({
17534
+ variable: input.variable,
17535
+ contextPath: input.path,
17536
+ targetUid: input.targetUid
17537
+ })
17538
+ }
17539
+ );
17540
+ }
17541
+ assertAIEmployeePromptVariablesAllowed(actionName, fieldPath, value, validation) {
17542
+ for (const variable of this.collectAIEmployeePromptVariables(value)) {
17543
+ const path = this.extractAIEmployeePromptContextPath(variable.inner);
17544
+ if (path === "") {
17545
+ this.throwAIEmployeePromptVariableInvalid({
17546
+ actionName,
17547
+ fieldPath,
17548
+ variable: variable.raw,
17549
+ targetUid: validation.targetUid,
17550
+ reason: "the whole ctx object is not a valid prompt variable; use a concrete ctx path"
17551
+ });
17552
+ }
17553
+ if (!path) {
17554
+ this.throwAIEmployeePromptVariableInvalid({
17555
+ actionName,
17556
+ fieldPath,
17557
+ variable: variable.raw,
17558
+ targetUid: validation.targetUid,
17559
+ reason: 'only simple ctx dot paths like "{{ ctx.record.id }}" are supported'
17560
+ });
17561
+ }
17562
+ if (!this.isAIEmployeeContextPathAllowed(path, validation)) {
17563
+ this.throwAIEmployeePromptVariableInvalid({
17564
+ actionName,
17565
+ fieldPath,
17566
+ variable: variable.raw,
17567
+ targetUid: validation.targetUid,
17568
+ path,
17569
+ reason: `path "${path}" is not available in the current Flow Surface context`
17570
+ });
17571
+ }
17572
+ }
17573
+ }
17574
+ assertAIEmployeeTaskPromptVariablesAllowed(actionName, tasks, validation, path = "settings.tasks") {
17575
+ if (!validation) {
17576
+ return;
17577
+ }
17578
+ tasks.forEach((task, index) => {
17579
+ const message = task == null ? void 0 : task.message;
17580
+ if (typeof (message == null ? void 0 : message.system) === "string") {
17581
+ this.assertAIEmployeePromptVariablesAllowed(
17582
+ actionName,
17583
+ `${path}[${index}].message.system`,
17584
+ message.system,
17585
+ validation
17586
+ );
17587
+ }
17588
+ if (typeof (message == null ? void 0 : message.user) === "string") {
17589
+ this.assertAIEmployeePromptVariablesAllowed(
17590
+ actionName,
17591
+ `${path}[${index}].message.user`,
17592
+ message.user,
17593
+ validation
17594
+ );
17595
+ }
17596
+ });
17597
+ }
17598
+ appendAIEmployeeCurrentRecordPromptVariable(value) {
17599
+ if (AI_EMPLOYEE_CURRENT_RECORD_PROMPT_VARIABLE_RE.test(value)) {
17600
+ return value;
17601
+ }
17602
+ return value ? `${value}
17603
+ ${AI_EMPLOYEE_CURRENT_RECORD_PROMPT_VARIABLE}` : AI_EMPLOYEE_CURRENT_RECORD_PROMPT_VARIABLE;
17604
+ }
17605
+ async resolveAIEmployeePromptActionTargetUid(context, transaction) {
17606
+ if (!context) {
17607
+ return void 0;
17608
+ }
17609
+ if (context.kind === "target") {
17610
+ return String(context.targetUid || "").trim() || void 0;
17611
+ }
17612
+ return String(context.ownerUid || "").trim() || void 0;
17613
+ }
17614
+ async buildAIEmployeePromptValidationContext(actionName, tasks, options) {
17615
+ var _a, _b;
17616
+ if (!this.hasAIEmployeePromptVariables(tasks)) {
17617
+ return void 0;
17618
+ }
17619
+ const targetUid = await this.resolveAIEmployeePromptActionTargetUid(options.promptContext, options.transaction);
17620
+ if (!targetUid) {
17621
+ (0, import_errors.throwBadRequest)(
17622
+ `flowSurfaces ${actionName} AI employee prompt variables require a resolvable Flow Surface context. ${this.buildAIEmployeePromptVariableHint()}`,
17623
+ AI_EMPLOYEE_PROMPT_VARIABLE_INVALID
17624
+ );
17625
+ }
17626
+ const target = { uid: targetUid };
17627
+ const resolved = await this.locator.resolve(target, { transaction: options.transaction });
17628
+ let semantic = await this.resolveContextSemantic(((_a = resolved == null ? void 0 : resolved.node) == null ? void 0 : _a.uid) || target.uid, resolved, options.transaction);
17629
+ if (((_b = options.promptContext) == null ? void 0 : _b.kind) === "container" && options.promptContext.scope === "record") {
17630
+ semantic = {
17631
+ ...semantic,
17632
+ recordCollection: semantic.recordCollection || semantic.collection || await this.resolveContextOwnerCollection(targetUid, options.transaction).catch(() => null)
17633
+ };
17634
+ }
17635
+ const context = (0, import_context.buildFlowSurfaceContextResponse)({
17636
+ semantic,
17637
+ maxDepth: AI_EMPLOYEE_PROMPT_CONTEXT_MAX_DEPTH
17638
+ });
17639
+ return {
17640
+ targetUid,
17641
+ semantic,
17642
+ contextPaths: this.collectAIEmployeeContextPathEntries(context)
17643
+ };
17644
+ }
17645
+ appendAIEmployeeCurrentRecordPromptVariableToTasks(tasks, validation) {
17646
+ if (!tasks.length || !validation || !this.isAIEmployeeContextPathAllowed("record", validation)) {
17647
+ return tasks;
17648
+ }
17649
+ return tasks.map((task) => {
17650
+ var _a;
17651
+ if (typeof ((_a = task == null ? void 0 : task.message) == null ? void 0 : _a.user) !== "string") {
17652
+ return task;
17653
+ }
17654
+ const next = import_lodash.default.cloneDeep(task);
17655
+ next.message.user = this.appendAIEmployeeCurrentRecordPromptVariable(next.message.user);
17656
+ return next;
17657
+ });
17658
+ }
17659
+ async assertAIEmployeeStepParamTaskPromptVariablesAllowedForUpdateSettings(current, nextPayload, writeTarget, options) {
17660
+ if (!this.isAIEmployeeActionUse(current == null ? void 0 : current.use) || !import_lodash.default.has(nextPayload, ["stepParams", ...AI_EMPLOYEE_TASK_STEP_PARAMS_PATH])) {
17661
+ return;
17662
+ }
17663
+ const tasks = import_lodash.default.get(nextPayload, ["stepParams", ...AI_EMPLOYEE_TASK_STEP_PARAMS_PATH]);
17664
+ if (!Array.isArray(tasks)) {
17665
+ return;
17666
+ }
17667
+ const actionName = options.openViewActionName || "updateSettings";
17668
+ this.assertAIEmployeeTaskPromptVariablesAllowed(
17669
+ actionName,
17670
+ tasks,
17671
+ await this.buildAIEmployeePromptValidationContext(actionName, tasks, {
17672
+ transaction: options.transaction,
17673
+ promptContext: {
17674
+ kind: "target",
17675
+ targetUid: (current == null ? void 0 : current.uid) || writeTarget.uid
17676
+ }
17677
+ }),
17678
+ "stepParams.shortcutSettings.editTasks.tasks"
17679
+ );
17680
+ }
17681
+ async normalizeAIEmployeeStepParamTasksForUpdateSettings(current, nextPayload, writeTarget, options) {
17682
+ if (!this.isAIEmployeeActionUse(current == null ? void 0 : current.use) || !import_lodash.default.has(nextPayload, ["stepParams", ...AI_EMPLOYEE_TASK_STEP_PARAMS_PATH])) {
17683
+ return;
17684
+ }
17685
+ const tasks = import_lodash.default.get(nextPayload, ["stepParams", ...AI_EMPLOYEE_TASK_STEP_PARAMS_PATH]);
17686
+ if (!Array.isArray(tasks)) {
17687
+ return;
17688
+ }
17689
+ const actionName = options.openViewActionName || "updateSettings";
17690
+ const normalizedTasks = this.normalizeAIEmployeeTasks(
17691
+ actionName,
17692
+ tasks,
17693
+ this.readAIEmployeePersistedTasks(current),
17694
+ {
17695
+ selfUid: await this.resolveAIEmployeeActionSelfUid(current || { uid: writeTarget.uid }, options.transaction),
17696
+ path: "stepParams.shortcutSettings.editTasks.tasks"
17697
+ }
17698
+ );
17699
+ import_lodash.default.set(nextPayload, ["stepParams", ...AI_EMPLOYEE_TASK_STEP_PARAMS_PATH], normalizedTasks);
17700
+ }
16579
17701
  normalizeAIEmployeeTaskMessage(actionName, path, value, existing, options) {
16580
17702
  if (!import_lodash.default.isPlainObject(value)) {
16581
17703
  (0, import_errors.throwBadRequest)(`flowSurfaces ${actionName} ${path} must be an object`);
@@ -16616,9 +17738,18 @@ class FlowSurfacesService {
16616
17738
  (0, import_errors.throwBadRequest)(`flowSurfaces ${actionName} ${options.path} must be an object`);
16617
17739
  }
16618
17740
  const next = import_lodash.default.pick(import_lodash.default.isPlainObject(existing) ? import_lodash.default.cloneDeep(existing) : {}, AI_EMPLOYEE_TASK_PUBLIC_SETTING_KEYS);
17741
+ if (Object.prototype.hasOwnProperty.call(patch, "prompt") && import_lodash.default.isPlainObject(patch.message) && Object.prototype.hasOwnProperty.call(patch.message, "user")) {
17742
+ (0, import_errors.throwBadRequest)(
17743
+ `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`
17744
+ );
17745
+ }
16619
17746
  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}'`);
17747
+ if (!AI_EMPLOYEE_TASK_PATCH_PUBLIC_SETTING_KEYS.includes(key)) {
17748
+ (0, import_errors.throwBadRequest)(
17749
+ `flowSurfaces ${actionName} ${options.path} does not support key '${key}'; allowed keys: ${AI_EMPLOYEE_TASK_PATCH_PUBLIC_SETTING_KEYS.join(
17750
+ ", "
17751
+ )}; repairHint: ${AI_EMPLOYEE_TASK_REPAIR_HINT}`
17752
+ );
16622
17753
  }
16623
17754
  if (key === "message") {
16624
17755
  next.message = this.normalizeAIEmployeeTaskMessage(actionName, `${options.path}.message`, value, next.message, {
@@ -16627,6 +17758,18 @@ class FlowSurfacesService {
16627
17758
  });
16628
17759
  return;
16629
17760
  }
17761
+ if (key === "prompt") {
17762
+ if (typeof value !== "string") {
17763
+ (0, import_errors.throwBadRequest)(
17764
+ `flowSurfaces ${actionName} ${options.path}.prompt must be a string; repairHint: ${AI_EMPLOYEE_TASK_REPAIR_HINT}`
17765
+ );
17766
+ }
17767
+ next.message = {
17768
+ ...import_lodash.default.isPlainObject(next.message) ? import_lodash.default.cloneDeep(next.message) : {},
17769
+ user: value
17770
+ };
17771
+ return;
17772
+ }
16630
17773
  if (key === "title") {
16631
17774
  if (typeof value !== "string") {
16632
17775
  (0, import_errors.throwBadRequest)(`flowSurfaces ${actionName} ${options.path}.title must be a string`);
@@ -16661,6 +17804,14 @@ class FlowSurfacesService {
16661
17804
  if (!import_lodash.default.isPlainObject(next.message)) {
16662
17805
  next.message = {};
16663
17806
  }
17807
+ if (import_lodash.default.isPlainObject(next.skillSettings)) {
17808
+ if (Array.isArray(next.skillSettings.skills) && !Object.prototype.hasOwnProperty.call(next.skillSettings, "skillsVersion")) {
17809
+ next.skillSettings.skillsVersion = 2;
17810
+ }
17811
+ if (Array.isArray(next.skillSettings.tools) && !Object.prototype.hasOwnProperty.call(next.skillSettings, "toolsVersion")) {
17812
+ next.skillSettings.toolsVersion = 2;
17813
+ }
17814
+ }
16664
17815
  return next;
16665
17816
  }
16666
17817
  normalizeAIEmployeeTasks(actionName, value, currentTasks, options) {
@@ -16723,6 +17874,7 @@ class FlowSurfacesService {
16723
17874
  });
16724
17875
  }
16725
17876
  const props = {};
17877
+ const stepParams = {};
16726
17878
  if (effectiveUsername && (hasUsername || options.requireUsername)) {
16727
17879
  props.aiEmployee = {
16728
17880
  ...import_lodash.default.isPlainObject(currentProps.aiEmployee) ? import_lodash.default.cloneDeep(currentProps.aiEmployee) : {},
@@ -16748,11 +17900,36 @@ class FlowSurfacesService {
16748
17900
  };
16749
17901
  }
16750
17902
  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"
17903
+ const normalizedTasks = this.normalizeAIEmployeeTasks(
17904
+ actionName,
17905
+ settings.tasks,
17906
+ this.readAIEmployeePersistedTasks(options.current),
17907
+ {
17908
+ selfUid: options.selfUid,
17909
+ keyMap: options.keyMap,
17910
+ path: "settings.tasks"
17911
+ }
17912
+ );
17913
+ const validation = options.promptContext && normalizedTasks.length ? await this.buildAIEmployeePromptValidationContext(
17914
+ actionName,
17915
+ [
17916
+ {
17917
+ message: {
17918
+ user: AI_EMPLOYEE_CURRENT_RECORD_PROMPT_VARIABLE
17919
+ }
17920
+ }
17921
+ ],
17922
+ {
17923
+ transaction: options.transaction,
17924
+ promptContext: options.promptContext
17925
+ }
17926
+ ) : await this.buildAIEmployeePromptValidationContext(actionName, normalizedTasks, {
17927
+ transaction: options.transaction,
17928
+ promptContext: options.promptContext
16755
17929
  });
17930
+ const tasks = this.appendAIEmployeeCurrentRecordPromptVariableToTasks(normalizedTasks, validation);
17931
+ this.assertAIEmployeeTaskPromptVariablesAllowed(actionName, tasks, validation);
17932
+ import_lodash.default.merge(stepParams, this.buildAIEmployeeTaskStepParams(tasks));
16756
17933
  }
16757
17934
  if (Object.prototype.hasOwnProperty.call(settings, "style") || options.requireUsername) {
16758
17935
  const style = Object.prototype.hasOwnProperty.call(settings, "style") ? settings.style : {};
@@ -16780,7 +17957,10 @@ class FlowSurfacesService {
16780
17957
  import_lodash.default.isPlainObject(style) ? import_lodash.default.pick(import_lodash.default.cloneDeep(style), AI_EMPLOYEE_STYLE_PUBLIC_KEYS) : {}
16781
17958
  );
16782
17959
  }
16783
- return props;
17960
+ return {
17961
+ props,
17962
+ stepParams
17963
+ };
16784
17964
  }
16785
17965
  async resolveAIEmployeeActionSelfUid(actionNode, transaction) {
16786
17966
  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 +17984,39 @@ class FlowSurfacesService {
16804
17984
  }) : null);
16805
17985
  changes = await this.normalizeActionPanelActionChanges(changes, options);
16806
17986
  const allowedKeys = (0, import_configure_options.getConfigureOptionKeysForUse)(use);
17987
+ if ((0, import_service_utils.hasOwnDefined)(changes, "openView") && !allowedKeys.includes("openView") && JS_POPUP_GUIDANCE_USES.has(use)) {
17988
+ (0, import_errors.throwBadRequest)(withJsPopupGuidance(`flowSurfaces configure action '${use}' does not support openView`, use));
17989
+ }
16807
17990
  (0, import_service_utils.assertSupportedSimpleChanges)("action", changes, allowedKeys);
16808
17991
  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
- },
17992
+ const aiEmployeeSettingsPayload = await this.normalizeAIEmployeeActionPublicSettings(
17993
+ "configure action",
17994
+ changes,
16821
17995
  {
16822
- ...options,
16823
- openViewActionName: options.openViewActionName || "configure action",
16824
- popupTemplateHostUid: target.uid,
16825
- allowAIEmployeeInternalProps: true
17996
+ transaction: options.transaction,
17997
+ enabledPackages: options.enabledPackages || await this.resolveEnabledPluginPackages(options),
17998
+ current: currentNode,
17999
+ currentRoles: options.currentRoles,
18000
+ selfUid: await this.resolveAIEmployeeActionSelfUid(currentNode, options.transaction),
18001
+ promptContext: {
18002
+ kind: "target",
18003
+ targetUid: (currentNode == null ? void 0 : currentNode.uid) || target.uid
18004
+ }
16826
18005
  }
16827
18006
  );
18007
+ const settingsValues = { target };
18008
+ if (Object.keys(aiEmployeeSettingsPayload.props).length) {
18009
+ settingsValues.props = aiEmployeeSettingsPayload.props;
18010
+ }
18011
+ if (Object.keys(aiEmployeeSettingsPayload.stepParams).length) {
18012
+ settingsValues.stepParams = aiEmployeeSettingsPayload.stepParams;
18013
+ }
18014
+ return this.updateSettings(settingsValues, {
18015
+ ...options,
18016
+ openViewActionName: options.openViewActionName || "configure action",
18017
+ popupTemplateHostUid: target.uid,
18018
+ allowAIEmployeeInternalProps: true
18019
+ });
16828
18020
  }
16829
18021
  const normalizedDefaultFilter = (0, import_service_utils.hasOwnDefined)(changes, "defaultFilter") ? this.normalizeFilterActionDefaultFilterValue(changes.defaultFilter) : void 0;
16830
18022
  const stepParams = {};
@@ -16888,7 +18080,7 @@ class FlowSurfacesService {
16888
18080
  openView: import_lodash.default.cloneDeep(changes.openView)
16889
18081
  };
16890
18082
  } else if (!POPUP_ACTION_USES.has(use)) {
16891
- (0, import_errors.throwBadRequest)(`flowSurfaces configure action '${use}' does not support openView`);
18083
+ (0, import_errors.throwBadRequest)(withJsPopupGuidance(`flowSurfaces configure action '${use}' does not support openView`, use));
16892
18084
  } else {
16893
18085
  let openView = import_lodash.default.cloneDeep(changes.openView);
16894
18086
  if (use === "AddChildActionModel") {
@@ -18117,7 +19309,8 @@ class FlowSurfacesService {
18117
19309
  }
18118
19310
  resolveRecordActionContextOwner(ancestors) {
18119
19311
  const currentNode = ancestors[0];
18120
- if (!String((currentNode == null ? void 0 : currentNode.use) || "").endsWith("ActionModel")) {
19312
+ const currentUse = String((currentNode == null ? void 0 : currentNode.use) || "").trim();
19313
+ if (!currentUse.endsWith("ActionModel") && !import_node_use_sets.ACTION_BUTTON_USES.has(currentUse)) {
18121
19314
  return null;
18122
19315
  }
18123
19316
  return ancestors.slice(1).find((node) => (0, import_action_scope.getActionContainerScope)(node == null ? void 0 : node.use) === "record") || null;
@@ -18368,11 +19561,12 @@ class FlowSurfacesService {
18368
19561
  };
18369
19562
  }
18370
19563
  getCollection(dataSourceKey, collectionName) {
18371
- var _a, _b, _c, _d, _e;
19564
+ var _a, _b, _c, _d, _e, _f, _g, _h;
18372
19565
  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));
19566
+ const app = (_a = this.plugin) == null ? void 0 : _a.app;
19567
+ const dataSourceManager = app == null ? void 0 : app.dataSourceManager;
19568
+ const dataSource = (_b = dataSourceManager == null ? void 0 : dataSourceManager.get) == null ? void 0 : _b.call(dataSourceManager, normalizedDataSourceKey);
19569
+ 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
19570
  }
18377
19571
  normalizeKanbanFieldNameInput(value, context, options = {}) {
18378
19572
  if (import_lodash.default.isUndefined(value)) {