@o-lang/olang 1.0.23 → 1.0.24
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.
- package/package.json +1 -1
- package/src/parser.js +206 -270
- package/src/runtime.js +41 -0
package/package.json
CHANGED
package/src/parser.js
CHANGED
|
@@ -1,319 +1,255 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
1
|
+
const fs = require('fs');
|
|
2
|
+
|
|
3
|
+
function parse(content, filename = '<unknown>') {
|
|
4
|
+
if (typeof content === 'string') {
|
|
5
|
+
const lines = content.split('\n').map(line => line.replace(/\r$/, ''));
|
|
6
|
+
return parseLines(lines, filename);
|
|
7
|
+
} else if (typeof content === 'object' && content !== null) {
|
|
8
|
+
// Already parsed
|
|
9
|
+
return content;
|
|
10
|
+
} else {
|
|
11
|
+
throw new Error('parse() expects string content or pre-parsed object');
|
|
5
12
|
}
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
function parseFromFile(filepath) {
|
|
16
|
+
const content = fs.readFileSync(filepath, 'utf8');
|
|
17
|
+
return parse(content, filepath);
|
|
18
|
+
}
|
|
10
19
|
|
|
20
|
+
function parseLines(lines, filename) {
|
|
21
|
+
// Remove evolution file parsing - evolution is now in-workflow
|
|
22
|
+
return parseWorkflowLines(lines, filename);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function parseWorkflowLines(lines, filename) {
|
|
11
26
|
const workflow = {
|
|
12
|
-
|
|
27
|
+
type: 'workflow',
|
|
28
|
+
name: null,
|
|
13
29
|
parameters: [],
|
|
14
30
|
steps: [],
|
|
15
31
|
returnValues: [],
|
|
16
32
|
allowedResolvers: [],
|
|
17
|
-
|
|
18
|
-
declared: [],
|
|
19
|
-
autoInjected: [],
|
|
20
|
-
used: [],
|
|
21
|
-
warnings: []
|
|
22
|
-
},
|
|
33
|
+
maxGenerations: null, // ✅ Updated field name for Constraint: max_generations = X
|
|
23
34
|
__warnings: [],
|
|
24
|
-
|
|
35
|
+
filename: filename
|
|
25
36
|
};
|
|
26
|
-
|
|
37
|
+
|
|
27
38
|
let i = 0;
|
|
39
|
+
let currentStep = null;
|
|
40
|
+
let inAllowResolvers = false;
|
|
41
|
+
let inIfBlock = false;
|
|
42
|
+
let ifCondition = null;
|
|
43
|
+
let ifBody = [];
|
|
44
|
+
|
|
28
45
|
while (i < lines.length) {
|
|
29
|
-
let line = lines[i];
|
|
30
|
-
|
|
31
|
-
//
|
|
32
|
-
|
|
33
|
-
if (allowMatch) {
|
|
34
|
-
i++;
|
|
35
|
-
while (i < lines.length) {
|
|
36
|
-
const nextLine = lines[i].trim();
|
|
37
|
-
// Stop if line is empty or looks like a new top-level section
|
|
38
|
-
if (nextLine === '' || /^[A-Z][a-z]/.test(nextLine)) {
|
|
39
|
-
break;
|
|
40
|
-
}
|
|
41
|
-
// Strip optional YAML list marker: "- name" → "name"
|
|
42
|
-
const cleanVal = nextLine.replace(/^\-\s*/, '').trim();
|
|
43
|
-
if (cleanVal) {
|
|
44
|
-
workflow.allowedResolvers.push(cleanVal);
|
|
45
|
-
workflow.resolverPolicy.declared.push(cleanVal);
|
|
46
|
-
}
|
|
47
|
-
i++;
|
|
48
|
-
}
|
|
46
|
+
let line = lines[i++].trim();
|
|
47
|
+
|
|
48
|
+
// Skip empty lines and comments
|
|
49
|
+
if (line === '' || line.startsWith('#')) {
|
|
49
50
|
continue;
|
|
50
51
|
}
|
|
51
|
-
|
|
52
|
-
//
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
52
|
+
|
|
53
|
+
// Parse Workflow declaration
|
|
54
|
+
if (line.startsWith('Workflow ')) {
|
|
55
|
+
const match = line.match(/^Workflow\s+"([^"]+)"(?:\s+with\s+(.+))?$/i);
|
|
56
|
+
if (match) {
|
|
57
|
+
workflow.name = match[1];
|
|
58
|
+
if (match[2]) {
|
|
59
|
+
workflow.parameters = match[2].split(',').map(p => p.trim()).filter(p => p !== '');
|
|
60
|
+
}
|
|
61
|
+
} else {
|
|
62
|
+
workflow.__warnings.push(`Invalid Workflow syntax: ${line}`);
|
|
63
|
+
}
|
|
61
64
|
continue;
|
|
62
65
|
}
|
|
63
|
-
|
|
64
|
-
//
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
}
|
|
72
|
-
i++;
|
|
66
|
+
|
|
67
|
+
// Parse Constraint: max_generations = X (✅ Updated syntax)
|
|
68
|
+
if (line.startsWith('Constraint: max_generations = ')) {
|
|
69
|
+
const match = line.match(/^Constraint:\s+max_generations\s*=\s*(\d+)$/i);
|
|
70
|
+
if (match) {
|
|
71
|
+
workflow.maxGenerations = parseInt(match[1], 10);
|
|
72
|
+
} else {
|
|
73
|
+
workflow.__warnings.push(`Invalid Constraint syntax: ${line}`);
|
|
74
|
+
}
|
|
73
75
|
continue;
|
|
74
76
|
}
|
|
75
|
-
|
|
76
|
-
//
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
workflow.__requiresMath = true;
|
|
80
|
-
workflow.steps.push({
|
|
81
|
-
type: 'calculate',
|
|
82
|
-
expression: `add({${mathAdd[1]}}, {${mathAdd[2]}})`,
|
|
83
|
-
saveAs: mathAdd[3].trim()
|
|
84
|
-
});
|
|
85
|
-
i++;
|
|
77
|
+
|
|
78
|
+
// Parse Allow resolvers section
|
|
79
|
+
if (line === 'Allow resolvers:') {
|
|
80
|
+
inAllowResolvers = true;
|
|
86
81
|
continue;
|
|
87
82
|
}
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
83
|
+
|
|
84
|
+
if (inAllowResolvers) {
|
|
85
|
+
if (line.startsWith('- ')) {
|
|
86
|
+
const resolverName = line.substring(2).trim();
|
|
87
|
+
if (resolverName) {
|
|
88
|
+
workflow.allowedResolvers.push(resolverName);
|
|
89
|
+
}
|
|
90
|
+
} else if (line === '' || line.startsWith('#')) {
|
|
91
|
+
// Continue
|
|
92
|
+
} else {
|
|
93
|
+
// End of Allow resolvers section
|
|
94
|
+
inAllowResolvers = false;
|
|
95
|
+
i--; // Re-process this line
|
|
96
|
+
continue;
|
|
97
|
+
}
|
|
98
98
|
continue;
|
|
99
99
|
}
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
}
|
|
109
|
-
|
|
100
|
+
|
|
101
|
+
// Parse Step declarations
|
|
102
|
+
const stepMatch = line.match(/^Step\s+(\d+)\s*:\s*(.+)$/i);
|
|
103
|
+
if (stepMatch) {
|
|
104
|
+
// Save previous step if it exists
|
|
105
|
+
if (currentStep) {
|
|
106
|
+
workflow.steps.push(currentStep);
|
|
107
|
+
currentStep = null;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
const stepNumber = parseInt(stepMatch[1], 10);
|
|
111
|
+
const stepContent = stepMatch[2];
|
|
112
|
+
|
|
113
|
+
currentStep = {
|
|
114
|
+
type: 'action',
|
|
115
|
+
stepNumber: stepNumber,
|
|
116
|
+
actionRaw: stepContent,
|
|
117
|
+
saveAs: null,
|
|
118
|
+
constraints: {}
|
|
119
|
+
};
|
|
110
120
|
continue;
|
|
111
121
|
}
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
+
|
|
123
|
+
// Parse Evolve steps (✅ NEW IN-WORKFLOW EVOLUTION)
|
|
124
|
+
const evolveMatch = line.match(/^Evolve\s+([^\s]+)\s+using\s+feedback:\s*"([^"]*)"$/i);
|
|
125
|
+
if (evolveMatch) {
|
|
126
|
+
if (currentStep) {
|
|
127
|
+
workflow.steps.push(currentStep);
|
|
128
|
+
currentStep = null;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
currentStep = {
|
|
132
|
+
type: 'evolve',
|
|
133
|
+
stepNumber: workflow.steps.length + 1,
|
|
134
|
+
targetResolver: evolveMatch[1].trim(),
|
|
135
|
+
feedback: evolveMatch[2],
|
|
136
|
+
saveAs: null,
|
|
137
|
+
constraints: {}
|
|
138
|
+
};
|
|
122
139
|
continue;
|
|
123
140
|
}
|
|
124
|
-
|
|
125
|
-
//
|
|
126
|
-
const
|
|
127
|
-
if (
|
|
128
|
-
|
|
129
|
-
workflow.parameters = wfMatch[2]
|
|
130
|
-
? wfMatch[2].split(',').map(p => p.trim())
|
|
131
|
-
: [];
|
|
132
|
-
i++;
|
|
141
|
+
|
|
142
|
+
// Parse Save as
|
|
143
|
+
const saveMatch = line.match(/^Save as\s+(.+)$/i);
|
|
144
|
+
if (saveMatch && currentStep) {
|
|
145
|
+
currentStep.saveAs = saveMatch[1].trim();
|
|
133
146
|
continue;
|
|
134
147
|
}
|
|
135
|
-
|
|
136
|
-
//
|
|
137
|
-
const
|
|
138
|
-
if (
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
for (const retVar of returns) {
|
|
143
|
-
const producedByMath = workflow.steps.some(
|
|
144
|
-
s => s.saveAs === retVar && s.type === 'calculate'
|
|
145
|
-
);
|
|
146
|
-
if (producedByMath) workflow.__requiresMath = true;
|
|
147
|
-
}
|
|
148
|
-
i++;
|
|
148
|
+
|
|
149
|
+
// Parse If/When conditions
|
|
150
|
+
const ifMatch = line.match(/^(?:If|When)\s+(.+)$/i);
|
|
151
|
+
if (ifMatch) {
|
|
152
|
+
ifCondition = ifMatch[1].trim();
|
|
153
|
+
inIfBlock = true;
|
|
154
|
+
ifBody = [];
|
|
149
155
|
continue;
|
|
150
156
|
}
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
let mathDetected = null;
|
|
159
|
-
let expr = '';
|
|
160
|
-
let saveVar = null;
|
|
161
|
-
const mathOps = [
|
|
162
|
-
{ re: /^Add\s+\{(.+?)\}\s+and\s+\{(.+?)\}\s+Save as\s+(.+)$/i, fn: 'add' },
|
|
163
|
-
{ re: /^Subtract\s+\{(.+?)\}\s+from\s+\{(.+?)\}\s+Save as\s+(.+)$/i, fn: 'subtract' },
|
|
164
|
-
{ re: /^Multiply\s+\{(.+?)\}\s+and\s+\{(.+?)\}\s+Save as\s+(.+)$/i, fn: 'multiply' },
|
|
165
|
-
{ re: /^Divide\s+\{(.+?)\}\s+by\s+\{(.+?)\}\s+Save as\s+(.+)$/i, fn: 'divide' }
|
|
166
|
-
];
|
|
167
|
-
for (const op of mathOps) {
|
|
168
|
-
const m = raw.match(op.re);
|
|
169
|
-
if (m) {
|
|
170
|
-
mathDetected = op.fn;
|
|
171
|
-
saveVar = m[3].trim();
|
|
172
|
-
if (op.fn === 'subtract') {
|
|
173
|
-
expr = `subtract({${m[2]}}, {${m[1]}})`;
|
|
174
|
-
} else {
|
|
175
|
-
expr = `${op.fn}({${m[1]}}, {${m[2]}})`;
|
|
176
|
-
}
|
|
177
|
-
break;
|
|
178
|
-
}
|
|
157
|
+
|
|
158
|
+
const endIfMatch = line.match(/^End(?:If)?$/i);
|
|
159
|
+
if (endIfMatch && inIfBlock) {
|
|
160
|
+
if (currentStep) {
|
|
161
|
+
workflow.steps.push(currentStep);
|
|
162
|
+
currentStep = null;
|
|
179
163
|
}
|
|
180
|
-
|
|
164
|
+
|
|
181
165
|
workflow.steps.push({
|
|
182
|
-
type:
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
saveAs: saveVar,
|
|
187
|
-
constraints: {}
|
|
166
|
+
type: 'if',
|
|
167
|
+
condition: ifCondition,
|
|
168
|
+
body: ifBody,
|
|
169
|
+
stepNumber: workflow.steps.length + 1
|
|
188
170
|
});
|
|
189
|
-
|
|
171
|
+
|
|
172
|
+
inIfBlock = false;
|
|
173
|
+
ifCondition = null;
|
|
174
|
+
ifBody = [];
|
|
190
175
|
continue;
|
|
191
176
|
}
|
|
192
|
-
|
|
193
|
-
//
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
const lastStep = workflow.steps[workflow.steps.length - 1];
|
|
197
|
-
lastStep.saveAs = saveMatch[1].trim();
|
|
198
|
-
if (lastStep.saveAs.match(/[A-Z][A-Za-z0-9_]*/)) {
|
|
199
|
-
workflow.__requiresMath = true;
|
|
200
|
-
}
|
|
201
|
-
i++;
|
|
177
|
+
|
|
178
|
+
// Handle lines inside If block
|
|
179
|
+
if (inIfBlock) {
|
|
180
|
+
ifBody.push(line);
|
|
202
181
|
continue;
|
|
203
182
|
}
|
|
204
|
-
|
|
205
|
-
//
|
|
206
|
-
const
|
|
207
|
-
if (
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
if (eq) {
|
|
212
|
-
let key = eq[1].trim();
|
|
213
|
-
let value = eq[2].trim();
|
|
214
|
-
if (value.startsWith('[') && value.endsWith(']')) {
|
|
215
|
-
value = value.slice(1, -1).split(',').map(v =>
|
|
216
|
-
v.trim().replace(/^"/, '').replace(/"$/, '')
|
|
217
|
-
);
|
|
218
|
-
} else if (!isNaN(value)) {
|
|
219
|
-
value = Number(value);
|
|
220
|
-
} else if (value.startsWith('"') && value.endsWith('"')) {
|
|
221
|
-
value = value.slice(1, -1);
|
|
222
|
-
}
|
|
223
|
-
lastStep.constraints[key] = value;
|
|
183
|
+
|
|
184
|
+
// Parse Return statement
|
|
185
|
+
const returnMatch = line.match(/^Return\s+(.+)$/i);
|
|
186
|
+
if (returnMatch) {
|
|
187
|
+
if (currentStep) {
|
|
188
|
+
workflow.steps.push(currentStep);
|
|
189
|
+
currentStep = null;
|
|
224
190
|
}
|
|
225
|
-
|
|
191
|
+
workflow.returnValues = returnMatch[1].split(',').map(r => r.trim()).filter(r => r !== '');
|
|
226
192
|
continue;
|
|
227
193
|
}
|
|
228
|
-
|
|
229
|
-
|
|
194
|
+
|
|
195
|
+
// If we reach here and have unprocessed content, it's likely a workflow line without "Step X:"
|
|
196
|
+
// Try to handle it as a step
|
|
197
|
+
if (line.trim() !== '') {
|
|
198
|
+
if (!currentStep) {
|
|
199
|
+
currentStep = {
|
|
200
|
+
type: 'action',
|
|
201
|
+
stepNumber: workflow.steps.length + 1,
|
|
202
|
+
actionRaw: line,
|
|
203
|
+
saveAs: null,
|
|
204
|
+
constraints: {}
|
|
205
|
+
};
|
|
206
|
+
} else {
|
|
207
|
+
// Append to current step action (multi-line)
|
|
208
|
+
currentStep.actionRaw += ' ' + line;
|
|
209
|
+
}
|
|
210
|
+
}
|
|
230
211
|
}
|
|
231
|
-
|
|
232
|
-
//
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
212
|
+
|
|
213
|
+
// Don't forget the last step
|
|
214
|
+
if (currentStep) {
|
|
215
|
+
workflow.steps.push(currentStep);
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
// Post-process steps to extract Save as from actionRaw
|
|
219
|
+
workflow.steps.forEach(step => {
|
|
220
|
+
if (step.actionRaw && step.saveAs === null) {
|
|
221
|
+
const saveInAction = step.actionRaw.match(/(.+?)\s+Save as\s+(.+)$/i);
|
|
222
|
+
if (saveInAction) {
|
|
223
|
+
step.actionRaw = saveInAction[1].trim();
|
|
224
|
+
step.saveAs = saveInAction[2].trim();
|
|
225
|
+
}
|
|
243
226
|
}
|
|
227
|
+
});
|
|
228
|
+
|
|
229
|
+
// Check for common issues
|
|
230
|
+
if (!workflow.name) {
|
|
231
|
+
workflow.__warnings.push('Workflow name not found');
|
|
244
232
|
}
|
|
245
|
-
|
|
246
|
-
if (workflow.
|
|
247
|
-
workflow.__warnings.push(
|
|
248
|
-
'No "Allow resolvers" section declared. Workflow will run in restricted mode.'
|
|
249
|
-
);
|
|
233
|
+
|
|
234
|
+
if (workflow.steps.length === 0) {
|
|
235
|
+
workflow.__warnings.push('No steps found in workflow');
|
|
250
236
|
}
|
|
251
|
-
|
|
252
|
-
workflow.
|
|
237
|
+
|
|
238
|
+
if (workflow.returnValues.length === 0 && workflow.steps.length > 0) {
|
|
239
|
+
workflow.__warnings.push('No Return statement found');
|
|
240
|
+
}
|
|
241
|
+
|
|
253
242
|
return workflow;
|
|
254
243
|
}
|
|
255
244
|
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
let current = null;
|
|
262
|
-
for (const line of lines) {
|
|
263
|
-
const stepMatch = line.match(/^Step\s+(\d+)\s*:\s*(.+)$/i);
|
|
264
|
-
if (stepMatch) {
|
|
265
|
-
current = {
|
|
266
|
-
type: 'action',
|
|
267
|
-
stepNumber: parseInt(stepMatch[1], 10),
|
|
268
|
-
actionRaw: stepMatch[2].trim(),
|
|
269
|
-
saveAs: null,
|
|
270
|
-
constraints: {}
|
|
271
|
-
};
|
|
272
|
-
steps.push(current);
|
|
273
|
-
continue;
|
|
274
|
-
}
|
|
275
|
-
const saveMatch = line.match(/^Save as\s+(.+)$/i);
|
|
276
|
-
if (saveMatch && current) current.saveAs = saveMatch[1].trim();
|
|
277
|
-
const debriefMatch = line.match(/^Debrief\s+(\w+)\s+with\s+"(.+)"$/i);
|
|
278
|
-
if (debriefMatch) {
|
|
279
|
-
steps.push({ type: 'debrief', agent: debriefMatch[1], message: debriefMatch[2] });
|
|
280
|
-
}
|
|
281
|
-
const evolveMatch = line.match(/^Evolve\s+(\w+)\s+using\s+feedback:\s+"(.+)"$/i);
|
|
282
|
-
if (evolveMatch) {
|
|
283
|
-
steps.push({ type: 'evolve', agent: evolveMatch[1], feedback: evolveMatch[2] });
|
|
284
|
-
}
|
|
285
|
-
const promptMatch = line.match(/^Prompt user to\s+"(.+)"$/i);
|
|
286
|
-
if (promptMatch) {
|
|
287
|
-
steps.push({ type: 'prompt', question: promptMatch[1], saveAs: null });
|
|
288
|
-
}
|
|
289
|
-
const useMatch = line.match(/^Use\s+(.+)$/i);
|
|
290
|
-
if (useMatch) {
|
|
291
|
-
steps.push({ type: 'use', tool: useMatch[1].trim(), saveAs: null, constraints: {} });
|
|
292
|
-
}
|
|
293
|
-
const askMatch = line.match(/^Ask\s+(.+)$/i);
|
|
294
|
-
if (askMatch) {
|
|
295
|
-
steps.push({ type: 'ask', target: askMatch[1].trim(), saveAs: null, constraints: {} });
|
|
296
|
-
}
|
|
297
|
-
// ✅ Parse file Persist in blocks
|
|
298
|
-
const persistMatch = line.match(/^Persist\s+([^\s]+)\s+to\s+"([^"]+)"$/i);
|
|
299
|
-
if (persistMatch) {
|
|
300
|
-
steps.push({
|
|
301
|
-
type: 'persist',
|
|
302
|
-
source: persistMatch[1].trim(),
|
|
303
|
-
destination: persistMatch[2].trim()
|
|
304
|
-
});
|
|
305
|
-
}
|
|
306
|
-
// ✅ NEW: Parse database Persist in blocks
|
|
307
|
-
const dbPersistMatch = line.match(/^Persist\s+([^\s]+)\s+to\s+db:([^\s]+)$/i);
|
|
308
|
-
if (dbPersistMatch) {
|
|
309
|
-
steps.push({
|
|
310
|
-
type: 'persist-db',
|
|
311
|
-
source: dbPersistMatch[1].trim(),
|
|
312
|
-
collection: dbPersistMatch[2].trim()
|
|
313
|
-
});
|
|
314
|
-
}
|
|
245
|
+
function validate(workflow) {
|
|
246
|
+
const errors = [];
|
|
247
|
+
|
|
248
|
+
if (workflow.maxGenerations !== null && workflow.maxGenerations <= 0) {
|
|
249
|
+
errors.push('max_generations must be positive');
|
|
315
250
|
}
|
|
316
|
-
|
|
251
|
+
|
|
252
|
+
return errors;
|
|
317
253
|
}
|
|
318
254
|
|
|
319
|
-
module.exports = { parse };
|
|
255
|
+
module.exports = { parse, parseFromFile, parseLines, validate };
|
package/src/runtime.js
CHANGED
|
@@ -328,6 +328,35 @@ class RuntimeAPI {
|
|
|
328
328
|
break;
|
|
329
329
|
}
|
|
330
330
|
|
|
331
|
+
case 'evolve': {
|
|
332
|
+
// ✅ Handle in-workflow Evolve steps
|
|
333
|
+
const { targetResolver, feedback } = step;
|
|
334
|
+
|
|
335
|
+
if (this.verbose) {
|
|
336
|
+
console.log(`🔄 Evolve step: ${targetResolver} with feedback: "${feedback}"`);
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
// Basic evolution: record the request (free tier)
|
|
340
|
+
const evolutionResult = {
|
|
341
|
+
resolver: targetResolver,
|
|
342
|
+
feedback: feedback,
|
|
343
|
+
status: 'evolution_requested',
|
|
344
|
+
timestamp: new Date().toISOString(),
|
|
345
|
+
workflow: this.context.workflow_name
|
|
346
|
+
};
|
|
347
|
+
|
|
348
|
+
// ✅ Check for Advanced Evolution Service (paid tier)
|
|
349
|
+
if (process.env.OLANG_EVOLUTION_API_KEY) {
|
|
350
|
+
evolutionResult.status = 'advanced_evolution_enabled';
|
|
351
|
+
evolution resultList.message = 'Advanced evolution service would process this request';
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
if (step.saveAs) {
|
|
355
|
+
this.context[step.saveAs] = evolutionResult;
|
|
356
|
+
}
|
|
357
|
+
break;
|
|
358
|
+
}
|
|
359
|
+
|
|
331
360
|
case 'if': {
|
|
332
361
|
if (this.evaluateCondition(step.condition, this.context)) {
|
|
333
362
|
for (const s of step.body) await this.executeStep(s, agentResolver);
|
|
@@ -452,11 +481,23 @@ class RuntimeAPI {
|
|
|
452
481
|
}
|
|
453
482
|
|
|
454
483
|
async executeWorkflow(workflow, inputs, agentResolver) {
|
|
484
|
+
// Handle regular workflows only (Evolve is a step type now)
|
|
485
|
+
if (workflow.type !== 'workflow') {
|
|
486
|
+
throw new Error(`Unknown workflow type: ${workflow.type}`);
|
|
487
|
+
}
|
|
488
|
+
|
|
455
489
|
// ✅ Inject workflow name into context
|
|
456
490
|
this.context = {
|
|
457
491
|
...inputs,
|
|
458
492
|
workflow_name: workflow.name
|
|
459
493
|
};
|
|
494
|
+
|
|
495
|
+
// ✅ Check generation constraint from Constraint: max_generations = X
|
|
496
|
+
const currentGeneration = inputs.__generation || 1;
|
|
497
|
+
if (workflow.maxGenerations !== null && currentGeneration > workflow.maxGenerations) {
|
|
498
|
+
throw new Error(`Workflow generation ${currentGeneration} exceeds Constraint: max_generations = ${workflow.maxGenerations}`);
|
|
499
|
+
}
|
|
500
|
+
|
|
460
501
|
this.workflowSteps = workflow.steps;
|
|
461
502
|
this.allowedResolvers = new Set(workflow.allowedResolvers || []);
|
|
462
503
|
|