@crewx/workflow 0.3.22-rc.22 → 0.3.22-rc.221

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 (54) hide show
  1. package/SKILL.md +15 -4
  2. package/dist/cli.d.ts +1 -1
  3. package/dist/cli.js +23 -1012
  4. package/dist/src/cancel-reason.d.ts +4 -0
  5. package/dist/src/cancel-reason.js +1 -0
  6. package/dist/src/card-state.d.ts +20 -0
  7. package/dist/src/card-state.js +1 -0
  8. package/dist/src/engine.d.ts +14 -3
  9. package/dist/src/engine.js +1 -348
  10. package/dist/src/failure.d.ts +41 -0
  11. package/dist/src/failure.js +1 -0
  12. package/dist/src/file-lock.d.ts +1 -1
  13. package/dist/src/file-lock.js +1 -97
  14. package/dist/src/list-view.d.ts +22 -0
  15. package/dist/src/list-view.js +3 -0
  16. package/dist/src/mermaid.d.ts +1 -1
  17. package/dist/src/mermaid.js +2 -133
  18. package/dist/src/parent-task-sync.d.ts +142 -0
  19. package/dist/src/parent-task-sync.js +1 -0
  20. package/dist/src/retry-policy.d.ts +38 -0
  21. package/dist/src/retry-policy.js +1 -0
  22. package/dist/src/run-anchor.d.ts +9 -0
  23. package/dist/src/run-anchor.js +1 -0
  24. package/dist/src/run-manager.d.ts +43 -3
  25. package/dist/src/run-manager.js +4 -1228
  26. package/dist/src/run-projection.d.ts +30 -0
  27. package/dist/src/run-projection.js +1 -0
  28. package/dist/src/types.d.ts +41 -4
  29. package/dist/src/types.js +1 -3
  30. package/dist/src/utils/output-format.d.ts +1 -1
  31. package/dist/src/utils/output-format.js +12 -157
  32. package/dist/src/utils/shell-security.d.ts +1 -1
  33. package/dist/src/utils/shell-security.js +1 -138
  34. package/dist/src/workflow-persistence.d.ts +48 -0
  35. package/dist/src/workflow-persistence.js +1 -0
  36. package/dist/workflow-schema.json +29 -4
  37. package/package.json +23 -4
  38. package/workflow-schema.json +29 -4
  39. package/dist/cli.d.ts.map +0 -1
  40. package/dist/cli.js.map +0 -1
  41. package/dist/src/engine.d.ts.map +0 -1
  42. package/dist/src/engine.js.map +0 -1
  43. package/dist/src/file-lock.d.ts.map +0 -1
  44. package/dist/src/file-lock.js.map +0 -1
  45. package/dist/src/mermaid.d.ts.map +0 -1
  46. package/dist/src/mermaid.js.map +0 -1
  47. package/dist/src/run-manager.d.ts.map +0 -1
  48. package/dist/src/run-manager.js.map +0 -1
  49. package/dist/src/types.d.ts.map +0 -1
  50. package/dist/src/types.js.map +0 -1
  51. package/dist/src/utils/output-format.d.ts.map +0 -1
  52. package/dist/src/utils/output-format.js.map +0 -1
  53. package/dist/src/utils/shell-security.d.ts.map +0 -1
  54. package/dist/src/utils/shell-security.js.map +0 -1
