@o-lang/olang 1.2.1 → 1.2.3

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 CHANGED
@@ -1,24 +1,25 @@
1
1
  {
2
2
  "name": "@o-lang/olang",
3
- "version": "1.2.1",
3
+ "version": "1.2.3",
4
4
  "author": "Olalekan Ogundipe <info@workfily.com>",
5
5
  "description": "O-Lang: A governance language for user-directed, rule-enforced agent workflows",
6
6
  "main": "./src/index.js",
7
- "bin": {
8
- "olang": "./cli/olang.js"
9
- },
7
+ "bin": {
8
+ "olang": "./cli/olang.js"
9
+ },
10
10
  "files": [
11
11
  "cli.js",
12
12
  "src/"
13
13
  ],
14
14
  "scripts": {
15
- "start": "node cli.js"
15
+ "start": "node cli.js",
16
+ "test": "jest"
16
17
  },
17
18
  "dependencies": {
18
- "commander": "^12.0.0",
19
- "dotenv": "^17.2.3",
20
- "lodash": "^4.17.21",
21
- "fastify": "^4.26.0"
19
+ "commander": "^12.0.0",
20
+ "dotenv": "^17.2.3",
21
+ "fastify": "^4.26.0",
22
+ "lodash": "^4.17.21"
22
23
  },
23
24
  "keywords": [
24
25
  "agent",
@@ -37,5 +38,8 @@
37
38
  "homepage": "https://github.com/O-Lang-Central/olang-kernel",
38
39
  "publishConfig": {
39
40
  "access": "public"
41
+ },
42
+ "devDependencies": {
43
+ "jest": "^30.2.0"
40
44
  }
41
45
  }
@@ -385,18 +385,18 @@ function parseWorkflowLines(lines, filename) {
385
385
  }
386
386
 
387
387
  // Ask (for Notify/resolver calls) - ✅ PRESERVE TARGET EXACTLY (NO NORMALIZATION)
388
- const askMatch = line.match(/^Ask\s+(.+)$/i);
389
- if (askMatch) {
390
- flushCurrentStep();
391
- workflow.steps.push({
392
- type: 'ask',
393
- target: askMatch[1].trim(), // ← PRESERVED EXACTLY (no normalizeAction)
394
- stepNumber: workflow.steps.length + 1,
395
- saveAs: null,
396
- constraints: {}
397
- });
398
- continue;
399
- }
388
+ const askMatch = line.match(/^Ask\s+(.+)$/i);
389
+ if (askMatch) {
390
+ flushCurrentStep();
391
+ workflow.steps.push({
392
+ type: 'action',
393
+ actionRaw: `Action ${askMatch[1].trim()}`,
394
+ stepNumber: workflow.steps.length + 1,
395
+ saveAs: null,
396
+ constraints: {}
397
+ });
398
+ continue;
399
+ }
400
400
 
401
401
  // Return
402
402
  const returnMatch = line.match(/^Return\s+(.+)$/i);
@@ -530,30 +530,33 @@ function parseBlock(lines) {
530
530
  }
531
531
 
532
532
  // Use in block - ✅ PRESERVE TOOL EXACTLY (NO NORMALIZATION)
533
- const useMatch = line.match(/^Use\s+(.+)$/i);
534
- if (useMatch) {
535
- flush();
536
- steps.push({
537
- type: 'use',
538
- tool: useMatch[1].trim(), // ← PRESERVED EXACTLY
539
- saveAs: null,
540
- constraints: {}
541
- });
542
- continue;
543
- }
533
+ const useMatch = line.match(/^Use\s+(.+)$/i);
534
+ if (useMatch) {
535
+ flushCurrentStep();
536
+ workflow.steps.push({
537
+ type: 'action',
538
+ actionRaw: `Action ${useMatch[1].trim()}`,
539
+ stepNumber: workflow.steps.length + 1,
540
+ saveAs: null,
541
+ constraints: {}
542
+ });
543
+ continue;
544
+ }
545
+
546
+
547
+ // Ask in block — CANONICALIZE AT PARSE TIME
548
+ const askMatch = line.match(/^Ask\s+(.+)$/i);
549
+ if (askMatch) {
550
+ flush();
551
+ steps.push({
552
+ type: 'action',
553
+ actionRaw: `Action ${askMatch[1].trim()}`,
554
+ saveAs: null,
555
+ constraints: {}
556
+ });
557
+ continue;
558
+ }
544
559
 
545
- // Ask in block - ✅ PRESERVE TARGET EXACTLY (NO NORMALIZATION)
546
- const askMatch = line.match(/^Ask\s+(.+)$/i);
547
- if (askMatch) {
548
- flush();
549
- steps.push({
550
- type: 'ask',
551
- target: askMatch[1].trim(), // ← PRESERVED EXACTLY
552
- saveAs: null,
553
- constraints: {}
554
- });
555
- continue;
556
- }
557
560
 
558
561
  // Constraint inside block
559
562
  const constraintMatch = line.match(/^Constraint:\s*(.+)$/i);
@@ -4,7 +4,7 @@ const path = require('path');
4
4
 
5
5
  class RuntimeAPI {
6
6
  constructor({ verbose = false } = {}) {
7
- console.log('✅ KERNEL FIX VERIFIED - Unwrapping active');
7
+ // console.log('✅ KERNEL FIX VERIFIED - Unwrapping active');
8
8
  this.context = {};
9
9
  this.resources = {};
10
10
  this.agentMap = {};
@@ -552,34 +552,59 @@ class RuntimeAPI {
552
552
  break;
553
553
  }
554
554
 
555
- case 'action': {
556
- // SAFE INTERPOLATION: Block object→string coercion BEFORE resolver invocation
557
- const action = this._safeInterpolate(step.actionRaw, this.context, 'action step');
555
+ case 'action': {
556
+ // 🔒 Interpolate workflow variables first
557
+ let action = this._safeInterpolate(
558
+ step.actionRaw,
559
+ this.context,
560
+ 'action step'
561
+ );
558
562
 
559
- const mathCall = action.match(/^(add|subtract|multiply|divide|sum|avg|min|max|round|floor|ceil|abs)\((.*)\)$/i);
560
- if (mathCall) {
561
- const fn = mathCall[1].toLowerCase();
562
- const args = mathCall[2].split(',').map(s => {
563
- s = s.trim();
564
- if (!isNaN(s)) return parseFloat(s);
565
- return this.getNested(this.context, s.replace(/^\{|\}$/g, ''));
566
- });
567
- if (this.mathFunctions[fn]) {
568
- const value = this.mathFunctions[fn](...args);
569
- if (step.saveAs) this.context[step.saveAs] = value;
570
- break;
571
- }
572
- }
563
+ // CANONICALIZATION: Normalize DSL verbs → runtime Action
564
+ if (action.startsWith('Ask ')) {
565
+ action = 'Action ' + action.slice(4);
566
+ } else if (action.startsWith('Use ')) {
567
+ action = 'Action ' + action.slice(4);
568
+ }
573
569
 
574
- // CRITICAL FIX: UNWRAP resolver result BEFORE saving to context
575
- const rawResult = await runResolvers(action);
576
- const unwrapped = this._unwrapResolverResult(rawResult);
577
-
578
- if (step.saveAs) {
579
- this.context[step.saveAs] = unwrapped;
580
- }
581
- break;
582
- }
570
+ // Reject non-canonical runtime actions early
571
+ if (!action.startsWith('Action ')) {
572
+ throw new Error(
573
+ `[O-Lang SAFETY] Non-canonical action received: "${action}"\n` +
574
+ ` → Expected format: Action <resolver> <args>\n` +
575
+ ` → This indicates a kernel or workflow authoring error.`
576
+ );
577
+ }
578
+
579
+ // ✅ Inline math support (language feature)
580
+ const mathCall = action.match(
581
+ /^(add|subtract|multiply|divide|sum|avg|min|max|round|floor|ceil|abs)\((.*)\)$/i
582
+ );
583
+
584
+ if (mathCall) {
585
+ const fn = mathCall[1].toLowerCase();
586
+ const args = mathCall[2].split(',').map(s => {
587
+ s = s.trim();
588
+ if (!isNaN(s)) return parseFloat(s);
589
+ return this.getNested(this.context, s.replace(/^\{|\}$/g, ''));
590
+ });
591
+
592
+ if (this.mathFunctions[fn]) {
593
+ const value = this.mathFunctions[fn](...args);
594
+ if (step.saveAs) this.context[step.saveAs] = value;
595
+ break;
596
+ }
597
+ }
598
+
599
+ // ✅ Resolver dispatch receives ONLY canonical actions
600
+ const rawResult = await runResolvers(action);
601
+ const unwrapped = this._unwrapResolverResult(rawResult);
602
+
603
+ if (step.saveAs) {
604
+ this.context[step.saveAs] = unwrapped;
605
+ }
606
+ break;
607
+ }
583
608
 
584
609
  case 'use': {
585
610
  // ✅ SAFE INTERPOLATION for tool name