@bpmnkit/core 0.1.0 → 0.1.2

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 (63) hide show
  1. package/README.md +2 -0
  2. package/dist/bpmn/agentic.d.ts +121 -0
  3. package/dist/bpmn/agentic.js +97 -0
  4. package/dist/bpmn/auto-layout.d.ts +5 -5
  5. package/dist/bpmn/auto-layout.js +592 -36
  6. package/dist/bpmn/bpmn-builder.d.ts +56 -0
  7. package/dist/bpmn/bpmn-builder.js +155 -182
  8. package/dist/bpmn/bpmn-model.d.ts +39 -2
  9. package/dist/bpmn/bpmn-parser.js +48 -2
  10. package/dist/bpmn/bpmn-serializer.js +33 -0
  11. package/dist/bpmn/compact.js +11 -1
  12. package/dist/bpmn/di-planes.d.ts +13 -0
  13. package/dist/bpmn/di-planes.js +20 -0
  14. package/dist/bpmn/optimize/agentic.d.ts +10 -0
  15. package/dist/bpmn/optimize/agentic.js +88 -0
  16. package/dist/bpmn/optimize/deploy.d.ts +16 -0
  17. package/dist/bpmn/optimize/deploy.js +143 -0
  18. package/dist/bpmn/optimize/feel-syntax.d.ts +12 -0
  19. package/dist/bpmn/optimize/feel-syntax.js +87 -0
  20. package/dist/bpmn/optimize/feel.js +5 -2
  21. package/dist/bpmn/optimize/flow.js +22 -2
  22. package/dist/bpmn/optimize/index.js +20 -9
  23. package/dist/bpmn/optimize/tasks.js +1 -0
  24. package/dist/bpmn/optimize/types.d.ts +10 -1
  25. package/dist/bpmn/svg.js +22 -3
  26. package/dist/bpmn/type-guards.d.ts +7 -1
  27. package/dist/bpmn/type-guards.js +13 -0
  28. package/dist/bpmn/zeebe-extensions.d.ts +27 -0
  29. package/dist/bpmn/zeebe-extensions.js +38 -0
  30. package/dist/index.d.ts +9 -3
  31. package/dist/index.js +4 -1
  32. package/dist/layout/annotations.js +36 -1
  33. package/dist/layout/collaboration/alignment.d.ts +26 -0
  34. package/dist/layout/collaboration/alignment.js +66 -0
  35. package/dist/layout/collaboration/ordering.d.ts +21 -0
  36. package/dist/layout/collaboration/ordering.js +102 -0
  37. package/dist/layout/index.d.ts +1 -0
  38. package/dist/layout/layout-engine.d.ts +13 -3
  39. package/dist/layout/layout-engine.js +9 -4
  40. package/dist/layout/semantic/bands.d.ts +19 -0
  41. package/dist/layout/semantic/bands.js +324 -0
  42. package/dist/layout/semantic/graph.d.ts +29 -0
  43. package/dist/layout/semantic/graph.js +217 -0
  44. package/dist/layout/semantic/index.d.ts +13 -0
  45. package/dist/layout/semantic/index.js +181 -0
  46. package/dist/layout/semantic/place.d.ts +40 -0
  47. package/dist/layout/semantic/place.js +271 -0
  48. package/dist/layout/semantic/route.d.ts +14 -0
  49. package/dist/layout/semantic/route.js +454 -0
  50. package/dist/layout/types.d.ts +17 -0
  51. package/dist/plan/compile.d.ts +39 -0
  52. package/dist/plan/compile.js +380 -0
  53. package/dist/plan/extract.d.ts +31 -0
  54. package/dist/plan/extract.js +248 -0
  55. package/dist/plan/index.d.ts +6 -0
  56. package/dist/plan/index.js +5 -0
  57. package/dist/plan/merge.d.ts +13 -0
  58. package/dist/plan/merge.js +80 -0
  59. package/dist/plan/slug.d.ts +5 -0
  60. package/dist/plan/slug.js +22 -0
  61. package/dist/plan/types.d.ts +225 -0
  62. package/dist/plan/types.js +13 -0
  63. package/package.json +2 -2
@@ -5,7 +5,7 @@ import type { XmlElement } from "../types/xml-element.js";
5
5
  * exhaustive `switch` statements and type-narrowing with the type guard helpers
6
6
  * exported from `@bpmnkit/core`.
7
7
  */
