@mesh-tech/mesh-cli 0.36.0 → 0.37.0

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 (103) hide show
  1. package/assets/docs-preview.css +1599 -0
  2. package/dist/bin/mesh.auth-preflight-JFKL4Y4X.js +48 -0
  3. package/dist/bin/mesh.auth-preflight-JFKL4Y4X.js.map +6 -0
  4. package/dist/bin/mesh.capture-history-A4IAVVIR.js +38 -0
  5. package/dist/bin/mesh.capture-history-A4IAVVIR.js.map +6 -0
  6. package/dist/bin/mesh.chunk-5A5OFSYR.js +187 -0
  7. package/dist/bin/mesh.chunk-5A5OFSYR.js.map +6 -0
  8. package/dist/bin/mesh.chunk-7DMAFUBQ.js +7715 -0
  9. package/dist/bin/mesh.chunk-7DMAFUBQ.js.map +6 -0
  10. package/dist/bin/mesh.chunk-A4DGMGHF.js +303 -0
  11. package/dist/bin/mesh.chunk-A4DGMGHF.js.map +6 -0
  12. package/dist/bin/mesh.chunk-CQ2IC4OP.js +217 -0
  13. package/dist/bin/mesh.chunk-CQ2IC4OP.js.map +6 -0
  14. package/dist/bin/mesh.chunk-F4654EPX.js +383 -0
  15. package/dist/bin/mesh.chunk-F4654EPX.js.map +6 -0
  16. package/dist/bin/mesh.chunk-IQOBTL42.js +60 -0
  17. package/dist/bin/mesh.chunk-IQOBTL42.js.map +6 -0
  18. package/dist/bin/mesh.chunk-J6DVWKRW.js +647 -0
  19. package/dist/bin/mesh.chunk-J6DVWKRW.js.map +6 -0
  20. package/dist/bin/mesh.chunk-JB7FWYSP.js +1751 -0
  21. package/dist/bin/mesh.chunk-JB7FWYSP.js.map +6 -0
  22. package/dist/bin/mesh.chunk-MCBMHWXP.js +837 -0
  23. package/dist/bin/mesh.chunk-MCBMHWXP.js.map +6 -0
  24. package/dist/bin/mesh.chunk-OW5EMVG5.js +507 -0
  25. package/dist/bin/mesh.chunk-OW5EMVG5.js.map +6 -0
  26. package/dist/bin/mesh.chunk-R5VVTXG4.js +10092 -0
  27. package/dist/bin/mesh.chunk-R5VVTXG4.js.map +6 -0
  28. package/dist/bin/mesh.chunk-VKV6Z3UI.js +1146 -0
  29. package/dist/bin/mesh.chunk-VKV6Z3UI.js.map +6 -0
  30. package/dist/bin/mesh.chunk-WFFDF346.js +59 -0
  31. package/dist/bin/mesh.chunk-WFFDF346.js.map +6 -0
  32. package/dist/bin/mesh.chunk-WOJOTUFZ.js +835 -0
  33. package/dist/bin/mesh.chunk-WOJOTUFZ.js.map +6 -0
  34. package/dist/bin/mesh.cli-reference-H3TOPIET.js +158 -0
  35. package/dist/bin/mesh.cli-reference-H3TOPIET.js.map +6 -0
  36. package/dist/bin/mesh.convert-KK6ZP256.js +274 -0
  37. package/dist/bin/mesh.convert-KK6ZP256.js.map +6 -0
  38. package/dist/bin/mesh.create-app-A5F45PWZ.js +39 -0
  39. package/dist/bin/mesh.create-app-A5F45PWZ.js.map +6 -0
  40. package/dist/bin/mesh.dev-token-server-GYDYPXYH.js +61 -0
  41. package/dist/bin/mesh.dev-token-server-GYDYPXYH.js.map +6 -0
  42. package/dist/bin/mesh.js +20 -26418
  43. package/dist/bin/mesh.js.map +3 -3
  44. package/dist/bin/mesh.login-JNHPVMCT.js +91 -0
  45. package/dist/bin/mesh.login-JNHPVMCT.js.map +6 -0
  46. package/dist/bin/mesh.platform-setup-EB64EFRN.js +37 -0
  47. package/dist/bin/mesh.platform-setup-EB64EFRN.js.map +6 -0
  48. package/dist/bin/mesh.preview-3IP34TDO.js +2410 -0
  49. package/dist/bin/mesh.preview-3IP34TDO.js.map +6 -0
  50. package/dist/bin/mesh.program-DWOKHNMQ.js +23 -0
  51. package/dist/bin/mesh.program-DWOKHNMQ.js.map +6 -0
  52. package/dist/bin/mesh.registry-OXF57OQW.js +47 -0
  53. package/dist/bin/mesh.registry-OXF57OQW.js.map +6 -0
  54. package/dist/bin/mesh.registry-identity-2HSGQGJC.js +42 -0
  55. package/dist/bin/mesh.registry-identity-2HSGQGJC.js.map +6 -0
  56. package/dist/bin/mesh.skills-MNDSMBIE.js +23 -0
  57. package/dist/bin/mesh.skills-MNDSMBIE.js.map +6 -0
  58. package/dist/bin/mesh.src-5D5DFSLX.js +1302 -0
  59. package/dist/bin/mesh.src-5D5DFSLX.js.map +6 -0
  60. package/dist/bin/mesh.src-G7TJBLNJ.js +40 -0
  61. package/dist/bin/mesh.src-G7TJBLNJ.js.map +6 -0
  62. package/dist/bin/mesh.stack-EFTFAOCV.js +64 -0
  63. package/dist/bin/mesh.stack-EFTFAOCV.js.map +6 -0
  64. package/dist/bin/mesh.start-ARIIY6CV.js +454 -0
  65. package/dist/bin/mesh.start-ARIIY6CV.js.map +6 -0
  66. package/dist/bin/mesh.wizard-ROAQAGSF.js +500 -0
  67. package/dist/bin/mesh.wizard-ROAQAGSF.js.map +6 -0
  68. package/dist/build-info.json +2 -2
  69. package/dist/src/commands/app-check.d.ts +2 -1
  70. package/dist/src/commands/app-check.js +32 -0
  71. package/dist/src/commands/dev.d.ts +6 -0
  72. package/dist/src/commands/dev.js +152 -3
  73. package/dist/src/commands/docs.js +33 -0
  74. package/dist/src/commands/local/act-as-local.d.ts +5 -0
  75. package/dist/src/commands/local/act-as-local.js +41 -0
  76. package/dist/src/commands/local/auth-provision.d.ts +1 -0
  77. package/dist/src/commands/local/auth-provision.js +1 -0
  78. package/dist/src/commands/local/index.js +4 -0
  79. package/dist/src/commands/local/local-api-key.d.ts +44 -0
  80. package/dist/src/commands/local/local-api-key.js +115 -0
  81. package/dist/src/commands/local/local-spicedb.d.ts +95 -0
  82. package/dist/src/commands/local/local-spicedb.js +304 -0
  83. package/dist/src/commands/local/publish-docs.d.ts +81 -0
  84. package/dist/src/commands/local/publish-docs.js +328 -0
  85. package/dist/src/commands/local/seed-zitadel.js +2 -0
  86. package/dist/src/commands/temporal.js +191 -61
  87. package/dist/src/commands/temporal.js.map +3 -3
  88. package/dist/src/docs/convert.d.ts +35 -0
  89. package/dist/src/docs/convert.js +276 -0
  90. package/dist/src/docs/preview-source.d.ts +13 -0
  91. package/dist/src/docs/preview-source.js +58 -0
  92. package/dist/src/docs/preview.d.ts +32 -0
  93. package/dist/src/docs/preview.js +279 -0
  94. package/fragments/agents/AGENTS.md.hbs +27 -27
  95. package/fragments/base/README.md.hbs +5 -2
  96. package/fragments/base/index.ts.hbs +17 -30
  97. package/fragments/base/package.json.hbs +4 -1
  98. package/fragments/service/api/openapi.json.hbs +1 -1
  99. package/fragments/service/docs/docs.yaml.hbs +10 -0
  100. package/fragments/service/docs/pages/getting-started.md.hbs +6 -9
  101. package/package.json +19 -3
  102. package/skills/mesh/SKILL.md +2 -2
  103. package/stack/docker-compose.hub.yml +6 -0
