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