8
- export type BpmnElementType = "startEvent" | "endEvent" | "intermediateThrowEvent" | "intermediateCatchEvent" | "boundaryEvent" | "task" | "serviceTask" | "scriptTask" | "userTask" | "sendTask" | "receiveTask" | "businessRuleTask" | "manualTask" | "callActivity" | "exclusiveGateway" | "parallelGateway" | "inclusiveGateway" | "eventBasedGateway" | "complexGateway" | "subProcess" | "adHocSubProcess" | "eventSubProcess" | "transaction";
8
+ export type BpmnElementType = "startEvent" | "endEvent" | "intermediateThrowEvent" | "intermediateCatchEvent" | "boundaryEvent" | "task" | "serviceTask" | "scriptTask" | "userTask" | "sendTask" | "receiveTask" | "businessRuleTask" | "manualTask" | "callActivity" | "exclusiveGateway" | "parallelGateway" | "inclusiveGateway" | "eventBasedGateway" | "complexGateway" | "subProcess" | "adHocSubProcess" | "eventSubProcess" | "transaction" | "dataObject" | "dataObjectReference" | "dataStoreReference";
9
9
  /** Axis-aligned bounding box used by BPMN diagram interchange (BPMNDi). */
10
10
  export interface BpmnBounds {
11
11
  x: number;
@@ -150,6 +150,26 @@ export interface BpmnCallActivity extends BpmnFlowNodeBase {
150
150
  type: "callActivity";
151
151
  loopCharacteristics?: BpmnMultiInstanceLoopCharacteristics;
152
152
  }
153
+ /** A data object — a piece of information flowing through the process. */
154
+ export interface BpmnDataObject extends BpmnFlowNodeBase {
155
+ type: "dataObject";
156
+ /** When true, represents a collection of items. */
157
+ isCollection?: boolean;
158
+ }
159
+ /** A visual reference to a {@link BpmnDataObject} (the shape drawn on the canvas). */
160
+ export interface BpmnDataObjectReference extends BpmnFlowNodeBase {
161
+ type: "dataObjectReference";
162
+ /** Id of the referenced `dataObject`. */
163
+ dataObjectRef?: string;
164
+ /** When true, drawn with the collection (parallel-bars) marker. */
165
+ isCollection?: boolean;
166
+ }
167
+ /** A visual reference to a data store (drawn as a cylinder). */
168
+ export interface BpmnDataStoreReference extends BpmnFlowNodeBase {
169
+ type: "dataStoreReference";
170
+ /** Id of the referenced `dataStore`. */
171
+ dataStoreRef?: string;
172
+ }
153
173
  export interface BpmnSendTask extends BpmnFlowNodeBase {
154
174
  type: "sendTask";
155
175
  messageRef?: string;
@@ -163,10 +183,15 @@ export interface BpmnReceiveTask extends BpmnFlowNodeBase {
163
183
  export interface BpmnAdHocSubProcess extends BpmnFlowNodeBase {
164
184
  type: "adHocSubProcess";
165
185
  loopCharacteristics?: BpmnMultiInstanceLoopCharacteristics;
186
+ /** FEEL expression evaluated after each tool activity completes; ends the ad-hoc scope when true. */
187
+ completionCondition?: BpmnConditionExpression;
188
+ /** Whether still-running inner activity instances are cancelled once the completion condition is met (default true). */
189
+ cancelRemainingInstances?: boolean;
166
190
  flowElements: BpmnFlowElement[];
167
191
  sequenceFlows: BpmnSequenceFlow[];
168
192
  textAnnotations: BpmnTextAnnotation[];
169
193
  associations: BpmnAssociation[];
194
+ groups: BpmnGroup[];
170
195
  }
171
196
  export interface BpmnSubProcess extends BpmnFlowNodeBase {
172
197
  type: "subProcess";
@@ -176,6 +201,7 @@ export interface BpmnSubProcess extends BpmnFlowNodeBase {
176
201
  sequenceFlows: BpmnSequenceFlow[];
177
202
  textAnnotations: BpmnTextAnnotation[];
178
203
  associations: BpmnAssociation[];
204
+ groups: BpmnGroup[];
179
205
  }
180
206
  export interface BpmnEventSubProcess extends BpmnFlowNodeBase {
181
207
  type: "eventSubProcess";
@@ -183,6 +209,7 @@ export interface BpmnEventSubProcess extends BpmnFlowNodeBase {
183
209
  sequenceFlows: BpmnSequenceFlow[];
184
210
  textAnnotations: BpmnTextAnnotation[];
185
211
  associations: BpmnAssociation[];
212
+ groups: BpmnGroup[];
186
213
  }
187
214
  export interface BpmnExclusiveGateway extends BpmnFlowNodeBase {
188
215
  type: "exclusiveGateway";
@@ -217,6 +244,7 @@ export interface BpmnTransaction extends BpmnFlowNodeBase {
217
244
  sequenceFlows: BpmnSequenceFlow[];
218
245
  textAnnotations: BpmnTextAnnotation[];
219
246
  associations: BpmnAssociation[];
247
+ groups: BpmnGroup[];
220
248
  }
221
249
  /**
222
250
  * Discriminated union of every BPMN flow node that can appear inside a
@@ -233,7 +261,7 @@ export interface BpmnTransaction extends BpmnFlowNodeBase {
233
261
  * }
234
262
  * ```
235
263
  */
236
- export type BpmnFlowElement = BpmnStartEvent | BpmnEndEvent | BpmnIntermediateCatchEvent | BpmnIntermediateThrowEvent | BpmnBoundaryEvent | BpmnTask | BpmnServiceTask | BpmnScriptTask | BpmnUserTask | BpmnSendTask | BpmnReceiveTask | BpmnBusinessRuleTask | BpmnManualTask | BpmnCallActivity | BpmnSubProcess | BpmnAdHocSubProcess | BpmnEventSubProcess | BpmnTransaction | BpmnExclusiveGateway | BpmnParallelGateway | BpmnInclusiveGateway | BpmnEventBasedGateway | BpmnComplexGateway;
264
+ export type BpmnFlowElement = BpmnStartEvent | BpmnEndEvent | BpmnIntermediateCatchEvent | BpmnIntermediateThrowEvent | BpmnBoundaryEvent | BpmnTask | BpmnServiceTask | BpmnScriptTask | BpmnUserTask | BpmnSendTask | BpmnReceiveTask | BpmnBusinessRuleTask | BpmnManualTask | BpmnCallActivity | BpmnSubProcess | BpmnAdHocSubProcess | BpmnEventSubProcess | BpmnTransaction | BpmnExclusiveGateway | BpmnParallelGateway | BpmnInclusiveGateway | BpmnEventBasedGateway | BpmnComplexGateway | BpmnDataObject | BpmnDataObjectReference | BpmnDataStoreReference;
237
265
  /** Backward-compat alias used by the layout module. */
238
266
  export type BpmnFlowNode = BpmnFlowElement;
239
267
  /**
@@ -264,6 +292,13 @@ export interface BpmnAssociation {
264
292
  associationDirection?: string;
265
293
  unknownAttributes: Record<string, string>;
266
294
  }
295
+ /** A group artifact — a dashed rounded rectangle visually grouping elements. */
296
+ export interface BpmnGroup {
297
+ id: string;
298
+ /** Id of the `categoryValue` supplying the group's label, if any. */
299
+ categoryValueRef?: string;
300
+ unknownAttributes: Record<string, string>;
301
+ }
267
302
  /** A swim lane within a pool (organises flow nodes visually). */
268
303
  export interface BpmnLane {
269
304
  id: string;
@@ -294,6 +329,7 @@ export interface BpmnProcess {
294
329
  sequenceFlows: BpmnSequenceFlow[];
295
330
  textAnnotations: BpmnTextAnnotation[];
296
331
  associations: BpmnAssociation[];
332
+ groups: BpmnGroup[];
297
333
  laneSet?: BpmnLaneSet;
298
334
  unknownAttributes: Record<string, string>;
299
335
  }
@@ -319,6 +355,7 @@ export interface BpmnCollaboration {
319
355
  messageFlows: BpmnMessageFlow[];
320
356
  textAnnotations: BpmnTextAnnotation[];
321
357
  associations: BpmnAssociation[];
358
+ groups: BpmnGroup[];
322
359
  extensionElements: XmlElement[];
323
360
  unknownAttributes: Record<string, string>;
324
361
  }
@@ -59,6 +59,10 @@ const KNOWN_ATTRS = new Set([
59
59
  "exporter",
60
60
  "exporterVersion",
61
61
  "processRef",
62
+ "dataObjectRef",
63
+ "dataStoreRef",
64
+ "isCollection",
65
+ "categoryValueRef",
62
66
  ]);
63
67
  /** Extract unknown (namespace-qualified) attributes from an element. */
64
68
  function unknownAttrs(element) {
@@ -261,6 +265,9 @@ const FLOW_ELEMENT_TYPES = new Set([
261
265
  "inclusiveGateway",
262
266
  "eventBasedGateway",
263
267
  "complexGateway",
268
+ "dataObject",
269
+ "dataObjectReference",
270
+ "dataStoreReference",
264
271
  ]);
265
272
  function parseFlowElement(element) {
266
273
  const ln = localName(element.name);
@@ -321,13 +328,21 @@ function parseFlowElement(element) {
321
328
  loopCharacteristics: parseLoopCharacteristics(element),
322
329
  isForCompensation: attr(element, "isForCompensation") === "true" ? true : undefined,
323
330
  };
324
- case "adHocSubProcess":
331
+ case "adHocSubProcess": {
332
+ const completionEl = findChild(element, "completionCondition");
325
333
  return {
326
334
  ...base,
327
335
  type: "adHocSubProcess",
328
336
  loopCharacteristics: parseLoopCharacteristics(element),
337
+ completionCondition: completionEl
338
+ ? { text: completionEl.text ?? "", attributes: { ...completionEl.attributes } }
339
+ : undefined,
340
+ cancelRemainingInstances: attr(element, "cancelRemainingInstances") !== undefined
341
+ ? attr(element, "cancelRemainingInstances") === "true"
342
+ : undefined,
329
343
  ...parseProcessContents(element),
330
344
  };
345
+ }
331
346
  case "subProcess":
332
347
  return {
333
348
  ...base,
@@ -361,6 +376,25 @@ function parseFlowElement(element) {
361
376
  return { ...base, type: "eventBasedGateway" };
362
377
  case "complexGateway":
363
378
  return { ...base, type: "complexGateway", default: attr(element, "default") };
379
+ case "dataObject":
380
+ return {
381
+ ...base,
382
+ type: "dataObject",
383
+ ...(attr(element, "isCollection") === "true" ? { isCollection: true } : {}),
384
+ };
385
+ case "dataObjectReference":
386
+ return {
387
+ ...base,
388
+ type: "dataObjectReference",
389
+ dataObjectRef: attr(element, "dataObjectRef"),
390
+ ...(attr(element, "isCollection") === "true" ? { isCollection: true } : {}),
391
+ };
392
+ case "dataStoreReference":
393
+ return {
394
+ ...base,
395
+ type: "dataStoreReference",
396
+ dataStoreRef: attr(element, "dataStoreRef"),
397
+ };
364
398
  default:
365
399
  return undefined;
366
400
  }
@@ -407,6 +441,13 @@ function parseAssociation(element) {
407
441
  unknownAttributes: unknownAttrs(element),
408
442
  };
409
443
  }
444
+ function parseGroup(element) {
445
+ return {
446
+ id: requiredAttr(element, "id"),
447
+ categoryValueRef: attr(element, "categoryValueRef"),
448
+ unknownAttributes: unknownAttrs(element),
449
+ };
450
+ }
410
451
  // ---------------------------------------------------------------------------
411
452
  // Process contents (shared between process and adHocSubProcess)
412
453
  // ---------------------------------------------------------------------------
@@ -415,6 +456,7 @@ function parseProcessContents(element) {
415
456
  const sequenceFlows = [];
416
457
  const textAnnotations = [];
417
458
  const associations = [];
459
+ const groups = [];
418
460
  for (const child of element.children) {
419
461
  const ln = localName(child.name);
420
462
  if (FLOW_ELEMENT_TYPES.has(ln)) {
@@ -431,8 +473,11 @@ function parseProcessContents(element) {
431
473
  else if (ln === "association") {
432
474
  associations.push(parseAssociation(child));
433
475
  }
476
+ else if (ln === "group") {
477
+ groups.push(parseGroup(child));
478
+ }
434
479
  }
435
- return { flowElements, sequenceFlows, textAnnotations, associations };
480
+ return { flowElements, sequenceFlows, textAnnotations, associations, groups };
436
481
  }
437
482
  // ---------------------------------------------------------------------------
438
483
  // Lanes
@@ -498,6 +543,7 @@ function parseCollaboration(element) {
498
543
  messageFlows: findChildren(element, "messageFlow").map(parseMessageFlow),
499
544
  textAnnotations: findChildren(element, "textAnnotation").map(parseTextAnnotation),
500
545
  associations: findChildren(element, "association").map(parseAssociation),
546
+ groups: findChildren(element, "group").map(parseGroup),
501
547
  extensionElements: parseExtensionElements(element),
502
548
  unknownAttributes: unknownAttrs(element),
503
549
  };
@@ -202,8 +202,14 @@ function serializeFlowElement(fe, ns) {
202
202
  children.push(...serializeLoopCharacteristics(fe.loopCharacteristics, bp));
203
203
  break;
204
204
  case "adHocSubProcess":
205
+ if (fe.cancelRemainingInstances !== undefined) {
206
+ attrs.cancelRemainingInstances = String(fe.cancelRemainingInstances);
207
+ }
205
208
  children.push(...serializeLoopCharacteristics(fe.loopCharacteristics, bp));
206
209
  children.push(...serializeProcessContents(fe, ns));
210
+ if (fe.completionCondition) {
211
+ children.push(el(`${bp}:completionCondition`, fe.completionCondition.attributes, [], fe.completionCondition.text));
212
+ }
207
213
  break;
208
214
  case "subProcess":
209
215
  if (fe.triggeredByEvent !== undefined)
@@ -233,6 +239,20 @@ function serializeFlowElement(fe, ns) {
233
239
  case "parallelGateway":
234
240
  case "eventBasedGateway":
235
241
  break;
242
+ case "dataObject":
243
+ if (fe.isCollection)
244
+ attrs.isCollection = "true";
245
+ break;
246
+ case "dataObjectReference":
247
+ if (fe.dataObjectRef !== undefined)
248
+ attrs.dataObjectRef = fe.dataObjectRef;
249
+ if (fe.isCollection)
250
+ attrs.isCollection = "true";
251
+ break;
252
+ case "dataStoreReference":
253
+ if (fe.dataStoreRef !== undefined)
254
+ attrs.dataStoreRef = fe.dataStoreRef;
255
+ break;
236
256
  }
237
257
  return el(`${bp}:${fe.type}`, attrs, children);
238
258
  }
@@ -276,6 +296,12 @@ function serializeAssociation(a, bp) {
276
296
  attrs.associationDirection = a.associationDirection;
277
297
  return el(`${bp}:association`, attrs, []);
278
298
  }
299
+ function serializeGroup(g, bp) {
300
+ const attrs = { id: g.id, ...g.unknownAttributes };
301
+ if (g.categoryValueRef !== undefined)
302
+ attrs.categoryValueRef = g.categoryValueRef;
303
+ return el(`${bp}:group`, attrs, []);
304
+ }
279
305
  // ---------------------------------------------------------------------------
280
306
  // Process contents
281
307
  // ---------------------------------------------------------------------------
@@ -294,6 +320,10 @@ function serializeProcessContents(p, ns) {
294
320
  for (const a of p.associations) {
295
321
  children.push(serializeAssociation(a, bp));
296
322
  }
323
+ // `groups` may be absent on hand-constructed partial models.
324
+ for (const g of p.groups ?? []) {
325
+ children.push(serializeGroup(g, bp));
326
+ }
297
327
  return children;
298
328
  }
299
329
  // ---------------------------------------------------------------------------
@@ -370,6 +400,9 @@ function serializeCollaboration(c, ns) {
370
400
  for (const a of c.associations) {
371
401
  children.push(serializeAssociation(a, bp));
372
402
  }
403
+ for (const g of c.groups ?? []) {
404
+ children.push(serializeGroup(g, bp));
405
+ }
373
406
  return el(`${bp}:collaboration`, { id: c.id, ...c.unknownAttributes }, children);
374
407
  }
375
408
  // ---------------------------------------------------------------------------
@@ -192,7 +192,13 @@ function makeExtensions(el) {
192
192
  }
193
193
  function buildSubContent(children) {
194
194
  if (!children || children.elements.length === 0) {
195
- return { flowElements: [], sequenceFlows: [], textAnnotations: [], associations: [] };
195
+ return {
196
+ flowElements: [],
197
+ sequenceFlows: [],
198
+ textAnnotations: [],
199
+ associations: [],
200
+ groups: [],
201
+ };
196
202
  }
197
203
  const inc = new Map();
198
204
  const out = new Map();
@@ -217,6 +223,7 @@ function buildSubContent(children) {
217
223
  })),
218
224
  textAnnotations: [],
219
225
  associations: [],
226
+ groups: [],
220
227
  };
221
228
  }
222
229
  function buildFlowElement(el, incoming, outgoing) {
@@ -284,6 +291,8 @@ function buildFlowElement(el, incoming, outgoing) {
284
291
  return { ...base, type: "eventBasedGateway" };
285
292
  case "complexGateway":
286
293
  return { ...base, type: "complexGateway" };
294
+ default:
295
+ return { ...base, type: "task" };
287
296
  }
288
297
  }
289
298
  function buildDiagram(processId, process) {
@@ -342,6 +351,7 @@ function expandProcess(compact) {
342
351
  sequenceFlows,
343
352
  textAnnotations: [],
344
353
  associations: [],
354
+ groups: [],
345
355
  unknownAttributes: {},
346
356
  };
347
357
  return { process, diagram: buildDiagram(compact.id, process) };
@@ -0,0 +1,13 @@
1
+ import type { BpmnDefinitions, BpmnDiPlane } from "./bpmn-model.js";
2
+ /**
3
+ * Returns the DI plane whose `bpmnElement` matches `elementId`, if any.
4
+ *
5
+ * The primary plane's `bpmnElement` is a process or collaboration id; a
6
+ * collapsed sub-process that carries its own layout has a separate
7
+ * `BPMNDiagram` whose plane `bpmnElement` is the sub-process id. This resolves
8
+ * either.
9
+ */
10
+ export declare function planeForElement(defs: BpmnDefinitions, elementId: string): BpmnDiPlane | undefined;
11
+ /** Lists every DI plane's `bpmnElement` id, in document order. */
12
+ export declare function listPlaneElementIds(defs: BpmnDefinitions): string[];
13
+ //# sourceMappingURL=di-planes.d.ts.map
@@ -0,0 +1,20 @@
1
+ /**
2
+ * Returns the DI plane whose `bpmnElement` matches `elementId`, if any.
3
+ *
4
+ * The primary plane's `bpmnElement` is a process or collaboration id; a
5
+ * collapsed sub-process that carries its own layout has a separate
6
+ * `BPMNDiagram` whose plane `bpmnElement` is the sub-process id. This resolves
7
+ * either.
8
+ */
9
+ export function planeForElement(defs, elementId) {
10
+ for (const diagram of defs.diagrams) {
11
+ if (diagram.plane.bpmnElement === elementId)
12
+ return diagram.plane;
13
+ }
14
+ return undefined;
15
+ }
16
+ /** Lists every DI plane's `bpmnElement` id, in document order. */
17
+ export function listPlaneElementIds(defs) {
18
+ return defs.diagrams.map((d) => d.plane.bpmnElement);
19
+ }
20
+ //# sourceMappingURL=di-planes.js.map
@@ -0,0 +1,10 @@
1
+ import type { BpmnProcess } from "../bpmn-model.js";
2
+ import type { OptimizationFinding } from "./types.js";
3
+ /**
4
+ * Agentic-specific checks for the Camunda 8 AI Agent Sub-process pattern:
5
+ * tools must be root nodes with a description the LLM can read, every
6
+ * `fromAi()` call must reference `toolCall.*`, and the agent should aggregate
7
+ * tool results and cap its model-call budget.
8
+ */
9
+ export declare function analyzeAgentic(p: BpmnProcess): OptimizationFinding[];
10
+ //# sourceMappingURL=agentic.d.ts.map
@@ -0,0 +1,88 @@
1
+ import { AI_AGENT_JOB_WORKER_TASK_TYPE } from "../agentic.js";
2
+ import { readZeebeIoMapping, readZeebeTaskType } from "./utils.js";
3
+ const FROM_AI_CALL = /fromAi\(\s*([^,)]+)/g;
4
+ function isAiAgentSubProcess(el) {
5
+ return readZeebeTaskType(el.extensionElements) === AI_AGENT_JOB_WORKER_TASK_TYPE;
6
+ }
7
+ /**
8
+ * Agentic-specific checks for the Camunda 8 AI Agent Sub-process pattern:
9
+ * tools must be root nodes with a description the LLM can read, every
10
+ * `fromAi()` call must reference `toolCall.*`, and the agent should aggregate
11
+ * tool results and cap its model-call budget.
12
+ */
13
+ export function analyzeAgentic(p) {
14
+ const findings = [];
15
+ const processId = p.id;
16
+ for (const el of p.flowElements) {
17
+ if (el.type !== "adHocSubProcess" || !isAiAgentSubProcess(el))
18
+ continue;
19
+ const adHocExt = el.extensionElements.find((e) => e.name === "zeebe:adHoc");
20
+ if (!adHocExt?.attributes.outputCollection) {
21
+ findings.push({
22
+ id: "agentic/no-output-collection",
23
+ category: "agentic",
24
+ severity: "warning",
25
+ message: `AI Agent "${el.name ?? el.id}" has no outputCollection — tool call results won't be aggregated.`,
26
+ suggestion: 'Set zeebe:adHoc outputCollection (e.g. "toolCallResults").',
27
+ processId,
28
+ elementIds: [el.id],
29
+ });
30
+ }
31
+ const io = readZeebeIoMapping(el.extensionElements);
32
+ const hasLimit = io?.inputs.some((i) => i.target === "data.limits.maxModelCalls") ?? false;
33
+ if (!hasLimit) {
34
+ findings.push({
35
+ id: "agentic/limits-missing",
36
+ category: "agentic",
37
+ severity: "info",
38
+ message: `AI Agent "${el.name ?? el.id}" has no data.limits.maxModelCalls binding.`,
39
+ suggestion: "Set a model-call limit as a safety net against infinite tool loops.",
40
+ processId,
41
+ elementIds: [el.id],
42
+ });
43
+ }
44
+ for (const tool of el.flowElements) {
45
+ if (tool.incoming.length > 0) {
46
+ findings.push({
47
+ id: "agentic/tool-not-root",
48
+ category: "agentic",
49
+ severity: "error",
50
+ message: `"${tool.name ?? tool.id}" inside AI Agent "${el.name ?? el.id}" has an incoming sequence flow — the connector only discovers tools with no incoming flow.`,
51
+ suggestion: "Remove the incoming sequence flow; tools must be root nodes.",
52
+ processId,
53
+ elementIds: [tool.id],
54
+ });
55
+ }
56
+ if (!tool.documentation?.trim()) {
57
+ findings.push({
58
+ id: "agentic/tool-no-description",
59
+ category: "agentic",
60
+ severity: "warning",
61
+ message: `Tool "${tool.name ?? tool.id}" has no documentation — the LLM sees no description for this tool.`,
62
+ suggestion: "Set <bpmn:documentation> describing what this tool does.",
63
+ processId,
64
+ elementIds: [tool.id],
65
+ });
66
+ }
67
+ const toolIo = readZeebeIoMapping(tool.extensionElements);
68
+ for (const input of toolIo?.inputs ?? []) {
69
+ for (const match of input.source.matchAll(FROM_AI_CALL)) {
70
+ const firstArg = match[1]?.trim();
71
+ if (firstArg && !firstArg.startsWith("toolCall.")) {
72
+ findings.push({
73
+ id: "agentic/fromai-bad-ref",
74
+ category: "agentic",
75
+ severity: "error",
76
+ message: `fromAi() on "${tool.name ?? tool.id}" input "${input.target}" references "${firstArg}", not a toolCall.* field.`,
77
+ suggestion: 'fromAi()\'s first argument must reference "toolCall.<param>".',
78
+ processId,
79
+ elementIds: [tool.id],
80
+ });
81
+ }
82
+ }
83
+ }
84
+ }
85
+ }
86
+ return findings;
87
+ }
88
+ //# sourceMappingURL=agentic.js.map
@@ -0,0 +1,16 @@
1
+ import type { BpmnProcess } from "../bpmn-model.js";
2
+ import type { OptimizationFinding } from "./types.js";
3
+ /**
4
+ * Given a bundled connector template id and the set of value keys bound on
5
+ * the element (io-mapping targets, task-header keys, zeebe:property names),
6
+ * returns the required keys that are missing. Inject `applyConnectorTemplate`-
7
+ * backed logic from `@bpmnkit/connectors` here; core stays dependency-free.
8
+ */
9
+ export type ConnectorRequirementsResolver = (templateId: string, boundKeys: string[]) => string[];
10
+ /**
11
+ * Zeebe/Reebe deploy-parity checks — mirrors what Camunda 8 rejects at
12
+ * deployment time (`apps/reebe/crates/reebe-bpmn/src/validator.rs`), so a
13
+ * process that passes this profile deploys without a validation error.
14
+ */
15
+ export declare function analyzeDeploy(p: BpmnProcess, resolveConnectorRequirements?: ConnectorRequirementsResolver): OptimizationFinding[];
16
+ //# sourceMappingURL=deploy.d.ts.map
@@ -0,0 +1,143 @@
1
+ import { readZeebeIoMapping, readZeebeTaskHeaders, readZeebeTaskType } from "./utils.js";
2
+ function findExt(el, name) {
3
+ return el.extensionElements.find((e) => e.name === name);
4
+ }
5
+ /**
6
+ * Zeebe/Reebe deploy-parity checks — mirrors what Camunda 8 rejects at
7
+ * deployment time (`apps/reebe/crates/reebe-bpmn/src/validator.rs`), so a
8
+ * process that passes this profile deploys without a validation error.
9
+ */
10
+ export function analyzeDeploy(p, resolveConnectorRequirements) {
11
+ const findings = [];
12
+ const processId = p.id;
13
+ if (p.isExecutable !== true) {
14
+ findings.push({
15
+ id: "deploy/process-not-executable",
16
+ category: "deploy",
17
+ severity: "error",
18
+ message: `Process "${processId}" is not marked executable.`,
19
+ suggestion: 'Set isExecutable="true" — Camunda 8 refuses to deploy a non-executable process.',
20
+ processId,
21
+ elementIds: [],
22
+ });
23
+ }
24
+ for (const el of p.flowElements) {
25
+ if (el.type === "serviceTask" || el.type === "sendTask") {
26
+ const type = readZeebeTaskType(el.extensionElements);
27
+ if (!type) {
28
+ findings.push({
29
+ id: "deploy/service-task-no-type",
30
+ category: "deploy",
31
+ severity: "error",
32
+ message: `"${el.name ?? el.id}" (${el.type}) has no zeebe:taskDefinition type.`,
33
+ suggestion: "Set a job type — Camunda 8 refuses to deploy a task with no task definition.",
34
+ processId,
35
+ elementIds: [el.id],
36
+ });
37
+ }
38
+ }
39
+ if (el.type === "businessRuleTask") {
40
+ const hasType = readZeebeTaskType(el.extensionElements) !== null;
41
+ const hasDecision = findExt(el, "zeebe:calledDecision") !== undefined;
42
+ if (!hasType && !hasDecision) {
43
+ findings.push({
44
+ id: "deploy/service-task-no-type",
45
+ category: "deploy",
46
+ severity: "error",
47
+ message: `"${el.name ?? el.id}" (businessRuleTask) has neither a zeebe:taskDefinition type nor a zeebe:calledDecision.`,
48
+ suggestion: "Set a decisionId or a job type.",
49
+ processId,
50
+ elementIds: [el.id],
51
+ });
52
+ }
53
+ }
54
+ if (el.type === "callActivity") {
55
+ const called = findExt(el, "zeebe:calledElement");
56
+ if (!called?.attributes.processId) {
57
+ findings.push({
58
+ id: "deploy/call-activity-no-process",
59
+ category: "deploy",
60
+ severity: "error",
61
+ message: `Call activity "${el.name ?? el.id}" has no zeebe:calledElement processId.`,
62
+ suggestion: "Set the process id to call.",
63
+ processId,
64
+ elementIds: [el.id],
65
+ });
66
+ }
67
+ }
68
+ if (el.type === "startEvent") {
69
+ const messageDef = el.eventDefinitions.find((d) => d.type === "message");
70
+ if (messageDef && !messageDef.messageRef) {
71
+ findings.push({
72
+ id: "deploy/message-start-no-name",
73
+ category: "deploy",
74
+ severity: "error",
75
+ message: `Message start event "${el.name ?? el.id}" has no message name.`,
76
+ suggestion: "Set a message name — Camunda 8 refuses to deploy an unnamed message reference.",
77
+ processId,
78
+ elementIds: [el.id],
79
+ });
80
+ }
81
+ }
82
+ if (el.type === "intermediateCatchEvent" ||
83
+ el.type === "boundaryEvent" ||
84
+ el.type === "receiveTask") {
85
+ const messageDef = el.type === "receiveTask"
86
+ ? undefined
87
+ : el.eventDefinitions.find((d) => d.type === "message");
88
+ const isMessageCatch = el.type === "receiveTask" ? el.messageRef !== undefined : messageDef !== undefined;
89
+ if (isMessageCatch) {
90
+ const subscription = findExt(el, "zeebe:subscription");
91
+ if (!subscription?.attributes.correlationKey) {
92
+ findings.push({
93
+ id: "deploy/message-catch-no-correlation",
94
+ category: "deploy",
95
+ severity: "error",
96
+ message: `Message catch "${el.name ?? el.id}" (${el.type}) has no zeebe:subscription correlationKey.`,
97
+ suggestion: "Set a correlation key — required for every message catch in Camunda 8.",
98
+ processId,
99
+ elementIds: [el.id],
100
+ });
101
+ }
102
+ }
103
+ }
104
+ if (resolveConnectorRequirements) {
105
+ const templateId = el.unknownAttributes["zeebe:modelerTemplate"];
106
+ if (templateId) {
107
+ const boundKeys = new Set();
108
+ const io = readZeebeIoMapping(el.extensionElements);
109
+ if (io) {
110
+ for (const i of io.inputs)
111
+ boundKeys.add(i.target);
112
+ for (const o of io.outputs)
113
+ boundKeys.add(o.target);
114
+ }
115
+ const headers = readZeebeTaskHeaders(el.extensionElements);
116
+ if (headers)
117
+ for (const h of headers.headers)
118
+ boundKeys.add(h.key);
119
+ const propsExt = findExt(el, "zeebe:properties");
120
+ if (propsExt) {
121
+ for (const child of propsExt.children ?? []) {
122
+ if (child.attributes.name)
123
+ boundKeys.add(child.attributes.name);
124
+ }
125
+ }
126
+ const missing = resolveConnectorRequirements(templateId, [...boundKeys]);
127
+ for (const key of missing) {
128
+ findings.push({
129
+ id: "connector/missing-required",
130
+ category: "connector",
131
+ severity: "error",
132
+ message: `"${el.name ?? el.id}" is missing required connector value "${key}" for template "${templateId}".`,
133
+ suggestion: `Set a value for "${key}".`,
134
+ processId,
135
+ elementIds: [el.id],
136
+ });
137
+ }
138
+ }
139
+ }
140
+ }
141
+ return findings;
142
+ }
143
+ //# sourceMappingURL=deploy.js.map
@@ -0,0 +1,12 @@
1
+ import type { BpmnProcess } from "../bpmn-model.js";
2
+ import type { OptimizationFinding } from "./types.js";
3
+ /**
4
+ * Parse-validates every FEEL-looking expression (leading "=") in the process:
5
+ * sequence-flow conditions, zeebe:input/output sources, script task
6
+ * expressions, and ad-hoc sub-process completion conditions/outputElement —
7
+ * including inside nested sub-processes. Unlike `feel.ts` (heuristic
8
+ * complexity scoring), this uses the real `@bpmnkit/feel` parser and reports
9
+ * genuine syntax errors, not style suggestions.
10
+ */
11
+ export declare function analyzeFeelSyntax(p: BpmnProcess): OptimizationFinding[];
12
+ //# sourceMappingURL=feel-syntax.d.ts.map