@o-lang/olang 1.0.7 → 1.0.8
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 +55 -21
- package/src/runtime.js +79 -42
package/package.json
CHANGED
package/src/parser.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// src/parser.js
|
|
2
2
|
|
|
3
3
|
function parse(code, fileName = null) {
|
|
4
|
-
// ---
|
|
4
|
+
// --- Enforce .ol extension if filename provided ---
|
|
5
5
|
if (fileName && !fileName.endsWith(".ol")) {
|
|
6
6
|
throw new Error(`Expected .ol workflow, got: ${fileName}`);
|
|
7
7
|
}
|
|
@@ -15,18 +15,30 @@ function parse(code, fileName = null) {
|
|
|
15
15
|
name: 'Unnamed Workflow',
|
|
16
16
|
parameters: [],
|
|
17
17
|
steps: [],
|
|
18
|
-
returnValues: []
|
|
18
|
+
returnValues: [],
|
|
19
|
+
allowedResolvers: [] // NEW: store allowed resolvers
|
|
19
20
|
};
|
|
20
21
|
|
|
21
22
|
let i = 0;
|
|
22
23
|
while (i < lines.length) {
|
|
23
24
|
let line = lines[i];
|
|
24
25
|
|
|
26
|
+
// ---------------------------
|
|
27
|
+
// NEW: Detect Allow resolvers
|
|
28
|
+
// ---------------------------
|
|
29
|
+
const allowMatch = line.match(/^Allow resolvers\s*:\s*$/i);
|
|
30
|
+
if (allowMatch) {
|
|
31
|
+
i++;
|
|
32
|
+
while (i < lines.length && lines[i].startsWith(' ')) {
|
|
33
|
+
workflow.allowedResolvers.push(lines[i].trim());
|
|
34
|
+
i++;
|
|
35
|
+
}
|
|
36
|
+
continue;
|
|
37
|
+
}
|
|
38
|
+
|
|
25
39
|
// ============================
|
|
26
|
-
//
|
|
40
|
+
// Math operations
|
|
27
41
|
// ============================
|
|
28
|
-
|
|
29
|
-
// Add X and Y
|
|
30
42
|
let mathAdd = line.match(/^Add\s+\{(.+?)\}\s+and\s+\{(.+?)\}\s+Save as\s+(.+)$/i);
|
|
31
43
|
if (mathAdd) {
|
|
32
44
|
workflow.steps.push({
|
|
@@ -38,7 +50,6 @@ function parse(code, fileName = null) {
|
|
|
38
50
|
continue;
|
|
39
51
|
}
|
|
40
52
|
|
|
41
|
-
// Subtract A from B => B - A
|
|
42
53
|
let mathSub = line.match(/^Subtract\s+\{(.+?)\}\s+from\s+\{(.+?)\}\s+Save as\s+(.+)$/i);
|
|
43
54
|
if (mathSub) {
|
|
44
55
|
workflow.steps.push({
|
|
@@ -50,7 +61,6 @@ function parse(code, fileName = null) {
|
|
|
50
61
|
continue;
|
|
51
62
|
}
|
|
52
63
|
|
|
53
|
-
// Multiply X and Y
|
|
54
64
|
let mathMul = line.match(/^Multiply\s+\{(.+?)\}\s+and\s+\{(.+?)\}\s+Save as\s+(.+)$/i);
|
|
55
65
|
if (mathMul) {
|
|
56
66
|
workflow.steps.push({
|
|
@@ -62,7 +72,6 @@ function parse(code, fileName = null) {
|
|
|
62
72
|
continue;
|
|
63
73
|
}
|
|
64
74
|
|
|
65
|
-
// Divide A by B
|
|
66
75
|
let mathDiv = line.match(/^Divide\s+\{(.+?)\}\s+by\s+\{(.+?)\}\s+Save as\s+(.+)$/i);
|
|
67
76
|
if (mathDiv) {
|
|
68
77
|
workflow.steps.push({
|
|
@@ -74,11 +83,9 @@ function parse(code, fileName = null) {
|
|
|
74
83
|
continue;
|
|
75
84
|
}
|
|
76
85
|
|
|
77
|
-
//
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
// Workflow
|
|
86
|
+
// ---------------------------
|
|
87
|
+
// Workflow definition
|
|
88
|
+
// ---------------------------
|
|
82
89
|
const wfMatch = line.match(/^Workflow\s+"([^"]+)"(?:\s+with\s+(.+))?/i);
|
|
83
90
|
if (wfMatch) {
|
|
84
91
|
workflow.name = wfMatch[1];
|
|
@@ -87,7 +94,9 @@ function parse(code, fileName = null) {
|
|
|
87
94
|
continue;
|
|
88
95
|
}
|
|
89
96
|
|
|
90
|
-
//
|
|
97
|
+
// ---------------------------
|
|
98
|
+
// Steps
|
|
99
|
+
// ---------------------------
|
|
91
100
|
const stepMatch = line.match(/^Step\s+(\d+)\s*:\s*(.+)$/i);
|
|
92
101
|
if (stepMatch) {
|
|
93
102
|
workflow.steps.push({
|
|
@@ -101,7 +110,6 @@ function parse(code, fileName = null) {
|
|
|
101
110
|
continue;
|
|
102
111
|
}
|
|
103
112
|
|
|
104
|
-
// Save as
|
|
105
113
|
const saveMatch = line.match(/^Save as\s+(.+)$/i);
|
|
106
114
|
if (saveMatch && workflow.steps.length > 0) {
|
|
107
115
|
workflow.steps[workflow.steps.length - 1].saveAs = saveMatch[1].trim();
|
|
@@ -109,7 +117,6 @@ function parse(code, fileName = null) {
|
|
|
109
117
|
continue;
|
|
110
118
|
}
|
|
111
119
|
|
|
112
|
-
// Constraint
|
|
113
120
|
const constraintMatch = line.match(/^Constraint:\s*(.+)$/i);
|
|
114
121
|
if (constraintMatch && workflow.steps.length > 0) {
|
|
115
122
|
const lastStep = workflow.steps[workflow.steps.length - 1];
|
|
@@ -135,7 +142,9 @@ function parse(code, fileName = null) {
|
|
|
135
142
|
continue;
|
|
136
143
|
}
|
|
137
144
|
|
|
138
|
-
//
|
|
145
|
+
// ---------------------------
|
|
146
|
+
// If blocks
|
|
147
|
+
// ---------------------------
|
|
139
148
|
const ifMatch = line.match(/^If\s+(.+)\s+then$/i);
|
|
140
149
|
if (ifMatch) {
|
|
141
150
|
const condition = ifMatch[1].trim();
|
|
@@ -150,7 +159,9 @@ function parse(code, fileName = null) {
|
|
|
150
159
|
continue;
|
|
151
160
|
}
|
|
152
161
|
|
|
153
|
-
//
|
|
162
|
+
// ---------------------------
|
|
163
|
+
// Parallel blocks
|
|
164
|
+
// ---------------------------
|
|
154
165
|
const parMatch = line.match(/^Run in parallel$/i);
|
|
155
166
|
if (parMatch) {
|
|
156
167
|
const steps = [];
|
|
@@ -164,7 +175,9 @@ function parse(code, fileName = null) {
|
|
|
164
175
|
continue;
|
|
165
176
|
}
|
|
166
177
|
|
|
178
|
+
// ---------------------------
|
|
167
179
|
// Connect
|
|
180
|
+
// ---------------------------
|
|
168
181
|
const connMatch = line.match(/^Connect\s+"([^"]+)"\s+using\s+"([^"]+)"$/i);
|
|
169
182
|
if (connMatch) {
|
|
170
183
|
workflow.steps.push({
|
|
@@ -176,7 +189,9 @@ function parse(code, fileName = null) {
|
|
|
176
189
|
continue;
|
|
177
190
|
}
|
|
178
191
|
|
|
192
|
+
// ---------------------------
|
|
179
193
|
// Agent uses
|
|
194
|
+
// ---------------------------
|
|
180
195
|
const agentUseMatch = line.match(/^Agent\s+"([^"]+)"\s+uses\s+"([^"]+)"$/i);
|
|
181
196
|
if (agentUseMatch) {
|
|
182
197
|
workflow.steps.push({
|
|
@@ -188,7 +203,9 @@ function parse(code, fileName = null) {
|
|
|
188
203
|
continue;
|
|
189
204
|
}
|
|
190
205
|
|
|
206
|
+
// ---------------------------
|
|
191
207
|
// Debrief
|
|
208
|
+
// ---------------------------
|
|
192
209
|
const debriefMatch = line.match(/^Debrief\s+(\w+)\s+with\s+"(.+)"$/i);
|
|
193
210
|
if (debriefMatch) {
|
|
194
211
|
workflow.steps.push({
|
|
@@ -200,7 +217,9 @@ function parse(code, fileName = null) {
|
|
|
200
217
|
continue;
|
|
201
218
|
}
|
|
202
219
|
|
|
220
|
+
// ---------------------------
|
|
203
221
|
// Evolve
|
|
222
|
+
// ---------------------------
|
|
204
223
|
const evolveMatch = line.match(/^Evolve\s+(\w+)\s+using\s+feedback:\s+"(.+)"$/i);
|
|
205
224
|
if (evolveMatch) {
|
|
206
225
|
workflow.steps.push({
|
|
@@ -212,7 +231,9 @@ function parse(code, fileName = null) {
|
|
|
212
231
|
continue;
|
|
213
232
|
}
|
|
214
233
|
|
|
215
|
-
//
|
|
234
|
+
// ---------------------------
|
|
235
|
+
// Prompt user
|
|
236
|
+
// ---------------------------
|
|
216
237
|
const promptMatch = line.match(/^Prompt user to\s+"(.+)"$/i);
|
|
217
238
|
if (promptMatch) {
|
|
218
239
|
workflow.steps.push({
|
|
@@ -224,7 +245,9 @@ function parse(code, fileName = null) {
|
|
|
224
245
|
continue;
|
|
225
246
|
}
|
|
226
247
|
|
|
248
|
+
// ---------------------------
|
|
227
249
|
// Persist
|
|
250
|
+
// ---------------------------
|
|
228
251
|
const persistMatch = line.match(/^Persist\s+(.+)\s+to\s+"(.+)"$/i);
|
|
229
252
|
if (persistMatch) {
|
|
230
253
|
workflow.steps.push({
|
|
@@ -236,7 +259,9 @@ function parse(code, fileName = null) {
|
|
|
236
259
|
continue;
|
|
237
260
|
}
|
|
238
261
|
|
|
262
|
+
// ---------------------------
|
|
239
263
|
// Emit
|
|
264
|
+
// ---------------------------
|
|
240
265
|
const emitMatch = line.match(/^Emit\s+"(.+)"\s+with\s+(.+)$/i);
|
|
241
266
|
if (emitMatch) {
|
|
242
267
|
workflow.steps.push({
|
|
@@ -248,7 +273,9 @@ function parse(code, fileName = null) {
|
|
|
248
273
|
continue;
|
|
249
274
|
}
|
|
250
275
|
|
|
276
|
+
// ---------------------------
|
|
251
277
|
// Return
|
|
278
|
+
// ---------------------------
|
|
252
279
|
const returnMatch = line.match(/^Return\s+(.+)$/i);
|
|
253
280
|
if (returnMatch) {
|
|
254
281
|
workflow.returnValues = returnMatch[1].split(',').map(v => v.trim());
|
|
@@ -256,7 +283,9 @@ function parse(code, fileName = null) {
|
|
|
256
283
|
continue;
|
|
257
284
|
}
|
|
258
285
|
|
|
259
|
-
//
|
|
286
|
+
// ---------------------------
|
|
287
|
+
// Use tool
|
|
288
|
+
// ---------------------------
|
|
260
289
|
const useMatch = line.match(/^Use\s+(.+)$/i);
|
|
261
290
|
if (useMatch) {
|
|
262
291
|
workflow.steps.push({
|
|
@@ -269,7 +298,9 @@ function parse(code, fileName = null) {
|
|
|
269
298
|
continue;
|
|
270
299
|
}
|
|
271
300
|
|
|
272
|
-
//
|
|
301
|
+
// ---------------------------
|
|
302
|
+
// Ask target
|
|
303
|
+
// ---------------------------
|
|
273
304
|
const askMatch = line.match(/^Ask\s+(.+)$/i);
|
|
274
305
|
if (askMatch) {
|
|
275
306
|
workflow.steps.push({
|
|
@@ -288,6 +319,9 @@ function parse(code, fileName = null) {
|
|
|
288
319
|
return workflow;
|
|
289
320
|
}
|
|
290
321
|
|
|
322
|
+
// ---------------------------
|
|
323
|
+
// Parse nested blocks (If / Parallel)
|
|
324
|
+
// ---------------------------
|
|
291
325
|
function parseBlock(lines) {
|
|
292
326
|
const steps = [];
|
|
293
327
|
let current = null;
|
package/src/runtime.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
const fs = require('fs');
|
|
2
|
+
const path = require('path');
|
|
2
3
|
|
|
3
4
|
class RuntimeAPI {
|
|
4
5
|
constructor({ verbose = false } = {}) {
|
|
@@ -6,10 +7,22 @@ class RuntimeAPI {
|
|
|
6
7
|
this.resources = {};
|
|
7
8
|
this.agentMap = {};
|
|
8
9
|
this.events = {};
|
|
9
|
-
this.workflowSteps = [];
|
|
10
|
-
this.
|
|
10
|
+
this.workflowSteps = [];
|
|
11
|
+
this.allowedResolvers = null;
|
|
12
|
+
this.verbose = verbose;
|
|
13
|
+
|
|
14
|
+
// Ensure logs folder exists
|
|
15
|
+
const logsDir = path.resolve('./logs');
|
|
16
|
+
if (!fs.existsSync(logsDir)) fs.mkdirSync(logsDir, { recursive: true });
|
|
17
|
+
this.disallowedLogFile = path.join(logsDir, 'disallowed_resolvers.json');
|
|
18
|
+
|
|
19
|
+
// In-memory store for summary
|
|
20
|
+
this.disallowedAttempts = [];
|
|
11
21
|
}
|
|
12
22
|
|
|
23
|
+
// -----------------------------
|
|
24
|
+
// Event handling
|
|
25
|
+
// -----------------------------
|
|
13
26
|
on(eventName, cb) {
|
|
14
27
|
if (!this.events[eventName]) this.events[eventName] = [];
|
|
15
28
|
this.events[eventName].push(cb);
|
|
@@ -21,6 +34,40 @@ class RuntimeAPI {
|
|
|
21
34
|
}
|
|
22
35
|
}
|
|
23
36
|
|
|
37
|
+
// -----------------------------
|
|
38
|
+
// Disallowed resolver handling
|
|
39
|
+
// -----------------------------
|
|
40
|
+
logDisallowedResolver(resolverName, stepAction) {
|
|
41
|
+
const entry = {
|
|
42
|
+
resolver: resolverName,
|
|
43
|
+
step: stepAction,
|
|
44
|
+
timestamp: new Date().toISOString()
|
|
45
|
+
};
|
|
46
|
+
fs.appendFileSync(this.disallowedLogFile, JSON.stringify(entry) + '\n', 'utf8');
|
|
47
|
+
this.disallowedAttempts.push(entry);
|
|
48
|
+
|
|
49
|
+
if (this.verbose) {
|
|
50
|
+
console.warn(`[O-Lang] Disallowed resolver blocked: ${resolverName} | step: ${stepAction}`);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
printDisallowedSummary() {
|
|
55
|
+
if (!this.disallowedAttempts.length) return;
|
|
56
|
+
console.log('\n[O-Lang] ⚠️ Disallowed resolver summary:');
|
|
57
|
+
console.log(`Total blocked attempts: ${this.disallowedAttempts.length}`);
|
|
58
|
+
const displayCount = Math.min(5, this.disallowedAttempts.length);
|
|
59
|
+
console.log(`First ${displayCount} entries:`);
|
|
60
|
+
this.disallowedAttempts.slice(0, displayCount).forEach((e, i) => {
|
|
61
|
+
console.log(`${i + 1}. Resolver: ${e.resolver}, Step: ${e.step}, Time: ${e.timestamp}`);
|
|
62
|
+
});
|
|
63
|
+
if (this.disallowedAttempts.length > displayCount) {
|
|
64
|
+
console.log(`...and ${this.disallowedAttempts.length - displayCount} more entries logged in ${this.disallowedLogFile}`);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// -----------------------------
|
|
69
|
+
// Condition & math utilities
|
|
70
|
+
// -----------------------------
|
|
24
71
|
evaluateCondition(cond, ctx) {
|
|
25
72
|
cond = cond.trim();
|
|
26
73
|
const eq = cond.match(/^\{(.+)\}\s+equals\s+"(.*)"$/);
|
|
@@ -37,16 +84,6 @@ class RuntimeAPI {
|
|
|
37
84
|
return path.split('.').reduce((o, k) => (o && o[k] !== undefined) ? o[k] : undefined, obj);
|
|
38
85
|
}
|
|
39
86
|
|
|
40
|
-
findLastSummaryStep() {
|
|
41
|
-
for (let i = this.workflowSteps.length - 1; i >= 0; i--) {
|
|
42
|
-
const step = this.workflowSteps[i];
|
|
43
|
-
if (step.type === 'action' && step.actionRaw?.startsWith('Ask ') && step.saveAs) {
|
|
44
|
-
return step;
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
return null;
|
|
48
|
-
}
|
|
49
|
-
|
|
50
87
|
mathFunctions = {
|
|
51
88
|
add: (a, b) => a + b,
|
|
52
89
|
subtract: (a, b) => a - b,
|
|
@@ -76,9 +113,7 @@ class RuntimeAPI {
|
|
|
76
113
|
|
|
77
114
|
const funcNames = Object.keys(this.mathFunctions);
|
|
78
115
|
const safeFunc = {};
|
|
79
|
-
funcNames.forEach(fn =>
|
|
80
|
-
safeFunc[fn] = this.mathFunctions[fn];
|
|
81
|
-
});
|
|
116
|
+
funcNames.forEach(fn => safeFunc[fn] = this.mathFunctions[fn]);
|
|
82
117
|
|
|
83
118
|
try {
|
|
84
119
|
const f = new Function(...funcNames, `return ${expr};`);
|
|
@@ -89,18 +124,37 @@ class RuntimeAPI {
|
|
|
89
124
|
}
|
|
90
125
|
}
|
|
91
126
|
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
127
|
+
findLastSummaryStep() {
|
|
128
|
+
for (let i = this.workflowSteps.length - 1; i >= 0; i--) {
|
|
129
|
+
const step = this.workflowSteps[i];
|
|
130
|
+
if (step.type === 'action' && step.actionRaw?.startsWith('Ask ') && step.saveAs) {
|
|
131
|
+
return step;
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
return null;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
// -----------------------------
|
|
138
|
+
// Step execution
|
|
139
|
+
// -----------------------------
|
|
95
140
|
async executeStep(step, agentResolver) {
|
|
96
141
|
const stepType = step.type;
|
|
97
142
|
|
|
98
|
-
|
|
143
|
+
const validateResolver = (resolver) => {
|
|
144
|
+
const resolverName = resolver?.name || resolver?.resolverName;
|
|
145
|
+
if (!resolverName) throw new Error('[O-Lang] Resolver missing name metadata');
|
|
146
|
+
if (!this.allowedResolvers.has(resolverName)) {
|
|
147
|
+
this.logDisallowedResolver(resolverName, step.actionRaw || step.tool || step.target);
|
|
148
|
+
throw new Error(`[O-Lang] Resolver "${resolverName}" is not allowed by workflow policy`);
|
|
149
|
+
}
|
|
150
|
+
};
|
|
151
|
+
|
|
99
152
|
const runAllResolvers = async (action) => {
|
|
100
153
|
const outputs = [];
|
|
101
154
|
if (agentResolver && Array.isArray(agentResolver._chain)) {
|
|
102
155
|
for (let idx = 0; idx < agentResolver._chain.length; idx++) {
|
|
103
156
|
const resolver = agentResolver._chain[idx];
|
|
157
|
+
validateResolver(resolver);
|
|
104
158
|
try {
|
|
105
159
|
const out = await resolver(action, this.context);
|
|
106
160
|
outputs.push(out);
|
|
@@ -111,11 +165,12 @@ class RuntimeAPI {
|
|
|
111
165
|
}
|
|
112
166
|
}
|
|
113
167
|
} else {
|
|
168
|
+
validateResolver(agentResolver);
|
|
114
169
|
const out = await agentResolver(action, this.context);
|
|
115
170
|
outputs.push(out);
|
|
116
171
|
this.context['__resolver_0'] = out;
|
|
117
172
|
}
|
|
118
|
-
return outputs[outputs.length - 1];
|
|
173
|
+
return outputs[outputs.length - 1];
|
|
119
174
|
};
|
|
120
175
|
|
|
121
176
|
switch (stepType) {
|
|
@@ -125,13 +180,11 @@ class RuntimeAPI {
|
|
|
125
180
|
if (step.saveAs) this.context[step.saveAs] = result;
|
|
126
181
|
break;
|
|
127
182
|
}
|
|
128
|
-
|
|
129
183
|
case 'action': {
|
|
130
184
|
const action = step.actionRaw.replace(/\{([^\}]+)\}/g, (_, path) => {
|
|
131
185
|
const value = this.getNested(this.context, path.trim());
|
|
132
186
|
return value !== undefined ? String(value) : `{${path}}`;
|
|
133
187
|
});
|
|
134
|
-
|
|
135
188
|
const mathCall = action.match(/^(add|subtract|multiply|divide|sum|avg|min|max|round|floor|ceil|abs)\((.*)\)$/i);
|
|
136
189
|
if (mathCall) {
|
|
137
190
|
const fn = mathCall[1].toLowerCase();
|
|
@@ -148,97 +201,79 @@ class RuntimeAPI {
|
|
|
148
201
|
break;
|
|
149
202
|
}
|
|
150
203
|
}
|
|
151
|
-
|
|
152
204
|
const res = await runAllResolvers(action);
|
|
153
205
|
if (step.saveAs) this.context[step.saveAs] = res;
|
|
154
206
|
break;
|
|
155
207
|
}
|
|
156
|
-
|
|
157
208
|
case 'use': {
|
|
158
209
|
const res = await runAllResolvers(`Use ${step.tool}`);
|
|
159
210
|
if (step.saveAs) this.context[step.saveAs] = res;
|
|
160
211
|
break;
|
|
161
212
|
}
|
|
162
|
-
|
|
163
213
|
case 'ask': {
|
|
164
214
|
const res = await runAllResolvers(`Ask ${step.target}`);
|
|
165
215
|
if (step.saveAs) this.context[step.saveAs] = res;
|
|
166
216
|
break;
|
|
167
217
|
}
|
|
168
|
-
|
|
169
218
|
case 'if': {
|
|
170
219
|
if (this.evaluateCondition(step.condition, this.context)) {
|
|
171
220
|
for (const s of step.body) await this.executeStep(s, agentResolver);
|
|
172
221
|
}
|
|
173
222
|
break;
|
|
174
223
|
}
|
|
175
|
-
|
|
176
224
|
case 'parallel': {
|
|
177
225
|
await Promise.all(step.steps.map(s => this.executeStep(s, agentResolver)));
|
|
178
226
|
break;
|
|
179
227
|
}
|
|
180
|
-
|
|
181
228
|
case 'connect': {
|
|
182
229
|
this.resources[step.resource] = step.endpoint;
|
|
183
230
|
break;
|
|
184
231
|
}
|
|
185
|
-
|
|
186
232
|
case 'agent_use': {
|
|
187
233
|
this.agentMap[step.logicalName] = step.resource;
|
|
188
234
|
break;
|
|
189
235
|
}
|
|
190
|
-
|
|
191
236
|
case 'debrief': {
|
|
192
237
|
this.emit('debrief', { agent: step.agent, message: step.message });
|
|
193
238
|
break;
|
|
194
239
|
}
|
|
195
|
-
|
|
196
240
|
case 'evolve': {
|
|
197
241
|
const maxGen = step.constraints?.max_generations || 1;
|
|
198
242
|
if (maxGen < 1) {
|
|
199
243
|
this.context['improved_summary'] = this.context['summary'] || '';
|
|
200
244
|
return;
|
|
201
245
|
}
|
|
202
|
-
|
|
203
246
|
const summaryStep = this.findLastSummaryStep();
|
|
204
247
|
if (!summaryStep) {
|
|
205
248
|
console.warn('[O-Lang] Evolve: No prior "Ask ... Save as" step found to evolve');
|
|
206
249
|
this.context['improved_summary'] = this.context['summary'] || '';
|
|
207
250
|
return;
|
|
208
251
|
}
|
|
209
|
-
|
|
210
252
|
const varName = summaryStep.saveAs;
|
|
211
253
|
let currentOutput = this.context[varName] || '';
|
|
212
|
-
|
|
213
254
|
for (let attempt = 0; attempt < maxGen; attempt++) {
|
|
214
255
|
let revisedAction = summaryStep.actionRaw.replace(/\{([^\}]+)\}/g, (_, path) => {
|
|
215
256
|
const val = this.getNested(this.context, path.trim());
|
|
216
257
|
return val !== undefined ? String(val) : `{${path}}`;
|
|
217
258
|
});
|
|
218
|
-
|
|
219
259
|
if (step.feedback) {
|
|
220
260
|
revisedAction = revisedAction.replace(/(")$/, `\n\n[IMPROVEMENT FEEDBACK: ${step.feedback}]$1`);
|
|
221
261
|
}
|
|
222
|
-
|
|
223
262
|
currentOutput = await runAllResolvers(revisedAction);
|
|
224
263
|
this.context[varName] = currentOutput;
|
|
225
|
-
|
|
226
264
|
this.emit('debrief', {
|
|
227
265
|
agent: step.agent || 'Evolver',
|
|
228
266
|
message: `Evolve attempt ${attempt + 1}/${maxGen}: ${currentOutput.substring(0, 80)}...`
|
|
229
267
|
});
|
|
230
268
|
}
|
|
231
|
-
|
|
232
269
|
this.context['improved_summary'] = currentOutput;
|
|
233
270
|
break;
|
|
234
271
|
}
|
|
235
|
-
|
|
236
272
|
case 'prompt': {
|
|
237
273
|
const input = await this.getUserInput(step.question);
|
|
238
274
|
if (step.saveAs) this.context[step.saveAs] = input;
|
|
239
275
|
break;
|
|
240
276
|
}
|
|
241
|
-
|
|
242
277
|
case 'persist': {
|
|
243
278
|
const val = this.context[step.variable];
|
|
244
279
|
if (val !== undefined) {
|
|
@@ -246,7 +281,6 @@ class RuntimeAPI {
|
|
|
246
281
|
}
|
|
247
282
|
break;
|
|
248
283
|
}
|
|
249
|
-
|
|
250
284
|
case 'emit': {
|
|
251
285
|
const payload = step.payload ? this.getNested(this.context, step.payload) : undefined;
|
|
252
286
|
this.emit(step.event, payload || step.payload);
|
|
@@ -254,7 +288,6 @@ class RuntimeAPI {
|
|
|
254
288
|
}
|
|
255
289
|
}
|
|
256
290
|
|
|
257
|
-
// Verbose logging of context after each step
|
|
258
291
|
if (this.verbose) {
|
|
259
292
|
console.log(`\n[Step: ${step.type} | saveAs: ${step.saveAs || 'N/A'}]`);
|
|
260
293
|
console.log(JSON.stringify(this.context, null, 2));
|
|
@@ -275,11 +308,15 @@ class RuntimeAPI {
|
|
|
275
308
|
async executeWorkflow(workflow, inputs, agentResolver) {
|
|
276
309
|
this.context = { ...inputs };
|
|
277
310
|
this.workflowSteps = workflow.steps;
|
|
311
|
+
this.allowedResolvers = new Set(workflow.allowedResolvers || []);
|
|
278
312
|
|
|
279
313
|
for (const step of workflow.steps) {
|
|
280
314
|
await this.executeStep(step, agentResolver);
|
|
281
315
|
}
|
|
282
316
|
|
|
317
|
+
// Print disallowed resolver summary at the end
|
|
318
|
+
this.printDisallowedSummary();
|
|
319
|
+
|
|
283
320
|
const result = {};
|
|
284
321
|
for (const key of workflow.returnValues) {
|
|
285
322
|
result[key] = this.getNested(this.context, key);
|