@crewx/workflow 0.3.22-rc.192 → 0.3.22-rc.194

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 (57) hide show
  1. package/dist/cli.d.ts +1 -1
  2. package/dist/cli.js +17 -1190
  3. package/dist/src/card-state.d.ts +1 -1
  4. package/dist/src/card-state.js +1 -121
  5. package/dist/src/engine.d.ts +1 -1
  6. package/dist/src/engine.js +1 -379
  7. package/dist/src/failure.d.ts +1 -1
  8. package/dist/src/failure.js +1 -122
  9. package/dist/src/file-lock.d.ts +1 -1
  10. package/dist/src/file-lock.js +1 -97
  11. package/dist/src/list-view.d.ts +1 -1
  12. package/dist/src/list-view.js +3 -188
  13. package/dist/src/mermaid.d.ts +1 -1
  14. package/dist/src/mermaid.js +2 -133
  15. package/dist/src/parent-task-sync.d.ts +1 -1
  16. package/dist/src/parent-task-sync.js +1 -445
  17. package/dist/src/retry-policy.d.ts +1 -1
  18. package/dist/src/retry-policy.js +1 -140
  19. package/dist/src/run-manager.d.ts +1 -1
  20. package/dist/src/run-manager.js +4 -2165
  21. package/dist/src/types.d.ts +1 -1
  22. package/dist/src/types.js +1 -5
  23. package/dist/src/utils/output-format.d.ts +1 -1
  24. package/dist/src/utils/output-format.js +12 -206
  25. package/dist/src/utils/shell-security.d.ts +1 -1
  26. package/dist/src/utils/shell-security.js +1 -138
  27. package/dist/src/workflow-persistence.d.ts +1 -1
  28. package/dist/src/workflow-persistence.js +1 -224
  29. package/package.json +4 -4
  30. package/dist/cli.d.ts.map +0 -1
  31. package/dist/cli.js.map +0 -1
  32. package/dist/src/card-state.d.ts.map +0 -1
  33. package/dist/src/card-state.js.map +0 -1
  34. package/dist/src/engine.d.ts.map +0 -1
  35. package/dist/src/engine.js.map +0 -1
  36. package/dist/src/failure.d.ts.map +0 -1
  37. package/dist/src/failure.js.map +0 -1
  38. package/dist/src/file-lock.d.ts.map +0 -1
  39. package/dist/src/file-lock.js.map +0 -1
  40. package/dist/src/list-view.d.ts.map +0 -1
  41. package/dist/src/list-view.js.map +0 -1
  42. package/dist/src/mermaid.d.ts.map +0 -1
  43. package/dist/src/mermaid.js.map +0 -1
  44. package/dist/src/parent-task-sync.d.ts.map +0 -1
  45. package/dist/src/parent-task-sync.js.map +0 -1
  46. package/dist/src/retry-policy.d.ts.map +0 -1
  47. package/dist/src/retry-policy.js.map +0 -1
  48. package/dist/src/run-manager.d.ts.map +0 -1
  49. package/dist/src/run-manager.js.map +0 -1
  50. package/dist/src/types.d.ts.map +0 -1
  51. package/dist/src/types.js.map +0 -1
  52. package/dist/src/utils/output-format.d.ts.map +0 -1
  53. package/dist/src/utils/output-format.js.map +0 -1
  54. package/dist/src/utils/shell-security.d.ts.map +0 -1
  55. package/dist/src/utils/shell-security.js.map +0 -1
  56. package/dist/src/workflow-persistence.d.ts.map +0 -1
  57. package/dist/src/workflow-persistence.js.map +0 -1
@@ -17,4 +17,4 @@ export interface WorkflowApprovalState {
17
17
  } | null;
18
18
  }
19
19
  export declare function buildWorkflowApprovalState(run: RunExecution, spec: WorkflowSpec): WorkflowApprovalState;
