@o-lang/olang 1.0.0
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/LICENSE +5 -0
- package/README.md +42 -0
- package/cli.js +191 -0
- package/package.json +28 -0
- package/src/parser.js +462 -0
- package/src/runtime.js +313 -0
package/LICENSE
ADDED
package/README.md
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
# O-Lang Kernel
|
|
2
|
+
|
|
3
|
+
Minimal O-Lang language core (parser + runtime) for agent orchestration.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Overview
|
|
8
|
+
|
|
9
|
+
O-Lang is a **plain-English orchestration language** designed to let users define workflows as sequences of actions.
|
|
10
|
+
This repository ships the **kernel only**—no agents, no integrations, no heavy dependencies. It is **language first**, not a framework.
|
|
11
|
+
|
|
12
|
+
> “Be the grammar. Let others build the world.” — Philosophy behind O-Lang
|
|
13
|
+
|
|
14
|
+
### What This Repository Ships
|
|
15
|
+
|
|
16
|
+
- **Parser** – Convert `.olang` files into an abstract syntax tree (AST).
|
|
17
|
+
- **Runtime** – Execute the AST with a user-provided `agentResolver` function.
|
|
18
|
+
- **CLI** – `olang run workflow.olang` example runner.
|
|
19
|
+
- **Spec & Examples** – Minimal documentation for syntax and execution.
|
|
20
|
+
|
|
21
|
+
### What This Repository Does NOT Ship
|
|
22
|
+
|
|
23
|
+
- Agents (Slack, Groq, Google Drive, OCR, etc.)
|
|
24
|
+
- PDF parsing, OCR, or databases
|
|
25
|
+
- Configuration files (`.env`, secrets)
|
|
26
|
+
- Desktop UI or setup wizard
|
|
27
|
+
|
|
28
|
+
> Agents, databases, and other integrations are the responsibility of the user or third-party packages.
|
|
29
|
+
|
|
30
|
+
---
|
|
31
|
+
|
|
32
|
+
## Getting Started
|
|
33
|
+
|
|
34
|
+
### Installation
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
# Clone the repo
|
|
38
|
+
git clone https://github.com/O-Lang-Central/olang-kernel.git
|
|
39
|
+
cd olang
|
|
40
|
+
|
|
41
|
+
# Install dependencies (minimal parser/runtime)
|
|
42
|
+
npm install
|
package/cli.js
ADDED
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
const { Command } = require('commander');
|
|
3
|
+
const { parse } = require('./src/parser');
|
|
4
|
+
const { execute } = require('./src/runtime');
|
|
5
|
+
const fs = require('fs');
|
|
6
|
+
const path = require('path');
|
|
7
|
+
|
|
8
|
+
async function defaultMockResolver(action, context) {
|
|
9
|
+
if (action.startsWith('Search for ')) {
|
|
10
|
+
return {
|
|
11
|
+
title: "HR Policy 2025",
|
|
12
|
+
text: "Employees are entitled to 20 days of paid leave per year. All requests must be submitted via the HR portal.",
|
|
13
|
+
url: "mock://hr-policy"
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
if (action.startsWith('Ask ')) {
|
|
17
|
+
return "✅ [Mock] Summarized for staff.";
|
|
18
|
+
}
|
|
19
|
+
if (action.startsWith('Notify ')) {
|
|
20
|
+
const recipient = action.match(/Notify (\S+)/)?.[1] || 'user@example.com';
|
|
21
|
+
return `📬 Notification sent to ${recipient}`;
|
|
22
|
+
}
|
|
23
|
+
if (action.startsWith('Debrief ') || action.startsWith('Evolve ')) {
|
|
24
|
+
console.log(`[O-Lang] ${action}`);
|
|
25
|
+
return 'Acknowledged';
|
|
26
|
+
}
|
|
27
|
+
return `[Unhandled: ${action}]`;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function createResolverChain(resolvers) {
|
|
31
|
+
return async (action, context) => {
|
|
32
|
+
for (const resolver of resolvers) {
|
|
33
|
+
try {
|
|
34
|
+
const result = await resolver(action, context);
|
|
35
|
+
if (result !== null && result !== undefined) {
|
|
36
|
+
return result;
|
|
37
|
+
}
|
|
38
|
+
} catch (err) {
|
|
39
|
+
console.error(`❌ Resolver error for action "${action}":`, err.message);
|
|
40
|
+
throw err;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
return `[Unhandled: ${action}]`;
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function loadSingleResolver(specifier) {
|
|
48
|
+
if (!specifier) return defaultMockResolver;
|
|
49
|
+
|
|
50
|
+
try {
|
|
51
|
+
const resolver = require(specifier);
|
|
52
|
+
if (typeof resolver !== 'function') {
|
|
53
|
+
throw new Error(`Resolver must export a function`);
|
|
54
|
+
}
|
|
55
|
+
console.log(`📦 Loaded resolver: ${specifier}`);
|
|
56
|
+
return resolver;
|
|
57
|
+
} catch (e1) {
|
|
58
|
+
try {
|
|
59
|
+
const absolutePath = path.resolve(process.cwd(), specifier);
|
|
60
|
+
const resolver = require(absolutePath);
|
|
61
|
+
console.log(`📁 Loaded resolver: ${absolutePath}`);
|
|
62
|
+
return resolver;
|
|
63
|
+
} catch (e2) {
|
|
64
|
+
throw new Error(`Failed to load resolver '${specifier}':\n npm: ${e1.message}\n file: ${e2.message}`);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
function loadResolverChain(specifiers) {
|
|
70
|
+
if (!specifiers || specifiers.length === 0) {
|
|
71
|
+
console.log('ℹ️ No resolver provided. Using default mock resolver.');
|
|
72
|
+
return defaultMockResolver;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
const resolvers = specifiers.map(loadSingleResolver);
|
|
76
|
+
return createResolverChain(resolvers);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
const program = new Command();
|
|
80
|
+
|
|
81
|
+
program
|
|
82
|
+
.name('olang')
|
|
83
|
+
.description('O-Lang CLI: run .olang workflows')
|
|
84
|
+
.command('run <file>')
|
|
85
|
+
.option('-r, --resolver <specifier>', 'Resolver (npm package or local path). Can be used multiple times.\nExample:\n -r @o-lang/llm-groq\n -r @o-lang/notify-telegram', (val, acc) => {
|
|
86
|
+
acc.push(val);
|
|
87
|
+
return acc;
|
|
88
|
+
}, [])
|
|
89
|
+
.option('-i, --input <k=v>', 'Input parameters', (val, acc = {}) => {
|
|
90
|
+
const [k, v] = val.split('=');
|
|
91
|
+
acc[k] = v;
|
|
92
|
+
return acc;
|
|
93
|
+
}, {})
|
|
94
|
+
.action(async (file, options) => {
|
|
95
|
+
try {
|
|
96
|
+
const content = fs.readFileSync(file, 'utf8');
|
|
97
|
+
const workflow = parse(content);
|
|
98
|
+
const resolver = loadResolverChain(options.resolver);
|
|
99
|
+
const result = await execute(workflow, options.input, resolver);
|
|
100
|
+
console.log('\n=== Workflow Result ===');
|
|
101
|
+
console.log(JSON.stringify(result, null, 2));
|
|
102
|
+
} catch (err) {
|
|
103
|
+
console.error('❌ Error:', err.message);
|
|
104
|
+
process.exit(1);
|
|
105
|
+
}
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
program.parse(process.argv);
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
// #!/usr/bin/env node
|
|
119
|
+
// const { Command } = require('commander');
|
|
120
|
+
// const { parse } = require('./src/parser');
|
|
121
|
+
// const { execute } = require('./src/runtime');
|
|
122
|
+
// const fs = require('fs');
|
|
123
|
+
// const path = require('path');
|
|
124
|
+
|
|
125
|
+
// // Default mock resolver (for demos)
|
|
126
|
+
// async function defaultMockResolver(action, context) {
|
|
127
|
+
// if (action.startsWith('Search for ')) {
|
|
128
|
+
// return {
|
|
129
|
+
// title: "HR Policy 2025",
|
|
130
|
+
// text: "Employees are entitled to 20 days of paid leave per year. All requests must be submitted via the HR portal.",
|
|
131
|
+
// url: "mock://hr-policy"
|
|
132
|
+
// };
|
|
133
|
+
// }
|
|
134
|
+
// if (action.startsWith('Ask ')) {
|
|
135
|
+
// return "✅ [Mock] Summarized for staff.";
|
|
136
|
+
// }
|
|
137
|
+
// if (action.startsWith('Notify ')) {
|
|
138
|
+
// const recipient = action.match(/Notify (\S+)/)?.[1] || 'user@example.com';
|
|
139
|
+
// return `📬 Notification sent to ${recipient}`;
|
|
140
|
+
// }
|
|
141
|
+
// if (action.startsWith('Debrief ') || action.startsWith('Evolve ')) {
|
|
142
|
+
// console.log(`[O-Lang] ${action}`);
|
|
143
|
+
// return 'Acknowledged';
|
|
144
|
+
// }
|
|
145
|
+
// return `[Unhandled: ${action}]`;
|
|
146
|
+
// }
|
|
147
|
+
|
|
148
|
+
// function loadResolver(resolverPath) {
|
|
149
|
+
// if (!resolverPath) {
|
|
150
|
+
// console.log('ℹ️ No resolver provided. Using default mock resolver.');
|
|
151
|
+
// return defaultMockResolver;
|
|
152
|
+
// }
|
|
153
|
+
|
|
154
|
+
// try {
|
|
155
|
+
// // Resolve relative to current working directory
|
|
156
|
+
// const absolutePath = path.resolve(process.cwd(), resolverPath);
|
|
157
|
+
// return require(absolutePath);
|
|
158
|
+
// } catch (err) {
|
|
159
|
+
// console.error(`❌ Failed to load resolver from: ${resolverPath}`);
|
|
160
|
+
// console.error(`Error: ${err.message}`);
|
|
161
|
+
// process.exit(1);
|
|
162
|
+
// }
|
|
163
|
+
// }
|
|
164
|
+
|
|
165
|
+
// const program = new Command();
|
|
166
|
+
|
|
167
|
+
// program
|
|
168
|
+
// .name('olang')
|
|
169
|
+
// .description('O-Lang CLI: run .olang workflows')
|
|
170
|
+
// .command('run <file>')
|
|
171
|
+
// .option('-r, --resolver <path>', 'Path to agent resolver (e.g., ./examples/groq-slack-resolver.js)')
|
|
172
|
+
// .option('-i, --input <k=v>', 'Input parameters', (val, acc = {}) => {
|
|
173
|
+
// const [k, v] = val.split('=');
|
|
174
|
+
// acc[k] = v;
|
|
175
|
+
// return acc;
|
|
176
|
+
// }, {})
|
|
177
|
+
// .action(async (file, options) => {
|
|
178
|
+
// try {
|
|
179
|
+
// const content = fs.readFileSync(file, 'utf8');
|
|
180
|
+
// const workflow = parse(content);
|
|
181
|
+
// const resolver = loadResolver(options.resolver);
|
|
182
|
+
// const result = await execute(workflow, options.input, resolver);
|
|
183
|
+
// console.log('\n=== Workflow Result ===');
|
|
184
|
+
// console.log(JSON.stringify(result, null, 2));
|
|
185
|
+
// } catch (err) {
|
|
186
|
+
// console.error('❌ Error:', err.message);
|
|
187
|
+
// process.exit(1);
|
|
188
|
+
// }
|
|
189
|
+
// });
|
|
190
|
+
|
|
191
|
+
// program.parse(process.argv);
|
package/package.json
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@o-lang/olang",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"author": "Olalekan Ogundipe <info@workfily.com>",
|
|
5
|
+
"description": "O-Lang: A governance language for user-directed, rule-enforced agent workflows",
|
|
6
|
+
"main": "./src/index.js",
|
|
7
|
+
"bin": {
|
|
8
|
+
"olang": "./cli.js"
|
|
9
|
+
},
|
|
10
|
+
"files": [
|
|
11
|
+
"cli.js",
|
|
12
|
+
"src/"
|
|
13
|
+
],
|
|
14
|
+
"scripts": {
|
|
15
|
+
"start": "node cli.js"
|
|
16
|
+
},
|
|
17
|
+
"dependencies": {
|
|
18
|
+
"commander": "^12.0.0",
|
|
19
|
+
"dotenv": "^17.2.3"
|
|
20
|
+
},
|
|
21
|
+
"keywords": ["agent", "governance", "workflow", "llm", "automation", "olang", "ai"],
|
|
22
|
+
"license": "MIT",
|
|
23
|
+
"repository": {
|
|
24
|
+
"type": "git",
|
|
25
|
+
"url": "https://github.com/O-Lang-Central/olang-kernel.git"
|
|
26
|
+
},
|
|
27
|
+
"homepage": "https://github.com/O-Lang-Central/olang-kernel"
|
|
28
|
+
}
|
package/src/parser.js
ADDED
|
@@ -0,0 +1,462 @@
|
|
|
1
|
+
// src/parser.js
|
|
2
|
+
function parse(code) {
|
|
3
|
+
const lines = code
|
|
4
|
+
.split(/\r?\n/)
|
|
5
|
+
.map(l => l.trim())
|
|
6
|
+
.filter(l => l && !l.startsWith('#') && !l.startsWith('//'));
|
|
7
|
+
|
|
8
|
+
const workflow = {
|
|
9
|
+
name: 'Unnamed Workflow',
|
|
10
|
+
parameters: [],
|
|
11
|
+
steps: [],
|
|
12
|
+
returnValues: []
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
let i = 0;
|
|
16
|
+
while (i < lines.length) {
|
|
17
|
+
let line = lines[i];
|
|
18
|
+
|
|
19
|
+
// Workflow
|
|
20
|
+
const wfMatch = line.match(/^Workflow\s+"([^"]+)"(?:\s+with\s+(.+))?/i);
|
|
21
|
+
if (wfMatch) {
|
|
22
|
+
workflow.name = wfMatch[1];
|
|
23
|
+
workflow.parameters = wfMatch[2] ? wfMatch[2].split(',').map(p => p.trim()) : [];
|
|
24
|
+
i++;
|
|
25
|
+
continue;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// Step
|
|
29
|
+
const stepMatch = line.match(/^Step\s+(\d+)\s*:\s*(.+)$/i);
|
|
30
|
+
if (stepMatch) {
|
|
31
|
+
workflow.steps.push({
|
|
32
|
+
type: 'action',
|
|
33
|
+
stepNumber: parseInt(stepMatch[1], 10),
|
|
34
|
+
actionRaw: stepMatch[2].trim(),
|
|
35
|
+
saveAs: null,
|
|
36
|
+
constraints: {} // Initialize constraints
|
|
37
|
+
});
|
|
38
|
+
i++;
|
|
39
|
+
continue;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// Save as
|
|
43
|
+
const saveMatch = line.match(/^Save as\s+(.+)$/i);
|
|
44
|
+
if (saveMatch && workflow.steps.length > 0) {
|
|
45
|
+
workflow.steps[workflow.steps.length - 1].saveAs = saveMatch[1].trim();
|
|
46
|
+
i++;
|
|
47
|
+
continue;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// Constraint (NEW: parse key = value lines and attach to last step)
|
|
51
|
+
const constraintMatch = line.match(/^Constraint:\s*(.+)$/i);
|
|
52
|
+
if (constraintMatch && workflow.steps.length > 0) {
|
|
53
|
+
const lastStep = workflow.steps[workflow.steps.length - 1];
|
|
54
|
+
if (!lastStep.constraints) lastStep.constraints = {};
|
|
55
|
+
|
|
56
|
+
const constraintLine = constraintMatch[1].trim();
|
|
57
|
+
const eq = constraintLine.match(/^([^=]+)=\s*(.+)$/);
|
|
58
|
+
if (eq) {
|
|
59
|
+
let key = eq[1].trim();
|
|
60
|
+
let value = eq[2].trim();
|
|
61
|
+
|
|
62
|
+
// Parse list: [a, b, c]
|
|
63
|
+
if (value.startsWith('[') && value.endsWith(']')) {
|
|
64
|
+
value = value.slice(1, -1).split(',').map(v => v.trim().replace(/^"/, '').replace(/"$/, ''));
|
|
65
|
+
}
|
|
66
|
+
// Parse number
|
|
67
|
+
else if (!isNaN(value)) {
|
|
68
|
+
value = Number(value);
|
|
69
|
+
}
|
|
70
|
+
// Keep string (remove quotes if present)
|
|
71
|
+
else if (value.startsWith('"') && value.endsWith('"')) {
|
|
72
|
+
value = value.slice(1, -1);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
lastStep.constraints[key] = value;
|
|
76
|
+
}
|
|
77
|
+
i++;
|
|
78
|
+
continue;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// If
|
|
82
|
+
const ifMatch = line.match(/^If\s+(.+)\s+then$/i);
|
|
83
|
+
if (ifMatch) {
|
|
84
|
+
const condition = ifMatch[1].trim();
|
|
85
|
+
const body = [];
|
|
86
|
+
i++;
|
|
87
|
+
while (i < lines.length && !/^\s*End If\s*$/i.test(lines[i])) {
|
|
88
|
+
body.push(lines[i]);
|
|
89
|
+
i++;
|
|
90
|
+
}
|
|
91
|
+
if (i < lines.length) i++; // skip 'End If'
|
|
92
|
+
workflow.steps.push({ type: 'if', condition, body: parseBlock(body) });
|
|
93
|
+
continue;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// Parallel
|
|
97
|
+
const parMatch = line.match(/^Run in parallel$/i);
|
|
98
|
+
if (parMatch) {
|
|
99
|
+
const steps = [];
|
|
100
|
+
i++;
|
|
101
|
+
while (i < lines.length && !/^\s*End\s*$/i.test(lines[i])) {
|
|
102
|
+
steps.push(lines[i]);
|
|
103
|
+
i++;
|
|
104
|
+
}
|
|
105
|
+
if (i < lines.length) i++;
|
|
106
|
+
workflow.steps.push({ type: 'parallel', steps: parseBlock(steps) });
|
|
107
|
+
continue;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
// Connect
|
|
111
|
+
const connMatch = line.match(/^Connect\s+"([^"]+)"\s+using\s+"([^"]+)"$/i);
|
|
112
|
+
if (connMatch) {
|
|
113
|
+
workflow.steps.push({
|
|
114
|
+
type: 'connect',
|
|
115
|
+
resource: connMatch[1],
|
|
116
|
+
endpoint: connMatch[2]
|
|
117
|
+
});
|
|
118
|
+
i++;
|
|
119
|
+
continue;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
// Agent ... uses ...
|
|
123
|
+
const agentUseMatch = line.match(/^Agent\s+"([^"]+)"\s+uses\s+"([^"]+)"$/i);
|
|
124
|
+
if (agentUseMatch) {
|
|
125
|
+
workflow.steps.push({
|
|
126
|
+
type: 'agent_use',
|
|
127
|
+
logicalName: agentUseMatch[1],
|
|
128
|
+
resource: agentUseMatch[2]
|
|
129
|
+
});
|
|
130
|
+
i++;
|
|
131
|
+
continue;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
// Debrief
|
|
135
|
+
const debriefMatch = line.match(/^Debrief\s+(\w+)\s+with\s+"(.+)"$/i);
|
|
136
|
+
if (debriefMatch) {
|
|
137
|
+
workflow.steps.push({
|
|
138
|
+
type: 'debrief',
|
|
139
|
+
agent: debriefMatch[1],
|
|
140
|
+
message: debriefMatch[2]
|
|
141
|
+
});
|
|
142
|
+
i++;
|
|
143
|
+
continue;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
// Evolve
|
|
147
|
+
const evolveMatch = line.match(/^Evolve\s+(\w+)\s+using\s+feedback:\s+"(.+)"$/i);
|
|
148
|
+
if (evolveMatch) {
|
|
149
|
+
workflow.steps.push({
|
|
150
|
+
type: 'evolve',
|
|
151
|
+
agent: evolveMatch[1],
|
|
152
|
+
feedback: evolveMatch[2]
|
|
153
|
+
});
|
|
154
|
+
i++;
|
|
155
|
+
continue;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
// Prompt
|
|
159
|
+
const promptMatch = line.match(/^Prompt user to\s+"(.+)"$/i);
|
|
160
|
+
if (promptMatch) {
|
|
161
|
+
workflow.steps.push({
|
|
162
|
+
type: 'prompt',
|
|
163
|
+
question: promptMatch[1],
|
|
164
|
+
saveAs: null
|
|
165
|
+
});
|
|
166
|
+
i++;
|
|
167
|
+
continue;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
// Persist
|
|
171
|
+
const persistMatch = line.match(/^Persist\s+(.+)\s+to\s+"(.+)"$/i);
|
|
172
|
+
if (persistMatch) {
|
|
173
|
+
workflow.steps.push({
|
|
174
|
+
type: 'persist',
|
|
175
|
+
variable: persistMatch[1].trim(),
|
|
176
|
+
target: persistMatch[2]
|
|
177
|
+
});
|
|
178
|
+
i++;
|
|
179
|
+
continue;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
// Emit
|
|
183
|
+
const emitMatch = line.match(/^Emit\s+"(.+)"\s+with\s+(.+)$/i);
|
|
184
|
+
if (emitMatch) {
|
|
185
|
+
workflow.steps.push({
|
|
186
|
+
type: 'emit',
|
|
187
|
+
event: emitMatch[1],
|
|
188
|
+
payload: emitMatch[2].trim()
|
|
189
|
+
});
|
|
190
|
+
i++;
|
|
191
|
+
continue;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
// Return
|
|
195
|
+
const returnMatch = line.match(/^Return\s+(.+)$/i);
|
|
196
|
+
if (returnMatch) {
|
|
197
|
+
workflow.returnValues = returnMatch[1].split(',').map(v => v.trim());
|
|
198
|
+
i++;
|
|
199
|
+
continue;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
i++;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
return workflow;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
function parseBlock(lines) {
|
|
209
|
+
const steps = [];
|
|
210
|
+
let current = null;
|
|
211
|
+
for (const line of lines) {
|
|
212
|
+
const stepMatch = line.match(/^Step\s+(\d+)\s*:\s*(.+)$/i);
|
|
213
|
+
if (stepMatch) {
|
|
214
|
+
current = {
|
|
215
|
+
type: 'action',
|
|
216
|
+
stepNumber: parseInt(stepMatch[1], 10),
|
|
217
|
+
actionRaw: stepMatch[2].trim(),
|
|
218
|
+
saveAs: null,
|
|
219
|
+
constraints: {}
|
|
220
|
+
};
|
|
221
|
+
steps.push(current);
|
|
222
|
+
continue;
|
|
223
|
+
}
|
|
224
|
+
const saveMatch = line.match(/^Save as\s+(.+)$/i);
|
|
225
|
+
if (saveMatch && current) {
|
|
226
|
+
current.saveAs = saveMatch[1].trim();
|
|
227
|
+
}
|
|
228
|
+
const debriefMatch = line.match(/^Debrief\s+(\w+)\s+with\s+"(.+)"$/i);
|
|
229
|
+
if (debriefMatch) {
|
|
230
|
+
steps.push({ type: 'debrief', agent: debriefMatch[1], message: debriefMatch[2] });
|
|
231
|
+
}
|
|
232
|
+
const evolveMatch = line.match(/^Evolve\s+(\w+)\s+using\s+feedback:\s+"(.+)"$/i);
|
|
233
|
+
if (evolveMatch) {
|
|
234
|
+
steps.push({ type: 'evolve', agent: evolveMatch[1], feedback: evolveMatch[2] });
|
|
235
|
+
}
|
|
236
|
+
const promptMatch = line.match(/^Prompt user to\s+"(.+)"$/i);
|
|
237
|
+
if (promptMatch) {
|
|
238
|
+
steps.push({ type: 'prompt', question: promptMatch[1], saveAs: null });
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
return steps;
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
module.exports = { parse };
|
|
245
|
+
|
|
246
|
+
|
|
247
|
+
|
|
248
|
+
|
|
249
|
+
|
|
250
|
+
|
|
251
|
+
|
|
252
|
+
// // src/parser.js
|
|
253
|
+
// function parse(code) {
|
|
254
|
+
// const lines = code
|
|
255
|
+
// .split(/\r?\n/)
|
|
256
|
+
// .map(l => l.trim())
|
|
257
|
+
// .filter(l => l && !l.startsWith('#') && !l.startsWith('//'));
|
|
258
|
+
|
|
259
|
+
// const workflow = {
|
|
260
|
+
// name: 'Unnamed Workflow',
|
|
261
|
+
// parameters: [],
|
|
262
|
+
// steps: [],
|
|
263
|
+
// returnValues: []
|
|
264
|
+
// };
|
|
265
|
+
|
|
266
|
+
// let i = 0;
|
|
267
|
+
// while (i < lines.length) {
|
|
268
|
+
// let line = lines[i];
|
|
269
|
+
|
|
270
|
+
// // Workflow
|
|
271
|
+
// const wfMatch = line.match(/^Workflow\s+"([^"]+)"(?:\s+with\s+(.+))?/i);
|
|
272
|
+
// if (wfMatch) {
|
|
273
|
+
// workflow.name = wfMatch[1];
|
|
274
|
+
// workflow.parameters = wfMatch[2] ? wfMatch[2].split(',').map(p => p.trim()) : [];
|
|
275
|
+
// i++;
|
|
276
|
+
// continue;
|
|
277
|
+
// }
|
|
278
|
+
|
|
279
|
+
// // Step
|
|
280
|
+
// const stepMatch = line.match(/^Step\s+(\d+)\s*:\s*(.+)$/i);
|
|
281
|
+
// if (stepMatch) {
|
|
282
|
+
// workflow.steps.push({
|
|
283
|
+
// type: 'action',
|
|
284
|
+
// stepNumber: parseInt(stepMatch[1], 10),
|
|
285
|
+
// actionRaw: stepMatch[2].trim(),
|
|
286
|
+
// saveAs: null
|
|
287
|
+
// });
|
|
288
|
+
// i++;
|
|
289
|
+
// continue;
|
|
290
|
+
// }
|
|
291
|
+
|
|
292
|
+
// // Save as
|
|
293
|
+
// const saveMatch = line.match(/^Save as\s+(.+)$/i);
|
|
294
|
+
// if (saveMatch && workflow.steps.length > 0) {
|
|
295
|
+
// workflow.steps[workflow.steps.length - 1].saveAs = saveMatch[1].trim();
|
|
296
|
+
// i++;
|
|
297
|
+
// continue;
|
|
298
|
+
// }
|
|
299
|
+
|
|
300
|
+
// // If
|
|
301
|
+
// const ifMatch = line.match(/^If\s+(.+)\s+then$/i);
|
|
302
|
+
// if (ifMatch) {
|
|
303
|
+
// const condition = ifMatch[1].trim();
|
|
304
|
+
// const body = [];
|
|
305
|
+
// i++;
|
|
306
|
+
// while (i < lines.length && !/^\s*End If\s*$/i.test(lines[i])) {
|
|
307
|
+
// body.push(lines[i]);
|
|
308
|
+
// i++;
|
|
309
|
+
// }
|
|
310
|
+
// if (i < lines.length) i++; // skip 'End If'
|
|
311
|
+
// workflow.steps.push({ type: 'if', condition, body: parseBlock(body) });
|
|
312
|
+
// continue;
|
|
313
|
+
// }
|
|
314
|
+
|
|
315
|
+
// // Parallel
|
|
316
|
+
// const parMatch = line.match(/^Run in parallel$/i);
|
|
317
|
+
// if (parMatch) {
|
|
318
|
+
// const steps = [];
|
|
319
|
+
// i++;
|
|
320
|
+
// while (i < lines.length && !/^\s*End\s*$/i.test(lines[i])) {
|
|
321
|
+
// steps.push(lines[i]);
|
|
322
|
+
// i++;
|
|
323
|
+
// }
|
|
324
|
+
// if (i < lines.length) i++;
|
|
325
|
+
// workflow.steps.push({ type: 'parallel', steps: parseBlock(steps) });
|
|
326
|
+
// continue;
|
|
327
|
+
// }
|
|
328
|
+
|
|
329
|
+
// // Connect
|
|
330
|
+
// const connMatch = line.match(/^Connect\s+"([^"]+)"\s+using\s+"([^"]+)"$/i);
|
|
331
|
+
// if (connMatch) {
|
|
332
|
+
// workflow.steps.push({
|
|
333
|
+
// type: 'connect',
|
|
334
|
+
// resource: connMatch[1],
|
|
335
|
+
// endpoint: connMatch[2]
|
|
336
|
+
// });
|
|
337
|
+
// i++;
|
|
338
|
+
// continue;
|
|
339
|
+
// }
|
|
340
|
+
|
|
341
|
+
// // Agent ... uses ...
|
|
342
|
+
// const agentUseMatch = line.match(/^Agent\s+"([^"]+)"\s+uses\s+"([^"]+)"$/i);
|
|
343
|
+
// if (agentUseMatch) {
|
|
344
|
+
// workflow.steps.push({
|
|
345
|
+
// type: 'agent_use',
|
|
346
|
+
// logicalName: agentUseMatch[1],
|
|
347
|
+
// resource: agentUseMatch[2]
|
|
348
|
+
// });
|
|
349
|
+
// i++;
|
|
350
|
+
// continue;
|
|
351
|
+
// }
|
|
352
|
+
|
|
353
|
+
// // Debrief
|
|
354
|
+
// const debriefMatch = line.match(/^Debrief\s+(\w+)\s+with\s+"(.+)"$/i);
|
|
355
|
+
// if (debriefMatch) {
|
|
356
|
+
// workflow.steps.push({
|
|
357
|
+
// type: 'debrief',
|
|
358
|
+
// agent: debriefMatch[1],
|
|
359
|
+
// message: debriefMatch[2]
|
|
360
|
+
// });
|
|
361
|
+
// i++;
|
|
362
|
+
// continue;
|
|
363
|
+
// }
|
|
364
|
+
|
|
365
|
+
// // Evolve
|
|
366
|
+
// const evolveMatch = line.match(/^Evolve\s+(\w+)\s+using\s+feedback:\s+"(.+)"$/i);
|
|
367
|
+
// if (evolveMatch) {
|
|
368
|
+
// workflow.steps.push({
|
|
369
|
+
// type: 'evolve',
|
|
370
|
+
// agent: evolveMatch[1],
|
|
371
|
+
// feedback: evolveMatch[2]
|
|
372
|
+
// });
|
|
373
|
+
// i++;
|
|
374
|
+
// continue;
|
|
375
|
+
// }
|
|
376
|
+
|
|
377
|
+
// // Prompt
|
|
378
|
+
// const promptMatch = line.match(/^Prompt user to\s+"(.+)"$/i);
|
|
379
|
+
// if (promptMatch) {
|
|
380
|
+
// workflow.steps.push({
|
|
381
|
+
// type: 'prompt',
|
|
382
|
+
// question: promptMatch[1],
|
|
383
|
+
// saveAs: null
|
|
384
|
+
// });
|
|
385
|
+
// i++;
|
|
386
|
+
// continue;
|
|
387
|
+
// }
|
|
388
|
+
|
|
389
|
+
// // Persist
|
|
390
|
+
// const persistMatch = line.match(/^Persist\s+(.+)\s+to\s+"(.+)"$/i);
|
|
391
|
+
// if (persistMatch) {
|
|
392
|
+
// workflow.steps.push({
|
|
393
|
+
// type: 'persist',
|
|
394
|
+
// variable: persistMatch[1].trim(),
|
|
395
|
+
// target: persistMatch[2]
|
|
396
|
+
// });
|
|
397
|
+
// i++;
|
|
398
|
+
// continue;
|
|
399
|
+
// }
|
|
400
|
+
|
|
401
|
+
// // Emit
|
|
402
|
+
// const emitMatch = line.match(/^Emit\s+"(.+)"\s+with\s+(.+)$/i);
|
|
403
|
+
// if (emitMatch) {
|
|
404
|
+
// workflow.steps.push({
|
|
405
|
+
// type: 'emit',
|
|
406
|
+
// event: emitMatch[1],
|
|
407
|
+
// payload: emitMatch[2].trim()
|
|
408
|
+
// });
|
|
409
|
+
// i++;
|
|
410
|
+
// continue;
|
|
411
|
+
// }
|
|
412
|
+
|
|
413
|
+
// // Return
|
|
414
|
+
// const returnMatch = line.match(/^Return\s+(.+)$/i);
|
|
415
|
+
// if (returnMatch) {
|
|
416
|
+
// workflow.returnValues = returnMatch[1].split(',').map(v => v.trim());
|
|
417
|
+
// i++;
|
|
418
|
+
// continue;
|
|
419
|
+
// }
|
|
420
|
+
|
|
421
|
+
// i++;
|
|
422
|
+
// }
|
|
423
|
+
|
|
424
|
+
// return workflow;
|
|
425
|
+
// }
|
|
426
|
+
|
|
427
|
+
// function parseBlock(lines) {
|
|
428
|
+
// const steps = [];
|
|
429
|
+
// let current = null;
|
|
430
|
+
// for (const line of lines) {
|
|
431
|
+
// const stepMatch = line.match(/^Step\s+(\d+)\s*:\s*(.+)$/i);
|
|
432
|
+
// if (stepMatch) {
|
|
433
|
+
// current = {
|
|
434
|
+
// type: 'action',
|
|
435
|
+
// stepNumber: parseInt(stepMatch[1], 10),
|
|
436
|
+
// actionRaw: stepMatch[2].trim(),
|
|
437
|
+
// saveAs: null
|
|
438
|
+
// };
|
|
439
|
+
// steps.push(current);
|
|
440
|
+
// continue;
|
|
441
|
+
// }
|
|
442
|
+
// const saveMatch = line.match(/^Save as\s+(.+)$/i);
|
|
443
|
+
// if (saveMatch && current) {
|
|
444
|
+
// current.saveAs = saveMatch[1].trim();
|
|
445
|
+
// }
|
|
446
|
+
// const debriefMatch = line.match(/^Debrief\s+(\w+)\s+with\s+"(.+)"$/i);
|
|
447
|
+
// if (debriefMatch) {
|
|
448
|
+
// steps.push({ type: 'debrief', agent: debriefMatch[1], message: debriefMatch[2] });
|
|
449
|
+
// }
|
|
450
|
+
// const evolveMatch = line.match(/^Evolve\s+(\w+)\s+using\s+feedback:\s+"(.+)"$/i);
|
|
451
|
+
// if (evolveMatch) {
|
|
452
|
+
// steps.push({ type: 'evolve', agent: evolveMatch[1], feedback: evolveMatch[2] });
|
|
453
|
+
// }
|
|
454
|
+
// const promptMatch = line.match(/^Prompt user to\s+"(.+)"$/i);
|
|
455
|
+
// if (promptMatch) {
|
|
456
|
+
// steps.push({ type: 'prompt', question: promptMatch[1], saveAs: null });
|
|
457
|
+
// }
|
|
458
|
+
// }
|
|
459
|
+
// return steps;
|
|
460
|
+
// }
|
|
461
|
+
|
|
462
|
+
// module.exports = { parse };
|
package/src/runtime.js
ADDED
|
@@ -0,0 +1,313 @@
|
|
|
1
|
+
// src/runtime.js
|
|
2
|
+
const fs = require('fs');
|
|
3
|
+
|
|
4
|
+
class RuntimeAPI {
|
|
5
|
+
constructor() {
|
|
6
|
+
this.context = {};
|
|
7
|
+
this.resources = {};
|
|
8
|
+
this.agentMap = {};
|
|
9
|
+
this.events = {};
|
|
10
|
+
this.workflowSteps = []; // store for evolve lookup
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
on(eventName, cb) {
|
|
14
|
+
if (!this.events[eventName]) this.events[eventName] = [];
|
|
15
|
+
this.events[eventName].push(cb);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
emit(eventName, payload) {
|
|
19
|
+
if (this.events[eventName]) {
|
|
20
|
+
this.events[eventName].forEach(cb => cb(payload));
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
evaluateCondition(cond, ctx) {
|
|
25
|
+
cond = cond.trim();
|
|
26
|
+
const eq = cond.match(/^\{(.+)\}\s+equals\s+"(.*)"$/);
|
|
27
|
+
if (eq) return this.getNested(ctx, eq[1]) === eq[2];
|
|
28
|
+
const gt = cond.match(/^\{(.+)\}\s+greater than\s+(\d+\.?\d*)$/);
|
|
29
|
+
if (gt) return parseFloat(this.getNested(ctx, gt[1])) > parseFloat(gt[2]);
|
|
30
|
+
return Boolean(this.getNested(ctx, cond.replace(/\{|\}/g, '')));
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
getNested(obj, path) {
|
|
34
|
+
if (!path) return undefined;
|
|
35
|
+
return path.split('.').reduce((o, k) => (o && o[k] !== undefined) ? o[k] : undefined, obj);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// 🔍 Find the last "Ask ... Save as ..." step that can be evolved
|
|
39
|
+
findLastSummaryStep() {
|
|
40
|
+
for (let i = this.workflowSteps.length - 1; i >= 0; i--) {
|
|
41
|
+
const step = this.workflowSteps[i];
|
|
42
|
+
if (step.type === 'action' && step.actionRaw?.startsWith('Ask ') && step.saveAs) {
|
|
43
|
+
return step;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
return null;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
async executeStep(step, agentResolver) {
|
|
50
|
+
switch (step.type) {
|
|
51
|
+
case 'action': {
|
|
52
|
+
const action = step.actionRaw.replace(/\{([^\}]+)\}/g, (_, path) => {
|
|
53
|
+
const value = this.getNested(this.context, path.trim());
|
|
54
|
+
return value !== undefined ? String(value) : `{${path}}`;
|
|
55
|
+
});
|
|
56
|
+
const res = await agentResolver(action, this.context);
|
|
57
|
+
if (step.saveAs) this.context[step.saveAs] = res;
|
|
58
|
+
break;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
case 'if': {
|
|
62
|
+
if (this.evaluateCondition(step.condition, this.context)) {
|
|
63
|
+
for (const s of step.body) await this.executeStep(s, agentResolver);
|
|
64
|
+
}
|
|
65
|
+
break;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
case 'parallel': {
|
|
69
|
+
await Promise.all(step.steps.map(s => this.executeStep(s, agentResolver)));
|
|
70
|
+
break;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
case 'connect': {
|
|
74
|
+
this.resources[step.resource] = step.endpoint;
|
|
75
|
+
break;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
case 'agent_use': {
|
|
79
|
+
this.agentMap[step.logicalName] = step.resource;
|
|
80
|
+
break;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
case 'debrief': {
|
|
84
|
+
this.emit('debrief', { agent: step.agent, message: step.message });
|
|
85
|
+
break;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
case 'evolve': {
|
|
89
|
+
// ✅ Runtime-enforced bounded evolution
|
|
90
|
+
const maxGen = step.constraints?.max_generations || 1;
|
|
91
|
+
if (maxGen < 1) {
|
|
92
|
+
this.context['improved_summary'] = this.context['summary'] || '';
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
const summaryStep = this.findLastSummaryStep();
|
|
97
|
+
if (!summaryStep) {
|
|
98
|
+
console.warn('[O-Lang] Evolve: No prior "Ask ... Save as" step found to evolve');
|
|
99
|
+
this.context['improved_summary'] = this.context['summary'] || '';
|
|
100
|
+
return;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
const varName = summaryStep.saveAs; // e.g., "summary"
|
|
104
|
+
let currentOutput = this.context[varName] || '';
|
|
105
|
+
|
|
106
|
+
// Run up to max_generations attempts
|
|
107
|
+
for (let attempt = 0; attempt < maxGen; attempt++) {
|
|
108
|
+
// Rebuild the original action with current context (re-interpolates {doc.text}, etc.)
|
|
109
|
+
let revisedAction = summaryStep.actionRaw.replace(/\{([^\}]+)\}/g, (_, path) => {
|
|
110
|
+
const val = this.getNested(this.context, path.trim());
|
|
111
|
+
return val !== undefined ? String(val) : `{${path}}`;
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
// Append improvement feedback on every attempt (including first)
|
|
115
|
+
if (step.feedback) {
|
|
116
|
+
revisedAction = revisedAction.replace(/(")$/ , `\n\n[IMPROVEMENT FEEDBACK: ${step.feedback}]$1`);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// Delegate ONLY the "Ask ..." action to the resolver
|
|
120
|
+
currentOutput = await agentResolver(revisedAction, this.context);
|
|
121
|
+
this.context[varName] = currentOutput; // update original variable
|
|
122
|
+
|
|
123
|
+
// Optional: emit debrief for observability
|
|
124
|
+
this.emit('debrief', {
|
|
125
|
+
agent: step.agent || 'Evolver',
|
|
126
|
+
message: `Evolve attempt ${attempt + 1}/${maxGen}: ${currentOutput.substring(0, 80)}...`
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
// ✅ Always expose final result as 'improved_summary' for downstream use
|
|
131
|
+
this.context['improved_summary'] = currentOutput;
|
|
132
|
+
break;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
case 'prompt': {
|
|
136
|
+
const input = await this.getUserInput(step.question);
|
|
137
|
+
if (step.saveAs) this.context[step.saveAs] = input;
|
|
138
|
+
break;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
case 'persist': {
|
|
142
|
+
const val = this.context[step.variable];
|
|
143
|
+
if (val !== undefined) {
|
|
144
|
+
fs.appendFileSync(step.target, JSON.stringify(val) + '\n', 'utf8');
|
|
145
|
+
}
|
|
146
|
+
break;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
case 'emit': {
|
|
150
|
+
const payload = step.payload ? this.getNested(this.context, step.payload) : undefined;
|
|
151
|
+
this.emit(step.event, payload || step.payload);
|
|
152
|
+
break;
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
async getUserInput(question) {
|
|
158
|
+
return new Promise(resolve => {
|
|
159
|
+
process.stdout.write(`${question}: `);
|
|
160
|
+
process.stdin.resume();
|
|
161
|
+
process.stdin.once('data', data => {
|
|
162
|
+
process.stdin.pause();
|
|
163
|
+
resolve(data.toString().trim());
|
|
164
|
+
});
|
|
165
|
+
});
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
async executeWorkflow(workflow, inputs, agentResolver) {
|
|
169
|
+
this.context = { ...inputs };
|
|
170
|
+
this.workflowSteps = workflow.steps; // critical for evolve lookup
|
|
171
|
+
|
|
172
|
+
for (const step of workflow.steps) {
|
|
173
|
+
await this.executeStep(step, agentResolver);
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
const result = {};
|
|
177
|
+
for (const key of workflow.returnValues) {
|
|
178
|
+
result[key] = this.getNested(this.context, key);
|
|
179
|
+
}
|
|
180
|
+
return result;
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
async function execute(workflow, inputs, agentResolver) {
|
|
185
|
+
const rt = new RuntimeAPI();
|
|
186
|
+
return rt.executeWorkflow(workflow, inputs, agentResolver);
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
module.exports = { execute, RuntimeAPI };
|
|
190
|
+
|
|
191
|
+
|
|
192
|
+
// // src/runtime.js
|
|
193
|
+
// const fs = require('fs');
|
|
194
|
+
|
|
195
|
+
// class RuntimeAPI {
|
|
196
|
+
// constructor() {
|
|
197
|
+
// this.context = {};
|
|
198
|
+
// this.resources = {};
|
|
199
|
+
// this.agentMap = {};
|
|
200
|
+
// this.events = {};
|
|
201
|
+
// }
|
|
202
|
+
|
|
203
|
+
// on(eventName, cb) {
|
|
204
|
+
// if (!this.events[eventName]) this.events[eventName] = [];
|
|
205
|
+
// this.events[eventName].push(cb);
|
|
206
|
+
// }
|
|
207
|
+
|
|
208
|
+
// emit(eventName, payload) {
|
|
209
|
+
// if (this.events[eventName]) {
|
|
210
|
+
// this.events[eventName].forEach(cb => cb(payload));
|
|
211
|
+
// }
|
|
212
|
+
// }
|
|
213
|
+
|
|
214
|
+
// evaluateCondition(cond, ctx) {
|
|
215
|
+
// cond = cond.trim();
|
|
216
|
+
// const eq = cond.match(/^\{(.+)\}\s+equals\s+"(.*)"$/);
|
|
217
|
+
// if (eq) return this.getNested(ctx, eq[1]) === eq[2];
|
|
218
|
+
// const gt = cond.match(/^\{(.+)\}\s+greater than\s+(\d+\.?\d*)$/);
|
|
219
|
+
// if (gt) return parseFloat(this.getNested(ctx, gt[1])) > parseFloat(gt[2]);
|
|
220
|
+
// return Boolean(this.getNested(ctx, cond.replace(/\{|\}/g, '')));
|
|
221
|
+
// }
|
|
222
|
+
|
|
223
|
+
// getNested(obj, path) {
|
|
224
|
+
// if (!path) return undefined;
|
|
225
|
+
// return path.split('.').reduce((o, k) => (o && o[k] !== undefined) ? o[k] : undefined, obj);
|
|
226
|
+
// }
|
|
227
|
+
|
|
228
|
+
// async executeStep(step, agentResolver) {
|
|
229
|
+
// switch (step.type) {
|
|
230
|
+
// case 'action':
|
|
231
|
+
// // ✅ Support nested interpolation: {doc.text}, {user.name}, etc.
|
|
232
|
+
// const action = step.actionRaw.replace(/\{([^\}]+)\}/g, (_, path) => {
|
|
233
|
+
// const value = this.getNested(this.context, path.trim());
|
|
234
|
+
// return value !== undefined ? String(value) : `{${path}}`;
|
|
235
|
+
// });
|
|
236
|
+
// const res = await agentResolver(action, this.context);
|
|
237
|
+
// if (step.saveAs) this.context[step.saveAs] = res;
|
|
238
|
+
// break;
|
|
239
|
+
|
|
240
|
+
// case 'if':
|
|
241
|
+
// if (this.evaluateCondition(step.condition, this.context)) {
|
|
242
|
+
// for (const s of step.body) await this.executeStep(s, agentResolver);
|
|
243
|
+
// }
|
|
244
|
+
// break;
|
|
245
|
+
|
|
246
|
+
// case 'parallel':
|
|
247
|
+
// await Promise.all(step.steps.map(s => this.executeStep(s, agentResolver)));
|
|
248
|
+
// break;
|
|
249
|
+
|
|
250
|
+
// case 'connect':
|
|
251
|
+
// this.resources[step.resource] = step.endpoint;
|
|
252
|
+
// break;
|
|
253
|
+
|
|
254
|
+
// case 'agent_use':
|
|
255
|
+
// this.agentMap[step.logicalName] = step.resource;
|
|
256
|
+
// break;
|
|
257
|
+
|
|
258
|
+
// case 'debrief':
|
|
259
|
+
// this.emit('debrief', { agent: step.agent, message: step.message });
|
|
260
|
+
// break;
|
|
261
|
+
|
|
262
|
+
// case 'evolve':
|
|
263
|
+
// // Pass evolve as a structured action (optional: could use dedicated handler)
|
|
264
|
+
// await agentResolver(`Evolve ${step.agent} using feedback: "${step.feedback}"`, this.context);
|
|
265
|
+
// break;
|
|
266
|
+
|
|
267
|
+
// case 'prompt':
|
|
268
|
+
// const input = await this.getUserInput(step.question);
|
|
269
|
+
// if (step.saveAs) this.context[step.saveAs] = input;
|
|
270
|
+
// break;
|
|
271
|
+
|
|
272
|
+
// case 'persist':
|
|
273
|
+
// const val = this.context[step.variable];
|
|
274
|
+
// fs.appendFileSync(step.target, JSON.stringify(val) + '\n', 'utf8');
|
|
275
|
+
// break;
|
|
276
|
+
|
|
277
|
+
// case 'emit':
|
|
278
|
+
// const payload = step.payload ? this.getNested(this.context, step.payload) : undefined;
|
|
279
|
+
// this.emit(step.event, payload || step.payload);
|
|
280
|
+
// break;
|
|
281
|
+
// }
|
|
282
|
+
// }
|
|
283
|
+
|
|
284
|
+
// async getUserInput(question) {
|
|
285
|
+
// return new Promise(resolve => {
|
|
286
|
+
// process.stdout.write(`${question}: `);
|
|
287
|
+
// process.stdin.resume();
|
|
288
|
+
// process.stdin.once('data', data => {
|
|
289
|
+
// process.stdin.pause();
|
|
290
|
+
// resolve(data.toString().trim());
|
|
291
|
+
// });
|
|
292
|
+
// });
|
|
293
|
+
// }
|
|
294
|
+
|
|
295
|
+
// async executeWorkflow(workflow, inputs, agentResolver) {
|
|
296
|
+
// this.context = { ...inputs };
|
|
297
|
+
// for (const step of workflow.steps) {
|
|
298
|
+
// await this.executeStep(step, agentResolver);
|
|
299
|
+
// }
|
|
300
|
+
// const result = {};
|
|
301
|
+
// for (const key of workflow.returnValues) {
|
|
302
|
+
// result[key] = this.getNested(this.context, key);
|
|
303
|
+
// }
|
|
304
|
+
// return result;
|
|
305
|
+
// }
|
|
306
|
+
// }
|
|
307
|
+
|
|
308
|
+
// async function execute(workflow, inputs, agentResolver) {
|
|
309
|
+
// const rt = new RuntimeAPI();
|
|
310
|
+
// return rt.executeWorkflow(workflow, inputs, agentResolver);
|
|
311
|
+
// }
|
|
312
|
+
|
|
313
|
+
// module.exports = { execute, RuntimeAPI };
|