@nocobase/plugin-flow-engine 2.1.0-beta.36 → 2.1.0-beta.37

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.
@@ -461,12 +461,17 @@ const AI_EMPLOYEE_ACTION_USE = "AIEmployeeButtonModel";
461
461
  const AI_EMPLOYEE_OWNER_PLUGIN = "@nocobase/plugin-ai";
462
462
  const AI_EMPLOYEE_PUBLIC_SETTING_KEYS = ["username", "auto", "workContext", "tasks", "style"];
463
463
  const AI_EMPLOYEE_INTERNAL_PROP_KEYS = ["aiEmployee", "context", "auto", "tasks", "style"];
464
+ const AI_EMPLOYEE_TASK_STEP_PARAMS_PATH = ["shortcutSettings", "editTasks", "tasks"];
464
465
  const AI_EMPLOYEE_WORK_CONTEXT_PUBLIC_KEYS = ["type", "uid", "target"];
465
466
  const AI_EMPLOYEE_TASK_PUBLIC_SETTING_KEYS = ["title", "message", "autoSend", "skillSettings", "model", "webSearch"];
466
467
  const AI_EMPLOYEE_TASK_MESSAGE_PUBLIC_KEYS = ["system", "user", "workContext"];
467
468
  const AI_EMPLOYEE_TASK_MODEL_PUBLIC_KEYS = ["llmService", "model"];
468
469
  const AI_EMPLOYEE_SKILL_SETTINGS_PUBLIC_KEYS = ["skills", "tools", "skillsVersion", "toolsVersion"];
469
470
  const AI_EMPLOYEE_STYLE_PUBLIC_KEYS = ["size", "mask"];
471
+ const AI_EMPLOYEE_PROMPT_CONTEXT_MAX_DEPTH = 8;
472
+ const AI_EMPLOYEE_PROMPT_VARIABLE_INVALID = "FLOW_SURFACE_AI_EMPLOYEE_PROMPT_VARIABLE_INVALID";
473
+ const AI_EMPLOYEE_CURRENT_RECORD_PROMPT_VARIABLE = "{{ ctx.record }}";
474
+ const AI_EMPLOYEE_CURRENT_RECORD_PROMPT_VARIABLE_RE = /\{\{\s*ctx\.record\s*\}\}/;
470
475
  const AI_EMPLOYEE_DEFAULT_STYLE = {
471
476
  size: 40,
472
477
  mask: false
@@ -710,10 +715,16 @@ class FlowSurfacesService {
710
715
  );
711
716
  }
712
717
  async loadEnabledPluginPackages(transaction) {
713
- if (!this.db.getCollection("applicationPlugins")) {
718
+ var _a;
719
+ const db = this.db;
720
+ if (!((_a = db == null ? void 0 : db.getCollection) == null ? void 0 : _a.call(db, "applicationPlugins")) || !(db == null ? void 0 : db.getRepository)) {
721
+ return /* @__PURE__ */ new Set();
722
+ }
723
+ const repository = db.getRepository("applicationPlugins");
724
+ if (!(repository == null ? void 0 : repository.find)) {
714
725
  return /* @__PURE__ */ new Set();
715
726
  }
716
- const plugins = await this.db.getRepository("applicationPlugins").find({
727
+ const plugins = await repository.find({
717
728
  fields: ["packageName"],
718
729
  filter: {
719
730
  enabled: true
@@ -721,8 +732,8 @@ class FlowSurfacesService {
721
732
  transaction
722
733
  });
723
734
  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);
735
+ var _a2;
736
+ return ((_a2 = plugin == null ? void 0 : plugin.get) == null ? void 0 : _a2.call(plugin, "packageName")) || (plugin == null ? void 0 : plugin.packageName);
726
737
  }).filter((packageName) => typeof packageName === "string" && !!packageName.trim()).map((packageName) => packageName.trim());
727
738
  return new Set(packageNames);
728
739
  }
@@ -2843,6 +2854,22 @@ class FlowSurfacesService {
2843
2854
  getCollection: (dataSourceKey, collectionName) => this.getCollection(dataSourceKey || "main", collectionName || "")
2844
2855
  });
2845
2856
  }
2857
+ async prevalidateApplyBlueprintChartAssets(document, transaction) {
2858
+ var _a;
2859
+ const chartAssets = import_lodash.default.isPlainObject((_a = document.assets) == null ? void 0 : _a.charts) ? document.assets.charts : {};
2860
+ for (const [chartKey, chartAsset] of Object.entries(chartAssets)) {
2861
+ if (!import_lodash.default.isPlainObject(chartAsset)) {
2862
+ continue;
2863
+ }
2864
+ (0, import_service_utils.assertSupportedSimpleChanges)("chart", chartAsset, (0, import_configure_options.getConfigureOptionKeysForUse)("ChartBlockModel"));
2865
+ const nextConfigure = (0, import_chart_config.buildChartConfigureFromSemanticChanges)(void 0, chartAsset);
2866
+ await this.validateChartConfigureForRuntime(
2867
+ `applyBlueprint assets.charts.${chartKey}`,
2868
+ nextConfigure,
2869
+ transaction
2870
+ );
2871
+ }
2872
+ }
2846
2873
  getApplyBlueprintKanbanBlockResourceObject(block) {
2847
2874
  return import_lodash.default.isPlainObject(block == null ? void 0 : block.resource) ? block.resource : {};
2848
2875
  }
@@ -3231,25 +3258,48 @@ class FlowSurfacesService {
3231
3258
  }