20
- //# sourceMappingURL=card-state.d.ts.map
20
+
@@ -1,121 +1 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.buildWorkflowApprovalState = buildWorkflowApprovalState;
4
- const DEFAULT_APPROVAL_QUESTION = 'Approval required.';
5
- function getStateValue(state, path) {
6
- const parts = path.split('.').map((part) => part.trim()).filter(Boolean);
7
- let value = state;
8
- for (const part of parts) {
9
- if (!value || typeof value !== 'object' || !(part in value))
10
- return undefined;
11
- value = value[part];
12
- }
13
- return value;
14
- }
15
- function formatStateValue(value) {
16
- if (value === undefined || value === null)
17
- return '';
18
- if (typeof value === 'string')
19
- return value;
20
- if (typeof value === 'number' || typeof value === 'boolean')
21
- return String(value);
22
- if (Array.isArray(value)) {
23
- return value.map((item) => formatStateListItem(item)).filter(Boolean).join(', ');
24
- }
25
- if (typeof value === 'object')
26
- return formatStateObject(value);
27
- return String(value);
28
- }
29
- function formatStateListItem(item) {
30
- if (!item || typeof item !== 'object' || Array.isArray(item)) {
31
- return formatStateValue(item);
32
- }
33
- const record = item;
34
- const name = typeof record.name === 'string' ? record.name : undefined;
35
- const version = typeof record.version === 'string' ? record.version : undefined;
36
- const action = typeof record.action === 'string' ? record.action : undefined;
37
- const reason = typeof record.reason === 'string' ? record.reason : undefined;
38
- const path = typeof record.path === 'string' ? record.path : undefined;
39
- if (name || path) {
40
- const label = [name ?? path, version].filter(Boolean).join('@');
41
- const details = [action, reason].filter(Boolean).join('; ');
42
- return details ? `${label} (${details})` : label;
43
- }
44
- return formatStateObject(record);
45
- }
46
- function formatStateObject(record) {
47
- const preferredKeys = [
48
- 'status',
49
- 'should_publish',
50
- 'next_version',
51
- 'version',
52
- 'action',
53
- 'reason',
54
- 'message',
55
- ];
56
- const entries = preferredKeys
57
- .filter((key) => key in record)
58
- .map((key) => `${key}=${formatStateValue(record[key])}`);
59
- return entries.length > 0 ? entries.join(', ') : JSON.stringify(record);
60
- }
61
- function interpolateStateTemplate(template, state) {
62
- return template.replace(/\{\{\s*([a-zA-Z0-9_.-]+)\s*\}\}/g, (_match, key) => formatStateValue(getStateValue(state, key)));
63
- }
64
- function safeApprovalQuestion(value) {
65
- if (typeof value !== 'string' || !value.trim())
66
- return DEFAULT_APPROVAL_QUESTION;
67
- if (/<\/?[A-Za-z][^>]*>/.test(value)
68
- || /^(?:https?|file|ftp|ws|wss):\/\//i.test(value)
69
- || /^(?:[A-Za-z]:[\\/]|\\\\|~[\\/]|\/)/.test(value)) {
70
- return DEFAULT_APPROVAL_QUESTION;
71
- }
72
- return value;
73
- }
74
- function toNodeState(id, node) {
75
- return node ? { id, type: node.type } : null;
76
- }
77
- function isTerminalStatus(status) {
78
- return status === 'completed' || status === 'failed' || status === 'cancelled';
79
- }
80
- function buildPhase(run, currentNode) {
81
- const status = run.status;
82
- if (isTerminalStatus(status))
83
- return status;
84
- if (currentNode?.type === 'approval')
85
- return 'waiting_approval';
86
- if (status === 'retrying')
87
- return 'retrying';
88
- if (status === 'pending' || (!run.current_node && status === undefined))
89
- return 'not_started';
90
- if (currentNode?.type === 'parallel')
91
- return 'paused';
92
- return 'running';
93
- }
94
- function buildWorkflowApprovalState(run, spec) {
95
- const nodes = spec.nodes ?? {};
96
- const currentNodeSpec = run.current_node ? nodes[run.current_node] : undefined;
97
- const currentNode = toNodeState(run.current_node, currentNodeSpec);
98
- let approval = null;
99
- if (currentNodeSpec?.type === 'approval') {
100
- const question = typeof currentNodeSpec.description === 'string'
101
- ? currentNodeSpec.description.trim()
102
- : 'Approving this request will run the next workflow node.';
103
- const approveNodeId = typeof currentNodeSpec.on_approve === 'string'
104
- ? currentNodeSpec.on_approve
105
- : undefined;
106
- approval = {
107
- question: safeApprovalQuestion(interpolateStateTemplate(question, run.state)),
108
- approveNode: approveNodeId ? toNodeState(approveNodeId, nodes[approveNodeId]) : null,
109
- };
110
- }
111
- return {
112
- phase: buildPhase(run, currentNodeSpec),
113
- status: run.status ?? null,
114
- workflowId: run.workflow_id,
115
- completedCount: run.completed_nodes?.length ?? 0,
116
- totalNodes: Object.keys(nodes).length,
117
- currentNode,
118
- approval,
119
- };
120
- }
121
- //# sourceMappingURL=card-state.js.map
1
+ "use strict";Object.defineProperty(exports,"__esModule",{value:true});exports.buildWorkflowApprovalState=h;const u="Approval required.";function l(t,e){const r=e.split(".").map(o=>o.trim()).filter(Boolean);let n=t;for(const o of r){if(!n||typeof n!=="object"||!(o in n))return void 0;n=n[o]}return n}function p(t){if(t===void 0||t===null)return"";if(typeof t==="string")return t;if(typeof t==="number"||typeof t==="boolean")return String(t);if(Array.isArray(t)){return t.map(e=>y(e)).filter(Boolean).join(", ")}if(typeof t==="object")return c(t);return String(t)}function y(t){if(!t||typeof t!=="object"||Array.isArray(t)){return p(t)}const e=t;const r=typeof e.name==="string"?e.name:void 0;const n=typeof e.version==="string"?e.version:void 0;const o=typeof e.action==="string"?e.action:void 0;const s=typeof e.reason==="string"?e.reason:void 0;const f=typeof e.path==="string"?e.path:void 0;if(r||f){const i=[r??f,n].filter(Boolean).join("@");const a=[o,s].filter(Boolean).join("; ");return a?`${i} (${a})`:i}return c(e)}function c(t){const e=["status","should_publish","next_version","version","action","reason","message"];const r=e.filter(n=>n in t).map(n=>`${n}=${p(t[n])}`);return r.length>0?r.join(", "):JSON.stringify(t)}function g(t,e){return t.replace(/\{\{\s*([a-zA-Z0-9_.-]+)\s*\}\}/g,(r,n)=>p(l(e,n)))}function _(t){if(typeof t!=="string"||!t.trim())return u;if(/<\/?[A-Za-z][^>]*>/.test(t)||/^(?:https?|file|ftp|ws|wss):\/\//i.test(t)||/^(?:[A-Za-z]:[\\/]|\\\\|~[\\/]|\/)/.test(t)){return u}return t}function d(t,e){return e?{id:t,type:e.type}:null}function m(t){return t==="completed"||t==="failed"||t==="cancelled"}function A(t,e){const r=t.status;if(m(r))return r;if(e?.type==="approval")return"waiting_approval";if(r==="retrying")return"retrying";if(r==="pending"||!t.current_node&&r===void 0)return"not_started";if(e?.type==="parallel")return"paused";return"running"}function h(t,e){const r=e.nodes??{};const n=t.current_node?r[t.current_node]:void 0;const o=d(t.current_node,n);let s=null;if(n?.type==="approval"){const f=typeof n.description==="string"?n.description.trim():"Approving this request will run the next workflow node.";const i=typeof n.on_approve==="string"?n.on_approve:void 0;s={question:_(g(f,t.state)),approveNode:i?d(i,r[i]):null}}return{phase:A(t,n),status:t.status??null,workflowId:t.workflow_id,completedCount:t.completed_nodes?.length??0,totalNodes:Object.keys(r).length,currentNode:o,approval:s}}
@@ -19,4 +19,4 @@ export type { WorkflowAuditSnapshotChange, WorkflowAuditSnapshotStore, WorkflowP
19
19
  export type { AutoRunResult, RunExecution, WorkflowAutoRunLease, WorkflowManualResume, WorkflowNode, WorkflowSpec, } from './types';
20
20
  export { buildWorkflowApprovalState } from './card-state';
21
21
  export type { WorkflowApprovalNodeState, WorkflowApprovalPhase, WorkflowApprovalState, } from './card-state';
22
- //# sourceMappingURL=engine.d.ts.map
22
+
@@ -1,379 +1 @@
1
- "use strict";
2
- var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
- if (k2 === undefined) k2 = k;
4
- var desc = Object.getOwnPropertyDescriptor(m, k);
5
- if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
- desc = { enumerable: true, get: function() { return m[k]; } };
7
- }
8
- Object.defineProperty(o, k2, desc);
9
- }) : (function(o, m, k, k2) {
10
- if (k2 === undefined) k2 = k;
11
- o[k2] = m[k];
12
- }));
13
- var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
- Object.defineProperty(o, "default", { enumerable: true, value: v });
15
- }) : function(o, v) {
16
- o["default"] = v;
17
- });
18
- var __importStar = (this && this.__importStar) || (function () {
19
- var ownKeys = function(o) {
20
- ownKeys = Object.getOwnPropertyNames || function (o) {
21
- var ar = [];
22
- for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
- return ar;
24
- };
25
- return ownKeys(o);
26
- };
27
- return function (mod) {
28
- if (mod && mod.__esModule) return mod;
29
- var result = {};
30
- if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
- __setModuleDefault(result, mod);
32
- return result;
33
- };
34
- })();
35
- Object.defineProperty(exports, "__esModule", { value: true });
36
- exports.buildWorkflowApprovalState = exports.previewWorkflowPersistenceProjection = exports.createWorkflowPersistenceProjection = exports.WORKFLOW_PARENT_TASK_KINDS = exports.WORKFLOW_PARENT_ORPHAN_GRACE_MS = exports.WORKFLOW_PARENT_ORPHAN_ERROR = exports.syncWorkflowParentTaskLifecycle = exports.reconcileWorkflowParentTasks = exports.reconcileWorkflowParentRunFiles = exports.previewWorkflowParentRepair = exports.deriveWorkflowParentLifecycleDecision = exports.deriveWorkflowParentLifecycleChange = exports.deriveWorkflowParentLifecycle = exports.createWorkflowParentPersistSink = exports.normalizeRunStatus = exports.WORKFLOW_RUN_LEGACY_RECOVERY_GRACE_MS = exports.WORKFLOW_RUN_INTERRUPTED_ERROR = exports.RunManager = exports.WorkflowEngine = void 0;
37
- const list_view_1 = require("./list-view");
38
- const fs = __importStar(require("fs"));
39
- const path = __importStar(require("path"));
40
- const yaml = __importStar(require("js-yaml"));
41
- const mermaid_1 = require("./mermaid");
42
- const shell_security_1 = require("./utils/shell-security");
43
- const run_manager_1 = require("./run-manager");
44
- const retry_policy_1 = require("./retry-policy");
45
- const NODE_REQUIRED_FIELDS = {
46
- agent_task: ['agent'],
47
- skill_task: ['skill'],
48
- shell_task: ['command'],
49
- parallel: ['branches', 'join'],
50
- join: [],
51
- branch: ['condition', 'branches'],
52
- approval: ['description', 'on_approve', 'on_reject'],
53
- expression: ['set', 'next'],
54
- end: [],
55
- error: [],
56
- };
57
- function resolveRef(ref, root, depth) {
58
- if (depth > 10)
59
- return null;
60
- const parts = ref.replace('#/', '').split('/');
61
- let node = root;
62
- for (const p of parts) {
63
- node = node?.[p];
64
- if (!node)
65
- return null;
66
- }
67
- const schemaNode = node;
68
- if (schemaNode.$ref)
69
- return resolveRef(schemaNode.$ref, root, depth + 1);
70
- return schemaNode;
71
- }
72
- function getJsonType(value) {
73
- if (value === null || value === undefined)
74
- return 'null';
75
- if (Array.isArray(value))
76
- return 'array';
77
- return typeof value;
78
- }
79
- function validateSchema(value, schemaNode, root, jsonPath, errors, depth) {
80
- if (depth > 20 || !schemaNode)
81
- return;
82
- if (schemaNode.$ref) {
83
- const resolved = resolveRef(schemaNode.$ref, root, 0);
84
- if (!resolved) {
85
- errors.push({ path: jsonPath, message: `cannot resolve $ref "${schemaNode.$ref}"` });
86
- return;
87
- }
88
- schemaNode = resolved;
89
- }
90
- if (schemaNode.type) {
91
- const actualType = getJsonType(value);
92
- const typeMatches = schemaNode.type === 'integer'
93
- ? actualType === 'number' && Number.isInteger(value)
94
- : schemaNode.type === actualType;
95
- if (!typeMatches) {
96
- errors.push({ path: jsonPath, message: `expected type "${schemaNode.type}", got "${actualType}"` });
97
- return;
98
- }
99
- }
100
- if (typeof value === 'number') {
101
- if (schemaNode.minimum !== undefined && value < schemaNode.minimum) {
102
- errors.push({ path: jsonPath, message: `number must be >= ${schemaNode.minimum}` });
103
- }
104
- if (schemaNode.maximum !== undefined && value > schemaNode.maximum) {
105
- errors.push({ path: jsonPath, message: `number must be <= ${schemaNode.maximum}` });
106
- }
107
- }
108
- if (schemaNode.enum) {
109
- if (!schemaNode.enum.includes(value)) {
110
- errors.push({ path: jsonPath, message: `value "${value}" not in enum [${schemaNode.enum.join(', ')}]` });
111
- }
112
- }
113
- if (schemaNode.type === 'object' && typeof value === 'object' && value !== null && !Array.isArray(value)) {
114
- const obj = value;
115
- if (schemaNode.required) {
116
- for (const req of schemaNode.required) {
117
- if (!(req in obj)) {
118
- errors.push({ path: `${jsonPath}.${req}`, message: 'required field missing' });
119
- }
120
- }
121
- }
122
- if (schemaNode.properties) {
123
- for (const [key, propSchema] of Object.entries(schemaNode.properties)) {
124
- if (key in obj) {
125
- validateSchema(obj[key], propSchema, root, `${jsonPath}.${key}`, errors, depth + 1);
126
- }
127
- }
128
- }
129
- if (schemaNode.additionalProperties && typeof schemaNode.additionalProperties === 'object') {
130
- const knownKeys = schemaNode.properties ? Object.keys(schemaNode.properties) : [];
131
- for (const key of Object.keys(obj)) {
132
- if (!knownKeys.includes(key)) {
133
- validateSchema(obj[key], schemaNode.additionalProperties, root, `${jsonPath}.${key}`, errors, depth + 1);
134
- }
135
- }
136
- }
137
- }
138
- if (schemaNode.type === 'array' && Array.isArray(value)) {
139
- if (schemaNode.items) {
140
- value.forEach((item, i) => {
141
- validateSchema(item, schemaNode.items, root, `${jsonPath}[${i}]`, errors, depth + 1);
142
- });
143
- }
144
- }
145
- }
146
- function validateNodeTypeRequirements(nodeId, node, jsonPath, errors) {
147
- const required = NODE_REQUIRED_FIELDS[node.type];
148
- if (!required)
149
- return;
150
- for (const field of required) {
151
- if (!(field in node)) {
152
- errors.push({ path: `${jsonPath}.${field}`, message: `"${node.type}" node requires "${field}" field` });
153
- }
154
- }
155
- if (node.type === 'parallel' && node.branches && !Array.isArray(node.branches)) {
156
- errors.push({ path: `${jsonPath}.branches`, message: '"parallel" node branches must be an array of node IDs' });
157
- }
158
- if (node.type === 'branch' && node.branches && Array.isArray(node.branches)) {
159
- errors.push({ path: `${jsonPath}.branches`, message: '"branch" node branches must be a Record (object), not an array' });
160
- }
161
- if (node.type === 'branch' && typeof node.condition === 'string') {
162
- try {
163
- (0, run_manager_1.validateSafeBranchCondition)(node.condition);
164
- }
165
- catch (error) {
166
- errors.push({ path: `${jsonPath}.condition`, message: `unsafe branch condition: ${error.message}` });
167
- }
168
- }
169
- if (typeof node.fail_when === 'string') {
170
- const taskTypes = ['agent_task', 'skill_task', 'shell_task'];
171
- if (!taskTypes.includes(node.type)) {
172
- errors.push({
173
- path: `${jsonPath}.fail_when`,
174
- message: `"fail_when" is only supported on task nodes (${taskTypes.join(', ')}), not "${node.type}"`,
175
- });
176
- }
177
- else {
178
- try {
179
- (0, run_manager_1.validateSafeBranchCondition)(node.fail_when);
180
- }
181
- catch (error) {
182
- errors.push({ path: `${jsonPath}.fail_when`, message: `unsafe fail_when condition: ${error.message}` });
183
- }
184
- }
185
- }
186
- if (node.retry !== undefined) {
187
- if (!retry_policy_1.RETRYABLE_NODE_TYPES.includes(node.type)) {
188
- errors.push({
189
- path: `${jsonPath}.retry`,
190
- message: '"retry" is only supported on agent_task, skill_task, and shell_task nodes',
191
- });
192
- }
193
- else {
194
- const parsed = (0, retry_policy_1.parseRetryPolicy)(node);
195
- if (!parsed.ok) {
196
- const errorPath = `${jsonPath}.${parsed.path}`;
197
- if (!errors.some((error) => error.path === errorPath)) {
198
- errors.push({ path: errorPath, message: parsed.reason });
199
- }
200
- }
201
- }
202
- }
203
- if (node.type === 'skill_task' && node.skill !== undefined && !shell_security_1.SKILL_NAME_RE.test(node.skill)) {
204
- errors.push({
205
- path: `${jsonPath}.skill`,
206
- message: `skill name "${node.skill}" is invalid (must match ^[a-z0-9][a-z0-9-]*$, no path traversal)`,
207
- });
208
- }
209
- if (node.type === 'shell_task' && node.command !== undefined) {
210
- const check = (0, shell_security_1.validateShellCommand)(node.command);
211
- if (!check.ok) {
212
- errors.push({ path: `${jsonPath}.command`, message: check.error });
213
- }
214
- const envCheck = (0, shell_security_1.validateEnvKeys)(node.env);
215
- if (!envCheck.ok) {
216
- errors.push({ path: `${jsonPath}.env`, message: envCheck.error });
217
- }
218
- }
219
- }
220
- function levenshtein(a, b) {
221
- const m = a.length;
222
- const n = b.length;
223
- const dp = Array.from({ length: m + 1 }, () => Array(n + 1).fill(0));
224
- for (let i = 0; i <= m; i++)
225
- dp[i][0] = i;
226
- for (let j = 0; j <= n; j++)
227
- dp[0][j] = j;
228
- for (let i = 1; i <= m; i++) {
229
- for (let j = 1; j <= n; j++) {
230
- dp[i][j] = a[i - 1] === b[j - 1]
231
- ? dp[i - 1][j - 1]
232
- : 1 + Math.min(dp[i - 1][j], dp[i][j - 1], dp[i - 1][j - 1]);
233
- }
234
- }
235
- return dp[m][n];
236
- }
237
- function findSimilar(target, candidates) {
238
- let best = null;
239
- let bestScore = Infinity;
240
- for (const c of candidates) {
241
- const score = levenshtein(target, c);
242
- if (score < bestScore && score <= 3) {
243
- bestScore = score;
244
- best = c;
245
- }
246
- }
247
- return best;
248
- }
249
- function validateReferences(wfName, spec, agents, errors) {
250
- const nodes = spec.nodes;
251
- const nodeIds = new Set(Object.keys(nodes));
252
- const prefix = `workflows.${wfName}`;
253
- for (const [nodeId, node] of Object.entries(nodes)) {
254
- const np = `${prefix}.nodes.${nodeId}`;
255
- if (node.next && !nodeIds.has(node.next)) {
256
- errors.push({ path: `${np}.next`, message: `"${node.next}" node not found` });
257
- }
258
- if (node.agent && agents && !agents[node.agent]) {
259
- errors.push({ path: `${np}.agent`, message: `agent "${node.agent}" not found in agents section` });
260
- }
261
- if (node.type === 'parallel' && Array.isArray(node.branches)) {
262
- node.branches.forEach((branchId, i) => {
263
- if (!nodeIds.has(branchId)) {
264
- errors.push({ path: `${np}.branches[${i}]`, message: `"${branchId}" node not found` });
265
- }
266
- });
267
- if (node.join && !nodeIds.has(node.join)) {
268
- errors.push({ path: `${np}.join`, message: `"${node.join}" node not found` });
269
- }
270
- }
271
- if (node.type === 'branch' && node.branches && typeof node.branches === 'object' && !Array.isArray(node.branches)) {
272
- for (const [condVal, targetId] of Object.entries(node.branches)) {
273
- if (!nodeIds.has(targetId)) {
274
- const similar = findSimilar(targetId, nodeIds);
275
- let msg = `"${targetId}" node not found`;
276
- if (similar)
277
- msg += ` (did you mean "${similar}"?)`;
278
- errors.push({ path: `${np}.branches.${condVal}`, message: msg });
279
- }
280
- }
281
- }
282
- if (node.default && !nodeIds.has(node.default)) {
283
- errors.push({ path: `${np}.default`, message: `"${node.default}" node not found` });
284
- }
285
- if (node.type === 'approval') {
286
- if (node.on_approve && !nodeIds.has(node.on_approve)) {
287
- errors.push({ path: `${np}.on_approve`, message: `"${node.on_approve}" node not found` });
288
- }
289
- if (node.on_reject && !nodeIds.has(node.on_reject)) {
290
- errors.push({ path: `${np}.on_reject`, message: `"${node.on_reject}" node not found` });
291
- }
292
- }
293
- if (node.on_error?.fallback_node && !nodeIds.has(node.on_error.fallback_node)) {
294
- errors.push({ path: `${np}.on_error.fallback_node`, message: `"${node.on_error.fallback_node}" node not found` });
295
- }
296
- }
297
- }
298
- class WorkflowEngine {
299
- constructor() {
300
- let schemaPath = path.join(__dirname, '..', 'workflow-schema.json');
301
- if (!fs.existsSync(schemaPath)) {
302
- schemaPath = path.join(__dirname, '..', '..', 'workflow-schema.json');
303
- }
304
- this.schema = JSON.parse(fs.readFileSync(schemaPath, 'utf-8'));
305
- }
306
- loadWorkflows(dir) {
307
- return (0, list_view_1.loadWorkflowDirectory)(dir);
308
- }
309
- validateFile(filePath) {
310
- let content;
311
- try {
312
- content = fs.readFileSync(filePath, 'utf-8');
313
- }
314
- catch {
315
- return { success: false, results: [], error: `File not found or unreadable: ${filePath}` };
316
- }
317
- let doc;
318
- try {
319
- doc = yaml.load(content);
320
- }
321
- catch (err) {
322
- const message = err instanceof Error ? err.message : String(err);
323
- return { success: false, results: [], error: `YAML parse error: ${message}` };
324
- }
325
- if (!doc || typeof doc !== 'object') {
326
- return { success: false, results: [], error: 'Invalid YAML: document is empty or not an object' };
327
- }
328
- if (!doc.workflows || typeof doc.workflows !== 'object') {
329
- return { success: true, results: [] };
330
- }
331
- const agents = doc.agents ?? null;
332
- const results = [];
333
- let hasErrors = false;
334
- for (const [wfName, wfSpec] of Object.entries(doc.workflows)) {
335
- const errors = [];
336
- const basePath = `workflows.${wfName}`;
337
- validateSchema(wfSpec, this.schema.definitions['WorkflowSpec'], this.schema, basePath, errors, 0);
338
- if (wfSpec.nodes && typeof wfSpec.nodes === 'object') {
339
- for (const [nodeId, node] of Object.entries(wfSpec.nodes)) {
340
- if (node && typeof node === 'object' && node.type) {
341
- validateNodeTypeRequirements(nodeId, node, `${basePath}.nodes.${nodeId}`, errors);
342
- }
343
- }
344
- }
345
- validateReferences(wfName, wfSpec, agents, errors);
346
- if (errors.length > 0)
347
- hasErrors = true;
348
- results.push({ name: wfName, valid: errors.length === 0, errors });
349
- }
350
- return { success: !hasErrors, results };
351
- }
352
- toMermaid(spec) {
353
- return (0, mermaid_1.toMermaid)(spec);
354
- }
355
- }
356
- exports.WorkflowEngine = WorkflowEngine;
357
- var run_manager_2 = require("./run-manager");
358
- Object.defineProperty(exports, "RunManager", { enumerable: true, get: function () { return run_manager_2.RunManager; } });
359
- Object.defineProperty(exports, "WORKFLOW_RUN_INTERRUPTED_ERROR", { enumerable: true, get: function () { return run_manager_2.WORKFLOW_RUN_INTERRUPTED_ERROR; } });
360
- Object.defineProperty(exports, "WORKFLOW_RUN_LEGACY_RECOVERY_GRACE_MS", { enumerable: true, get: function () { return run_manager_2.WORKFLOW_RUN_LEGACY_RECOVERY_GRACE_MS; } });
361
- Object.defineProperty(exports, "normalizeRunStatus", { enumerable: true, get: function () { return run_manager_2.normalizeRunStatus; } });
362
- var parent_task_sync_1 = require("./parent-task-sync");
363
- Object.defineProperty(exports, "createWorkflowParentPersistSink", { enumerable: true, get: function () { return parent_task_sync_1.createWorkflowParentPersistSink; } });
364
- Object.defineProperty(exports, "deriveWorkflowParentLifecycle", { enumerable: true, get: function () { return parent_task_sync_1.deriveWorkflowParentLifecycle; } });
365
- Object.defineProperty(exports, "deriveWorkflowParentLifecycleChange", { enumerable: true, get: function () { return parent_task_sync_1.deriveWorkflowParentLifecycleChange; } });
366
- Object.defineProperty(exports, "deriveWorkflowParentLifecycleDecision", { enumerable: true, get: function () { return parent_task_sync_1.deriveWorkflowParentLifecycleDecision; } });
367
- Object.defineProperty(exports, "previewWorkflowParentRepair", { enumerable: true, get: function () { return parent_task_sync_1.previewWorkflowParentRepair; } });
368
- Object.defineProperty(exports, "reconcileWorkflowParentRunFiles", { enumerable: true, get: function () { return parent_task_sync_1.reconcileWorkflowParentRunFiles; } });
369
- Object.defineProperty(exports, "reconcileWorkflowParentTasks", { enumerable: true, get: function () { return parent_task_sync_1.reconcileWorkflowParentTasks; } });
370
- Object.defineProperty(exports, "syncWorkflowParentTaskLifecycle", { enumerable: true, get: function () { return parent_task_sync_1.syncWorkflowParentTaskLifecycle; } });
371
- Object.defineProperty(exports, "WORKFLOW_PARENT_ORPHAN_ERROR", { enumerable: true, get: function () { return parent_task_sync_1.WORKFLOW_PARENT_ORPHAN_ERROR; } });
372
- Object.defineProperty(exports, "WORKFLOW_PARENT_ORPHAN_GRACE_MS", { enumerable: true, get: function () { return parent_task_sync_1.WORKFLOW_PARENT_ORPHAN_GRACE_MS; } });
373
- Object.defineProperty(exports, "WORKFLOW_PARENT_TASK_KINDS", { enumerable: true, get: function () { return parent_task_sync_1.WORKFLOW_PARENT_TASK_KINDS; } });
374
- var workflow_persistence_1 = require("./workflow-persistence");
375
- Object.defineProperty(exports, "createWorkflowPersistenceProjection", { enumerable: true, get: function () { return workflow_persistence_1.createWorkflowPersistenceProjection; } });
376
- Object.defineProperty(exports, "previewWorkflowPersistenceProjection", { enumerable: true, get: function () { return workflow_persistence_1.previewWorkflowPersistenceProjection; } });
377
- var card_state_1 = require("./card-state");
378
- Object.defineProperty(exports, "buildWorkflowApprovalState", { enumerable: true, get: function () { return card_state_1.buildWorkflowApprovalState; } });
379
- //# sourceMappingURL=engine.js.map
1
+ "use strict";var P=exports&&exports.__createBinding||(Object.create?(function(a,e,i,t){if(t===void 0)t=i;var n=Object.getOwnPropertyDescriptor(e,i);if(!n||("get"in n?!e.__esModule:n.writable||n.configurable)){n={enumerable:true,get:function(){return e[i]}}}Object.defineProperty(a,t,n)}):(function(a,e,i,t){if(t===void 0)t=i;a[t]=e[i]}));var W=exports&&exports.__setModuleDefault||(Object.create?(function(a,e){Object.defineProperty(a,"default",{enumerable:true,value:e})}):function(a,e){a["default"]=e});var b=exports&&exports.__importStar||(function(){var a=function(e){a=Object.getOwnPropertyNames||function(i){var t=[];for(var n in i)if(Object.prototype.hasOwnProperty.call(i,n))t[t.length]=n;return t};return a(e)};return function(e){if(e&&e.__esModule)return e;var i={};if(e!=null){for(var t=a(e),n=0;n<t.length;n++)if(t[n]!=="default")P(i,e,t[n])}W(i,e);return i}})();Object.defineProperty(exports,"__esModule",{value:true});exports.buildWorkflowApprovalState=exports.previewWorkflowPersistenceProjection=exports.createWorkflowPersistenceProjection=exports.WORKFLOW_PARENT_TASK_KINDS=exports.WORKFLOW_PARENT_ORPHAN_GRACE_MS=exports.WORKFLOW_PARENT_ORPHAN_ERROR=exports.syncWorkflowParentTaskLifecycle=exports.reconcileWorkflowParentTasks=exports.reconcileWorkflowParentRunFiles=exports.previewWorkflowParentRepair=exports.deriveWorkflowParentLifecycleDecision=exports.deriveWorkflowParentLifecycleChange=exports.deriveWorkflowParentLifecycle=exports.createWorkflowParentPersistSink=exports.normalizeRunStatus=exports.WORKFLOW_RUN_LEGACY_RECOVERY_GRACE_MS=exports.WORKFLOW_RUN_INTERRUPTED_ERROR=exports.RunManager=exports.WorkflowEngine=void 0;const E=require("./list-view");const m=b(require("fs"));const R=b(require("path"));const j=b(require("js-yaml"));const A=require("./mermaid");const g=require("./utils/shell-security");const k=require("./run-manager");const O=require("./retry-policy");const v={agent_task:["agent"],skill_task:["skill"],shell_task:["command"],parallel:["branches","join"],join:[],branch:["condition","branches"],approval:["description","on_approve","on_reject"],expression:["set","next"],end:[],error:[]};function w(a,e,i){if(i>10)return null;const t=a.replace("#/","").split("/");let n=e;for(const s of t){n=n?.[s];if(!n)return null}const r=n;if(r.$ref)return w(r.$ref,e,i+1);return r}function S(a){if(a===null||a===void 0)return"null";if(Array.isArray(a))return"array";return typeof a}function y(a,e,i,t,n,r){if(r>20||!e)return;if(e.$ref){const s=w(e.$ref,i,0);if(!s){n.push({path:t,message:`cannot resolve $ref "${e.$ref}"`});return}e=s}if(e.type){const s=S(a);const f=e.type==="integer"?s==="number"&&Number.isInteger(a):e.type===s;if(!f){n.push({path:t,message:`expected type "${e.type}", got "${s}"`});return}}if(typeof a==="number"){if(e.minimum!==void 0&&a<e.minimum){n.push({path:t,message:`number must be >= ${e.minimum}`})}if(e.maximum!==void 0&&a>e.maximum){n.push({path:t,message:`number must be <= ${e.maximum}`})}}if(e.enum){if(!e.enum.includes(a)){n.push({path:t,message:`value "${a}" not in enum [${e.enum.join(", ")}]`})}}if(e.type==="object"&&typeof a==="object"&&a!==null&&!Array.isArray(a)){const s=a;if(e.required){for(const f of e.required){if(!(f in s)){n.push({path:`${t}.${f}`,message:"required field missing"})}}}if(e.properties){for(const[f,o]of Object.entries(e.properties)){if(f in s){y(s[f],o,i,`${t}.${f}`,n,r+1)}}}if(e.additionalProperties&&typeof e.additionalProperties==="object"){const f=e.properties?Object.keys(e.properties):[];for(const o of Object.keys(s)){if(!f.includes(o)){y(s[o],e.additionalProperties,i,`${t}.${o}`,n,r+1)}}}}if(e.type==="array"&&Array.isArray(a)){if(e.items){a.forEach((s,f)=>{y(s,e.items,i,`${t}[${f}]`,n,r+1)})}}}function L(a,e,i,t){const n=v[e.type];if(!n)return;for(const r of n){if(!(r in e)){t.push({path:`${i}.${r}`,message:`"${e.type}" node requires "${r}" field`})}}if(e.type==="parallel"&&e.branches&&!Array.isArray(e.branches)){t.push({path:`${i}.branches`,message:'"parallel" node branches must be an array of node IDs'})}if(e.type==="branch"&&e.branches&&Array.isArray(e.branches)){t.push({path:`${i}.branches`,message:'"branch" node branches must be a Record (object), not an array'})}if(e.type==="branch"&&typeof e.condition==="string"){try{(0,k.validateSafeBranchCondition)(e.condition)}catch(r){t.push({path:`${i}.condition`,message:`unsafe branch condition: ${r.message}`})}}if(typeof e.fail_when==="string"){const r=["agent_task","skill_task","shell_task"];if(!r.includes(e.type)){t.push({path:`${i}.fail_when`,message:`"fail_when" is only supported on task nodes (${r.join(", ")}), not "${e.type}"`})}else{try{(0,k.validateSafeBranchCondition)(e.fail_when)}catch(s){t.push({path:`${i}.fail_when`,message:`unsafe fail_when condition: ${s.message}`})}}}if(e.retry!==void 0){if(!O.RETRYABLE_NODE_TYPES.includes(e.type)){t.push({path:`${i}.retry`,message:'"retry" is only supported on agent_task, skill_task, and shell_task nodes'})}else{const r=(0,O.parseRetryPolicy)(e);if(!r.ok){const s=`${i}.${r.path}`;if(!t.some(f=>f.path===s)){t.push({path:s,message:r.reason})}}}}if(e.type==="skill_task"&&e.skill!==void 0&&!g.SKILL_NAME_RE.test(e.skill)){t.push({path:`${i}.skill`,message:`skill name "${e.skill}" is invalid (must match ^[a-z0-9][a-z0-9-]*$, no path traversal)`})}if(e.type==="shell_task"&&e.command!==void 0){const r=(0,g.validateShellCommand)(e.command);if(!r.ok){t.push({path:`${i}.command`,message:r.error})}const s=(0,g.validateEnvKeys)(e.env);if(!s.ok){t.push({path:`${i}.env`,message:s.error})}}}function T(a,e){const i=a.length;const t=e.length;const n=Array.from({length:i+1},()=>Array(t+1).fill(0));for(let r=0;r<=i;r++)n[r][0]=r;for(let r=0;r<=t;r++)n[0][r]=r;for(let r=1;r<=i;r++){for(let s=1;s<=t;s++){n[r][s]=a[r-1]===e[s-1]?n[r-1][s-1]:1+Math.min(n[r-1][s],n[r][s-1],n[r-1][s-1])}}return n[i][t]}function K(a,e){let i=null;let t=Infinity;for(const n of e){const r=T(a,n);if(r<t&&r<=3){t=r;i=n}}return i}function F(a,e,i,t){const n=e.nodes;const r=new Set(Object.keys(n));const s=`workflows.${a}`;for(const[f,o]of Object.entries(n)){const c=`${s}.nodes.${f}`;if(o.next&&!r.has(o.next)){t.push({path:`${c}.next`,message:`"${o.next}" node not found`})}if(o.agent&&i&&!i[o.agent]){t.push({path:`${c}.agent`,message:`agent "${o.agent}" not found in agents section`})}if(o.type==="parallel"&&Array.isArray(o.branches)){o.branches.forEach((p,l)=>{if(!r.has(p)){t.push({path:`${c}.branches[${l}]`,message:`"${p}" node not found`})}});if(o.join&&!r.has(o.join)){t.push({path:`${c}.join`,message:`"${o.join}" node not found`})}}if(o.type==="branch"&&o.branches&&typeof o.branches==="object"&&!Array.isArray(o.branches)){for(const[p,l]of Object.entries(o.branches)){if(!r.has(l)){const _=K(l,r);let h=`"${l}" node not found`;if(_)h+=` (did you mean "${_}"?)`;t.push({path:`${c}.branches.${p}`,message:h})}}}if(o.default&&!r.has(o.default)){t.push({path:`${c}.default`,message:`"${o.default}" node not found`})}if(o.type==="approval"){if(o.on_approve&&!r.has(o.on_approve)){t.push({path:`${c}.on_approve`,message:`"${o.on_approve}" node not found`})}if(o.on_reject&&!r.has(o.on_reject)){t.push({path:`${c}.on_reject`,message:`"${o.on_reject}" node not found`})}}if(o.on_error?.fallback_node&&!r.has(o.on_error.fallback_node)){t.push({path:`${c}.on_error.fallback_node`,message:`"${o.on_error.fallback_node}" node not found`})}}}class M{constructor(){let e=R.join(__dirname,"..","workflow-schema.json");if(!m.existsSync(e)){e=R.join(__dirname,"..","..","workflow-schema.json")}this.schema=JSON.parse(m.readFileSync(e,"utf-8"))}loadWorkflows(e){return(0,E.loadWorkflowDirectory)(e)}validateFile(e){let i;try{i=m.readFileSync(e,"utf-8")}catch{return{success:false,results:[],error:`File not found or unreadable: ${e}`}}let t;try{t=j.load(i)}catch(f){const o=f instanceof Error?f.message:String(f);return{success:false,results:[],error:`YAML parse error: ${o}`}}if(!t||typeof t!=="object"){return{success:false,results:[],error:"Invalid YAML: document is empty or not an object"}}if(!t.workflows||typeof t.workflows!=="object"){return{success:true,results:[]}}const n=t.agents??null;const r=[];let s=false;for(const[f,o]of Object.entries(t.workflows)){const c=[];const p=`workflows.${f}`;y(o,this.schema.definitions["WorkflowSpec"],this.schema,p,c,0);if(o.nodes&&typeof o.nodes==="object"){for(const[l,_]of Object.entries(o.nodes)){if(_&&typeof _==="object"&&_.type){L(l,_,`${p}.nodes.${l}`,c)}}}F(f,o,n,c);if(c.length>0)s=true;r.push({name:f,valid:c.length===0,errors:c})}return{success:!s,results:r}}toMermaid(e){return(0,A.toMermaid)(e)}}exports.WorkflowEngine=M;var d=require("./run-manager");Object.defineProperty(exports,"RunManager",{enumerable:true,get:function(){return d.RunManager}});Object.defineProperty(exports,"WORKFLOW_RUN_INTERRUPTED_ERROR",{enumerable:true,get:function(){return d.WORKFLOW_RUN_INTERRUPTED_ERROR}});Object.defineProperty(exports,"WORKFLOW_RUN_LEGACY_RECOVERY_GRACE_MS",{enumerable:true,get:function(){return d.WORKFLOW_RUN_LEGACY_RECOVERY_GRACE_MS}});Object.defineProperty(exports,"normalizeRunStatus",{enumerable:true,get:function(){return d.normalizeRunStatus}});var u=require("./parent-task-sync");Object.defineProperty(exports,"createWorkflowParentPersistSink",{enumerable:true,get:function(){return u.createWorkflowParentPersistSink}});Object.defineProperty(exports,"deriveWorkflowParentLifecycle",{enumerable:true,get:function(){return u.deriveWorkflowParentLifecycle}});Object.defineProperty(exports,"deriveWorkflowParentLifecycleChange",{enumerable:true,get:function(){return u.deriveWorkflowParentLifecycleChange}});Object.defineProperty(exports,"deriveWorkflowParentLifecycleDecision",{enumerable:true,get:function(){return u.deriveWorkflowParentLifecycleDecision}});Object.defineProperty(exports,"previewWorkflowParentRepair",{enumerable:true,get:function(){return u.previewWorkflowParentRepair}});Object.defineProperty(exports,"reconcileWorkflowParentRunFiles",{enumerable:true,get:function(){return u.reconcileWorkflowParentRunFiles}});Object.defineProperty(exports,"reconcileWorkflowParentTasks",{enumerable:true,get:function(){return u.reconcileWorkflowParentTasks}});Object.defineProperty(exports,"syncWorkflowParentTaskLifecycle",{enumerable:true,get:function(){return u.syncWorkflowParentTaskLifecycle}});Object.defineProperty(exports,"WORKFLOW_PARENT_ORPHAN_ERROR",{enumerable:true,get:function(){return u.WORKFLOW_PARENT_ORPHAN_ERROR}});Object.defineProperty(exports,"WORKFLOW_PARENT_ORPHAN_GRACE_MS",{enumerable:true,get:function(){return u.WORKFLOW_PARENT_ORPHAN_GRACE_MS}});Object.defineProperty(exports,"WORKFLOW_PARENT_TASK_KINDS",{enumerable:true,get:function(){return u.WORKFLOW_PARENT_TASK_KINDS}});var $=require("./workflow-persistence");Object.defineProperty(exports,"createWorkflowPersistenceProjection",{enumerable:true,get:function(){return $.createWorkflowPersistenceProjection}});Object.defineProperty(exports,"previewWorkflowPersistenceProjection",{enumerable:true,get:function(){return $.previewWorkflowPersistenceProjection}});var q=require("./card-state");Object.defineProperty(exports,"buildWorkflowApprovalState",{enumerable:true,get:function(){return q.buildWorkflowApprovalState}});
@@ -38,4 +38,4 @@ export interface EvidenceInput {
38
38
  export declare function buildEvidencePreview(input: EvidenceInput): string | null;
39
39
  export declare function sanitizeLocalRunError(raw: unknown, fallback: string): string;
40
40
  export declare function readNodeAttempt(run: RunExecution, nodeId: string): number;
41
- //# sourceMappingURL=failure.d.ts.map
41
+