@stndrds/schema 1.0.0-alpha.84 → 1.0.0-alpha.87

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.
@@ -1,8 +1,9 @@
1
1
  import {
2
2
  asTenantId,
3
3
  asUserId,
4
+ deepEqual,
4
5
  generateId
5
- } from "./chunk-V2RPPE2Y.mjs";
6
+ } from "./chunk-XAORXFAX.mjs";
6
7
  import {
7
8
  computeRecordStatus,
8
9
  createFormAttributeValidator,
@@ -12,7 +13,7 @@ import {
12
13
  validateDraftOrThrow,
13
14
  validateObject,
14
15
  validateObjectOrThrow
15
- } from "./chunk-7VNLLASJ.mjs";
16
+ } from "./chunk-JUVFK6XD.mjs";
16
17
  import {
17
18
  __require
18
19
  } from "./chunk-Y6FXYEAI.mjs";
@@ -1389,6 +1390,9 @@ function isConditionNode(node) {
1389
1390
  function isAssignNode(node) {
1390
1391
  return node.type === "assign";
1391
1392
  }
1393
+ function isAINode(node) {
1394
+ return node.type === "ai";
1395
+ }
1392
1396
  function isEndNode(node) {
1393
1397
  return node.type === "end";
1394
1398
  }
@@ -1529,6 +1533,47 @@ var assignNodeType = {
1529
1533
  };
1530
1534
  nodeTypeRegistry.register(assignNodeType);
1531
1535
 
1536
+ // src/types/workflows/node-types/ai.node-type.ts
1537
+ var aiNodeType = {
1538
+ type: "ai",
1539
+ isLinear: true,
1540
+ structural: false,
1541
+ getOutputs(node) {
1542
+ return node.next ? [node.next] : [];
1543
+ },
1544
+ setNext(node, targetId) {
1545
+ return { ...node, next: targetId };
1546
+ },
1547
+ getSlotIds(node) {
1548
+ const slotIds = /* @__PURE__ */ new Set();
1549
+ const action = node.action;
1550
+ if (action.type === "document-generation") {
1551
+ for (const id of action.inputSlotIds) slotIds.add(id);
1552
+ for (const id of action.targetSlotIds) slotIds.add(id);
1553
+ } else if (action.type === "code-execution") {
1554
+ for (const id of action.inputSlotIds ?? []) slotIds.add(id);
1555
+ }
1556
+ return [...slotIds];
1557
+ },
1558
+ validate(node) {
1559
+ const errors = [];
1560
+ if (!node.label) errors.push("AINode must have a label");
1561
+ if (!node.action) errors.push("AINode must have an action");
1562
+ if (!node.next) errors.push("AINode must have a 'next' target");
1563
+ if (node.action?.type === "document-generation") {
1564
+ if (!node.action.templateId) errors.push("Document generation must have a templateId");
1565
+ if (!node.action.inputSlotIds?.length)
1566
+ errors.push("Document generation must have input slots");
1567
+ if (!node.action.targetSlotIds?.length)
1568
+ errors.push("Document generation must have target slots");
1569
+ } else if (node.action?.type === "code-execution") {
1570
+ if (!node.action.code) errors.push("Code execution must have code");
1571
+ }
1572
+ return errors;
1573
+ }
1574
+ };
1575
+ nodeTypeRegistry.register(aiNodeType);
1576
+
1532
1577
  // src/types/workflows/node-types/end.node-type.ts
1533
1578
  var endNodeType = {
1534
1579
  type: "end",
@@ -1741,14 +1786,15 @@ function isInvitationOrGrantEvent(event) {
1741
1786
  var ZONE_ORDER = ["collect", "actions"];
1742
1787
  var ZONE_CONFIG = {
1743
1788
  collect: { allowedNodeTypes: ["form", "condition"] },
1744
- actions: { allowedNodeTypes: ["condition", "assign"] }
1789
+ actions: { allowedNodeTypes: ["condition", "assign", "ai"] }
1745
1790
  };
1746
1791
  var TERMINAL_NODE_TYPES = /* @__PURE__ */ new Set(["start", "end"]);
1747
1792
  var NODE_TYPE_ZONE = {
1748
1793
  form: "collect",
1749
1794
  condition: "collect",
1750
1795
  // default — overridden by assignNodeZones logic
1751
- assign: "actions"
1796
+ assign: "actions",
1797
+ ai: "actions"
1752
1798
  };
1753
1799
  function assignNodeZones(orderedNodes) {
1754
1800
  const result = /* @__PURE__ */ new Map();
@@ -1833,9 +1879,14 @@ var StartNodeSchema = z.object({
1833
1879
  id: z.string().min(1),
1834
1880
  next: z.string().nullish()
1835
1881
  });
1882
+ var RelationFieldConfigSchema = z.object({
1883
+ visibleProperties: z.array(z.string()).optional(),
1884
+ allowCreate: z.boolean().optional()
1885
+ }).optional();
1836
1886
  var FormFieldRefSchema = z.object({
1837
1887
  slotId: z.string().min(1, "slotId is required"),
1838
- attribute: z.string().min(1, "attribute is required")
1888
+ attribute: z.string().min(1, "attribute is required"),
1889
+ relationConfig: RelationFieldConfigSchema
1839
1890
  });
1840
1891
  var FlowRowFieldSchema = z.object({
1841
1892
  id: z.string().min(1),
@@ -1843,7 +1894,8 @@ var FlowRowFieldSchema = z.object({
1843
1894
  attribute: z.string().min(1),
1844
1895
  label: z.string().max(200).optional(),
1845
1896
  tooltip: z.string().max(1e3).optional(),
1846
- required: z.boolean().optional()
1897
+ required: z.boolean().optional(),
1898
+ relationConfig: RelationFieldConfigSchema
1847
1899
  });
1848
1900
  var FlowFieldsRowSchema = z.object({
1849
1901
  id: z.string().min(1),
@@ -1924,6 +1976,35 @@ var AssignNodeSchema = z.object({
1924
1976
  assignments: z.array(AssignmentMappingSchema).min(1),
1925
1977
  next: z.string().nullish()
1926
1978
  });
1979
+ var DocumentGenerationActionSchema = z.object({
1980
+ type: z.literal("document-generation"),
1981
+ templateId: z.string().min(1, "Template ID is required"),
1982
+ inputSlotIds: z.array(z.string().min(1)).min(1, "At least one input slot is required"),
1983
+ targetSlotIds: z.array(z.string().min(1)).min(1, "At least one target slot is required"),
1984
+ outputFormat: z.enum(["pdf", "docx"]),
1985
+ aiInstructions: z.string().nullish()
1986
+ });
1987
+ var CodeExecutionActionSchema = z.object({
1988
+ type: z.literal("code-execution"),
1989
+ code: z.string().min(1, "Code is required"),
1990
+ language: z.enum(["javascript", "typescript", "python"]),
1991
+ inputSlotIds: z.array(z.string().min(1)).optional(),
1992
+ outputVariable: z.string().optional(),
1993
+ packages: z.array(z.string()).optional()
1994
+ });
1995
+ var AIActionConfigSchema = z.discriminatedUnion("type", [
1996
+ DocumentGenerationActionSchema,
1997
+ CodeExecutionActionSchema
1998
+ ]);
1999
+ var AINodeSchema = z.object({
2000
+ type: z.literal("ai"),
2001
+ id: z.string().min(1),
2002
+ label: z.string().min(1),
2003
+ description: z.string().nullish(),
2004
+ action: AIActionConfigSchema,
2005
+ timeoutMs: z.number().positive().optional(),
2006
+ next: z.string().nullish()
2007
+ });
1927
2008
  var EndNodeSchema = z.object({
1928
2009
  type: z.literal("end"),
1929
2010
  id: z.string().min(1),
@@ -1935,6 +2016,7 @@ var WorkflowNodeSchema = z.discriminatedUnion("type", [
1935
2016
  FormNodeSchema,
1936
2017
  ConditionNodeSchema,
1937
2018
  AssignNodeSchema,
2019
+ AINodeSchema,
1938
2020
  EndNodeSchema
1939
2021
  ]);
1940
2022
  var SlotModeSchema = z.enum(["create", "select", "optional"]);
@@ -2898,6 +2980,214 @@ function resolveSource(source, slotValues) {
2898
2980
  }
2899
2981
  }
2900
2982
 
2983
+ // src/runtime/executors/ai.executor.ts
2984
+ var AIExecutor = class {
2985
+ constructor(actionRegistry) {
2986
+ this.actionRegistry = actionRegistry;
2987
+ this.nodeType = "ai";
2988
+ }
2989
+ async execute(node, ctx) {
2990
+ if (!node.next) {
2991
+ return error("MISSING_NEXT", "AINode must have a 'next' target");
2992
+ }
2993
+ const handler = this.actionRegistry.get(node.action.type);
2994
+ if (!handler) {
2995
+ return error(
2996
+ "UNKNOWN_AI_ACTION",
2997
+ `No handler registered for AI action type: ${node.action.type}`
2998
+ );
2999
+ }
3000
+ const result = await handler.execute(node.action, ctx);
3001
+ if (!result.success) {
3002
+ return error("AI_ACTION_FAILED", result.error ?? "AI action failed", true);
3003
+ }
3004
+ return success(node.next, result.contextUpdates);
3005
+ }
3006
+ validate(node) {
3007
+ const errors = [];
3008
+ if (!node.label) errors.push("AINode must have a label");
3009
+ if (!node.action) errors.push("AINode must have an action");
3010
+ if (!node.next) errors.push("AINode must have a 'next' target");
3011
+ return errors;
3012
+ }
3013
+ };
3014
+
3015
+ // src/runtime/executors/ai-actions/types.ts
3016
+ var AIActionRegistry = class {
3017
+ constructor() {
3018
+ this.handlers = /* @__PURE__ */ new Map();
3019
+ }
3020
+ register(handler) {
3021
+ this.handlers.set(handler.actionType, handler);
3022
+ }
3023
+ get(actionType) {
3024
+ return this.handlers.get(actionType);
3025
+ }
3026
+ has(actionType) {
3027
+ return this.handlers.has(actionType);
3028
+ }
3029
+ };
3030
+
3031
+ // src/runtime/executors/ai-actions/document-generation.handler.ts
3032
+ var DocumentGenerationHandler = class {
3033
+ constructor(deps = null) {
3034
+ this.deps = deps;
3035
+ this.actionType = "document-generation";
3036
+ }
3037
+ async execute(action, ctx) {
3038
+ if (!this.deps) {
3039
+ return {
3040
+ success: false,
3041
+ error: "Document generation not configured. Provide sandbox, fileService, and storage in SchemaModule."
3042
+ };
3043
+ }
3044
+ const { sandbox, fileService, storage } = this.deps;
3045
+ const file2 = await fileService.getFile(action.templateId);
3046
+ if (!file2) {
3047
+ return {
3048
+ success: false,
3049
+ error: `Template file not found: ${action.templateId}`
3050
+ };
3051
+ }
3052
+ if (!storage.download) {
3053
+ return {
3054
+ success: false,
3055
+ error: "StorageAdapter.download() not implemented. Required for document generation."
3056
+ };
3057
+ }
3058
+ let templateContent;
3059
+ try {
3060
+ const buffer = await storage.download(file2.storagePath);
3061
+ templateContent = new Uint8Array(buffer);
3062
+ } catch (err) {
3063
+ return {
3064
+ success: false,
3065
+ error: `Failed to download template "${file2.name}": ${err instanceof Error ? err.message : "unknown error"}`
3066
+ };
3067
+ }
3068
+ const slotData = {};
3069
+ for (const slotId of action.inputSlotIds) {
3070
+ slotData[slotId] = ctx.executionContext.slots[slotId] ?? {};
3071
+ }
3072
+ const instructions = [
3073
+ "Generate a professional document based on the provided template and data.",
3074
+ action.aiInstructions ?? "",
3075
+ "",
3076
+ "## Data Context",
3077
+ "```json",
3078
+ JSON.stringify(slotData, null, 2),
3079
+ "```",
3080
+ "",
3081
+ "## Instructions",
3082
+ "1. Unpack the template: python3 /workspace/scripts/office/unpack.py /workspace/template.docx /workspace/unpacked/",
3083
+ "2. Analyze the XML structure in /workspace/unpacked/word/document.xml",
3084
+ "3. Read the data from /workspace/data.json",
3085
+ "4. Modify the XML content using the provided data \u2014 replace placeholders, fill tables, update fields",
3086
+ "5. Pack: python3 /workspace/scripts/office/pack.py /workspace/unpacked/ /workspace/output.docx",
3087
+ "6. Validate: python3 /workspace/scripts/office/validate.py /workspace/output.docx",
3088
+ action.outputFormat === "pdf" ? "7. Convert to PDF: python3 /workspace/scripts/office/convert.py /workspace/output.docx pdf /workspace/" : ""
3089
+ ].filter(Boolean).join("\n");
3090
+ const outputPath = action.outputFormat === "pdf" ? "/workspace/output.pdf" : "/workspace/output.docx";
3091
+ try {
3092
+ const result = await sandbox.executeAgent({
3093
+ instructions,
3094
+ template: "stndrds-pdf",
3095
+ inputFiles: [
3096
+ {
3097
+ path: "/workspace/template.docx",
3098
+ content: templateContent
3099
+ },
3100
+ {
3101
+ path: "/workspace/data.json",
3102
+ content: JSON.stringify(slotData, null, 2)
3103
+ }
3104
+ ],
3105
+ expectedOutputs: [outputPath],
3106
+ timeoutMs: 6e5,
3107
+ maxIterations: 20
3108
+ });
3109
+ if (!result.success || result.artifacts.length === 0) {
3110
+ return {
3111
+ success: false,
3112
+ error: result.summary ?? "Agent failed to generate document"
3113
+ };
3114
+ }
3115
+ const contextUpdates = {};
3116
+ for (const slotId of action.targetSlotIds) {
3117
+ contextUpdates[slotId] = {
3118
+ ...ctx.executionContext.slots[slotId] ?? {},
3119
+ _generatedDocuments: result.artifacts.map((a) => ({
3120
+ path: a.path,
3121
+ mimeType: a.mimeType
3122
+ }))
3123
+ };
3124
+ }
3125
+ return { success: true, contextUpdates };
3126
+ } catch (err) {
3127
+ return {
3128
+ success: false,
3129
+ error: err instanceof Error ? err.message : "Document generation failed"
3130
+ };
3131
+ }
3132
+ }
3133
+ };
3134
+
3135
+ // src/runtime/executors/ai-actions/code-execution.handler.ts
3136
+ var CodeExecutionHandler = class {
3137
+ constructor(sandbox = null) {
3138
+ this.sandbox = sandbox;
3139
+ this.actionType = "code-execution";
3140
+ }
3141
+ async execute(action, ctx) {
3142
+ if (!this.sandbox) {
3143
+ return {
3144
+ success: false,
3145
+ error: "Code execution handler not configured. Inject a SandboxExecutor via constructor."
3146
+ };
3147
+ }
3148
+ const env = {};
3149
+ for (const slotId of action.inputSlotIds ?? []) {
3150
+ const slotData = ctx.executionContext.slots[slotId] ?? {};
3151
+ env[`SLOT_${slotId.toUpperCase()}`] = JSON.stringify(slotData);
3152
+ }
3153
+ try {
3154
+ const result = await this.sandbox.executeCode({
3155
+ code: action.code,
3156
+ language: action.language,
3157
+ env,
3158
+ packages: action.packages,
3159
+ timeoutMs: 3e4
3160
+ // 30s for code mode
3161
+ });
3162
+ if (result.exitCode !== 0) {
3163
+ return {
3164
+ success: false,
3165
+ error: `Code execution failed (exit ${result.exitCode}): ${result.stderr}`
3166
+ };
3167
+ }
3168
+ const contextUpdates = {};
3169
+ if (action.outputVariable) {
3170
+ contextUpdates[action.outputVariable] = result.stdout.trim();
3171
+ }
3172
+ return { success: true, contextUpdates };
3173
+ } catch (err) {
3174
+ return {
3175
+ success: false,
3176
+ error: err instanceof Error ? err.message : "Code execution failed"
3177
+ };
3178
+ }
3179
+ }
3180
+ };
3181
+
3182
+ // src/runtime/executors/ai-actions/index.ts
3183
+ function createDefaultAIActionRegistry(deps) {
3184
+ const registry2 = new AIActionRegistry();
3185
+ const docGenDeps = deps?.sandbox && deps?.fileService && deps?.storage ? { sandbox: deps.sandbox, fileService: deps.fileService, storage: deps.storage } : null;
3186
+ registry2.register(new DocumentGenerationHandler(docGenDeps));
3187
+ registry2.register(new CodeExecutionHandler(deps?.sandbox ?? null));
3188
+ return registry2;
3189
+ }
3190
+
2901
3191
  // src/runtime/executors/start.executor.ts
2902
3192
  var StartExecutor = class {
2903
3193
  constructor() {
@@ -5822,6 +6112,9 @@ var FORBIDDEN_PROPERTY_TYPES = [
5822
6112
  "document",
5823
6113
  "richtext"
5824
6114
  ];
6115
+ function hasOptions2(attr) {
6116
+ return attr.type === "select" || attr.type === "status" || attr.type === "multiselect";
6117
+ }
5825
6118
 
5826
6119
  // src/builders/attribute-validators.ts
5827
6120
  import { ICONS } from "@stndrds/constants";
@@ -5926,6 +6219,20 @@ function validateOptions(options, attributeName) {
5926
6219
  validateIconName(option.icon, `option "${option.id}" of "${attributeName}"`);
5927
6220
  }
5928
6221
  }
6222
+ for (const option of options) {
6223
+ if (option.inverse === void 0) continue;
6224
+ const inverseOption = options.find((o) => o.value === option.inverse);
6225
+ if (!inverseOption) {
6226
+ throw new Error(
6227
+ `[AttributeBuilder] Option "${option.value}" in "${attributeName}" has inverse "${option.inverse}" which does not match any option value.`
6228
+ );
6229
+ }
6230
+ if (inverseOption.inverse !== option.value) {
6231
+ throw new Error(
6232
+ `[AttributeBuilder] Inverse mismatch in "${attributeName}": "${option.value}" points to "${option.inverse}", but "${option.inverse}" points to "${inverseOption.inverse ?? "(none)"}". Inverse pairs must be symmetric.`
6233
+ );
6234
+ }
6235
+ }
5929
6236
  }
5930
6237
 
5931
6238
  // src/builders/attribute-builders.ts
@@ -7880,7 +8187,10 @@ var ListViewBuilder = class {
7880
8187
  groupByAttribute: tab.groupByAttribute,
7881
8188
  cardUserAttribute: tab.cardUserAttribute,
7882
8189
  cardDateAttribute: tab.cardDateAttribute,
7883
- createMode: tab.createMode
8190
+ createMode: tab.createMode,
8191
+ kanbanColumnOrder: tab.kanbanColumnOrder,
8192
+ kanbanColumnVisibility: tab.kanbanColumnVisibility,
8193
+ kanbanPinnedColumns: tab.kanbanPinnedColumns
7884
8194
  }))
7885
8195
  };
7886
8196
  return {
@@ -7969,6 +8279,34 @@ var ListViewTabConfigBuilder = class _ListViewTabConfigBuilder {
7969
8279
  this.tabData.cardDateAttribute = attributeName;
7970
8280
  return this;
7971
8281
  }
8282
+ /**
8283
+ * Set the order of kanban columns by option values (for kanban layout only)
8284
+ * @param order - Array of option values in desired order
8285
+ * @example .kanbanColumnOrder(["new", "in_progress", "done"])
8286
+ */
8287
+ kanbanColumnOrder(order) {
8288
+ this.tabData.kanbanColumnOrder = order;
8289
+ return this;
8290
+ }
8291
+ /**
8292
+ * Set the visibility of kanban columns by option values (for kanban layout only)
8293
+ * @param visibility - Record mapping option values to visibility (true = visible, false = hidden)
8294
+ * @example .kanbanColumnVisibility({ "new": true, "archived": false })
8295
+ */
8296
+ kanbanColumnVisibility(visibility) {
8297
+ this.tabData.kanbanColumnVisibility = visibility;
8298
+ return this;
8299
+ }
8300
+ /**
8301
+ * Set pinned kanban columns by option values (for kanban layout only)
8302
+ * Pinned columns remain fixed during horizontal scrolling
8303
+ * @param pinnedColumns - Array of option values to pin
8304
+ * @example .kanbanPinnedColumns(["new", "done"])
8305
+ */
8306
+ kanbanPinnedColumns(pinnedColumns) {
8307
+ this.tabData.kanbanPinnedColumns = pinnedColumns;
8308
+ return this;
8309
+ }
7972
8310
  /**
7973
8311
  * Set creation behavior when clicking "+"
7974
8312
  * @param mode - "redirect" (navigate to detail), "inline" (empty row), or "modal" (stacked modal)
@@ -8085,12 +8423,55 @@ var WorkflowFormRowBuilder = class {
8085
8423
  this.rowData.fields.push(field);
8086
8424
  return this;
8087
8425
  }
8426
+ /**
8427
+ * Add a relation field to the current row.
8428
+ * Use this for relation attributes that need specific configuration
8429
+ * (e.g., which qualified properties to display).
8430
+ *
8431
+ * @param slotId - The slot containing the relation attribute
8432
+ * @param attribute - The relation attribute name
8433
+ * @param options - Optional label, required flag, and relation config
8434
+ */
8435
+ relationField(slotId, attribute, options) {
8436
+ const relationConfig = options?.visibleProperties || options?.allowCreate !== void 0 ? {
8437
+ visibleProperties: options?.visibleProperties,
8438
+ allowCreate: options?.allowCreate
8439
+ } : void 0;
8440
+ const field = {
8441
+ id: `${this.rowData.id}-${slotId}-${attribute}`,
8442
+ slotId,
8443
+ attribute,
8444
+ label: options?.label,
8445
+ required: options?.required,
8446
+ relationConfig
8447
+ };
8448
+ this.rowData.fields.push(field);
8449
+ return this;
8450
+ }
8088
8451
  /**
8089
8452
  * Start a new row
8090
8453
  */
8091
8454
  row(id) {
8092
8455
  return this.formBuilder._finalizeRow(this.rowData).row(id);
8093
8456
  }
8457
+ /**
8458
+ * Add a heading row (finalizes the current field row first)
8459
+ */
8460
+ heading(content, level) {
8461
+ return this.formBuilder._finalizeRow(this.rowData).heading(content, level);
8462
+ }
8463
+ /**
8464
+ * Add a visual separator (finalizes the current field row first)
8465
+ */
8466
+ separator() {
8467
+ return this.formBuilder._finalizeRow(this.rowData).separator();
8468
+ }
8469
+ /**
8470
+ * Add a static text row (finalizes the current field row first)
8471
+ */
8472
+ text(content) {
8473
+ return this.formBuilder._finalizeRow(this.rowData).text(content);
8474
+ }
8094
8475
  /**
8095
8476
  * Set the next node and finish this form
8096
8477
  */
@@ -8121,13 +8502,56 @@ var WorkflowFormBuilder = class {
8121
8502
  this.rowOrder++;
8122
8503
  return new WorkflowFormRowBuilder(this, id, this.rowOrder);
8123
8504
  }
8505
+ /**
8506
+ * Add a heading row to the form
8507
+ */
8508
+ heading(content, level) {
8509
+ this.rowOrder++;
8510
+ const row = {
8511
+ id: `heading-${this.rowOrder}`,
8512
+ order: this.rowOrder,
8513
+ type: "heading",
8514
+ content,
8515
+ level
8516
+ };
8517
+ this.rows.push(row);
8518
+ return this;
8519
+ }
8520
+ /**
8521
+ * Add a visual separator row to the form
8522
+ */
8523
+ separator() {
8524
+ this.rowOrder++;
8525
+ const row = {
8526
+ id: `separator-${this.rowOrder}`,
8527
+ order: this.rowOrder,
8528
+ type: "separator"
8529
+ };
8530
+ this.rows.push(row);
8531
+ return this;
8532
+ }
8533
+ /**
8534
+ * Add a static text row to the form
8535
+ */
8536
+ text(content) {
8537
+ this.rowOrder++;
8538
+ const row = {
8539
+ id: `text-${this.rowOrder}`,
8540
+ order: this.rowOrder,
8541
+ type: "text",
8542
+ content
8543
+ };
8544
+ this.rows.push(row);
8545
+ return this;
8546
+ }
8124
8547
  /**
8125
8548
  * Set the next node and complete the form definition
8126
8549
  */
8127
8550
  next(nodeId) {
8128
- if (this.rows.length === 0) {
8551
+ const hasFieldRows = this.rows.some((r) => !r.type || r.type === "fields");
8552
+ if (!hasFieldRows) {
8129
8553
  throw new Error(
8130
- `[WorkflowBuilder] Form "${this.nodeId}" has no rows. Use .row() to add rows.`
8554
+ `[WorkflowBuilder] Form "${this.nodeId}" has no field rows. Use .row() to add fields.`
8131
8555
  );
8132
8556
  }
8133
8557
  return this.workflowBuilder._addFormNode({
@@ -8178,6 +8602,22 @@ var WorkflowSimpleFormBuilder = class {
8178
8602
  }
8179
8603
  return this;
8180
8604
  }
8605
+ /**
8606
+ * Add a relation field with optional configuration.
8607
+ * Use this for relation attributes that need specific configuration
8608
+ * (e.g., which qualified properties to display).
8609
+ */
8610
+ relationField(slotId, attribute, options) {
8611
+ this.fieldList.push({
8612
+ slotId,
8613
+ attribute,
8614
+ relationConfig: options ? {
8615
+ visibleProperties: options.visibleProperties,
8616
+ allowCreate: options.allowCreate
8617
+ } : void 0
8618
+ });
8619
+ return this;
8620
+ }
8181
8621
  /**
8182
8622
  * Set the next node and complete the form definition
8183
8623
  */
@@ -8319,6 +8759,68 @@ var WorkflowAssignBuilder = class {
8319
8759
  });
8320
8760
  }
8321
8761
  };
8762
+ var WorkflowAIBuilder = class {
8763
+ /** @internal */
8764
+ constructor(workflowBuilder, nodeId, label) {
8765
+ this.action = null;
8766
+ this.workflowBuilder = workflowBuilder;
8767
+ this.nodeId = nodeId;
8768
+ this.label = label;
8769
+ }
8770
+ /**
8771
+ * Set the description for this AI node
8772
+ */
8773
+ describe(description) {
8774
+ this.nodeDescription = description;
8775
+ return this;
8776
+ }
8777
+ /**
8778
+ * Set a custom timeout (default: 120_000ms)
8779
+ */
8780
+ timeout(ms) {
8781
+ this.nodeTimeoutMs = ms;
8782
+ return this;
8783
+ }
8784
+ /**
8785
+ * Configure document generation action
8786
+ */
8787
+ documentGeneration(config) {
8788
+ this.action = {
8789
+ type: "document-generation",
8790
+ ...config
8791
+ };
8792
+ return this;
8793
+ }
8794
+ /**
8795
+ * Configure code execution action
8796
+ */
8797
+ codeExecution(config) {
8798
+ this.action = {
8799
+ type: "code-execution",
8800
+ ...config
8801
+ };
8802
+ return this;
8803
+ }
8804
+ /**
8805
+ * Set the next node and complete the AI node definition
8806
+ */
8807
+ next(nodeId) {
8808
+ if (!this.action) {
8809
+ throw new Error(
8810
+ `[WorkflowBuilder] AI node "${this.nodeId}" must have an action. Use .documentGeneration() or .codeExecution() first.`
8811
+ );
8812
+ }
8813
+ return this.workflowBuilder._addNode({
8814
+ type: "ai",
8815
+ id: this.nodeId,
8816
+ label: this.label,
8817
+ description: this.nodeDescription,
8818
+ action: this.action,
8819
+ timeoutMs: this.nodeTimeoutMs,
8820
+ next: nodeId
8821
+ });
8822
+ }
8823
+ };
8322
8824
  var WorkflowEndBuilder = class {
8323
8825
  /** @internal */
8324
8826
  constructor(workflowBuilder, nodeId) {
@@ -8490,6 +8992,12 @@ var WorkflowBuilder = class {
8490
8992
  assign(id, label, targetSlotId) {
8491
8993
  return new WorkflowAssignBuilder(this, id, label, targetSlotId);
8492
8994
  }
8995
+ /**
8996
+ * Define an AI node (run an AI action like document generation or code execution)
8997
+ */
8998
+ ai(id, label) {
8999
+ return new WorkflowAIBuilder(this, id, label);
9000
+ }
8493
9001
  /**
8494
9002
  * Define an end node
8495
9003
  */
@@ -10210,6 +10718,7 @@ var BilateralSyncService = class extends BaseService {
10210
10718
  const addedIds = newData.ids.filter((id) => !oldData.ids.includes(id));
10211
10719
  const removedIds = oldData.ids.filter((id) => !newData.ids.includes(id));
10212
10720
  const commonIds = newData.ids.filter((id) => oldData.ids.includes(id));
10721
+ const inverseMappings = this.buildInverseMappings(sourceAttr);
10213
10722
  await Promise.all([
10214
10723
  // Add new relations
10215
10724
  ...addedIds.map(
@@ -10219,7 +10728,9 @@ var BilateralSyncService = class extends BaseService {
10219
10728
  sourceRecordId,
10220
10729
  sourceSchema.name,
10221
10730
  sourceAttr.name,
10222
- newData.properties.get(targetId)
10731
+ newData.properties.get(targetId),
10732
+ inverseMappings,
10733
+ bilateral
10223
10734
  )
10224
10735
  ),
10225
10736
  // Remove deleted relations
@@ -10235,7 +10746,9 @@ var BilateralSyncService = class extends BaseService {
10235
10746
  sourceSchema.name,
10236
10747
  sourceAttr.name,
10237
10748
  newData.properties.get(targetId),
10238
- oldData.properties.get(targetId)
10749
+ oldData.properties.get(targetId),
10750
+ inverseMappings,
10751
+ bilateral
10239
10752
  )
10240
10753
  )
10241
10754
  ]);
@@ -10279,7 +10792,7 @@ var BilateralSyncService = class extends BaseService {
10279
10792
  * Add an ID to an inverse relation (with properties).
10280
10793
  * @private
10281
10794
  */
10282
- async addInverseRelation(targetRecordId, inverseAttr, sourceRecordId, sourceObject, sourceAttribute, properties) {
10795
+ async addInverseRelation(targetRecordId, inverseAttr, sourceRecordId, sourceObject, sourceAttribute, properties, inverseMappings, bilateral) {
10283
10796
  const targetRecord = await this.adapter.objectRecords.findById(targetRecordId);
10284
10797
  if (!targetRecord) {
10285
10798
  return;
@@ -10310,14 +10823,27 @@ var BilateralSyncService = class extends BaseService {
10310
10823
  this.adapter
10311
10824
  );
10312
10825
  }
10826
+ if (inverseMappings && bilateral) {
10827
+ const invertedProps = this.invertProperties(properties, inverseMappings);
10828
+ const targetSchemaObj = await this.schemaService.getObjectSchemaByName(bilateral.object);
10829
+ if (targetSchemaObj) {
10830
+ await this.relationPropertiesService.syncRelationProperties(
10831
+ targetSchemaObj,
10832
+ targetRecordId,
10833
+ bilateral.attribute,
10834
+ [{ id: sourceRecordId, props: invertedProps }],
10835
+ this.adapter
10836
+ );
10837
+ }
10838
+ }
10313
10839
  }
10314
10840
  }
10315
10841
  /**
10316
10842
  * Update properties of an existing inverse relation.
10317
10843
  * @private
10318
10844
  */
10319
- async updateInverseRelationProperties(targetRecordId, _inverseAttr, sourceRecordId, sourceObject, sourceAttribute, newProperties, oldProperties) {
10320
- if (JSON.stringify(newProperties) === JSON.stringify(oldProperties)) {
10845
+ async updateInverseRelationProperties(targetRecordId, _inverseAttr, sourceRecordId, sourceObject, sourceAttribute, newProperties, oldProperties, inverseMappings, bilateral) {
10846
+ if (deepEqual(newProperties, oldProperties)) {
10321
10847
  return;
10322
10848
  }
10323
10849
  if (!this.adapter.relationAttributes) {
@@ -10335,12 +10861,32 @@ var BilateralSyncService = class extends BaseService {
10335
10861
  [{ id: targetRecordId, props: newProperties }],
10336
10862
  this.adapter
10337
10863
  );
10864
+ if (inverseMappings && bilateral) {
10865
+ const invertedProps = this.invertProperties(newProperties, inverseMappings);
10866
+ const targetSchemaObj = await this.schemaService.getObjectSchemaByName(bilateral.object);
10867
+ if (targetSchemaObj) {
10868
+ await this.relationPropertiesService.syncRelationProperties(
10869
+ targetSchemaObj,
10870
+ targetRecordId,
10871
+ bilateral.attribute,
10872
+ [{ id: sourceRecordId, props: invertedProps }],
10873
+ this.adapter
10874
+ );
10875
+ }
10876
+ }
10338
10877
  } else {
10339
10878
  await this.adapter.relationAttributes.deleteBySource(
10340
10879
  sourceObject,
10341
10880
  sourceRecordId,
10342
10881
  sourceAttribute
10343
10882
  );
10883
+ if (bilateral) {
10884
+ await this.adapter.relationAttributes.deleteBySource(
10885
+ bilateral.object,
10886
+ targetRecordId,
10887
+ bilateral.attribute
10888
+ );
10889
+ }
10344
10890
  }
10345
10891
  }
10346
10892
  /**
@@ -10372,6 +10918,54 @@ var BilateralSyncService = class extends BaseService {
10372
10918
  });
10373
10919
  await this.invalidateTargetRecordCaches(targetRecordId, targetRecord.objectId);
10374
10920
  }
10921
+ /**
10922
+ * Build inverse value mappings from property definitions.
10923
+ * Scans all select/status properties for options with `inverse` field.
10924
+ * @returns Map<propertyName, Map<sourceValue, inverseValue>>
10925
+ * @private
10926
+ */
10927
+ buildInverseMappings(sourceAttr) {
10928
+ const mappings = /* @__PURE__ */ new Map();
10929
+ const definitions = sourceAttr.properties?.definitions;
10930
+ if (!definitions) return mappings;
10931
+ for (const def of definitions) {
10932
+ if (!hasOptions2(def)) continue;
10933
+ const options = def.options;
10934
+ const valueMap = /* @__PURE__ */ new Map();
10935
+ for (const option of options) {
10936
+ if (option.inverse) {
10937
+ valueMap.set(option.value, option.inverse);
10938
+ }
10939
+ }
10940
+ if (valueMap.size > 0) {
10941
+ mappings.set(def.name, valueMap);
10942
+ }
10943
+ }
10944
+ return mappings;
10945
+ }
10946
+ /**
10947
+ * Apply inverse mappings to a set of properties.
10948
+ * Properties without a mapping are copied as-is.
10949
+ * @private
10950
+ */
10951
+ invertProperties(props, mappings) {
10952
+ const result = {};
10953
+ for (const [key, value] of Object.entries(props)) {
10954
+ const valueMap = mappings.get(key);
10955
+ if (!valueMap) {
10956
+ result[key] = value;
10957
+ } else if (typeof value === "string") {
10958
+ result[key] = valueMap.get(value) ?? value;
10959
+ } else if (Array.isArray(value)) {
10960
+ result[key] = value.map(
10961
+ (v) => typeof v === "string" ? valueMap.get(v) ?? v : v
10962
+ );
10963
+ } else {
10964
+ result[key] = value;
10965
+ }
10966
+ }
10967
+ return result;
10968
+ }
10375
10969
  /**
10376
10970
  * Normalize a relation value to an array of IDs.
10377
10971
  * @private
@@ -12454,7 +13048,7 @@ var RecordService = class extends BaseService {
12454
13048
  }
12455
13049
  }
12456
13050
  }
12457
- for (const [attrName, value] of Object.entries(normalizedData)) {
13051
+ for (const [attrName, value] of Object.entries(dataWithDefaults)) {
12458
13052
  const attr = schema.attributes.find((a) => a.name === attrName);
12459
13053
  if (attr?.type === "relation" && isBilateralRelation(attr)) {
12460
13054
  await this.bilateralSyncService.syncBilateralRelation(
@@ -12666,7 +13260,7 @@ var RecordService = class extends BaseService {
12666
13260
  }
12667
13261
  }
12668
13262
  }
12669
- for (const [attrName, value] of Object.entries(normalizedUpdate)) {
13263
+ for (const [attrName, value] of Object.entries(dataToUpdate)) {
12670
13264
  const attr = schema.attributes.find((a) => a.name === attrName);
12671
13265
  if (attr?.type === "relation" && isBilateralRelation(attr)) {
12672
13266
  const oldValue = bilateralOldValues[attrName];
@@ -13526,7 +14120,7 @@ var WorkflowInstanceService = class extends BaseService {
13526
14120
  * Start a new workflow instance
13527
14121
  */
13528
14122
  async startWorkflow(input) {
13529
- const workflow2 = await this.workflowService.getWorkflow(input.workflowName);
14123
+ let workflow2 = await this.workflowService.getWorkflow(input.workflowName);
13530
14124
  if (!workflow2) {
13531
14125
  throw new SchemaError(
13532
14126
  `Workflow "${input.workflowName}" not found`,
@@ -13539,6 +14133,20 @@ var WorkflowInstanceService = class extends BaseService {
13539
14133
  SchemaErrorCode.VALIDATION_FAILED
13540
14134
  );
13541
14135
  }
14136
+ if (workflow2.system && this.adapter.workflows) {
14137
+ const dbWorkflow = await this.adapter.workflows.upsert({
14138
+ name: workflow2.name,
14139
+ label: workflow2.label ?? workflow2.name,
14140
+ description: workflow2.description,
14141
+ status: "published",
14142
+ version: workflow2.version,
14143
+ slots: workflow2.slots ?? [],
14144
+ nodes: workflow2.nodes,
14145
+ startNodeId: workflow2.startNodeId,
14146
+ system: true
14147
+ });
14148
+ workflow2 = { ...workflow2, id: dbWorkflow.id };
14149
+ }
13542
14150
  const context = createEmptyContext();
13543
14151
  if (input.initialSlots) {
13544
14152
  for (const [slotId, data] of Object.entries(input.initialSlots)) {
@@ -16956,6 +17564,74 @@ var PermissionService = class extends BaseService {
16956
17564
  // ============================================================================
16957
17565
  // INITIALIZATION
16958
17566
  // ============================================================================
17567
+ /**
17568
+ * Reset all roles and permissions to their default configuration.
17569
+ *
17570
+ * This will:
17571
+ * 1. Delete all custom (non-system) roles and reassign their users to the default member role
17572
+ * 2. Reset permissions on the default roles (owner, member) back to defaults
17573
+ * 3. Invalidate all permission caches
17574
+ *
17575
+ * @example
17576
+ * ```typescript
17577
+ * const service = new PermissionService(adapter);
17578
+ * await service.resetToDefaults();
17579
+ * ```
17580
+ */
17581
+ async resetToDefaults() {
17582
+ const { DEFAULT_ROLES, DEFAULT_ROLE_PERMISSIONS } = await import("./default-roles-6D3HQ3DQ.mjs");
17583
+ await this.initializeDefaultRoles();
17584
+ const allRoles = await this.getRoles();
17585
+ const memberRole = allRoles.find((r) => r.name === DEFAULT_ROLES.MEMBER);
17586
+ if (!memberRole) {
17587
+ throw new Error("Default member role not found after initialization");
17588
+ }
17589
+ const customRoles = allRoles.filter(
17590
+ (r) => !Object.values(DEFAULT_ROLES).includes(
17591
+ r.name
17592
+ )
17593
+ );
17594
+ for (const role of customRoles) {
17595
+ const usersWithRole = await this.permissionsRepo.countUsersWithRole(role.name);
17596
+ if (usersWithRole > 0) {
17597
+ }
17598
+ await this.permissionsRepo.deleteRole(role.id);
17599
+ }
17600
+ for (const roleName of Object.values(DEFAULT_ROLES)) {
17601
+ const role = allRoles.find((r) => r.name === roleName);
17602
+ if (!role) continue;
17603
+ const permConfig = DEFAULT_ROLE_PERMISSIONS[roleName];
17604
+ const permissionInputs = [];
17605
+ for (const perm of permConfig.system) {
17606
+ permissionInputs.push({
17607
+ scope: "system",
17608
+ target: perm.target,
17609
+ actions: perm.actions
17610
+ });
17611
+ }
17612
+ for (const perm of permConfig.object) {
17613
+ permissionInputs.push({
17614
+ scope: "object",
17615
+ target: perm.target,
17616
+ actions: perm.actions
17617
+ });
17618
+ }
17619
+ await this.permissionsRepo.setPermissions(role.id, permissionInputs);
17620
+ }
17621
+ await this.invalidateAllCache();
17622
+ if (this.auditService && this.userId) {
17623
+ await this.auditService.logRoleAction({
17624
+ action: "role.updated",
17625
+ actorId: this.userId,
17626
+ roleId: "all",
17627
+ roleLabel: "All roles",
17628
+ metadata: {
17629
+ resetToDefaults: true,
17630
+ customRolesDeleted: customRoles.length
17631
+ }
17632
+ });
17633
+ }
17634
+ }
16959
17635
  /**
16960
17636
  * Initialize default roles for the tenant if they don't exist.
16961
17637
  *
@@ -18015,6 +18691,7 @@ export {
18015
18691
  RESERVED_ATTRIBUTE_NAMES,
18016
18692
  PolicyViolationError,
18017
18693
  FORBIDDEN_PROPERTY_TYPES,
18694
+ hasOptions2 as hasOptions,
18018
18695
  SYSTEM_ATTRIBUTES,
18019
18696
  getSystemAttributeList,
18020
18697
  isSystemAttribute,
@@ -18031,6 +18708,7 @@ export {
18031
18708
  isFormNode,
18032
18709
  isConditionNode,
18033
18710
  isAssignNode,
18711
+ isAINode,
18034
18712
  isEndNode,
18035
18713
  isConditionRule,
18036
18714
  isConditionGroup,
@@ -18081,6 +18759,10 @@ export {
18081
18759
  AssignmentSourceSchema,
18082
18760
  AssignmentMappingSchema,
18083
18761
  AssignNodeSchema,
18762
+ DocumentGenerationActionSchema,
18763
+ CodeExecutionActionSchema,
18764
+ AIActionConfigSchema,
18765
+ AINodeSchema,
18084
18766
  EndNodeSchema,
18085
18767
  WorkflowNodeSchema,
18086
18768
  SlotModeSchema,
@@ -18178,6 +18860,7 @@ export {
18178
18860
  WorkflowSimpleFormBuilder,
18179
18861
  WorkflowConditionBuilder,
18180
18862
  WorkflowAssignBuilder,
18863
+ WorkflowAIBuilder,
18181
18864
  WorkflowEndBuilder,
18182
18865
  WorkflowStartBuilder,
18183
18866
  WorkflowBuilder,
@@ -18231,6 +18914,11 @@ export {
18231
18914
  EndExecutor,
18232
18915
  FormExecutor,
18233
18916
  AssignExecutor,
18917
+ AIExecutor,
18918
+ AIActionRegistry,
18919
+ DocumentGenerationHandler,
18920
+ CodeExecutionHandler,
18921
+ createDefaultAIActionRegistry,
18234
18922
  StartExecutor,
18235
18923
  createDefaultExecutorRegistry,
18236
18924
  getDefaultExecutorRegistry,