@o-lang/olang 1.0.8 → 1.0.10
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 +67 -53
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
|
}
|
|
@@ -141,15 +145,28 @@ class RuntimeAPI {
|
|
|
141
145
|
const stepType = step.type;
|
|
142
146
|
|
|
143
147
|
const validateResolver = (resolver) => {
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
148
|
+
// Get resolver name from metadata, trim whitespace
|
|
149
|
+
const resolverName = (resolver?.resolverName || resolver?.name || '').trim();
|
|
150
|
+
|
|
151
|
+
if (!resolverName) throw new Error('[O-Lang] Resolver missing name metadata');
|
|
152
|
+
|
|
153
|
+
// Normalize allowed resolver names for comparison
|
|
154
|
+
const allowed = Array.from(this.allowedResolvers || []).map(r => r.trim());
|
|
155
|
+
|
|
156
|
+
// Auto-inject builtInMathResolver if math is required
|
|
157
|
+
if (resolverName === 'builtInMathResolver' && workflow.__requiresMath && !allowed.includes('builtInMathResolver')) {
|
|
158
|
+
this.allowedResolvers.add('builtInMathResolver');
|
|
159
|
+
allowed.push('builtInMathResolver');
|
|
160
|
+
}
|
|
151
161
|
|
|
152
|
-
|
|
162
|
+
if (!allowed.includes(resolverName)) {
|
|
163
|
+
this.logDisallowedResolver(resolverName, step.actionRaw || step.tool || step.target);
|
|
164
|
+
throw new Error(`[O-Lang] Resolver "${resolverName}" is not allowed by workflow policy`);
|
|
165
|
+
}
|
|
166
|
+
};
|
|
167
|
+
|
|
168
|
+
|
|
169
|
+
const runResolvers = async (action) => {
|
|
153
170
|
const outputs = [];
|
|
154
171
|
if (agentResolver && Array.isArray(agentResolver._chain)) {
|
|
155
172
|
for (let idx = 0; idx < agentResolver._chain.length; idx++) {
|
|
@@ -160,7 +177,7 @@ class RuntimeAPI {
|
|
|
160
177
|
outputs.push(out);
|
|
161
178
|
this.context[`__resolver_${idx}`] = out;
|
|
162
179
|
} catch (e) {
|
|
163
|
-
|
|
180
|
+
this.addWarning(`Resolver ${resolver?.name || idx} failed for action "${action}": ${e.message}`);
|
|
164
181
|
outputs.push(null);
|
|
165
182
|
}
|
|
166
183
|
}
|
|
@@ -173,10 +190,10 @@ class RuntimeAPI {
|
|
|
173
190
|
return outputs[outputs.length - 1];
|
|
174
191
|
};
|
|
175
192
|
|
|
193
|
+
// --- execute based on step.type ---
|
|
176
194
|
switch (stepType) {
|
|
177
195
|
case 'calculate': {
|
|
178
|
-
const
|
|
179
|
-
const result = this.evaluateMath(expr);
|
|
196
|
+
const result = this.evaluateMath(step.expression || step.actionRaw);
|
|
180
197
|
if (step.saveAs) this.context[step.saveAs] = result;
|
|
181
198
|
break;
|
|
182
199
|
}
|
|
@@ -185,6 +202,7 @@ class RuntimeAPI {
|
|
|
185
202
|
const value = this.getNested(this.context, path.trim());
|
|
186
203
|
return value !== undefined ? String(value) : `{${path}}`;
|
|
187
204
|
});
|
|
205
|
+
|
|
188
206
|
const mathCall = action.match(/^(add|subtract|multiply|divide|sum|avg|min|max|round|floor|ceil|abs)\((.*)\)$/i);
|
|
189
207
|
if (mathCall) {
|
|
190
208
|
const fn = mathCall[1].toLowerCase();
|
|
@@ -192,8 +210,7 @@ class RuntimeAPI {
|
|
|
192
210
|
const args = argsRaw.split(',').map(s => s.trim()).map(s => {
|
|
193
211
|
if (/^".*"$/.test(s) || /^'.*'$/.test(s)) return s.slice(1, -1);
|
|
194
212
|
if (!isNaN(s)) return parseFloat(s);
|
|
195
|
-
|
|
196
|
-
return this.getNested(this.context, lookup);
|
|
213
|
+
return this.getNested(this.context, s.replace(/^\{|\}$/g, '').trim());
|
|
197
214
|
});
|
|
198
215
|
if (this.mathFunctions[fn]) {
|
|
199
216
|
const value = this.mathFunctions[fn](...args);
|
|
@@ -201,17 +218,18 @@ class RuntimeAPI {
|
|
|
201
218
|
break;
|
|
202
219
|
}
|
|
203
220
|
}
|
|
204
|
-
|
|
221
|
+
|
|
222
|
+
const res = await runResolvers(action);
|
|
205
223
|
if (step.saveAs) this.context[step.saveAs] = res;
|
|
206
224
|
break;
|
|
207
225
|
}
|
|
208
226
|
case 'use': {
|
|
209
|
-
const res = await
|
|
227
|
+
const res = await runResolvers(`Use ${step.tool}`);
|
|
210
228
|
if (step.saveAs) this.context[step.saveAs] = res;
|
|
211
229
|
break;
|
|
212
230
|
}
|
|
213
231
|
case 'ask': {
|
|
214
|
-
const res = await
|
|
232
|
+
const res = await runResolvers(`Ask ${step.target}`);
|
|
215
233
|
if (step.saveAs) this.context[step.saveAs] = res;
|
|
216
234
|
break;
|
|
217
235
|
}
|
|
@@ -239,14 +257,10 @@ class RuntimeAPI {
|
|
|
239
257
|
}
|
|
240
258
|
case 'evolve': {
|
|
241
259
|
const maxGen = step.constraints?.max_generations || 1;
|
|
242
|
-
if (maxGen < 1)
|
|
243
|
-
this.context['improved_summary'] = this.context['summary'] || '';
|
|
244
|
-
return;
|
|
245
|
-
}
|
|
260
|
+
if (maxGen < 1) return;
|
|
246
261
|
const summaryStep = this.findLastSummaryStep();
|
|
247
262
|
if (!summaryStep) {
|
|
248
|
-
|
|
249
|
-
this.context['improved_summary'] = this.context['summary'] || '';
|
|
263
|
+
this.addWarning('Evolve step has no prior "Ask ... Save as" step to evolve');
|
|
250
264
|
return;
|
|
251
265
|
}
|
|
252
266
|
const varName = summaryStep.saveAs;
|
|
@@ -256,10 +270,8 @@ class RuntimeAPI {
|
|
|
256
270
|
const val = this.getNested(this.context, path.trim());
|
|
257
271
|
return val !== undefined ? String(val) : `{${path}}`;
|
|
258
272
|
});
|
|
259
|
-
if (step.feedback) {
|
|
260
|
-
|
|
261
|
-
}
|
|
262
|
-
currentOutput = await runAllResolvers(revisedAction);
|
|
273
|
+
if (step.feedback) revisedAction += `\n[IMPROVEMENT FEEDBACK: ${step.feedback}]`;
|
|
274
|
+
currentOutput = await runResolvers(revisedAction);
|
|
263
275
|
this.context[varName] = currentOutput;
|
|
264
276
|
this.emit('debrief', {
|
|
265
277
|
agent: step.agent || 'Evolver',
|
|
@@ -276,9 +288,7 @@ class RuntimeAPI {
|
|
|
276
288
|
}
|
|
277
289
|
case 'persist': {
|
|
278
290
|
const val = this.context[step.variable];
|
|
279
|
-
if (val !== undefined)
|
|
280
|
-
fs.appendFileSync(step.target, JSON.stringify(val) + '\n', 'utf8');
|
|
281
|
-
}
|
|
291
|
+
if (val !== undefined) fs.appendFileSync(step.target, JSON.stringify(val) + '\n', 'utf8');
|
|
282
292
|
break;
|
|
283
293
|
}
|
|
284
294
|
case 'emit': {
|
|
@@ -310,13 +320,17 @@ class RuntimeAPI {
|
|
|
310
320
|
this.workflowSteps = workflow.steps;
|
|
311
321
|
this.allowedResolvers = new Set(workflow.allowedResolvers || []);
|
|
312
322
|
|
|
313
|
-
for (const step of workflow.steps)
|
|
314
|
-
await this.executeStep(step, agentResolver);
|
|
315
|
-
}
|
|
323
|
+
for (const step of workflow.steps) await this.executeStep(step, agentResolver);
|
|
316
324
|
|
|
317
|
-
// Print disallowed resolver summary at the end
|
|
318
325
|
this.printDisallowedSummary();
|
|
319
326
|
|
|
327
|
+
if (this.__warnings.length) {
|
|
328
|
+
console.log(`\n[O-Lang] ⚠️ Parser/Runtime Warnings (${this.__warnings.length}):`);
|
|
329
|
+
this.__warnings.slice(0, 5).forEach((w, i) => {
|
|
330
|
+
console.log(`${i + 1}. ${w.timestamp} | ${w.message}`);
|
|
331
|
+
});
|
|
332
|
+
}
|
|
333
|
+
|
|
320
334
|
const result = {};
|
|
321
335
|
for (const key of workflow.returnValues) {
|
|
322
336
|
result[key] = this.getNested(this.context, key);
|