@@ -0,0 +1,30 @@
1
+ import type { RunExecution, WorkflowSpec } from './types.js';
2
+ export type RunStepStatus = 'not_started' | 'running' | 'waiting_approval' | 'approved' | 'rejected' | 'completed' | 'failed' | 'stopped';
3
+ export interface RunStepProjection {
4
+ nodeId: string;
5
+ label: string;
6
+ type: string | null;
7
+ status: RunStepStatus;
8
+ isApproval: boolean;
9
+ startedAt: string | null;
10
+ endedAt: string | null;
11
+ durationMs: number | null;
12
+ exitCode: number | null;
13
+ retryCount: number;
14
+ taskIds: string[];
15
+ }
16
+ export type RunStepTaskLookup = (workflowRunId: string, nodeId: string) => readonly string[];
17
+ export interface RunProgressSummary {
18
+ done: number;
19
+ total: number | null;
20
+ current: {
21
+ nodeId: string;
22
+ label: string;
23
+ } | null;
24
+ waitingApproval: boolean;
25
+ }
26
+ export declare function projectRunSteps(definition: WorkflowSpec | null | undefined, run: RunExecution, options?: {
27
+ findTaskIds?: RunStepTaskLookup;
28
+ }): RunStepProjection[];
29
+ export declare function summarizeRunProgress(definition: WorkflowSpec | null | undefined, run: RunExecution): RunProgressSummary;
30
+
@@ -0,0 +1 @@
1
+ "use strict";Object.defineProperty(exports,"__esModule",{value:true});exports.projectRunSteps=N;exports.summarizeRunProgress=A;const j=require("./cancel-reason.js");const p="approval";const m="retry";function v(e){return e.status??(e.auto_run?"running":"pending")}function g(e){return(0,j.isUserCancelledRun)(e)?"stopped":"failed"}function y(e){return e==="end"||e==="error"}function R(e,t){for(let r=e.length-1;r>=0;r-=1){const n=e[r];if(n.node_id===t&&n.type===p&&n.decision)return n}return void 0}function h(e){let t;for(const r of e){const n=Date.parse(r);if(Number.isFinite(n)&&(!t||n<t.ms))t={value:r,ms:n}}return t?.value??null}function D(e){let t;for(const r of e){const n=Date.parse(r);if(Number.isFinite(n)&&(!t||n>t.ms))t={value:r,ms:n}}return t?.value??null}function O(e,t,r,n){if(n)return n.decision==="reject"?"rejected":"approved";const o=v(r);if(r.current_node===e){if(o==="cancelled")return g(r);if(o==="failed")return"failed";if(o==="completed")return"completed";return"waiting_approval"}if(!r.completed_nodes.includes(e))return"not_started";const c=a=>typeof a==="string"&&(r.completed_nodes.includes(a)||r.current_node===a);if(c(t?.on_approve))return"approved";if(c(t?.on_reject))return"rejected";return"completed"}function P(e,t,r){const n=v(t);const o=t.auto_run?.node_id||t.current_node;if(n==="cancelled"&&o===e&&!t.completed_nodes.includes(e)){return g(t)}if(t.completed_nodes.includes(e))return"completed";if(t.current_node===e){if(n==="running"||n==="retrying")return"running";if(n==="failed")return"failed";if(n==="completed")return"completed";return"not_started"}const c=r.at(-1);if(c)return c.error?"failed":"completed";return"not_started"}function T(e,t,r,n,o){const c=n.filter(d=>d.node_id===e&&d.type!==m&&d.type!==p);const a=R(n,e);const s=t?.type==="approval"||a!==void 0;const i=t?.type??(s?p:null);const l=s?a?.decided_at??null:h(c.map(d=>d.started_at));const u=s?a?.decided_at??null:D(c.map(d=>d.ended_at));const f=l?Date.parse(l):NaN;const _=u?Date.parse(u):NaN;const S=Number.isFinite(f)&&Number.isFinite(_)?Math.max(0,_-f):null;return{nodeId:e,label:t?.label??e,type:i,status:s?O(e,t,r,a):P(e,r,c),isApproval:s,startedAt:l,endedAt:u,durationMs:S,exitCode:s?null:c.at(-1)?.exit_code??null,retryCount:n.filter(d=>d.node_id===e&&d.type===m).length,taskIds:o?[...o(r.id,e)]:[]}}function b(e,t){return Object.prototype.hasOwnProperty.call(e,t)}function w(e,t){const r=new Set;const n=[...e.completed_nodes,e.current_node,e.auto_run?.node_id,...(e.audit??[]).map(o=>o.node_id)];for(const o of n){if(o&&!b(t,o))r.add(o)}return[...r]}function N(e,t,r={}){const n=e?.nodes??{};const o=t.audit??[];const c=Object.keys(n);const a=[...c,...w(t,n)];return a.map(s=>T(s,b(n,s)?n[s]:void 0,t,o,r.findTaskIds))}function A(e,t){const r=N(e,t);const n=s=>s==="completed"||s==="approved"||s==="rejected";let o;let c;if(e){const s=Object.keys(e.nodes).filter(l=>!y(e.nodes[l]?.type));const i=new Map(r.map(l=>[l.nodeId,l]));o=s.filter(l=>{const u=i.get(l);return u!==void 0&&n(u.status)}).length;c=s.length}else{const s=r.filter(i=>!y(i.type));o=s.filter(i=>n(i.status)).length;c=null}const a=t.current_node?r.find(s=>s.nodeId===t.current_node):void 0;return{done:o,total:c,current:a?{nodeId:a.nodeId,label:a.label}:null,waitingApproval:r.some(s=>s.status==="waiting_approval")}}
@@ -8,7 +8,7 @@ export interface RetryConfig {
8
8
  max: number;
9
9
  delay: 'exponential' | 'fixed';
10
10
  max_delay?: number;
11
- on?: string[];
11
+ on?: WorkflowFailureCode[];
12
12
  }