@@ -0,0 +1,1302 @@
1
+ // packages/workflow-model/src/workflow-ir.ts
2
+ function computeHappyPath(nodes, edges) {
3
+ const startNode = nodes.find((n) => n.type === "start");
4
+ const endNodes = nodes.filter((n) => n.type === "end");
5
+ if (!startNode || endNodes.length === 0) return [];
6
+ const adjacency = /* @__PURE__ */ new Map();
7
+ for (const edge of edges) {
8
+ const list = adjacency.get(edge.from) ?? [];
9
+ list.push(edge);
10
+ adjacency.set(edge.from, list);
11
+ }
12
+ const waitNodeIds = new Set(nodes.filter((n) => n.type === "wait").map((n) => n.id));
13
+ function pathHasWait(startId) {
14
+ const visited = /* @__PURE__ */ new Set();
15
+ const queue = [startId];
16
+ for (let depth = 0; depth < 6 && queue.length > 0; depth++) {
17
+ const next = [];
18
+ for (const id of queue) {
19
+ if (waitNodeIds.has(id)) return true;
20
+ if (visited.has(id)) continue;
21
+ visited.add(id);
22
+ for (const e of adjacency.get(id) ?? []) {
23
+ if (!e.isExceptional && !e.isTimeout) next.push(e.to);
24
+ }
25
+ }
26
+ queue.length = 0;
27
+ queue.push(...next);
28
+ }
29
+ return false;
30
+ }
31
+ const successEndIds = new Set(
32
+ endNodes.filter((n) => n.outcome !== "failure").map((n) => n.id)
33
+ );
34
+ const failureEndIds = new Set(
35
+ endNodes.filter((n) => n.outcome === "failure").map((n) => n.id)
36
+ );
37
+ const allEndIds = new Set(endNodes.map((n) => n.id));
38
+ function bfs(targetIds, allowExceptional) {
39
+ const queue = [
40
+ { id: startNode.id, path: [startNode.id] }
41
+ ];
42
+ const visited = /* @__PURE__ */ new Set([startNode.id]);
43
+ while (queue.length > 0) {
44
+ const { id, path } = queue.shift();
45
+ if (targetIds.has(id)) return path;
46
+ const neighbors = adjacency.get(id) ?? [];
47
+ const hasMainEdge = neighbors.some((e) => e.isMainPath);
48
+ const candidates = hasMainEdge ? neighbors.filter((e) => e.isMainPath) : neighbors;
49
+ const sorted = [...candidates].sort((a, b) => {
50
+ const aErr = a.isExceptional || a.isTimeout;
51
+ const bErr = b.isExceptional || b.isTimeout;
52
+ if (aErr && !bErr) return 1;
53
+ if (!aErr && bErr) return -1;
54
+ const aWait = waitNodeIds.has(a.to) || pathHasWait(a.to);
55
+ const bWait = waitNodeIds.has(b.to) || pathHasWait(b.to);
56
+ if (aWait && !bWait) return 1;
57
+ if (!aWait && bWait) return -1;
58
+ return 0;
59
+ });
60
+ for (const neighbor of sorted) {
61
+ if (visited.has(neighbor.to)) continue;
62
+ if (!allowExceptional && (neighbor.isExceptional || neighbor.isTimeout || failureEndIds.has(neighbor.to)))
63
+ continue;
64
+ visited.add(neighbor.to);
65
+ queue.push({ id: neighbor.to, path: [...path, neighbor.to] });
66
+ }
67
+ }
68
+ return null;
69
+ }
70
+ return bfs(successEndIds, false) ?? bfs(allEndIds, false) ?? bfs(allEndIds, true) ?? [];
71
+ }
72
+ function isProcessArtifactProvenance(value) {
73
+ return typeof value === "object" && value !== null && value.source === "process-artifact";
74
+ }
75
+ function childGraphsOf(node) {
76
+ const graphs = [];
77
+ const withChild = node;
78
+ if (withChild.childGraph) graphs.push(withChild.childGraph);
79
+ const withBacking = node;
80
+ if (withBacking.backing?.backingGraph) graphs.push(withBacking.backing.backingGraph);
81
+ return graphs;
82
+ }
83
+ function walkProcessNodes(ir, scope = []) {
84
+ const out = [];
85
+ for (const node of ir.nodes) {
86
+ if (isProcessArtifactProvenance(node.provenance)) {
87
+ out.push({ nodeId: node.id, scope, node, provenance: node.provenance });
88
+ }
89
+ for (const child of childGraphsOf(node)) {
90
+ out.push(...walkProcessNodes(child, [...scope, node.id]));
91
+ }
92
+ }
93
+ return out;
94
+ }
95
+ function findProcessNodesByCommand(ir, command) {
96
+ return walkProcessNodes(ir).filter((m) => m.provenance.bind?.commands?.includes(command));
97
+ }
98
+ function findProcessNodesByElementId(ir, elementId, element) {
99
+ return walkProcessNodes(ir).filter(
100
+ (m) => m.provenance.elementId === elementId && (element === void 0 || m.provenance.element === element)
101
+ );
102
+ }
103
+ var WorkflowPatchError = class extends Error {
104
+ constructor(message) {
105
+ super(message);
106
+ this.name = "WorkflowPatchError";
107
+ }
108
+ };
109
+ function cloneWorkflow(workflow) {
110
+ return JSON.parse(JSON.stringify(workflow));
111
+ }
112
+ function cloneMaybeWorkflow(workflow) {
113
+ return workflow ? cloneWorkflow(workflow) : null;
114
+ }
115
+ function upsertAtIndex(items, item, index) {
116
+ const next = [...items];
117
+ if (index === void 0 || index < 0 || index >= next.length) {
118
+ next.push(item);
119
+ return next;
120
+ }
121
+ next.splice(index, 0, item);
122
+ return next;
123
+ }
124
+ function edgeMatches(edge, from, to) {
125
+ return edge.from === from && edge.to === to;
126
+ }
127
+ function ensureNodeExists(workflow, nodeId) {
128
+ if (!workflow.nodes.some((node) => node.id === nodeId)) {
129
+ throw new WorkflowPatchError(`Node not found: ${nodeId}`);
130
+ }
131
+ }
132
+ function ensureNoDuplicateNode(workflow, nodeId) {
133
+ if (workflow.nodes.some((node) => node.id === nodeId)) {
134
+ throw new WorkflowPatchError(`Duplicate node id: ${nodeId}`);
135
+ }
136
+ }
137
+ function isDuplicateEdge(workflow, edge) {
138
+ return workflow.edges.some(
139
+ (existing) => existing.from === edge.from && existing.to === edge.to && existing.fromExit === edge.fromExit
140
+ );
141
+ }
142
+ function sanitizeMainPath(workflow, mainPath) {
143
+ const nodeIds = new Set(workflow.nodes.map((node) => node.id));
144
+ const seen = /* @__PURE__ */ new Set();
145
+ const normalized = [];
146
+ for (const nodeId of mainPath) {
147
+ if (!nodeIds.has(nodeId)) {
148
+ throw new WorkflowPatchError(`Main path references unknown node: ${nodeId}`);
149
+ }
150
+ if (seen.has(nodeId)) {
151
+ continue;
152
+ }
153
+ seen.add(nodeId);
154
+ normalized.push(nodeId);
155
+ }
156
+ return normalized;
157
+ }
158
+ function getChildGraph(node) {
159
+ if ("childGraph" in node && node.childGraph) return node.childGraph;
160
+ if ("backing" in node && node.backing?.backingGraph)
161
+ return node.backing.backingGraph;
162
+ return null;
163
+ }
164
+ function setChildGraph(node, childGraph) {
165
+ if ("childGraph" in node) return { ...node, childGraph };
166
+ if ("backing" in node && node.backing?.backingGraph) {
167
+ return {
168
+ ...node,
169
+ backing: { ...node.backing, backingGraph: childGraph }
170
+ };
171
+ }
172
+ return { ...node, childGraph };
173
+ }
174
+ function applyScopedPatch(workflow, scope, patch) {
175
+ const [headId, ...rest] = scope;
176
+ const next = cloneWorkflow(workflow);
177
+ const nodeIndex = next.nodes.findIndex((n) => n.id === headId);
178
+ if (nodeIndex === -1) {
179
+ throw new WorkflowPatchError(`Scoped patch: node '${headId}' not found in current graph`);
180
+ }
181
+ const node = next.nodes[nodeIndex];
182
+ let childGraph = getChildGraph(node);
183
+ if (!childGraph) {
184
+ const canHoldChildGraph = node.type === "group" || node.type === "child_workflow";
185
+ if (!canHoldChildGraph) {
186
+ throw new WorkflowPatchError(
187
+ `Scoped patch: node '${headId}' (type '${node.type}') cannot hold a child graph`
188
+ );
189
+ }
190
+ childGraph = { name: "", description: "", nodes: [], edges: [], mainPath: [] };
191
+ }
192
+ const unscopedPatch = { ...patch, scope: rest.length > 0 ? rest : void 0 };
193
+ const updatedChild = applyWorkflowPatch(childGraph, unscopedPatch);
194
+ next.nodes[nodeIndex] = setChildGraph(node, updatedChild);
195
+ return next;
196
+ }
197
+ function wrapIntoGroup(workflow, nodeIds, groupId, label, groupType) {
198
+ if (nodeIds.length === 0) throw new WorkflowPatchError("group: nodeIds is empty");
199
+ const inSet = new Set(nodeIds);
200
+ if (workflow.nodes.some((n) => n.id === groupId)) {
201
+ throw new WorkflowPatchError(`group: id '${groupId}' already exists`);
202
+ }
203
+ for (const id of nodeIds) {
204
+ const node = workflow.nodes.find((n) => n.id === id);
205
+ if (!node) throw new WorkflowPatchError(`group: node '${id}' not found`);
206
+ if (node.type === "start" || node.type === "end") {
207
+ throw new WorkflowPatchError(`group: cannot wrap a '${node.type}' node ('${id}')`);
208
+ }
209
+ }
210
+ const internal = [];
211
+ const incoming = [];
212
+ const outgoing = [];
213
+ const external = [];
214
+ for (const e of workflow.edges) {
215
+ const f = inSet.has(e.from);
216
+ const t = inSet.has(e.to);
217
+ if (f && t) internal.push(e);
218
+ else if (!f && t) incoming.push(e);
219
+ else if (f && !t) outgoing.push(e);
220
+ else external.push(e);
221
+ }
222
+ const entryNodes = new Set(incoming.map((e) => e.to));
223
+ if (entryNodes.size === 0) {
224
+ const internalIn = new Set(internal.map((e) => e.to));
225
+ for (const id of nodeIds) if (!internalIn.has(id)) entryNodes.add(id);
226
+ }
227
+ const exitNodes = new Set(outgoing.map((e) => e.from));
228
+ if (exitNodes.size === 0) {
229
+ const internalOut = new Set(internal.map((e) => e.from));
230
+ for (const id of nodeIds) if (!internalOut.has(id)) exitNodes.add(id);
231
+ }
232
+ if (entryNodes.size !== 1) {
233
+ throw new WorkflowPatchError(
234
+ `group: the selection has ${entryNodes.size} entry points; wrap a single-entry region`
235
+ );
236
+ }
237
+ const entry = [...entryNodes][0];
238
+ const startId = `${groupId}__start`;
239
+ const groupedNodes = workflow.nodes.filter((n) => inSet.has(n.id));
240
+ const inParentMain = workflow.mainPath.filter((id) => inSet.has(id));
241
+ let childGraph;
242
+ let parentExitEdges;
243
+ const pulledIds = /* @__PURE__ */ new Set();
244
+ if (outgoing.length === 0) {
245
+ const endId = `${groupId}__end`;
246
+ const childNodes = [
247
+ { id: startId, type: "start", label: "Start" },
248
+ ...groupedNodes,
249
+ { id: endId, type: "end", label: "Done", outcome: "success" }
250
+ ];
251
+ const childEdges = [
252
+ { from: startId, to: entry, isMainPath: true },
253
+ ...internal,
254
+ ...[...exitNodes].map((from) => ({ from, to: endId, isMainPath: true }))
255
+ ];
256
+ childGraph = {
257
+ name: label,
258
+ description: "",
259
+ nodes: childNodes,
260
+ edges: childEdges,
261
+ mainPath: [startId, ...inParentMain.length ? inParentMain : nodeIds, endId]
262
+ };
263
+ parentExitEdges = [];
264
+ } else {
265
+ const byTarget = /* @__PURE__ */ new Map();
266
+ for (const e of outgoing) {
267
+ const arr = byTarget.get(e.to);
268
+ if (arr) arr.push(e);
269
+ else byTarget.set(e.to, [e]);
270
+ }
271
+ const isSoleRefTerminal = (target) => {
272
+ const node = workflow.nodes.find((n) => n.id === target);
273
+ if (!node || node.type !== "end") return false;
274
+ return workflow.edges.every((e) => e.to !== target || inSet.has(e.from));
275
+ };
276
+ const toChildEdge = (e, to) => ({
277
+ from: e.from,
278
+ to,
279
+ ...e.label ? { label: e.label } : {},
280
+ ...e.isMainPath ? { isMainPath: true } : {},
281
+ ...e.isExceptional ? { isExceptional: true } : {},
282
+ ...e.isTimeout ? { isTimeout: true } : {}
283
+ });
284
+ const extraChildNodes = [];
285
+ const exitChildEdges = [];
286
+ parentExitEdges = [];
287
+ let exitN = 0;
288
+ for (const [target, edges] of byTarget) {
289
+ if (isSoleRefTerminal(target)) {
290
+ const endNode = workflow.nodes.find((n) => n.id === target);
291
+ extraChildNodes.push(endNode);
292
+ pulledIds.add(target);
293
+ for (const e of edges) exitChildEdges.push(toChildEdge(e, target));
294
+ } else {
295
+ exitN += 1;
296
+ const exitId = `${groupId}__exit_${exitN}`;
297
+ const first = edges[0];
298
+ const anyMain = edges.some((e) => e.isMainPath);
299
+ const allExceptional = edges.every((e) => e.isExceptional || e.isTimeout);
300
+ const exitLabel = first.label ? first.label.charAt(0).toUpperCase() + first.label.slice(1) : workflow.nodes.find((n) => n.id === target)?.label ?? `Exit ${exitN}`;
301
+ extraChildNodes.push({
302
+ id: exitId,
303
+ type: "end",
304
+ label: exitLabel,
305
+ outcome: allExceptional ? "failure" : "success"
306
+ });
307
+ for (const e of edges) exitChildEdges.push(toChildEdge(e, exitId));
308
+ parentExitEdges.push({
309
+ from: groupId,
310
+ to: target,
311
+ fromExit: exitId,
312
+ ...first.label ? { label: first.label } : {},
313
+ ...anyMain ? { isMainPath: true } : {},
314
+ ...!anyMain && edges.every((e) => e.isExceptional) ? { isExceptional: true } : {},
315
+ ...!anyMain && edges.every((e) => e.isTimeout) ? { isTimeout: true } : {}
316
+ });
317
+ }
318
+ }
319
+ const successExit = extraChildNodes.find(
320
+ (n) => n.type === "end" && !pulledIds.has(n.id) && n.outcome === "success"
321
+ );
322
+ const mainEdgeKeys = /* @__PURE__ */ new Set();
323
+ const edgeKey = (a, b) => `${a}\0${b}`;
324
+ let mainBody = inParentMain.length ? inParentMain : nodeIds;
325
+ let mainTail = [];
326
+ if (successExit) {
327
+ const exitSources = new Set(
328
+ exitChildEdges.filter((e) => e.to === successExit.id).map((e) => e.from)
329
+ );
330
+ const adj = /* @__PURE__ */ new Map();
331
+ for (const e of internal) {
332
+ if (e.isExceptional || e.isTimeout) continue;
333
+ const arr = adj.get(e.from);
334
+ if (arr) arr.push(e.to);
335
+ else adj.set(e.from, [e.to]);
336
+ }
337
+ const prev = /* @__PURE__ */ new Map();
338
+ const visited = /* @__PURE__ */ new Set([entry]);
339
+ const queue = [entry];
340
+ let found;
341
+ while (queue.length > 0) {
342
+ const cur = queue.shift();
343
+ if (exitSources.has(cur)) {
344
+ found = cur;
345
+ break;
346
+ }
347
+ for (const nx of adj.get(cur) ?? []) {
348
+ if (!visited.has(nx)) {
349
+ visited.add(nx);
350
+ prev.set(nx, cur);
351
+ queue.push(nx);
352
+ }
353
+ }
354
+ }
355
+ if (found) {
356
+ const path = [];
357
+ for (let c = found; c !== void 0; c = prev.get(c)) path.unshift(c);
358
+ for (let i = 0; i < path.length - 1; i++) mainEdgeKeys.add(edgeKey(path[i], path[i + 1]));
359
+ mainEdgeKeys.add(edgeKey(found, successExit.id));
360
+ mainBody = path;
361
+ mainTail = [successExit.id];
362
+ }
363
+ }
364
+ const markMain = (e) => mainEdgeKeys.has(edgeKey(e.from, e.to)) ? { ...e, isMainPath: true } : { ...e };
365
+ childGraph = {
366
+ name: label,
367
+ description: "",
368
+ nodes: [
369
+ { id: startId, type: "start", label: "Start" },
370
+ ...groupedNodes,
371
+ ...extraChildNodes
372
+ ],
373
+ edges: [
374
+ { from: startId, to: entry, isMainPath: true },
375
+ ...internal.map(markMain),
376
+ ...exitChildEdges.map(markMain)
377
+ ],
378
+ mainPath: [startId, ...mainBody, ...mainTail]
379
+ };
380
+ }
381
+ const groupNode = {
382
+ id: groupId,
383
+ type: "group",
384
+ groupType,
385
+ label,
386
+ childGraph
387
+ };
388
+ const newNodes = [];
389
+ let placed = false;
390
+ for (const n of workflow.nodes) {
391
+ if (inSet.has(n.id)) {
392
+ if (!placed) {
393
+ newNodes.push(groupNode);
394
+ placed = true;
395
+ }
396
+ } else if (!pulledIds.has(n.id)) {
397
+ newNodes.push(n);
398
+ }
399
+ }
400
+ const seen = /* @__PURE__ */ new Set();
401
+ const newEdges = [];
402
+ const pushEdge = (e) => {
403
+ const key = `${e.from} ${e.to} ${e.fromExit ?? ""} ${e.label ?? ""}`;
404
+ if (seen.has(key)) return;
405
+ seen.add(key);
406
+ newEdges.push(e);
407
+ };
408
+ for (const e of external) pushEdge(e);
409
+ for (const e of incoming) pushEdge({ ...e, to: groupId });
410
+ for (const e of parentExitEdges) pushEdge(e);
411
+ const newMain = [];
412
+ let mainInserted = false;
413
+ for (const id of workflow.mainPath) {
414
+ if (inSet.has(id)) {
415
+ if (!mainInserted) {
416
+ newMain.push(groupId);
417
+ mainInserted = true;
418
+ }
419
+ } else {
420
+ newMain.push(id);
421
+ }
422
+ }
423
+ return { ...workflow, nodes: newNodes, edges: newEdges, mainPath: newMain };
424
+ }
425
+ function emptyWorkflowIR(name = "untitled") {
426
+ return { name, description: "", nodes: [], edges: [], mainPath: [] };
427
+ }
428
+ function applyWorkflowPatch(workflow, patch) {
429
+ if (patch.scope && patch.scope.length > 0) {
430
+ return applyScopedPatch(workflow, patch.scope, patch);
431
+ }
432
+ if (patch.op === "batch") {
433
+ return patch.patches.reduce(
434
+ (current, operation) => applyWorkflowPatch(current, operation),
435
+ workflow
436
+ );
437
+ }
438
+ if (patch.op === "replace") {
439
+ return cloneWorkflow(patch.workflow);
440
+ }
441
+ const next = cloneWorkflow(workflow);
442
+ switch (patch.op) {
443
+ case "addNode": {
444
+ ensureNoDuplicateNode(next, patch.node.id);
445
+ next.nodes = upsertAtIndex(next.nodes, patch.node, patch.index);
446
+ return next;
447
+ }
448
+ case "updateNode": {
449
+ const index = next.nodes.findIndex((node) => node.id === patch.nodeId);
450
+ if (index === -1) {
451
+ throw new WorkflowPatchError(`Node not found: ${patch.nodeId}`);
452
+ }
453
+ const existing = next.nodes.at(index);
454
+ if (!existing) {
455
+ throw new WorkflowPatchError(`Node not found at index: ${index}`);
456
+ }
457
+ next.nodes[index] = {
458
+ ...existing,
459
+ ...patch.patch,
460
+ id: existing.id
461
+ };
462
+ return next;
463
+ }
464
+ case "removeNode": {
465
+ ensureNodeExists(next, patch.nodeId);
466
+ next.nodes = next.nodes.filter((node) => node.id !== patch.nodeId);
467
+ next.mainPath = next.mainPath.filter((nodeId) => nodeId !== patch.nodeId);
468
+ const shouldRemoveEdges = patch.removeAttachedEdges ?? true;
469
+ if (shouldRemoveEdges) {
470
+ next.edges = next.edges.filter(
471
+ (edge) => edge.from !== patch.nodeId && edge.to !== patch.nodeId
472
+ );
473
+ }
474
+ return next;
475
+ }
476
+ case "addEdge": {
477
+ ensureNodeExists(next, patch.from);
478
+ ensureNodeExists(next, patch.to);
479
+ const edge = {
480
+ from: patch.from,
481
+ to: patch.to,
482
+ ...patch.fromExit !== void 0 ? { fromExit: patch.fromExit } : {},
483
+ ...patch.label !== void 0 ? { label: patch.label } : {},
484
+ ...patch.isMainPath !== void 0 ? { isMainPath: patch.isMainPath } : {},
485
+ ...patch.isExceptional !== void 0 ? { isExceptional: patch.isExceptional } : {},
486
+ ...patch.isTimeout !== void 0 ? { isTimeout: patch.isTimeout } : {}
487
+ };
488
+ if (isDuplicateEdge(next, edge)) {
489
+ return next;
490
+ }
491
+ next.edges = upsertAtIndex(next.edges, edge, patch.index);
492
+ return next;
493
+ }
494
+ case "updateEdge": {
495
+ const index = next.edges.findIndex((edge) => edgeMatches(edge, patch.from, patch.to));
496
+ if (index === -1) {
497
+ throw new WorkflowPatchError(`Edge not found: ${patch.from} -> ${patch.to}`);
498
+ }
499
+ const existing = next.edges.at(index);
500
+ if (!existing) {
501
+ throw new WorkflowPatchError(`Edge not found at index: ${index}`);
502
+ }
503
+ next.edges[index] = {
504
+ ...existing,
505
+ ...patch.patch,
506
+ from: patch.from,
507
+ to: patch.to
508
+ };
509
+ return next;
510
+ }
511
+ case "removeEdge": {
512
+ const initialLength = next.edges.length;
513
+ next.edges = next.edges.filter((edge) => !edgeMatches(edge, patch.from, patch.to));
514
+ if (next.edges.length === initialLength) {
515
+ throw new WorkflowPatchError(`Edge not found: ${patch.from} -> ${patch.to}`);
516
+ }
517
+ return next;
518
+ }
519
+ case "group": {
520
+ return wrapIntoGroup(
521
+ next,
522
+ patch.nodeIds,
523
+ patch.groupId,
524
+ patch.label,
525
+ patch.groupType ?? "function"
526
+ );
527
+ }
528
+ case "setMainPath": {
529
+ next.mainPath = sanitizeMainPath(next, patch.mainPath);
530
+ return next;
531
+ }
532
+ case "setUiSpecs": {
533
+ next.uiSpecs = { ...next.uiSpecs, ...patch.uiSpecs };
534
+ return next;
535
+ }
536
+ case "setMeta": {
537
+ if (patch.name !== void 0) next.name = patch.name;
538
+ if (patch.description !== void 0) next.description = patch.description;
539
+ if (patch.signals !== void 0) next.signals = patch.signals;
540
+ if (patch.changes !== void 0) next.changes = patch.changes;
541
+ return next;
542
+ }
543
+ default:
544
+ return next;
545
+ }
546
+ }
547
+ function applyWorkflowPatches(workflow, patches) {
548
+ return patches.reduce((current, patch) => applyWorkflowPatch(current, patch), workflow);
549
+ }
550
+ function validateWorkflowPatches(ir, patches) {
551
+ const errors = [];
552
+ let clone = cloneWorkflow(ir);
553
+ patches.forEach((patch, index) => {
554
+ try {
555
+ clone = applyWorkflowPatch(clone, patch);
556
+ } catch (err) {
557
+ errors.push({
558
+ index,
559
+ op: patch.op ?? "?",
560
+ message: err instanceof Error ? err.message : String(err)
561
+ });
562
+ }
563
+ });
564
+ return errors;
565
+ }
566
+ var WorkflowPatchStream = class {
567
+ currentWorkflow;
568
+ undoStack = [];
569
+ redoStack = [];
570
+ constructor(initialWorkflow = null) {
571
+ this.currentWorkflow = cloneMaybeWorkflow(initialWorkflow);
572
+ }
573
+ get current() {
574
+ return cloneMaybeWorkflow(this.currentWorkflow);
575
+ }
576
+ get canUndo() {
577
+ return this.undoStack.length > 0;
578
+ }
579
+ get canRedo() {
580
+ return this.redoStack.length > 0;
581
+ }
582
+ reset(workflow = null) {
583
+ this.currentWorkflow = cloneMaybeWorkflow(workflow);
584
+ this.undoStack = [];
585
+ this.redoStack = [];
586
+ return this.current;
587
+ }
588
+ apply(patchOrPatches) {
589
+ const patches = Array.isArray(patchOrPatches) ? patchOrPatches : [patchOrPatches];
590
+ if (patches.length === 0) {
591
+ if (!this.currentWorkflow) {
592
+ throw new WorkflowPatchError("Cannot apply empty patch list without an initial workflow");
593
+ }
594
+ return cloneWorkflow(this.currentWorkflow);
595
+ }
596
+ const previous = cloneMaybeWorkflow(this.currentWorkflow);
597
+ let next = cloneMaybeWorkflow(this.currentWorkflow);
598
+ for (const patch of patches) {
599
+ if (!next) {
600
+ if (patch.op !== "replace") {
601
+ throw new WorkflowPatchError(
602
+ "Patch stream is empty. First patch must be a replace operation."
603
+ );
604
+ }
605
+ next = cloneWorkflow(patch.workflow);
606
+ continue;
607
+ }
608
+ next = applyWorkflowPatch(next, patch);
609
+ }
610
+ if (!next) {
611
+ throw new WorkflowPatchError("Patch application produced an empty workflow");
612
+ }
613
+ this.undoStack.push(previous);
614
+ this.redoStack = [];
615
+ this.currentWorkflow = next;
616
+ return cloneWorkflow(next);
617
+ }
618
+ undo() {
619
+ if (!this.canUndo) {
620
+ return this.current;
621
+ }
622
+ const previous = this.undoStack.pop() ?? null;
623
+ this.redoStack.push(cloneMaybeWorkflow(this.currentWorkflow));
624
+ this.currentWorkflow = cloneMaybeWorkflow(previous);
625
+ return this.current;
626
+ }
627
+ redo() {
628
+ if (!this.canRedo) {
629
+ return this.current;
630
+ }
631
+ const next = this.redoStack.pop() ?? null;
632
+ this.undoStack.push(cloneMaybeWorkflow(this.currentWorkflow));
633
+ this.currentWorkflow = cloneMaybeWorkflow(next);
634
+ return this.current;
635
+ }
636
+ };
637
+
638
+ // packages/workflow-model/src/workflow-ir-validate.ts
639
+ function outgoingEdges(edges, nodeId) {
640
+ return edges.filter((e) => e.from === nodeId);
641
+ }
642
+ function incomingEdges(edges, nodeId) {
643
+ return edges.filter((e) => e.to === nodeId);
644
+ }
645
+ function hasFailureEnd(workflow) {
646
+ return workflow.nodes.some(
647
+ (n) => n.type === "end" && n.outcome === "failure"
648
+ );
649
+ }
650
+ function hasExceptionalEdge(edges, nodeId) {
651
+ return edges.some((e) => e.from === nodeId && e.isExceptional);
652
+ }
653
+ function validateWorkflowIR(workflow, options) {
654
+ const maxDepth = options?.maxDepth ?? 10;
655
+ const diagnostics = [];
656
+ collectDiagnostics(workflow, diagnostics, 0, maxDepth);
657
+ const byNodeId = /* @__PURE__ */ new Map();
658
+ for (const d of diagnostics) {
659
+ const existing = byNodeId.get(d.nodeId);
660
+ if (existing) {
661
+ existing.push(d);
662
+ } else {
663
+ byNodeId.set(d.nodeId, [d]);
664
+ }
665
+ }
666
+ return { diagnostics, byNodeId };
667
+ }
668
+ function collectDiagnostics(workflow, out, depth, maxDepth) {
669
+ if (depth > maxDepth) return;
670
+ const { nodes, edges } = workflow;
671
+ const nodeMap = /* @__PURE__ */ new Map();
672
+ for (const node of nodes) {
673
+ nodeMap.set(node.id, node);
674
+ }
675
+ for (const node of nodes) {
676
+ const outEdges = outgoingEdges(edges, node.id);
677
+ const inEdges = incomingEdges(edges, node.id);
678
+ if (node.type === "wait" && node.timeout) {
679
+ if (!outEdges.some((e) => e.isTimeout)) {
680
+ out.push({
681
+ code: "WAIT_NO_TIMEOUT_EDGE",
682
+ severity: "warning",
683
+ nodeId: node.id,
684
+ message: `Wait node "${node.label}" has timeout (${node.timeout}) but no timeout edge.`
685
+ });
686
+ }
687
+ }
688
+ if (node.type === "decision" && outEdges.length < 2) {
689
+ out.push({
690
+ code: "DECISION_DEAD_END",
691
+ severity: "warning",
692
+ nodeId: node.id,
693
+ message: `Decision node "${node.label}" has ${outEdges.length} outgoing edge(s); expected at least 2.`
694
+ });
695
+ }
696
+ if (node.type === "child_workflow" && node.childGraph) {
697
+ if (hasFailureEnd(node.childGraph) && !hasExceptionalEdge(edges, node.id)) {
698
+ out.push({
699
+ code: "CHILD_FAILURE_UNHANDLED",
700
+ severity: "warning",
701
+ nodeId: node.id,
702
+ message: `Child workflow "${node.label}" can fail but parent has no error edge.`
703
+ });
704
+ }
705
+ collectDiagnostics(node.childGraph, out, depth + 1, maxDepth);
706
+ }
707
+ if (node.type === "nexus_operation" && node.backing?.backingGraph) {
708
+ if (hasFailureEnd(node.backing.backingGraph) && !hasExceptionalEdge(edges, node.id)) {
709
+ out.push({
710
+ code: "NEXUS_FAILURE_UNHANDLED",
711
+ severity: "warning",
712
+ nodeId: node.id,
713
+ message: `Nexus operation "${node.label}" backing graph can fail but parent has no error edge.`
714
+ });
715
+ }
716
+ collectDiagnostics(node.backing.backingGraph, out, depth + 1, maxDepth);
717
+ }
718
+ if (node.type !== "start" && inEdges.length === 0 && edges.length > 0) {
719
+ out.push({
720
+ code: "UNREACHABLE_NODE",
721
+ severity: "error",
722
+ nodeId: node.id,
723
+ message: `Node "${node.label}" has no incoming edges and is unreachable.`
724
+ });
725
+ }
726
+ if (node.type !== "end" && outEdges.length === 0 && edges.length > 0) {
727
+ out.push({
728
+ code: "TERMINAL_NON_END",
729
+ severity: "error",
730
+ nodeId: node.id,
731
+ message: `Non-terminal node "${node.label}" has no outgoing edges (dead end).`
732
+ });
733
+ }
734
+ }
735
+ for (const edge of edges) {
736
+ if (edge.isTimeout) {
737
+ const sourceNode = nodeMap.get(edge.from);
738
+ if (sourceNode && sourceNode.type !== "wait") {
739
+ out.push({
740
+ code: "TIMEOUT_ON_NON_WAIT",
741
+ severity: "warning",
742
+ nodeId: edge.from,
743
+ message: `Edge from "${sourceNode.label}" to "${nodeMap.get(edge.to)?.label ?? edge.to}" has isTimeout but source is a ${sourceNode.type} node, not a wait node.`
744
+ });
745
+ }
746
+ }
747
+ }
748
+ }
749
+
750
+ // packages/workflow-model/src/apply-preview-patch.ts
751
+ function applyPreviewPatch(ir, patch) {
752
+ try {
753
+ return applyWorkflowPatch(ir, patch);
754
+ } catch {
755
+ return ir;
756
+ }
757
+ }
758
+
759
+ // packages/workflow-model/src/change-counts.ts
760
+ var zero = () => ({ extend: 0, add: 0, remove: 0, rewire: 0, total: 0 });
761
+ function countChanges(changes) {
762
+ if (!changes) return null;
763
+ const counts = {
764
+ asBuilt: zero(),
765
+ asPractised: zero(),
766
+ total: changes.length,
767
+ newSites: 0
768
+ };
769
+ const sites = /* @__PURE__ */ new Set();
770
+ for (const change of changes) {
771
+ const face = change.face === "as-practised" ? counts.asPractised : counts.asBuilt;
772
+ face[change.kind] += 1;
773
+ face.total += 1;
774
+ if (change.kind === "add") sites.add(change.site);
775
+ }
776
+ counts.newSites = sites.size;
777
+ return counts;
778
+ }
779
+ function describeChanges(counts) {
780
+ if (!counts) return "no change list yet \u2014 nothing has counted what this proposal changes";
781
+ if (counts.total === 0) return "no changes \u2014 this proposal is the captures, unaltered";
782
+ const surface = counts.newSites === 0 ? "no new places for work to land" : `${counts.newSites} new place${counts.newSites === 1 ? "" : "s"} for work to land`;
783
+ const faces = [];
784
+ if (counts.asBuilt.total > 0) {
785
+ faces.push(`${counts.asBuilt.total} to what the system does`);
786
+ }
787
+ if (counts.asPractised.total > 0) {
788
+ faces.push(`${counts.asPractised.total} to what people do`);
789
+ }
790
+ return `${surface} \xB7 ${faces.join(", ")}`;
791
+ }
792
+
793
+ // packages/workflow-model/src/process-artifact.ts
794
+ import { z } from "zod";
795
+ var CURRENT_PROCESS_VERSION = 2;
796
+ var SUPPORTED_PROCESS_VERSIONS = [1, 2];
797
+ var UNVERSIONED_REVISION = "unversioned";
798
+ var LEGACY_V1_ACTOR_CATEGORY = {
799
+ customer: "human",
800
+ banker: "approval",
801
+ system: "data"
802
+ };
803
+ var actorSchema = z.object({
804
+ label: z.string(),
805
+ category: z.string().min(1).optional(),
806
+ selector: z.string().min(1).optional(),
807
+ cardinality: z.enum(["one", "many"]).optional()
808
+ }).strict();
809
+ var selectorSchema = z.object({
810
+ label: z.string(),
811
+ description: z.string().optional()
812
+ }).strict();
813
+ var substepBindSchema = z.object({
814
+ commands: z.array(z.string()).optional(),
815
+ activities: z.array(z.string()).optional()
816
+ }).strict().superRefine((bind, ctx) => {
817
+ if (!bind.commands?.length && !bind.activities?.length) {
818
+ ctx.addIssue({
819
+ code: "custom",
820
+ message: "bind must include at least one of commands or activities",
821
+ params: { code: "EMPTY_SUBSTEP_BIND" }
822
+ });
823
+ }
824
+ });
825
+ var substepSchema = z.object({
826
+ id: z.string(),
827
+ label: z.string(),
828
+ actor: z.string().optional(),
829
+ /**
830
+ * Overrides the actor's own `selector` for this substep only — the case
831
+ * where one cast entry is resolved differently at one step (a handoff
832
+ * target, an escalation pool). Must be declared in `process.selectors`.
833
+ */
834
+ selector: z.string().min(1).optional(),
835
+ bind: substepBindSchema
836
+ }).strict();
837
+ var outcomeTriggerSchema = z.union([
838
+ z.object({ predicate: z.string().min(1) }).strict(),
839
+ z.object({ timeoutOf: z.string().min(1) }).strict(),
840
+ z.object({ command: z.string().min(1) }).strict()
841
+ ]);
842
+ var outcomeWhenSchema = z.union([
843
+ outcomeTriggerSchema,
844
+ z.array(outcomeTriggerSchema).superRefine((triggers, ctx) => {
845
+ if (triggers.length === 0) {
846
+ ctx.addIssue({
847
+ code: "custom",
848
+ message: "when must declare at least one trigger",
849
+ params: { code: "EMPTY_OUTCOME_TRIGGER" }
850
+ });
851
+ }
852
+ })
853
+ ]);
854
+ var outcomeSchema = z.object({
855
+ id: z.string(),
856
+ label: z.string(),
857
+ kind: z.enum(["success", "failure"]),
858
+ when: outcomeWhenSchema,
859
+ description: z.string().optional()
860
+ }).strict();
861
+ var stageCompleteSchema = z.object({
862
+ predicate: z.string()
863
+ }).strict();
864
+ var stageSchema = z.object({
865
+ id: z.string(),
866
+ label: z.string(),
867
+ description: z.string().optional(),
868
+ actor: z.string().optional(),
869
+ substeps: z.array(substepSchema).optional(),
870
+ complete: stageCompleteSchema,
871
+ pointOfNoReturn: z.literal(true).optional()
872
+ }).strict();
873
+ var processBlockSchema = z.object({
874
+ version: z.number(),
875
+ /**
876
+ * Pinned identity of this authored content. Opaque to the platform —
877
+ * a date, a semver, a content hash, a monotonic counter all work. What
878
+ * matters is that changing the process changes it, so an execution can
879
+ * be pinned to the revision it started under. Required from V2; a V1
880
+ * artifact is upgraded with {@link UNVERSIONED_REVISION} and a warning.
881
+ */
882
+ revision: z.string().min(1).optional(),
883
+ workflowType: z.string(),
884
+ actors: z.record(z.string(), actorSchema),
885
+ /** Named selector registry — every `selector` reference resolves here. */
886
+ selectors: z.record(z.string(), selectorSchema).optional(),
887
+ stages: z.array(stageSchema),
888
+ outcomes: z.array(outcomeSchema),
889
+ internalCommands: z.array(z.string()).optional()
890
+ }).strict().superRefine((process, ctx) => {
891
+ if (!SUPPORTED_PROCESS_VERSIONS.includes(process.version)) {
892
+ ctx.addIssue({
893
+ code: "custom",
894
+ path: ["version"],
895
+ message: `Unsupported process.version ${JSON.stringify(process.version)}; this parser understands ${SUPPORTED_PROCESS_VERSIONS.join(" and ")}. Upgrade the parser or downgrade the artifact.`,
896
+ params: { code: "UNSUPPORTED_VERSION" }
897
+ });
898
+ }
899
+ if (process.version >= CURRENT_PROCESS_VERSION && process.revision === void 0) {
900
+ ctx.addIssue({
901
+ code: "custom",
902
+ path: ["revision"],
903
+ message: `process.revision is required from version ${CURRENT_PROCESS_VERSION}. Give this authored content a pinned identity (a date, a semver, or a content hash) so an execution can name the revision it is running.`,
904
+ params: { code: "MISSING_PROCESS_REVISION" }
905
+ });
906
+ }
907
+ const declaredSelectors = new Set(Object.keys(process.selectors ?? {}));
908
+ const checkActor = (actorId, path) => {
909
+ if (actorId === void 0) return;
910
+ if (!Object.prototype.hasOwnProperty.call(process.actors, actorId)) {
911
+ ctx.addIssue({
912
+ code: "custom",
913
+ path,
914
+ message: `Actor "${actorId}" is referenced at ${z.core.toDotPath(path)} but not declared in process.actors. Declare the actor (label, and optionally category/selector) or fix the reference.`,
915
+ params: { code: "UNDECLARED_ACTOR" }
916
+ });
917
+ }
918
+ };
919
+ const checkSelector = (selector, path) => {
920
+ if (selector === void 0) return;
921
+ if (!declaredSelectors.has(selector)) {
922
+ ctx.addIssue({
923
+ code: "custom",
924
+ path,
925
+ message: `Selector "${selector}" is referenced at ${z.core.toDotPath(path)} but not declared in process.selectors. Declare the selector or fix the reference.`,
926
+ params: { code: "UNDECLARED_SELECTOR" }
927
+ });
928
+ }
929
+ };
930
+ for (const [actorId, actor] of Object.entries(process.actors)) {
931
+ checkSelector(actor.selector, ["actors", actorId, "selector"]);
932
+ }
933
+ const SAME_KIND_CODE = {
934
+ stage: "DUPLICATE_STAGE_ID",
935
+ substep: "DUPLICATE_SUBSTEP_ID",
936
+ outcome: "DUPLICATE_OUTCOME_ID"
937
+ };
938
+ const idFirstSeenAt = /* @__PURE__ */ new Map();
939
+ const checkId = (id, kind, path) => {
940
+ const first = idFirstSeenAt.get(id);
941
+ if (first) {
942
+ const kindLabel = kind === first.kind ? kind : "process";
943
+ ctx.addIssue({
944
+ code: "custom",
945
+ path,
946
+ message: `Duplicate ${kindLabel} id "${id}": ${kind} at ${z.core.toDotPath(path)} collides with ${first.kind} at ${z.core.toDotPath(first.path)}.`,
947
+ params: {
948
+ code: kind === first.kind ? SAME_KIND_CODE[kind] : "DUPLICATE_PROCESS_ID"
949
+ }
950
+ });
951
+ } else {
952
+ idFirstSeenAt.set(id, { kind, path });
953
+ }
954
+ };
955
+ process.stages.forEach((stage, stageIndex) => {
956
+ checkId(stage.id, "stage", ["stages", stageIndex, "id"]);
957
+ checkActor(stage.actor, ["stages", stageIndex, "actor"]);
958
+ stage.substeps?.forEach((substep, substepIndex) => {
959
+ checkId(substep.id, "substep", ["stages", stageIndex, "substeps", substepIndex, "id"]);
960
+ checkActor(substep.actor, ["stages", stageIndex, "substeps", substepIndex, "actor"]);
961
+ checkSelector(substep.selector, [
962
+ "stages",
963
+ stageIndex,
964
+ "substeps",
965
+ substepIndex,
966
+ "selector"
967
+ ]);
968
+ });
969
+ });
970
+ process.outcomes.forEach((outcome, outcomeIndex) => {
971
+ checkId(outcome.id, "outcome", ["outcomes", outcomeIndex, "id"]);
972
+ });
973
+ });
974
+ var processArtifactSchema = z.object({
975
+ name: z.string(),
976
+ description: z.string(),
977
+ phase: z.enum(["draft", "workflow_review"]).optional(),
978
+ process: processBlockSchema
979
+ }).passthrough();
980
+ function outcomeTriggers(outcome) {
981
+ const raw = Array.isArray(outcome.when) ? outcome.when : [outcome.when];
982
+ return raw.map((trigger) => {
983
+ if ("predicate" in trigger) return { kind: "predicate", name: trigger.predicate };
984
+ if ("timeoutOf" in trigger) return { kind: "timeout", name: trigger.timeoutOf };
985
+ return { kind: "command", name: trigger.command };
986
+ });
987
+ }
988
+ function resolveSubstepActor(artifact, stage, substep) {
989
+ const actorId = substep.actor ?? stage.actor;
990
+ if (!actorId) return void 0;
991
+ const actor = artifact.process.actors[actorId];
992
+ if (!actor) return void 0;
993
+ return {
994
+ id: actorId,
995
+ label: actor.label,
996
+ ...actor.category ? { category: actor.category } : {},
997
+ ...substep.selector ?? actor.selector ? { selector: substep.selector ?? actor.selector } : {},
998
+ ...actor.cardinality ? { cardinality: actor.cardinality } : {}
999
+ };
1000
+ }
1001
+ function resolveActorRef(artifact, actorId) {
1002
+ const actor = artifact.process.actors[actorId];
1003
+ if (!actor) return void 0;
1004
+ return {
1005
+ id: actorId,
1006
+ label: actor.label,
1007
+ ...actor.category ? { category: actor.category } : {},
1008
+ ...actor.selector ? { selector: actor.selector } : {},
1009
+ ...actor.cardinality ? { cardinality: actor.cardinality } : {}
1010
+ };
1011
+ }
1012
+ function buildProcessManifest(artifact, outcomeNodeIds) {
1013
+ const { process } = artifact;
1014
+ const actors = {};
1015
+ for (const actorId of Object.keys(process.actors)) {
1016
+ actors[actorId] = resolveActorRef(artifact, actorId);
1017
+ }
1018
+ let selectors;
1019
+ if (process.selectors) {
1020
+ selectors = {};
1021
+ for (const [name, selector] of Object.entries(process.selectors)) {
1022
+ selectors[name] = {
1023
+ name,
1024
+ label: selector.label,
1025
+ ...selector.description ? { description: selector.description } : {}
1026
+ };
1027
+ }
1028
+ }
1029
+ const outcomes = process.outcomes.map((outcome) => {
1030
+ const nodeId = outcomeNodeIds?.get(outcome.id);
1031
+ return {
1032
+ id: outcome.id,
1033
+ label: outcome.label,
1034
+ kind: outcome.kind,
1035
+ triggers: outcomeTriggers(outcome),
1036
+ ...outcome.description ? { description: outcome.description } : {},
1037
+ ...nodeId ? { nodeId } : {}
1038
+ };
1039
+ });
1040
+ const pointOfNoReturnStage = process.stages.find((stage) => stage.pointOfNoReturn);
1041
+ return {
1042
+ source: "process-artifact",
1043
+ artifactVersion: process.version,
1044
+ revision: process.revision,
1045
+ workflowType: process.workflowType,
1046
+ actors,
1047
+ ...selectors ? { selectors } : {},
1048
+ outcomes,
1049
+ ...pointOfNoReturnStage ? { pointOfNoReturnStageId: pointOfNoReturnStage.id } : {}
1050
+ };
1051
+ }
1052
+ function formatPath(path) {
1053
+ return path.length > 0 ? z.core.toDotPath(path) : "(root)";
1054
+ }
1055
+ function issueToDiagnostic(issue) {
1056
+ const path = formatPath(issue.path);
1057
+ let code = "SCHEMA_INVALID";
1058
+ if (issue.code === "custom") {
1059
+ const paramCode = issue.params?.code;
1060
+ if (typeof paramCode === "string") {
1061
+ code = paramCode;
1062
+ }
1063
+ }
1064
+ return {
1065
+ severity: "error",
1066
+ code,
1067
+ message: `${path}: ${issue.message}`,
1068
+ path
1069
+ };
1070
+ }
1071
+ function upgradeV1(artifact) {
1072
+ const diagnostics = [
1073
+ {
1074
+ severity: "warning",
1075
+ code: "DEPRECATED_ARTIFACT_VERSION",
1076
+ message: `process.version: artifact is version 1; upgraded in memory to version ${CURRENT_PROCESS_VERSION}. Migrate the file (add process.revision, declare actor categories/selectors) \u2014 version 1 is a deprecated input, not a second authoring format.`,
1077
+ path: "process.version"
1078
+ },
1079
+ {
1080
+ severity: "warning",
1081
+ code: "MISSING_PROCESS_REVISION",
1082
+ message: `process.revision: version 1 has no revision; recorded as "${UNVERSIONED_REVISION}". Anything pinned to this artifact cannot name which version of the process it ran.`,
1083
+ path: "process.revision"
1084
+ }
1085
+ ];
1086
+ const process = artifact.process;
1087
+ const actors = {};
1088
+ for (const [actorId, actor] of Object.entries(process.actors)) {
1089
+ const legacyCategory = LEGACY_V1_ACTOR_CATEGORY[actorId];
1090
+ actors[actorId] = {
1091
+ ...actor,
1092
+ ...actor.category === void 0 && legacyCategory ? { category: legacyCategory } : {}
1093
+ };
1094
+ }
1095
+ return {
1096
+ artifact: {
1097
+ ...artifact,
1098
+ process: {
1099
+ ...process,
1100
+ version: CURRENT_PROCESS_VERSION,
1101
+ revision: UNVERSIONED_REVISION,
1102
+ actors
1103
+ }
1104
+ },
1105
+ diagnostics
1106
+ };
1107
+ }
1108
+ function parseProcessArtifact(json) {
1109
+ const result = processArtifactSchema.safeParse(json);
1110
+ if (!result.success) {
1111
+ return { diagnostics: result.error.issues.map(issueToDiagnostic) };
1112
+ }
1113
+ if (result.data.process.version < CURRENT_PROCESS_VERSION) {
1114
+ return upgradeV1(result.data);
1115
+ }
1116
+ return { artifact: result.data, diagnostics: [] };
1117
+ }
1118
+
1119
+ // packages/workflow-model/src/process-lint.ts
1120
+ function stagePath(stageIndex) {
1121
+ return `process.stages[${stageIndex}]`;
1122
+ }
1123
+ function substepPath(stageIndex, substepIndex) {
1124
+ return `${stagePath(stageIndex)}.substeps[${substepIndex}]`;
1125
+ }
1126
+ function lintProcess(artifact, inventory, predicateNames, options = {}) {
1127
+ const diagnostics = [];
1128
+ const process = artifact.process;
1129
+ const commandNames = new Set(inventory.commands.map((c) => c.name));
1130
+ const activityNames = new Set(inventory.activities);
1131
+ const predicateSet = new Set(predicateNames);
1132
+ const internalCommands = process.internalCommands ?? [];
1133
+ const internalSet = new Set(internalCommands);
1134
+ const boundCommandPaths = /* @__PURE__ */ new Map();
1135
+ const checkPredicate = (predicate, path) => {
1136
+ if (!predicateSet.has(predicate)) {
1137
+ diagnostics.push({
1138
+ severity: "error",
1139
+ code: "UNKNOWN_PREDICATE",
1140
+ message: `Predicate "${predicate}" referenced at ${path} is not in the predicate registry.`,
1141
+ path
1142
+ });
1143
+ }
1144
+ };
1145
+ if (options.selectorNames) {
1146
+ const selectorSet = new Set(options.selectorNames);
1147
+ for (const [name] of Object.entries(process.selectors ?? {})) {
1148
+ if (!selectorSet.has(name)) {
1149
+ diagnostics.push({
1150
+ severity: "error",
1151
+ code: "UNKNOWN_SELECTOR",
1152
+ message: `Selector "${name}" declared at process.selectors.${name} is not in the selector registry; the application has no rule resolving it to subjects.`,
1153
+ path: `process.selectors.${name}`
1154
+ });
1155
+ }
1156
+ }
1157
+ }
1158
+ process.stages.forEach((stage, stageIndex) => {
1159
+ const predicatePath = `${stagePath(stageIndex)}.complete.predicate`;
1160
+ const predicate = stage.complete?.predicate;
1161
+ if (typeof predicate !== "string" || predicate.length === 0) {
1162
+ diagnostics.push({
1163
+ severity: "error",
1164
+ code: "MISSING_STAGE_PREDICATE",
1165
+ message: `Stage "${stage.id}" (${stagePath(stageIndex)}) has no complete.predicate.`,
1166
+ path: predicatePath
1167
+ });
1168
+ } else {
1169
+ checkPredicate(predicate, predicatePath);
1170
+ }
1171
+ stage.substeps?.forEach((substep, substepIndex) => {
1172
+ const bindPath = substepPath(stageIndex, substepIndex);
1173
+ substep.bind.commands?.forEach((commandName, commandIndex) => {
1174
+ if (!boundCommandPaths.has(commandName)) {
1175
+ boundCommandPaths.set(commandName, `${bindPath}.bind.commands[${commandIndex}]`);
1176
+ }
1177
+ if (!commandNames.has(commandName)) {
1178
+ diagnostics.push({
1179
+ severity: "error",
1180
+ code: "BOUND_COMMAND_NOT_IN_INVENTORY",
1181
+ message: `Command "${commandName}" bound by substep "${substep.id}" (${bindPath}.bind.commands[${commandIndex}]) does not exist in the inventory.`,
1182
+ path: `${bindPath}.bind.commands[${commandIndex}]`
1183
+ });
1184
+ }
1185
+ });
1186
+ substep.bind.activities?.forEach((activityName, activityIndex) => {
1187
+ if (!activityNames.has(activityName)) {
1188
+ diagnostics.push({
1189
+ severity: "error",
1190
+ code: "BOUND_ACTIVITY_NOT_IN_INVENTORY",
1191
+ message: `Activity "${activityName}" bound by substep "${substep.id}" (${bindPath}.bind.activities[${activityIndex}]) does not exist in the inventory.`,
1192
+ path: `${bindPath}.bind.activities[${activityIndex}]`
1193
+ });
1194
+ }
1195
+ });
1196
+ });
1197
+ });
1198
+ process.outcomes.forEach((outcome, outcomeIndex) => {
1199
+ const whenPath = `process.outcomes[${outcomeIndex}].when`;
1200
+ outcomeTriggers(outcome).forEach((trigger, triggerIndex) => {
1201
+ const path = Array.isArray(outcome.when) ? `${whenPath}[${triggerIndex}]` : whenPath;
1202
+ if (trigger.kind === "predicate") {
1203
+ checkPredicate(trigger.name, `${path}.predicate`);
1204
+ } else if (trigger.kind === "command") {
1205
+ if (!boundCommandPaths.has(trigger.name)) {
1206
+ boundCommandPaths.set(trigger.name, `${path}.command`);
1207
+ }
1208
+ if (!commandNames.has(trigger.name)) {
1209
+ diagnostics.push({
1210
+ severity: "error",
1211
+ code: "BOUND_COMMAND_NOT_IN_INVENTORY",
1212
+ message: `Command "${trigger.name}" triggering outcome "${outcome.id}" (${path}.command) does not exist in the inventory.`,
1213
+ path: `${path}.command`
1214
+ });
1215
+ }
1216
+ }
1217
+ });
1218
+ });
1219
+ const hasSuccessOutcome = process.outcomes.some((o) => o.kind === "success");
1220
+ const hasFailureOutcome = process.outcomes.some((o) => o.kind === "failure");
1221
+ if (!hasSuccessOutcome) {
1222
+ diagnostics.push({
1223
+ severity: "error",
1224
+ code: "MISSING_SUCCESS_OUTCOME",
1225
+ message: 'process.outcomes has no outcome with kind "success"; every process needs at least one.',
1226
+ path: "process.outcomes"
1227
+ });
1228
+ }
1229
+ if (!hasFailureOutcome) {
1230
+ diagnostics.push({
1231
+ severity: "error",
1232
+ code: "MISSING_FAILURE_OUTCOME",
1233
+ message: 'process.outcomes has no outcome with kind "failure"; every process needs at least one.',
1234
+ path: "process.outcomes"
1235
+ });
1236
+ }
1237
+ internalCommands.forEach((commandName, index) => {
1238
+ if (!commandNames.has(commandName)) {
1239
+ diagnostics.push({
1240
+ severity: "warning",
1241
+ code: "STALE_INTERNAL_COMMAND",
1242
+ message: `internalCommands entry "${commandName}" (process.internalCommands[${index}]) does not exist in the inventory; this looks like a stale marker.`,
1243
+ path: `process.internalCommands[${index}]`
1244
+ });
1245
+ }
1246
+ });
1247
+ inventory.commands.forEach((command) => {
1248
+ const boundPath = boundCommandPaths.get(command.name);
1249
+ const isBound = boundPath !== void 0;
1250
+ const isInternal = internalSet.has(command.name);
1251
+ if (isBound && isInternal) {
1252
+ diagnostics.push({
1253
+ severity: "warning",
1254
+ code: "CONTRADICTORY_COMMAND_DECLARATION",
1255
+ message: `Inventory command "${command.name}" is both bound (at ${boundPath}) and listed in internalCommands \u2014 contradictory declaration.`,
1256
+ path: boundPath
1257
+ });
1258
+ } else if (!isBound && !isInternal) {
1259
+ diagnostics.push({
1260
+ severity: "error",
1261
+ code: "UNBOUND_INVENTORY_COMMAND",
1262
+ message: `Inventory command "${command.name}" is neither bound by any substep, nor named as an outcome trigger, nor listed in process.internalCommands.`,
1263
+ path: "process.internalCommands"
1264
+ });
1265
+ }
1266
+ });
1267
+ if (process.workflowType !== inventory.workflowType) {
1268
+ diagnostics.push({
1269
+ severity: "error",
1270
+ code: "WORKFLOW_TYPE_MISMATCH",
1271
+ message: `process.workflowType "${process.workflowType}" does not match inventory.workflowType "${inventory.workflowType}"; this artifact is bound to the wrong workflow.`,
1272
+ path: "process.workflowType"
1273
+ });
1274
+ }
1275
+ return diagnostics;
1276
+ }
1277
+ export {
1278
+ CURRENT_PROCESS_VERSION,
1279
+ SUPPORTED_PROCESS_VERSIONS,
1280
+ UNVERSIONED_REVISION,
1281
+ WorkflowPatchError,
1282
+ WorkflowPatchStream,
1283
+ applyPreviewPatch,
1284
+ applyWorkflowPatch,
1285
+ applyWorkflowPatches,
1286
+ buildProcessManifest,
1287
+ computeHappyPath,
1288
+ countChanges,
1289
+ describeChanges,
1290
+ emptyWorkflowIR,
1291
+ findProcessNodesByCommand,
1292
+ findProcessNodesByElementId,
1293
+ lintProcess,
1294
+ outcomeTriggers,
1295
+ parseProcessArtifact,
1296
+ processArtifactSchema,
1297
+ resolveActorRef,
1298
+ resolveSubstepActor,
1299
+ validateWorkflowIR,
1300
+ validateWorkflowPatches,
1301
+ walkProcessNodes
1302
+ };