3232
3259
  if (!options.transaction) {
3233
3260
  const createdKanbanSortFields = [];
3234
- try {
3235
- return await this.transaction(
3236
- (transaction) => this.applyBlueprintWithTransaction(
3237
- values,
3238
- {
3239
- ...options,
3240
- transaction
3241
- },
3242
- createdKanbanSortFields
3243
- )
3261
+ const mutationResult = await this.applyBlueprintMutationWithoutExternalTransaction(
3262
+ values,
3263
+ options,
3264
+ createdKanbanSortFields
3265
+ );
3266
+ const surface = await this.get(mutationResult.pageLocator, { currentRoles: options.currentRoles });
3267
+ return this.buildApplyBlueprintResponse(mutationResult.mode, mutationResult.pageLocator, surface);
3268
+ }
3269
+ }
3270
+ async applyBlueprintMutationWithoutExternalTransaction(values, options = {}, createdKanbanSortFields) {
3271
+ try {
3272
+ await this.assertApplyBlueprintAuthoringPayload(values, options);
3273
+ const document = (0, import_blueprint.prepareFlowSurfaceApplyBlueprintDocument)(values);
3274
+ await this.prevalidateApplyBlueprintChartAssets(document);
3275
+ if (document.mode === "create") {
3276
+ return await this.applyBlueprintWithTransaction(
3277
+ values,
3278
+ { ...options, skipAuthoringValidation: true },
3279
+ createdKanbanSortFields,
3280
+ {
3281
+ readSurface: false
3282
+ }
3244
3283
  );
3245
- } catch (error) {
3246
- await this.cleanupApplyBlueprintKanbanSortFields(createdKanbanSortFields);
3247
- throw error;
3248
3284
  }
3285
+ return await this.transaction(
3286
+ (transaction) => this.applyBlueprintWithTransaction(
3287
+ values,
3288
+ {
3289
+ ...options,
3290
+ transaction,
3291
+ skipAuthoringValidation: true
3292
+ },
3293
+ createdKanbanSortFields,
3294
+ { readSurface: false }
3295
+ )
3296
+ );
3297
+ } catch (error) {
3298
+ await this.cleanupApplyBlueprintKanbanSortFields(createdKanbanSortFields);
3299
+ throw error;
3249
3300
  }
3250
3301
  }
3251
- async applyBlueprintWithTransaction(values, options = {}, createdKanbanSortFields) {
3252
- var _a;
3302
+ async assertApplyBlueprintAuthoringPayload(values, options = {}) {
3253
3303
  const enabledPackages = await this.resolveEnabledPluginPackages(options);
3254
3304
  await (0, import_authoring_validation.assertFlowSurfaceAuthoringPayload)("applyBlueprint", values, {
3255
3305
  transaction: options.transaction,
@@ -3257,6 +3307,11 @@ class FlowSurfacesService {
3257
3307
  findMenuGroupRoutesByTitle: (title, transaction) => this.findMenuGroupRoutesByTitle(title, transaction),
3258
3308
  getCollection: (dataSourceKey, collectionName) => this.getCollection(dataSourceKey || "main", collectionName || "")
3259
3309
  });
3310
+ }
3311
+ async applyBlueprintWithTransaction(values, options = {}, createdKanbanSortFields, resultOptions = {}) {
3312
+ if (options.skipAuthoringValidation !== true) {
3313
+ await this.assertApplyBlueprintAuthoringPayload(values, options);
3314
+ }
3260
3315
  const prepared = await this.prepareApplyBlueprintRequest(values, options.transaction, createdKanbanSortFields);
3261
3316
  const popupTemplateAliasSession = this.createPopupTemplateAliasSession();
3262
3317
  const popupTemplateTreeCache = /* @__PURE__ */ new Map();
@@ -3277,10 +3332,21 @@ class FlowSurfacesService {
3277
3332
  options
3278
3333
  );
3279
3334
  const pageLocator = (0, import_blueprint.resolveApplyBlueprintPageLocator)(prepared, result);
3335
+ if (resultOptions.readSurface === false) {
3336
+ return {
3337
+ version: "1",
3338
+ mode: prepared.document.mode,
3339
+ pageLocator
3340
+ };
3341
+ }
3280
3342
  const surface = await this.get(pageLocator, options);
3343
+ return this.buildApplyBlueprintResponse(prepared.document.mode, pageLocator, surface);
3344
+ }
3345
+ buildApplyBlueprintResponse(mode, pageLocator, surface) {
3346
+ var _a;
3281
3347
  return {
3282
3348
  version: "1",
3283
- mode: prepared.document.mode,
3349
+ mode,
3284
3350
  target: (0, import_service_utils.buildDefinedPayload)({
3285
3351
  pageSchemaUid: pageLocator.pageSchemaUid,
3286
3352
  pageUid: (_a = surface == null ? void 0 : surface.target) == null ? void 0 : _a.uid
@@ -7041,20 +7107,36 @@ class FlowSurfacesService {
7041
7107
  return result2;
7042
7108
  }
7043
7109
  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 || {}, {
7110
+ const aiEmployeeSettingsPayload = this.isAIEmployeeActionUse(actionCatalogItem.use) ? await this.normalizeAIEmployeeActionPublicSettings("addAction", inlineSettings || {}, {
7045
7111
  transaction: options.transaction,
7046
7112
  enabledPackages,
7047
7113
  requireUsername: true,
7048
7114
  currentRoles: options.currentRoles,
7049
- selfUid: container.ownerUid
7115
+ selfUid: container.ownerUid,
7116
+ promptContext: {
7117
+ kind: "container",
7118
+ ownerUid: container.ownerUid,
7119
+ scope: resolvedScope
7120
+ }
7050
7121
  }) : void 0;
7122
+ const actionSettingsPayload = {
7123
+ props: values.props,
7124
+ stepParams: values.stepParams
7125
+ };
7126
+ if (aiEmployeeSettingsPayload) {
7127
+ this.mergeAIEmployeeActionSettingsPayload(
7128
+ { use: actionCatalogItem.use },
7129
+ actionSettingsPayload,
7130
+ aiEmployeeSettingsPayload
7131
+ );
7132
+ }
7051
7133
  const action = (0, import_builder.buildActionTree)({
7052
7134
  use: actionCatalogItem.use,
7053
7135
  containerUse: container.ownerUse,
7054
7136
  resourceInit: values.resourceInit || (resourceContext == null ? void 0 : resourceContext.resourceInit),
7055
- props: aiEmployeeProps || values.props,
7137
+ props: actionSettingsPayload.props,
7056
7138
  decoratorProps: values.decoratorProps,
7057
- stepParams: values.stepParams,
7139
+ stepParams: actionSettingsPayload.stepParams,
7058
7140
  flowRegistry: values.flowRegistry
7059
7141
  });
7060
7142
  this.contractGuard.validateNodeTreeAgainstContract(action);
@@ -7141,20 +7223,36 @@ class FlowSurfacesService {
7141
7223
  }
7142
7224
  const resourceContext = container.ownerUid ? await this.locator.resolveCollectionContext(container.ownerUid, options.transaction).catch(() => null) : null;
7143
7225
  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 || {}, {
7226
+ const aiEmployeeSettingsPayload = this.isAIEmployeeActionUse(actionCatalogItem.use) ? await this.normalizeAIEmployeeActionPublicSettings("addRecordAction", inlineSettings || {}, {
7145
7227
  transaction: options.transaction,
7146
7228
  enabledPackages,
7147
7229
  requireUsername: true,
7148
7230
  currentRoles: options.currentRoles,
7149
- selfUid: container.ownerUid
7231
+ selfUid: container.ownerUid,
7232
+ promptContext: {
7233
+ kind: "container",
7234
+ ownerUid: container.ownerUid,
7235
+ scope: resolvedScope
7236
+ }
7150
7237
  }) : void 0;
7238
+ const actionSettingsPayload = {
7239
+ props: values.props,
7240
+ stepParams: values.stepParams
7241
+ };
7242
+ if (aiEmployeeSettingsPayload) {
7243
+ this.mergeAIEmployeeActionSettingsPayload(
7244
+ { use: actionCatalogItem.use },
7245
+ actionSettingsPayload,
7246
+ aiEmployeeSettingsPayload
7247
+ );
7248
+ }
7151
7249
  const action = (0, import_builder.buildActionTree)({
7152
7250
  use: actionCatalogItem.use,
7153
7251
  containerUse: container.containerUse,
7154
7252
  resourceInit,
7155
- props: aiEmployeeProps || values.props,
7253
+ props: actionSettingsPayload.props,
7156
7254
  decoratorProps: values.decoratorProps,
7157
- stepParams: values.stepParams,
7255
+ stepParams: actionSettingsPayload.stepParams,
7158
7256
  flowRegistry: values.flowRegistry
7159
7257
  });
7160
7258
  this.contractGuard.validateNodeTreeAgainstContract(action);
@@ -10641,7 +10739,7 @@ class FlowSurfacesService {
10641
10739
  }
10642
10740
  if (this.isAIEmployeeActionUse(current == null ? void 0 : current.use) && this.hasAIEmployeePublicSettings(normalizedValues)) {
10643
10741
  const enabledPackages = await this.resolveEnabledPluginPackages(options);
10644
- const aiEmployeeProps = await this.normalizeAIEmployeeActionPublicSettings(
10742
+ const aiEmployeeSettingsPayload = await this.normalizeAIEmployeeActionPublicSettings(
10645
10743
  "updateSettings",
10646
10744
  import_lodash.default.pick(normalizedValues, AI_EMPLOYEE_PUBLIC_SETTING_KEYS),
10647
10745
  {
@@ -10649,13 +10747,17 @@ class FlowSurfacesService {
10649
10747
  enabledPackages,
10650
10748
  current,
10651
10749
  currentRoles: options.currentRoles,
10652
- selfUid: await this.resolveAIEmployeeActionSelfUid(current, options.transaction)
10750
+ selfUid: await this.resolveAIEmployeeActionSelfUid(current, options.transaction),
10751
+ promptContext: {
10752
+ kind: "target",
10753
+ targetUid: (current == null ? void 0 : current.uid) || writeTarget.uid
10754
+ }
10653
10755
  }
10654
10756
  );
10655
10757
  AI_EMPLOYEE_PUBLIC_SETTING_KEYS.forEach((key) => {
10656
10758
  delete normalizedValues[key];
10657
10759
  });
10658
- normalizedValues.props = import_lodash.default.merge({}, normalizedValues.props || {}, aiEmployeeProps);
10760
+ this.mergeAIEmployeeActionSettingsPayload(current, normalizedValues, aiEmployeeSettingsPayload);
10659
10761
  }
10660
10762
  assertNoFlowSurfaceIdTitleFieldSettings(normalizedValues, {
10661
10763
  action: "updateSettings",
@@ -10703,6 +10805,14 @@ class FlowSurfacesService {
10703
10805
  nextPayload,
10704
10806
  options.replaceRecordHistorySettings === true
10705
10807
  );
10808
+ this.syncAIEmployeeTaskStepParamsForUpdateSettings(current, nextPayload);
10809
+ await this.normalizeAIEmployeeStepParamTasksForUpdateSettings(current, nextPayload, writeTarget, options);
10810
+ await this.assertAIEmployeeStepParamTaskPromptVariablesAllowedForUpdateSettings(
10811
+ current,
10812
+ nextPayload,
10813
+ writeTarget,
10814
+ options
10815
+ );
10706
10816
  this.syncCalendarPopupPropsForUpdateSettings(current, normalizedValues, nextPayload);
10707
10817
  this.syncKanbanPopupPropsForUpdateSettings(current, normalizedValues, nextPayload);
10708
10818
  this.syncDefaultSortingForUpdateSettings(current, normalizedValues, nextPayload);
@@ -11679,6 +11789,36 @@ class FlowSurfacesService {
11679
11789
  suggestedFieldPath: `${fieldPath}.<field>`
11680
11790
  };
11681
11791
  }
11792
+ async validateChartConfigureForRuntime(actionName, configure, transaction) {
11793
+ var _a, _b, _c;
11794
+ if (!import_lodash.default.isPlainObject(configure)) {
11795
+ return;
11796
+ }
11797
+ this.validateBuilderChartFieldsForRuntime(actionName, configure);
11798
+ const state = (0, import_chart_config.deriveChartSemanticState)(configure);
11799
+ if (((_a = state.query) == null ? void 0 : _a.mode) !== "sql") {
11800
+ return;
11801
+ }
11802
+ const sqlPreview = await this.resolveSqlChartPreview(state.query, transaction);
11803
+ if (((_b = state.visual) == null ? void 0 : _b.mode) !== "basic") {
11804
+ return;
11805
+ }
11806
+ if (!((_c = sqlPreview.queryOutputs) == null ? void 0 : _c.length)) {
11807
+ (0, import_errors.throwBadRequest)(
11808
+ "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"
11809
+ );
11810
+ }
11811
+ const supportedOutputs = new Set(
11812
+ sqlPreview.queryOutputs.map((output) => String((output == null ? void 0 : output.alias) || "").trim()).filter(Boolean)
11813
+ );
11814
+ for (const mappingField of (0, import_chart_config.getChartVisualMappingAliases)(state.visual)) {
11815
+ if (!supportedOutputs.has(mappingField)) {
11816
+ (0, import_errors.throwBadRequest)(
11817
+ `chart visual mappings only support SQL query output fields: ${Array.from(supportedOutputs).join(", ")}`
11818
+ );
11819
+ }
11820
+ }
11821
+ }
11682
11822
  stripChartSqlForInspection(sql) {
11683
11823
  return sql.replace(/\/\*[\s\S]*?\*\//g, " ").replace(/--[^\r\n]*/g, " ").replace(/'(?:''|[^'])*'/g, "''").replace(/"(?:[""]|[^"])*"/g, '""');
11684
11824
  }
@@ -16381,6 +16521,67 @@ class FlowSurfacesService {
16381
16521
  )}; use top-level username, auto, workContext, tasks or style instead`
16382
16522
  );
16383
16523
  }
16524
+ readAIEmployeePersistedTasks(current) {
16525
+ var _a;
16526
+ const stepTasks = import_lodash.default.get(current, ["stepParams", ...AI_EMPLOYEE_TASK_STEP_PARAMS_PATH]);
16527
+ if (Array.isArray(stepTasks)) {
16528
+ return stepTasks;
16529
+ }
16530
+ const legacyPropsTasks = (_a = current == null ? void 0 : current.props) == null ? void 0 : _a.tasks;
16531
+ return Array.isArray(legacyPropsTasks) ? legacyPropsTasks : [];
16532
+ }
16533
+ buildAIEmployeeTaskStepParams(tasks) {
16534
+ const stepParams = {};
16535
+ import_lodash.default.set(stepParams, AI_EMPLOYEE_TASK_STEP_PARAMS_PATH, import_lodash.default.cloneDeep(tasks));
16536
+ return stepParams;
16537
+ }
16538
+ mergeAIEmployeeActionSettingsPayload(current, values, payload) {
16539
+ if (Object.keys(payload.props).length) {
16540
+ values.props = import_lodash.default.merge({}, values.props || {}, payload.props);
16541
+ }
16542
+ if (Object.keys(payload.stepParams).length) {
16543
+ values.stepParams = import_lodash.default.mergeWith({}, values.stepParams || {}, payload.stepParams, (_currentValue, nextValue) => {
16544
+ if (Array.isArray(nextValue)) {
16545
+ return import_lodash.default.cloneDeep(nextValue);
16546
+ }
16547
+ return void 0;
16548
+ });
16549
+ }
16550
+ if (import_lodash.default.has(payload.stepParams, AI_EMPLOYEE_TASK_STEP_PARAMS_PATH) && this.isAIEmployeeActionUse(current == null ? void 0 : current.use)) {
16551
+ if (import_lodash.default.isPlainObject(values.props)) {
16552
+ delete values.props.tasks;
16553
+ }
16554
+ }
16555
+ }
16556
+ syncAIEmployeeTaskStepParamsForUpdateSettings(current, nextPayload) {
16557
+ var _a;
16558
+ if (!this.isAIEmployeeActionUse(current == null ? void 0 : current.use)) {
16559
+ return;
16560
+ }
16561
+ const legacyPropsTasks = (_a = current == null ? void 0 : current.props) == null ? void 0 : _a.tasks;
16562
+ const hasLegacyPropsTasks = Array.isArray(legacyPropsTasks);
16563
+ const hasNextStepTasks = import_lodash.default.has(nextPayload, ["stepParams", ...AI_EMPLOYEE_TASK_STEP_PARAMS_PATH]);
16564
+ const currentStepTasks = import_lodash.default.get(current, ["stepParams", ...AI_EMPLOYEE_TASK_STEP_PARAMS_PATH]);
16565
+ if (!hasNextStepTasks && hasLegacyPropsTasks && !Array.isArray(currentStepTasks)) {
16566
+ nextPayload.stepParams = import_lodash.default.mergeWith(
16567
+ {},
16568
+ (current == null ? void 0 : current.stepParams) || {},
16569
+ this.buildAIEmployeeTaskStepParams(legacyPropsTasks),
16570
+ (_currentValue, nextValue) => {
16571
+ if (Array.isArray(nextValue)) {
16572
+ return import_lodash.default.cloneDeep(nextValue);
16573
+ }
16574
+ return void 0;
16575
+ }
16576
+ );
16577
+ }
16578
+ if (!hasLegacyPropsTasks && !import_lodash.default.has(nextPayload, ["props", "tasks"])) {
16579
+ return;
16580
+ }
16581
+ const nextProps = import_lodash.default.cloneDeep(nextPayload.props ?? (current == null ? void 0 : current.props) ?? {});
16582
+ delete nextProps.tasks;
16583
+ nextPayload.props = nextProps;
16584
+ }
16384
16585
  assertAIEmployeePluginEnabled(actionName, enabledPackages) {
16385
16586
  if (!enabledPackages.has(AI_EMPLOYEE_OWNER_PLUGIN)) {
16386
16587
  (0, import_errors.throwBadRequest)(
@@ -16574,8 +16775,290 @@ class FlowSurfacesService {
16574
16775
  if (Object.prototype.hasOwnProperty.call(next, "toolsVersion") && typeof next.toolsVersion !== "number") {
16575
16776
  (0, import_errors.throwBadRequest)(`flowSurfaces ${actionName} ${path}.toolsVersion must be a number`);
16576
16777
  }
16778
+ const hasSkills = Object.prototype.hasOwnProperty.call(next, "skills");
16779
+ const hasTools = Object.prototype.hasOwnProperty.call(next, "tools");
16780
+ const hasSkillsVersion = Object.prototype.hasOwnProperty.call(next, "skillsVersion");
16781
+ const hasToolsVersion = Object.prototype.hasOwnProperty.call(next, "toolsVersion");
16782
+ if (hasSkills && hasTools && !hasSkillsVersion && !hasToolsVersion && Array.isArray(next.skills) && Array.isArray(next.tools) && next.skills.length === 0 && next.tools.length === 0) {
16783
+ return null;
16784
+ }
16785
+ if (hasSkills && !hasSkillsVersion) {
16786
+ next.skillsVersion = 2;
16787
+ }
16788
+ if (hasTools && !hasToolsVersion) {
16789
+ next.toolsVersion = 2;
16790
+ }
16577
16791
  return next;
16578
16792
  }
16793
+ collectAIEmployeePromptVariables(value) {
16794
+ if (!value.includes("{{")) {
16795
+ return [];
16796
+ }
16797
+ return [...value.matchAll(/\{\{\s*([\s\S]+?)\s*\}\}/g)].map(([raw, inner]) => ({
16798
+ raw,
16799
+ inner: String(inner || "").trim()
16800
+ }));
16801
+ }
16802
+ hasAIEmployeePromptVariables(tasks) {
16803
+ return import_lodash.default.castArray(tasks || []).some((task) => {
16804
+ const message = task == null ? void 0 : task.message;
16805
+ 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;
16806
+ });
16807
+ }
16808
+ collectAIEmployeeContextPathEntries(context) {
16809
+ const result = /* @__PURE__ */ new Map();
16810
+ const visit = (prefix, info) => {
16811
+ if (!prefix || !info) {
16812
+ return;
16813
+ }
16814
+ result.set(prefix, info);
16815
+ if (info.dynamicProperties) {
16816
+ result.set(`${prefix}.*`, info.dynamicProperties);
16817
+ }
16818
+ for (const [childKey, childInfo] of Object.entries(info.properties || {})) {
16819
+ visit(`${prefix}.${childKey}`, childInfo);
16820
+ }
16821
+ };
16822
+ for (const [key, info] of Object.entries((context == null ? void 0 : context.vars) || {})) {
16823
+ visit(key, info);
16824
+ }
16825
+ return result;
16826
+ }
16827
+ isAIEmployeeContextInfoPathAllowed(info, segments) {
16828
+ var _a;
16829
+ if (!info) {
16830
+ return false;
16831
+ }
16832
+ if (!segments.length) {
16833
+ return true;
16834
+ }
16835
+ const [segment, ...rest] = segments;
16836
+ const staticChild = (_a = info.properties) == null ? void 0 : _a[segment];
16837
+ if (staticChild) {
16838
+ return this.isAIEmployeeContextInfoPathAllowed(staticChild, rest);
16839
+ }
16840
+ if (info.dynamicProperties) {
16841
+ return this.isAIEmployeeContextInfoPathAllowed(info.dynamicProperties, rest);
16842
+ }
16843
+ return false;
16844
+ }
16845
+ canResolveAIEmployeeContextPath(path, semantic) {
16846
+ var _a;
16847
+ return !!((_a = (0, import_context.buildFlowSurfaceContextResponse)({
16848
+ semantic,
16849
+ path,
16850
+ maxDepth: 1
16851
+ }).vars) == null ? void 0 : _a[path]);
16852
+ }
16853
+ isAIEmployeeContextPathAllowed(path, validation) {
16854
+ if (validation.contextPaths.has(path)) {
16855
+ return true;
16856
+ }
16857
+ for (const [allowedPath, info] of validation.contextPaths) {
16858
+ if (!allowedPath.endsWith(".*")) {
16859
+ continue;
16860
+ }
16861
+ const prefix = allowedPath.slice(0, -2);
16862
+ if (!path.startsWith(`${prefix}.`)) {
16863
+ continue;
16864
+ }
16865
+ const dynamicSegments = path.slice(prefix.length + 1).split(".").filter(Boolean);
16866
+ if (dynamicSegments.length && this.isAIEmployeeContextInfoPathAllowed(info, dynamicSegments.slice(1))) {
16867
+ return true;
16868
+ }
16869
+ }
16870
+ return this.canResolveAIEmployeeContextPath(path, validation.semantic);
16871
+ }
16872
+ extractAIEmployeePromptContextPath(expression) {
16873
+ const normalized = String(expression || "").trim();
16874
+ if (normalized === "ctx") {
16875
+ return "";
16876
+ }
16877
+ if (!normalized.startsWith("ctx.")) {
16878
+ return null;
16879
+ }
16880
+ const path = normalized.slice(4).trim();
16881
+ return (0, import_context.isBareFlowContextPath)(path) ? path : null;
16882
+ }
16883
+ buildAIEmployeePromptVariableHint(targetUid) {
16884
+ return `Call flowSurfaces:context${targetUid ? ` with target.uid "${targetUid}"` : ""} to inspect available ctx paths before writing prompt variables.`;
16885
+ }
16886
+ throwAIEmployeePromptVariableInvalid(input) {
16887
+ (0, import_errors.throwBadRequest)(
16888
+ `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(
16889
+ input.targetUid
16890
+ )}`,
16891
+ AI_EMPLOYEE_PROMPT_VARIABLE_INVALID,
16892
+ {
16893
+ path: input.fieldPath,
16894
+ details: (0, import_service_utils.buildDefinedPayload)({
16895
+ variable: input.variable,
16896
+ contextPath: input.path,
16897
+ targetUid: input.targetUid
16898
+ })
16899
+ }
16900
+ );
16901
+ }
16902
+ assertAIEmployeePromptVariablesAllowed(actionName, fieldPath, value, validation) {
16903
+ for (const variable of this.collectAIEmployeePromptVariables(value)) {
16904
+ const path = this.extractAIEmployeePromptContextPath(variable.inner);
16905
+ if (path === "") {
16906
+ this.throwAIEmployeePromptVariableInvalid({
16907
+ actionName,
16908
+ fieldPath,
16909
+ variable: variable.raw,
16910
+ targetUid: validation.targetUid,
16911
+ reason: "the whole ctx object is not a valid prompt variable; use a concrete ctx path"
16912
+ });
16913
+ }
16914
+ if (!path) {
16915
+ this.throwAIEmployeePromptVariableInvalid({
16916
+ actionName,
16917
+ fieldPath,
16918
+ variable: variable.raw,
16919
+ targetUid: validation.targetUid,
16920
+ reason: 'only simple ctx dot paths like "{{ ctx.record.id }}" are supported'
16921
+ });
16922
+ }
16923
+ if (!this.isAIEmployeeContextPathAllowed(path, validation)) {
16924
+ this.throwAIEmployeePromptVariableInvalid({
16925
+ actionName,
16926
+ fieldPath,
16927
+ variable: variable.raw,
16928
+ targetUid: validation.targetUid,
16929
+ path,
16930
+ reason: `path "${path}" is not available in the current Flow Surface context`
16931
+ });
16932
+ }
16933
+ }
16934
+ }
16935
+ assertAIEmployeeTaskPromptVariablesAllowed(actionName, tasks, validation, path = "settings.tasks") {
16936
+ if (!validation) {
16937
+ return;
16938
+ }
16939
+ tasks.forEach((task, index) => {
16940
+ const message = task == null ? void 0 : task.message;
16941
+ if (typeof (message == null ? void 0 : message.system) === "string") {
16942
+ this.assertAIEmployeePromptVariablesAllowed(
16943
+ actionName,
16944
+ `${path}[${index}].message.system`,
16945
+ message.system,
16946
+ validation
16947
+ );
16948
+ }
16949
+ if (typeof (message == null ? void 0 : message.user) === "string") {
16950
+ this.assertAIEmployeePromptVariablesAllowed(
16951
+ actionName,
16952
+ `${path}[${index}].message.user`,
16953
+ message.user,
16954
+ validation
16955
+ );
16956
+ }
16957
+ });
16958
+ }
16959
+ appendAIEmployeeCurrentRecordPromptVariable(value) {
16960
+ if (AI_EMPLOYEE_CURRENT_RECORD_PROMPT_VARIABLE_RE.test(value)) {
16961
+ return value;
16962
+ }
16963
+ return value ? `${value}
16964
+ ${AI_EMPLOYEE_CURRENT_RECORD_PROMPT_VARIABLE}` : AI_EMPLOYEE_CURRENT_RECORD_PROMPT_VARIABLE;
16965
+ }
16966
+ async resolveAIEmployeePromptActionTargetUid(context, transaction) {
16967
+ if (!context) {
16968
+ return void 0;
16969
+ }
16970
+ if (context.kind === "target") {
16971
+ return String(context.targetUid || "").trim() || void 0;
16972
+ }
16973
+ return String(context.ownerUid || "").trim() || void 0;
16974
+ }
16975
+ async buildAIEmployeePromptValidationContext(actionName, tasks, options) {
16976
+ var _a, _b;
16977
+ if (!this.hasAIEmployeePromptVariables(tasks)) {
16978
+ return void 0;
16979
+ }
16980
+ const targetUid = await this.resolveAIEmployeePromptActionTargetUid(options.promptContext, options.transaction);
16981
+ if (!targetUid) {
16982
+ (0, import_errors.throwBadRequest)(
16983
+ `flowSurfaces ${actionName} AI employee prompt variables require a resolvable Flow Surface context. ${this.buildAIEmployeePromptVariableHint()}`,
16984
+ AI_EMPLOYEE_PROMPT_VARIABLE_INVALID
16985
+ );
16986
+ }
16987
+ const target = { uid: targetUid };
16988
+ const resolved = await this.locator.resolve(target, { transaction: options.transaction });
16989
+ let semantic = await this.resolveContextSemantic(((_a = resolved == null ? void 0 : resolved.node) == null ? void 0 : _a.uid) || target.uid, resolved, options.transaction);
16990
+ if (((_b = options.promptContext) == null ? void 0 : _b.kind) === "container" && options.promptContext.scope === "record") {
16991
+ semantic = {
16992
+ ...semantic,
16993
+ recordCollection: semantic.recordCollection || semantic.collection || await this.resolveContextOwnerCollection(targetUid, options.transaction).catch(() => null)
16994
+ };
16995
+ }
16996
+ const context = (0, import_context.buildFlowSurfaceContextResponse)({
16997
+ semantic,
16998
+ maxDepth: AI_EMPLOYEE_PROMPT_CONTEXT_MAX_DEPTH
16999
+ });
17000
+ return {
17001
+ targetUid,
17002
+ semantic,
17003
+ contextPaths: this.collectAIEmployeeContextPathEntries(context)
17004
+ };
17005
+ }
17006
+ appendAIEmployeeCurrentRecordPromptVariableToTasks(tasks, validation) {
17007
+ if (!tasks.length || !validation || !this.isAIEmployeeContextPathAllowed("record", validation)) {
17008
+ return tasks;
17009
+ }
17010
+ return tasks.map((task) => {
17011
+ var _a;
17012
+ if (typeof ((_a = task == null ? void 0 : task.message) == null ? void 0 : _a.user) !== "string") {
17013
+ return task;
17014
+ }
17015
+ const next = import_lodash.default.cloneDeep(task);
17016
+ next.message.user = this.appendAIEmployeeCurrentRecordPromptVariable(next.message.user);
17017
+ return next;
17018
+ });
17019
+ }
17020
+ async assertAIEmployeeStepParamTaskPromptVariablesAllowedForUpdateSettings(current, nextPayload, writeTarget, options) {
17021
+ if (!this.isAIEmployeeActionUse(current == null ? void 0 : current.use) || !import_lodash.default.has(nextPayload, ["stepParams", ...AI_EMPLOYEE_TASK_STEP_PARAMS_PATH])) {
17022
+ return;
17023
+ }
17024
+ const tasks = import_lodash.default.get(nextPayload, ["stepParams", ...AI_EMPLOYEE_TASK_STEP_PARAMS_PATH]);
17025
+ if (!Array.isArray(tasks)) {
17026
+ return;
17027
+ }
17028
+ const actionName = options.openViewActionName || "updateSettings";
17029
+ this.assertAIEmployeeTaskPromptVariablesAllowed(
17030
+ actionName,
17031
+ tasks,
17032
+ await this.buildAIEmployeePromptValidationContext(actionName, tasks, {
17033
+ transaction: options.transaction,
17034
+ promptContext: {
17035
+ kind: "target",
17036
+ targetUid: (current == null ? void 0 : current.uid) || writeTarget.uid
17037
+ }
17038
+ }),
17039
+ "stepParams.shortcutSettings.editTasks.tasks"
17040
+ );
17041
+ }
17042
+ async normalizeAIEmployeeStepParamTasksForUpdateSettings(current, nextPayload, writeTarget, options) {
17043
+ if (!this.isAIEmployeeActionUse(current == null ? void 0 : current.use) || !import_lodash.default.has(nextPayload, ["stepParams", ...AI_EMPLOYEE_TASK_STEP_PARAMS_PATH])) {
17044
+ return;
17045
+ }
17046
+ const tasks = import_lodash.default.get(nextPayload, ["stepParams", ...AI_EMPLOYEE_TASK_STEP_PARAMS_PATH]);
17047
+ if (!Array.isArray(tasks)) {
17048
+ return;
17049
+ }
17050
+ const actionName = options.openViewActionName || "updateSettings";
17051
+ const normalizedTasks = this.normalizeAIEmployeeTasks(
17052
+ actionName,
17053
+ tasks,
17054
+ this.readAIEmployeePersistedTasks(current),
17055
+ {
17056
+ selfUid: await this.resolveAIEmployeeActionSelfUid(current || { uid: writeTarget.uid }, options.transaction),
17057
+ path: "stepParams.shortcutSettings.editTasks.tasks"
17058
+ }
17059
+ );
17060
+ import_lodash.default.set(nextPayload, ["stepParams", ...AI_EMPLOYEE_TASK_STEP_PARAMS_PATH], normalizedTasks);
17061
+ }
16579
17062
  normalizeAIEmployeeTaskMessage(actionName, path, value, existing, options) {
16580
17063
  if (!import_lodash.default.isPlainObject(value)) {
16581
17064
  (0, import_errors.throwBadRequest)(`flowSurfaces ${actionName} ${path} must be an object`);
@@ -16661,6 +17144,14 @@ class FlowSurfacesService {
16661
17144
  if (!import_lodash.default.isPlainObject(next.message)) {
16662
17145
  next.message = {};
16663
17146
  }
17147
+ if (import_lodash.default.isPlainObject(next.skillSettings)) {
17148
+ if (Array.isArray(next.skillSettings.skills) && !Object.prototype.hasOwnProperty.call(next.skillSettings, "skillsVersion")) {
17149
+ next.skillSettings.skillsVersion = 2;
17150
+ }
17151
+ if (Array.isArray(next.skillSettings.tools) && !Object.prototype.hasOwnProperty.call(next.skillSettings, "toolsVersion")) {
17152
+ next.skillSettings.toolsVersion = 2;
17153
+ }
17154
+ }
16664
17155
  return next;
16665
17156
  }
16666
17157
  normalizeAIEmployeeTasks(actionName, value, currentTasks, options) {
@@ -16723,6 +17214,7 @@ class FlowSurfacesService {
16723
17214
  });
16724
17215
  }
16725
17216
  const props = {};
17217
+ const stepParams = {};
16726
17218
  if (effectiveUsername && (hasUsername || options.requireUsername)) {
16727
17219
  props.aiEmployee = {
16728
17220
  ...import_lodash.default.isPlainObject(currentProps.aiEmployee) ? import_lodash.default.cloneDeep(currentProps.aiEmployee) : {},
@@ -16748,11 +17240,36 @@ class FlowSurfacesService {
16748
17240
  };
16749
17241
  }
16750
17242
  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"
17243
+ const normalizedTasks = this.normalizeAIEmployeeTasks(
17244
+ actionName,
17245
+ settings.tasks,
17246
+ this.readAIEmployeePersistedTasks(options.current),
17247
+ {
17248
+ selfUid: options.selfUid,
17249
+ keyMap: options.keyMap,
17250
+ path: "settings.tasks"
17251
+ }
17252
+ );
17253
+ const validation = options.promptContext && normalizedTasks.length ? await this.buildAIEmployeePromptValidationContext(
17254
+ actionName,
17255
+ [
17256
+ {
17257
+ message: {
17258
+ user: AI_EMPLOYEE_CURRENT_RECORD_PROMPT_VARIABLE
17259
+ }
17260
+ }
17261
+ ],
17262
+ {
17263
+ transaction: options.transaction,
17264
+ promptContext: options.promptContext
17265
+ }
17266
+ ) : await this.buildAIEmployeePromptValidationContext(actionName, normalizedTasks, {
17267
+ transaction: options.transaction,
17268
+ promptContext: options.promptContext
16755
17269
  });
17270
+ const tasks = this.appendAIEmployeeCurrentRecordPromptVariableToTasks(normalizedTasks, validation);
17271
+ this.assertAIEmployeeTaskPromptVariablesAllowed(actionName, tasks, validation);
17272
+ import_lodash.default.merge(stepParams, this.buildAIEmployeeTaskStepParams(tasks));
16756
17273
  }
16757
17274
  if (Object.prototype.hasOwnProperty.call(settings, "style") || options.requireUsername) {
16758
17275
  const style = Object.prototype.hasOwnProperty.call(settings, "style") ? settings.style : {};
@@ -16780,7 +17297,10 @@ class FlowSurfacesService {
16780
17297
  import_lodash.default.isPlainObject(style) ? import_lodash.default.pick(import_lodash.default.cloneDeep(style), AI_EMPLOYEE_STYLE_PUBLIC_KEYS) : {}
16781
17298
  );
16782
17299
  }
16783
- return props;
17300
+ return {
17301
+ props,
17302
+ stepParams
17303
+ };
16784
17304
  }
16785
17305
  async resolveAIEmployeeActionSelfUid(actionNode, transaction) {
16786
17306
  const parentUid = String((actionNode == null ? void 0 : actionNode.parentId) || "").trim() || ((actionNode == null ? void 0 : actionNode.uid) ? await this.locator.findParentUid(actionNode.uid, transaction).catch(() => "") : "");
@@ -16806,25 +17326,34 @@ class FlowSurfacesService {
16806
17326
  const allowedKeys = (0, import_configure_options.getConfigureOptionKeysForUse)(use);
16807
17327
  (0, import_service_utils.assertSupportedSimpleChanges)("action", changes, allowedKeys);
16808
17328
  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(
17329
+ const aiEmployeeSettingsPayload = await this.normalizeAIEmployeeActionPublicSettings(
17330
+ "configure action",
17331
+ changes,
16817
17332
  {
16818
- target,
16819
- props: props2
16820
- },
16821
- {
16822
- ...options,
16823
- openViewActionName: options.openViewActionName || "configure action",
16824
- popupTemplateHostUid: target.uid,
16825
- allowAIEmployeeInternalProps: true
17333
+ transaction: options.transaction,
17334
+ enabledPackages: options.enabledPackages || await this.resolveEnabledPluginPackages(options),
17335
+ current: currentNode,
17336
+ currentRoles: options.currentRoles,
17337
+ selfUid: await this.resolveAIEmployeeActionSelfUid(currentNode, options.transaction),
17338
+ promptContext: {
17339
+ kind: "target",
17340
+ targetUid: (currentNode == null ? void 0 : currentNode.uid) || target.uid
17341
+ }
16826
17342
  }
16827
17343
  );
17344
+ const settingsValues = { target };
17345
+ if (Object.keys(aiEmployeeSettingsPayload.props).length) {
17346
+ settingsValues.props = aiEmployeeSettingsPayload.props;
17347
+ }
17348
+ if (Object.keys(aiEmployeeSettingsPayload.stepParams).length) {
17349
+ settingsValues.stepParams = aiEmployeeSettingsPayload.stepParams;
17350
+ }
17351
+ return this.updateSettings(settingsValues, {
17352
+ ...options,
17353
+ openViewActionName: options.openViewActionName || "configure action",
17354
+ popupTemplateHostUid: target.uid,
17355
+ allowAIEmployeeInternalProps: true
17356
+ });
16828
17357
  }
16829
17358
  const normalizedDefaultFilter = (0, import_service_utils.hasOwnDefined)(changes, "defaultFilter") ? this.normalizeFilterActionDefaultFilterValue(changes.defaultFilter) : void 0;
16830
17359
  const stepParams = {};
@@ -18117,7 +18646,8 @@ class FlowSurfacesService {
18117
18646
  }
18118
18647
  resolveRecordActionContextOwner(ancestors) {
18119
18648
  const currentNode = ancestors[0];
18120
- if (!String((currentNode == null ? void 0 : currentNode.use) || "").endsWith("ActionModel")) {
18649
+ const currentUse = String((currentNode == null ? void 0 : currentNode.use) || "").trim();
18650
+ if (!currentUse.endsWith("ActionModel") && !import_node_use_sets.ACTION_BUTTON_USES.has(currentUse)) {
18121
18651
  return null;
18122
18652
  }
18123
18653
  return ancestors.slice(1).find((node) => (0, import_action_scope.getActionContainerScope)(node == null ? void 0 : node.use) === "record") || null;
@@ -18368,11 +18898,12 @@ class FlowSurfacesService {
18368
18898
  };
18369
18899
  }
18370
18900
  getCollection(dataSourceKey, collectionName) {
18371
- var _a, _b, _c, _d, _e;
18901
+ var _a, _b, _c, _d, _e, _f, _g, _h;
18372
18902
  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));
18903
+ const app = (_a = this.plugin) == null ? void 0 : _a.app;
18904
+ const dataSourceManager = app == null ? void 0 : app.dataSourceManager;
18905
+ const dataSource = (_b = dataSourceManager == null ? void 0 : dataSourceManager.get) == null ? void 0 : _b.call(dataSourceManager, normalizedDataSourceKey);
18906
+ 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
18907
  }
18377
18908
  normalizeKanbanFieldNameInput(value, context, options = {}) {
18378
18909
  if (import_lodash.default.isUndefined(value)) {