13
13
  export interface OnError {
14
14
  handler?: string;
@@ -26,6 +26,7 @@ export interface WorkflowNode {
26
26
  command?: string[];
27
27
  cwd?: string;
28
28
  env?: Record<string, string>;
29
+ stdin?: string;
29
30
  output?: string;
30
31
  output_format?: 'json';
31
32
  output_schema?: JsonSchema;
@@ -40,6 +41,7 @@ export interface WorkflowNode {
40
41
  default?: string;
41
42
  join?: string;
42
43
  on_failure?: 'fail_fast' | 'continue' | 'ignore';
44
+ label?: string;
43
45
  description?: string;
44
46
  assignee?: string;
45
47
  timeout?: number;
@@ -79,7 +81,7 @@ export interface ValidationResult {
79
81
  export interface AuditEntry {
80
82
  exec_id: string;
81
83
  node_id: string;
82
- type: NodeType | string;
84
+ type: NodeType | 'retry' | string;
83
85
  summary: string;
84
86
  started_at: string;
85
87
  ended_at: string;
@@ -87,6 +89,36 @@ export interface AuditEntry {
87
89
  exit_code: number | null;
88
90
  error?: string;
89
91
  trigger: 'manual' | 'auto';
92
+ decision?: 'approve' | 'reject';
93
+ decided_at?: string;
94
+ actor?: string;
95
+ }
96
+ export interface WorkflowAutoRunLease {
97
+ pid: number;
98
+ token: string;
99
+ node_id: string;
100
+ started_at: string;
101
+ }
102
+ export interface WorkflowManualResume {
103
+ node_id: string;
104
+ requested_at: string;
105
+ }
106
+ export type WorkflowFailureCode = 'CLI_UNRESOLVABLE' | 'SPAWN_FAILED' | 'NODE_TIMEOUT' | 'NODE_EXIT_NONZERO' | 'OUTPUT_INVALID' | 'RATE_LIMITED' | 'CONFIG_INVALID' | 'INTERRUPTED' | 'CANCELLED' | 'UNKNOWN';
107
+ export interface RetryPolicy {
108
+ max: number;
109
+ delay: 'exponential' | 'fixed';
110
+ max_delay: number;
111
+ on: WorkflowFailureCode[];
112
+ }
113
+ export declare const NEVER_RETRYABLE: readonly WorkflowFailureCode[];
114
+ export interface WorkflowNodeFailure {
115
+ failedNode: string;
116
+ attempt: number;
117
+ causeCode: WorkflowFailureCode;
118
+ causeMessage: string;
119
+ exitCode: number | null;
120
+ evidencePreview: string | null;
121
+ occurredAt: string;
90
122
  }
91
123
  export interface RunExecution {
92
124
  id: string;
@@ -98,10 +130,15 @@ export interface RunExecution {
98
130
  completed_nodes: string[];
99
131
  state: Record<string, unknown>;
100
132
  initial_state: Record<string, unknown>;
101
- status?: 'running' | 'completed' | 'failed' | 'cancelled';
133
+ status?: 'pending' | 'running' | 'retrying' | 'completed' | 'failed' | 'cancelled';
102
134
  tasks?: Record<string, string>;
103
135
  error?: string;
136
+ last_failure?: WorkflowNodeFailure;
104
137
  audit?: AuditEntry[];
138
+ retry_count?: number;
139
+ node_attempts?: Record<string, number>;
140
+ auto_run?: WorkflowAutoRunLease;
141
+ manual_resume?: WorkflowManualResume;
105
142
  }
106
143
  export interface AutoRunResult {
107
144
  run: RunExecution;
@@ -109,4 +146,4 @@ export interface AutoRunResult {
109
146
  reason?: string;
110
147
  executed: string[];
111
148
  }
112
- //# sourceMappingURL=types.d.ts.map
149
+
package/dist/src/types.js CHANGED
@@ -1,3 +1 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- //# sourceMappingURL=types.js.map
1
+ "use strict";Object.defineProperty(exports,"__esModule",{value:true});exports.NEVER_RETRYABLE=void 0;exports.NEVER_RETRYABLE=["CANCELLED","CONFIG_INVALID"];
@@ -15,4 +15,4 @@ export declare function buildJsonOutputSuffix(schema?: JsonSchema, strict?: bool
15
15
  export declare function extractJson(text: string): string;
16
16
  export declare function validateJsonSchema(value: unknown, schema: JsonSchema): Promise<void>;
17
17
  export declare function toOpenAIStrictSchema(schema: JsonSchema): JsonSchema;
18
- //# sourceMappingURL=output-format.d.ts.map
18
+
@@ -1,53 +1,8 @@
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.WORKFLOW_SIGNAL_KEY = void 0;
37
- exports.buildWorkflowPreamble = buildWorkflowPreamble;
38
- exports.detectWorkflowSignal = detectWorkflowSignal;
39
- exports.buildJsonOutputSuffix = buildJsonOutputSuffix;
40
- exports.extractJson = extractJson;
41
- exports.validateJsonSchema = validateJsonSchema;
42
- exports.toOpenAIStrictSchema = toOpenAIStrictSchema;
43
- const node_crypto_1 = require("node:crypto");
44
- exports.WORKFLOW_SIGNAL_KEY = 'workflow_signal';
45
- function buildWorkflowPreamble(ctx) {
46
- return `<crewx.workflow>
1
+ "use strict";var y=exports&&exports.__createBinding||(Object.create?(function(n,e,r,t){if(t===void 0)t=r;var o=Object.getOwnPropertyDescriptor(e,r);if(!o||("get"in o?!e.__esModule:o.writable||o.configurable)){o={enumerable:true,get:function(){return e[r]}}}Object.defineProperty(n,t,o)}):(function(n,e,r,t){if(t===void 0)t=r;n[t]=e[r]}));var _=exports&&exports.__setModuleDefault||(Object.create?(function(n,e){Object.defineProperty(n,"default",{enumerable:true,value:e})}):function(n,e){n["default"]=e});var v=exports&&exports.__importStar||(function(){var n=function(e){n=Object.getOwnPropertyNames||function(r){var t=[];for(var o in r)if(Object.prototype.hasOwnProperty.call(r,o))t[t.length]=o;return t};return n(e)};return function(e){if(e&&e.__esModule)return e;var r={};if(e!=null){for(var t=n(e),o=0;o<t.length;o++)if(t[o]!=="default")y(r,e,t[o])}_(r,e);return r}})();Object.defineProperty(exports,"__esModule",{value:true});exports.WORKFLOW_SIGNAL_KEY=void 0;exports.buildWorkflowPreamble=j;exports.detectWorkflowSignal=E;exports.buildJsonOutputSuffix=P;exports.extractJson=O;exports.validateJsonSchema=W;exports.toOpenAIStrictSchema=S;const N=require("node:crypto");exports.WORKFLOW_SIGNAL_KEY="workflow_signal";function j(n){return`<crewx.workflow>
47
2
  ## Execution Context
48
- You are running headless as node "${ctx.nodeId}" of workflow "${ctx.workflowId}" (run ${ctx.execId}).
3
+ You are running headless as node "${n.nodeId}" of workflow "${n.workflowId}" (run ${n.execId}).
49
4
  - No human is watching this session. NEVER ask clarifying questions or wait for
50
- confirmation — state your assumptions and proceed, or report failure.
5
+ confirmation \u2014 state your assumptions and proceed, or report failure.
51
6
  - Your output is consumed verbatim by downstream nodes. Output the result only;
52
7
  skip greetings, progress notes, and meta commentary.
53
8
  - If the task cannot be completed (broken precondition, missing data, failed
@@ -57,118 +12,18 @@ You are running headless as node "${ctx.nodeId}" of workflow "${ctx.workflowId}"
57
12
  The engine will halt the workflow at this node and record the reason.
58
13
  </crewx.workflow>
59
14
 
60
- `;
61
- }
62
- function detectWorkflowSignal(output) {
63
- if (!output.includes(`"${exports.WORKFLOW_SIGNAL_KEY}"`))
64
- return null;
65
- const toSignal = (parsed) => {
66
- if (typeof parsed === 'object' && parsed !== null && !Array.isArray(parsed) &&
67
- parsed[exports.WORKFLOW_SIGNAL_KEY] === 'fail') {
68
- const reason = parsed['reason'];
69
- return { signal: 'fail', ...(typeof reason === 'string' && reason ? { reason } : {}) };
70
- }
71
- return null;
72
- };
73
- try {
74
- const signal = toSignal(JSON.parse(extractJson(output)));
75
- if (signal)
76
- return signal;
77
- }
78
- catch { }
79
- const inline = output.match(new RegExp(`\\{[^{}]*"${exports.WORKFLOW_SIGNAL_KEY}"\\s*:\\s*"fail"[^{}]*\\}`));
80
- if (inline) {
81
- try {
82
- return toSignal(JSON.parse(inline[0]));
83
- }
84
- catch {
85
- return { signal: 'fail' };
86
- }
87
- }
88
- return null;
89
- }
90
- function buildJsonOutputSuffix(schema, strict = false) {
91
- const finalSchema = strict && schema ? toOpenAIStrictSchema(schema) : schema;
92
- const schemaStr = finalSchema ? `JSON Schema:\n${JSON.stringify(finalSchema, null, 2)}\n` : '';
93
- return `\n\n<crewx.workflow>
94
- ## ⚠️ Output Format (read first — drives branch evaluation)
15
+ `}function E(n){if(!n.includes(`"${exports.WORKFLOW_SIGNAL_KEY}"`))return null;const e=t=>{if(typeof t==="object"&&t!==null&&!Array.isArray(t)&&t[exports.WORKFLOW_SIGNAL_KEY]==="fail"){const o=t["reason"];return{signal:"fail",...typeof o==="string"&&o?{reason:o}:{}}}return null};try{const t=e(JSON.parse(O(n)));if(t)return t}catch{}const r=n.match(new RegExp(`\\{[^{}]*"${exports.WORKFLOW_SIGNAL_KEY}"\\s*:\\s*"fail"[^{}]*\\}`));if(r){try{return e(JSON.parse(r[0]))}catch{return{signal:"fail"}}}return null}function P(n,e=false){const r=e&&n?S(n):n;const t=r?`JSON Schema:
16
+ ${JSON.stringify(r,null,2)}
17
+ `:"";return`
18
+
19
+ <crewx.workflow>
20
+ ## \u26A0\uFE0F Output Format (read first \u2014 drives branch evaluation)
95
21
 
96
22
  **The first character of your output MUST be \`{\`. The last character MUST be \`}\`.**
97
23
  No other text is permitted (no explanations, greetings, progress notes, or analysis remarks).
98
24
  **NEVER use markdown code fences (\`\`\`json ... \`\`\`).** Do not output even a single backtick.
99
- Branch evaluation calls \`JSON.parse(state.<output>)\` directly —
25
+ Branch evaluation calls \`JSON.parse(state.<output>)\` directly \u2014
100
26
  even one non-JSON character at start or end will halt the workflow.
101
27
 
102
- ${schemaStr}Execute the task, then output **exactly one JSON object** matching the schema above. Nothing else.
103
- </crewx.workflow>`;
104
- }
105
- function extractJson(text) {
106
- let trimmed = text.trim();
107
- const fenceMatch = trimmed.match(/^```(?:json|JSON)?\s*\n?([\s\S]*?)\n?```\s*$/);
108
- if (fenceMatch && fenceMatch[1] !== undefined) {
109
- trimmed = fenceMatch[1].trim();
110
- }
111
- const objMatch = trimmed.match(/\{[\s\S]*\}/);
112
- const arrMatch = trimmed.match(/\[[\s\S]*\]/);
113
- if (objMatch && arrMatch) {
114
- return objMatch.index < arrMatch.index ? objMatch[0] : arrMatch[0];
115
- }
116
- return objMatch?.[0] ?? arrMatch?.[0] ?? trimmed;
117
- }
118
- async function validateJsonSchema(value, schema) {
119
- const { registerSchema, validate } = await Promise.resolve().then(() => __importStar(require('@hyperjump/json-schema/draft-2020-12')));
120
- const uri = `urn:crewx:schema:${(0, node_crypto_1.randomUUID)()}`;
121
- registerSchema({ $schema: 'https://json-schema.org/draft/2020-12/schema', ...schema }, uri);
122
- const result = await validate(uri, value);
123
- if (!result.valid) {
124
- const errors = (result.errors ?? [])
125
- .map((e) => e.instanceLocation)
126
- .join('; ');
127
- throw new Error(`Schema validation failed: ${errors || 'unknown error'}`);
128
- }
129
- }
130
- function toOpenAIStrictSchema(schema) {
131
- return transformStrict(schema);
132
- }
133
- const OPENAI_UNSUPPORTED_KEYWORDS = new Set([
134
- 'default', 'format', 'pattern', 'minLength', 'maxLength',
135
- 'minimum', 'maximum', 'not',
136
- ]);
137
- function transformStrict(schema) {
138
- const result = {};
139
- for (const [key, value] of Object.entries(schema)) {
140
- if (OPENAI_UNSUPPORTED_KEYWORDS.has(key))
141
- continue;
142
- if (key === 'properties' && isRecord(value)) {
143
- const transformed = {};
144
- for (const [propKey, propVal] of Object.entries(value)) {
145
- transformed[propKey] = transformStrict(propVal);
146
- }
147
- result['properties'] = transformed;
148
- result['required'] = Object.keys(transformed);
149
- result['additionalProperties'] = false;
150
- }
151
- else if (key === 'items' && isRecord(value)) {
152
- result['items'] = transformStrict(value);
153
- }
154
- else if (key === 'required') {
155
- if (!('properties' in schema)) {
156
- result['required'] = value;
157
- }
158
- }
159
- else if (key === 'additionalProperties') {
160
- result['additionalProperties'] = false;
161
- }
162
- else {
163
- result[key] = value;
164
- }
165
- }
166
- if (result['type'] === 'object' && !('additionalProperties' in result)) {
167
- result['additionalProperties'] = false;
168
- }
169
- return result;
170
- }
171
- function isRecord(val) {
172
- return typeof val === 'object' && val !== null && !Array.isArray(val);
173
- }
174
- //# sourceMappingURL=output-format.js.map
28
+ ${t}Execute the task, then output **exactly one JSON object** matching the schema above. Nothing else.
29
+ </crewx.workflow>`}function O(n){let e=n.trim();const r=e.match(/^```(?:json|JSON)?\s*\n?([\s\S]*?)\n?```\s*$/);if(r&&r[1]!==void 0){e=r[1].trim()}const t=(i,d,b)=>{const w=[];let h=false;let p=false;let l=0;let u=-1;for(let s=0;s<i.length;s++){const f=i[s];if(p){p=false;continue}if(h){if(f==="\\"){p=true;continue}if(f==='"'){h=false}continue}if(f==='"'){h=true;continue}if(f===d){if(l===0)u=s;l++}else if(f===b){if(l>0){l--;if(l===0&&u!==-1){w.push({text:i.slice(u,s+1),start:u,end:s});u=-1}}}}return w};const o=t(e,"{","}");const a=t(e,"[","]");const c=[...o,...a].sort((i,d)=>d.end-i.end);for(const i of c){try{JSON.parse(i.text);return i.text}catch{}}return e}async function W(n,e){const{registerSchema:r,validate:t}=await Promise.resolve().then(()=>v(require("@hyperjump/json-schema/draft-2020-12")));const o=`urn:crewx:schema:${(0,N.randomUUID)()}`;r({$schema:"https://json-schema.org/draft/2020-12/schema",...e},o);const a=await t(o,n);if(!a.valid){const c=(a.errors??[]).map(i=>i.instanceLocation).join("; ");throw new Error(`Schema validation failed: ${c||"unknown error"}`)}}function S(n){return m(n)}const J=new Set(["default","format","pattern","minLength","maxLength","minimum","maximum","not"]);function m(n){const e={};for(const[r,t]of Object.entries(n)){if(J.has(r))continue;if(r==="properties"&&g(t)){const o={};for(const[a,c]of Object.entries(t)){o[a]=m(c)}e["properties"]=o;e["required"]=Object.keys(o);e["additionalProperties"]=false}else if(r==="items"&&g(t)){e["items"]=m(t)}else if(r==="required"){if(!("properties"in n)){e["required"]=t}}else if(r==="additionalProperties"){e["additionalProperties"]=false}else{e[r]=t}}if(e["type"]==="object"&&!("additionalProperties"in e)){e["additionalProperties"]=false}return e}function g(n){return typeof n==="object"&&n!==null&&!Array.isArray(n)}
@@ -15,4 +15,4 @@ export interface CwdCheck {
15
15
  }
16
16
  export declare function normalizeCwd(cwd: string | undefined, projectRoot: string): CwdCheck;
17
17
  export declare function buildShellEnv(nodeEnv: Record<string, string> | undefined, parentEnv?: NodeJS.ProcessEnv): NodeJS.ProcessEnv;
18
- //# sourceMappingURL=shell-security.d.ts.map
18
+
@@ -1,138 +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.DANGEROUS_ENV_KEYS = exports.INLINE_CODE_INTERPRETERS = exports.SHELL_DENYLIST = exports.SKILL_NAME_RE = void 0;
37
- exports.validateShellCommand = validateShellCommand;
38
- exports.validateEnvKeys = validateEnvKeys;
39
- exports.normalizeCwd = normalizeCwd;
40
- exports.buildShellEnv = buildShellEnv;
41
- const path = __importStar(require("path"));
42
- const fs = __importStar(require("fs"));
43
- exports.SKILL_NAME_RE = /^[a-z0-9][a-z0-9-]*$/;
44
- exports.SHELL_DENYLIST = new Set([
45
- 'bash', 'sh', 'zsh', 'dash', 'fish',
46
- 'cmd', 'cmd.exe', 'powershell', 'powershell.exe', 'pwsh', 'pwsh.exe',
47
- ]);
48
- exports.INLINE_CODE_INTERPRETERS = new Set([
49
- 'python', 'python3', 'node', 'nodejs', 'deno', 'perl', 'ruby', 'php',
50
- ]);
51
- const INLINE_CODE_FLAGS = new Set(['-c', '-e', '--eval', '--exec']);
52
- exports.DANGEROUS_ENV_KEYS = new Set([
53
- 'LD_PRELOAD', 'LD_LIBRARY_PATH', 'LD_AUDIT',
54
- 'DYLD_INSERT_LIBRARIES', 'DYLD_LIBRARY_PATH', 'DYLD_FRAMEWORK_PATH',
55
- 'NODE_OPTIONS',
56
- ]);
57
- function baseName(cmd) {
58
- return path.basename(cmd).toLowerCase();
59
- }
60
- function validateShellCommand(command) {
61
- if (!Array.isArray(command)) {
62
- return { ok: false, error: 'shell_task.command must be an array of strings (argv), not a string' };
63
- }
64
- if (command.length === 0) {
65
- return { ok: false, error: 'shell_task.command must not be empty' };
66
- }
67
- if (!command.every((c) => typeof c === 'string')) {
68
- return { ok: false, error: 'shell_task.command must contain only strings' };
69
- }
70
- const bin = baseName(command[0]);
71
- const binNoExt = bin.replace(/\.exe$/, '');
72
- if (exports.SHELL_DENYLIST.has(bin) || exports.SHELL_DENYLIST.has(binNoExt)) {
73
- return { ok: false, error: `shell_task.command[0] "${command[0]}" is a blocked shell interpreter` };
74
- }
75
- if (exports.INLINE_CODE_INTERPRETERS.has(binNoExt)) {
76
- const hasInlineFlag = command
77
- .slice(1)
78
- .some((arg) => INLINE_CODE_FLAGS.has(String(arg)));
79
- if (hasInlineFlag) {
80
- return {
81
- ok: false,
82
- error: `shell_task.command "${binNoExt} ${command[1]}" uses an inline-code flag (escape vector blocked)`,
83
- };
84
- }
85
- }
86
- return { ok: true };
87
- }
88
- function validateEnvKeys(env) {
89
- if (!env)
90
- return { ok: true };
91
- for (const key of Object.keys(env)) {
92
- if (exports.DANGEROUS_ENV_KEYS.has(key)) {
93
- return { ok: false, error: `shell_task.env key "${key}" is not allowed` };
94
- }
95
- }
96
- return { ok: true };
97
- }
98
- function normalizeCwd(cwd, projectRoot) {
99
- let root;
100
- try {
101
- root = fs.realpathSync(projectRoot);
102
- }
103
- catch (error) {
104
- return { ok: false, error: `project root "${projectRoot}" is not accessible: ${error.message}` };
105
- }
106
- if (cwd === undefined || cwd === '') {
107
- return { ok: true, resolved: root };
108
- }
109
- const candidate = path.resolve(root, cwd);
110
- let resolved;
111
- try {
112
- resolved = fs.realpathSync(candidate);
113
- }
114
- catch (error) {
115
- return { ok: false, error: `cwd "${cwd}" is not accessible: ${error.message}` };
116
- }
117
- if (resolved !== root && !resolved.startsWith(root + path.sep)) {
118
- return { ok: false, error: `cwd "${cwd}" resolves outside the project root` };
119
- }
120
- return { ok: true, resolved };
121
- }
122
- function buildShellEnv(nodeEnv, parentEnv = process.env) {
123
- const allowlist = process.platform === 'win32'
124
- ? ['PATH', 'Path', 'SystemRoot', 'COMSPEC', 'TEMP', 'TMP']
125
- : ['PATH', 'HOME', 'LANG', 'TMPDIR'];
126
- const env = {};
127
- for (const key of allowlist) {
128
- if (parentEnv[key] !== undefined)
129
- env[key] = parentEnv[key];
130
- }
131
- if (nodeEnv) {
132
- for (const [key, value] of Object.entries(nodeEnv)) {
133
- env[key] = value;
134
- }
135
- }
136
- return env;
137
- }
138
- //# sourceMappingURL=shell-security.js.map
1
+ "use strict";var u=exports&&exports.__createBinding||(Object.create?(function(e,r,n,s){if(s===void 0)s=n;var t=Object.getOwnPropertyDescriptor(r,n);if(!t||("get"in t?!r.__esModule:t.writable||t.configurable)){t={enumerable:true,get:function(){return r[n]}}}Object.defineProperty(e,s,t)}):(function(e,r,n,s){if(s===void 0)s=n;e[s]=r[n]}));var f=exports&&exports.__setModuleDefault||(Object.create?(function(e,r){Object.defineProperty(e,"default",{enumerable:true,value:r})}):function(e,r){e["default"]=r});var l=exports&&exports.__importStar||(function(){var e=function(r){e=Object.getOwnPropertyNames||function(n){var s=[];for(var t in n)if(Object.prototype.hasOwnProperty.call(n,t))s[s.length]=t;return s};return e(r)};return function(r){if(r&&r.__esModule)return r;var n={};if(r!=null){for(var s=e(r),t=0;t<s.length;t++)if(s[t]!=="default")u(n,r,s[t])}f(n,r);return n}})();Object.defineProperty(exports,"__esModule",{value:true});exports.DANGEROUS_ENV_KEYS=exports.INLINE_CODE_INTERPRETERS=exports.SHELL_DENYLIST=exports.SKILL_NAME_RE=void 0;exports.validateShellCommand=E;exports.validateEnvKeys=h;exports.normalizeCwd=d;exports.buildShellEnv=S;const a=l(require("path"));const i=l(require("fs"));exports.SKILL_NAME_RE=/^[a-z0-9][a-z0-9-]*$/;exports.SHELL_DENYLIST=new Set(["bash","sh","zsh","dash","fish","cmd","cmd.exe","powershell","powershell.exe","pwsh","pwsh.exe"]);exports.INLINE_CODE_INTERPRETERS=new Set(["python","python3","node","nodejs","deno","perl","ruby","php"]);const c=new Set(["-c","-e","--eval","--exec"]);exports.DANGEROUS_ENV_KEYS=new Set(["LD_PRELOAD","LD_LIBRARY_PATH","LD_AUDIT","DYLD_INSERT_LIBRARIES","DYLD_LIBRARY_PATH","DYLD_FRAMEWORK_PATH","NODE_OPTIONS"]);function _(e){return a.basename(e).toLowerCase()}function E(e){if(!Array.isArray(e)){return{ok:false,error:"shell_task.command must be an array of strings (argv), not a string"}}if(e.length===0){return{ok:false,error:"shell_task.command must not be empty"}}if(!e.every(s=>typeof s==="string")){return{ok:false,error:"shell_task.command must contain only strings"}}const r=_(e[0]);const n=r.replace(/\.exe$/,"");if(exports.SHELL_DENYLIST.has(r)||exports.SHELL_DENYLIST.has(n)){return{ok:false,error:`shell_task.command[0] "${e[0]}" is a blocked shell interpreter`}}if(exports.INLINE_CODE_INTERPRETERS.has(n)){const s=e.slice(1).some(t=>c.has(String(t)));if(s){return{ok:false,error:`shell_task.command "${n} ${e[1]}" uses an inline-code flag (escape vector blocked)`}}}return{ok:true}}function h(e){if(!e)return{ok:true};for(const r of Object.keys(e)){if(exports.DANGEROUS_ENV_KEYS.has(r)){return{ok:false,error:`shell_task.env key "${r}" is not allowed`}}}return{ok:true}}function d(e,r){let n;try{n=i.realpathSync(r)}catch(o){return{ok:false,error:`project root "${r}" is not accessible: ${o.message}`}}if(e===void 0||e===""){return{ok:true,resolved:n}}const s=a.resolve(n,e);let t;try{t=i.realpathSync(s)}catch(o){return{ok:false,error:`cwd "${e}" is not accessible: ${o.message}`}}if(t!==n&&!t.startsWith(n+a.sep)){return{ok:false,error:`cwd "${e}" resolves outside the project root`}}return{ok:true,resolved:t}}function S(e,r=process.env){const n=process.platform==="win32"?["PATH","Path","SystemRoot","COMSPEC","TEMP","TMP"]:["PATH","HOME","LANG","TMPDIR"];const s={};for(const t of n){if(r[t]!==void 0)s[t]=r[t]}if(e){for(const[t,o]of Object.entries(e)){s[t]=o}}return s}
@@ -0,0 +1,48 @@
1
+ import type { JsonObject, UiAuditSnapshotInput, UiInstance } from '@crewx/sdk';
2
+ import { type WorkflowApprovalState } from './card-state.js';
3
+ import { type WorkflowParentLifecycleChange, type WorkflowParentPersistTaskRepository, type WorkflowParentTaskKind } from './parent-task-sync.js';
4
+ import type { RunExecution, WorkflowSpec } from './types.js';
5
+ export interface WorkflowAuditSnapshotStore {
6
+ get(instanceId: string): UiInstance | null;
7
+ updateAuditSnapshot(input: UiAuditSnapshotInput): UiInstance | null;
8
+ }
9
+ export interface WorkflowPersistenceProjectionResult {
10
+ runId: string;
11
+ changedParentTaskIds: string[];
12
+ projectedAuditInstanceIds: string[];
13
+ errors: string[];
14
+ }
15
+ export interface WorkflowAuditSnapshotChange {
16
+ instanceId: string;
17
+ before: JsonObject;
18
+ after: WorkflowApprovalState;
19
+ }
20
+ export interface WorkflowPersistenceProjectionPreview {
21
+ runId: string;
22
+ runStatus: NonNullable<RunExecution['status']>;
23
+ parentChanges: WorkflowParentLifecycleChange[];
24
+ auditSnapshotChanges: WorkflowAuditSnapshotChange[];
25
+ errors: string[];
26
+ }
27
+ export interface WorkflowPersistenceProjectionPreviewOptions {
28
+ taskRepo: Pick<WorkflowParentPersistTaskRepository, 'findWorkflowParentTask'>;
29
+ workspaceId: string;
30
+ resolveWorkflowSpec: (run: RunExecution) => WorkflowSpec | null | undefined;
31
+ uiInstanceStore?: Pick<WorkflowAuditSnapshotStore, 'get'>;
32
+ getUiInstanceStore?: () => Pick<WorkflowAuditSnapshotStore, 'get'>;
33
+ parentKinds?: readonly WorkflowParentTaskKind[];
34
+ }
35
+ export interface WorkflowPersistenceProjectionOptions {
36
+ taskRepo: WorkflowParentPersistTaskRepository;
37
+ workspaceId: string;
38
+ resolveWorkflowSpec: (run: RunExecution) => WorkflowSpec | null | undefined;
39
+ uiInstanceStore?: WorkflowAuditSnapshotStore;
40
+ getUiInstanceStore?: () => WorkflowAuditSnapshotStore;
41
+ parentKinds?: readonly WorkflowParentTaskKind[];
42
+ onChanged?: (result: WorkflowPersistenceProjectionResult) => void;
43
+ warn?: (message: string) => void;
44
+ }
45
+ export type WorkflowPersistenceProjection = (run: RunExecution) => WorkflowPersistenceProjectionResult;
46
+ export declare function createWorkflowPersistenceProjection(options: WorkflowPersistenceProjectionOptions): WorkflowPersistenceProjection;
47
+ export declare function previewWorkflowPersistenceProjection(options: WorkflowPersistenceProjectionPreviewOptions, run: RunExecution): WorkflowPersistenceProjectionPreview;
48
+
@@ -0,0 +1 @@
1
+ "use strict";Object.defineProperty(exports,"__esModule",{value:true});exports.createWorkflowPersistenceProjection=b;exports.previewWorkflowPersistenceProjection=m;const g=require("./card-state.js");const S=require("./parent-task-sync.js");function h(e){return e instanceof Error?e.message:String(e)}function _(e){return e}function w(e){if(Array.isArray(e))return`[${e.map(r=>w(r)).join(",")}]`;if(e!==null&&typeof e==="object"){const r=e;return`{${Object.keys(r).sort().map(i=>`${JSON.stringify(i)}:${w(r[i])}`).join(",")}}`}return JSON.stringify(e)??"null"}function A(e,r){return w(e)===w(r)}function P(e,r){return{runId:e,changedParentTaskIds:[...r.changedParentTaskIds],projectedAuditInstanceIds:[],errors:[]}}function b(e){const r={taskRepo:e.taskRepo,workspaceId:e.workspaceId,...e.parentKinds?{parentKinds:e.parentKinds}:{}};const i=(0,S.createWorkflowParentPersistSink)(r);const o=e.warn??(n=>console.warn(n));return n=>{let u;try{u=i(n)}catch(s){const t=`workflow persistence parent projection failed for ${n.id}: ${h(s)}`;o(t);return{runId:n.id,changedParentTaskIds:[],projectedAuditInstanceIds:[],errors:[t]}}const c=P(n.id,u);const d=(s,t)=>{const a=`workflow persistence ${s} failed for ${n.id}: ${h(t)}`;c.errors.push(a);o(a)};const p=[...new Set(u.linkedAuditInstanceIds)];if(p.length>0){let s;try{s=e.resolveWorkflowSpec(n);if(!s)throw new Error(`workflow spec not found for ${n.workflow_id}`)}catch(t){d("spec resolution",t)}if(s){let t=null;try{t=(0,g.buildWorkflowApprovalState)(n,s)}catch(a){d("audit state build",a)}if(t){const a=Date.parse(n.updated_at);if(!Number.isSafeInteger(a)||a<0){d("audit snapshot",new Error(`invalid run updated_at: ${n.updated_at}`))}else{let I=e.uiInstanceStore;if(!I&&e.getUiInstanceStore){try{I=e.getUiInstanceStore()}catch(l){d("audit store creation",l)}}if(!I){d("audit snapshot",new Error("no UiInstanceStore was provided"))}else{for(const l of p){try{const f=I.get(l);const $=f?.domainRef?.workflowRunId;if(!f||f.workspaceId!==e.workspaceId||$!==n.id){continue}const j=w(f.state);const y=f.updatedAt;const k=I.updateAuditSnapshot({instanceId:l,workspaceId:e.workspaceId,updatedAt:a,state:_(t)});if(k&&(w(k.state)!==j||k.updatedAt!==y)){c.projectedAuditInstanceIds.push(l)}}catch(f){d(`audit snapshot ${l}`,f)}}}}}}}if((c.changedParentTaskIds.length>0||c.projectedAuditInstanceIds.length>0)&&e.onChanged){try{e.onChanged(c)}catch(s){d("change callback",s)}}return c}}function m(e,r){const i=(0,S.previewWorkflowParentRepair)({taskRepo:e.taskRepo,run:r,workspaceId:e.workspaceId,...e.parentKinds?{parentKinds:e.parentKinds}:{}});const o={runId:r.id,runStatus:i.runStatus,parentChanges:[...i.changes],auditSnapshotChanges:[],errors:[]};const n=(t,a)=>{o.errors.push(`workflow persistence preview ${t} failed for ${r.id}: ${h(a)}`)};const u=[...new Set(i.linkedAuditInstanceIds)];if(u.length===0)return o;let c;try{c=e.resolveWorkflowSpec(r);if(!c)throw new Error(`workflow spec not found for ${r.workflow_id}`)}catch(t){n("spec resolution",t)}if(!c)return o;let d;try{d=(0,g.buildWorkflowApprovalState)(r,c)}catch(t){n("audit state build",t);return o}const p=Date.parse(r.updated_at);if(!Number.isSafeInteger(p)||p<0){n("audit snapshot",new Error(`invalid run updated_at: ${r.updated_at}`));return o}let s=e.uiInstanceStore;if(!s&&e.getUiInstanceStore){try{s=e.getUiInstanceStore()}catch(t){n("audit store creation",t)}}if(!s){n("audit snapshot",new Error("no UiInstanceStore was provided"));return o}for(const t of u){try{const a=s.get(t);if(!a||a.workspaceId!==e.workspaceId||a.domainRef.workflowRunId!==r.id){continue}if(!A(a.state,d)){o.auditSnapshotChanges.push({instanceId:t,before:a.state,after:d})}}catch(a){n(`audit snapshot ${t}`,a)}}void p;return o}
@@ -81,7 +81,7 @@
81
81
  },
82
82
  "cwd": {
83
83
  "type": "string",
84
- "description": "skill_task/shell_task: working directory, normalized and constrained under the project root."
84
+ "description": "agent_task/skill_task/shell_task: working directory, normalized and constrained under the project root."
85
85
  },
86
86
  "env": {
87
87
  "type": "object",
@@ -90,6 +90,10 @@
90
90
  },
91
91
  "description": "shell_task: explicit env pass-through. Dangerous keys (LD_PRELOAD, etc.) are rejected."
92
92
  },
93
+ "stdin": {
94
+ "type": "string",
95
+ "description": "skill_task/shell_task: optional stdin payload. Supports {{template}} interpolation."
96
+ },
93
97
  "mode": {
94
98
  "type": "string",
95
99
  "enum": ["query", "execute"]
@@ -166,6 +170,10 @@
166
170
  "type": "string",
167
171
  "description": "error: failure message recorded in run.error. Supports {{template}} interpolation."
168
172
  },
173
+ "label": {
174
+ "type": "string",
175
+ "description": "Short human-facing step name shown in run views. Falls back to the node id."
176
+ },
169
177
  "on_approve": {
170
178
  "type": "string"
171
179
  },
@@ -218,19 +226,36 @@
218
226
  "required": ["max", "delay"],
219
227
  "properties": {
220
228
  "max": {
221
- "type": "number"
229
+ "type": "integer",
230
+ "minimum": 0,
231
+ "maximum": 10,
232
+ "description": "Number of additional retries after the first attempt (0 disables retry)."
222
233
  },
223
234
  "delay": {
224
235
  "type": "string",
225
236
  "enum": ["exponential", "fixed"]
226
237
  },
227
238
  "max_delay": {
228
- "type": "number"
239
+ "type": "integer",
240
+ "minimum": 0,
241
+ "description": "Maximum retry delay in milliseconds; runtime hard-caps this at 60000."
229
242
  },
230
243
  "on": {
231
244
  "type": "array",
232
245
  "items": {
233
- "type": "string"
246
+ "type": "string",
247
+ "enum": [
248
+ "CLI_UNRESOLVABLE",
249
+ "SPAWN_FAILED",
250
+ "NODE_TIMEOUT",
251
+ "NODE_EXIT_NONZERO",
252
+ "OUTPUT_INVALID",
253
+ "RATE_LIMITED",
254
+ "CONFIG_INVALID",
255
+ "INTERRUPTED",
256
+ "CANCELLED",
257
+ "UNKNOWN"
258
+ ]
234
259
  }
235
260
  }
236
261
  }