@o-lang/olang 1.1.3 → 1.1.4

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.
Files changed (2) hide show
  1. package/package.json +1 -1
  2. package/src/runtime.js +104 -16
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@o-lang/olang",
3
- "version": "1.1.3",
3
+ "version": "1.1.4",
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",
package/src/runtime.js CHANGED
@@ -343,7 +343,7 @@ class RuntimeAPI {
343
343
  }
344
344
  };
345
345
 
346
- // ✅ CORRECTED: Strict safety WITH proper resolver chaining
346
+ // ✅ CORRECTED: Strict safety WITH dynamic diagnostics
347
347
  const runResolvers = async (action) => {
348
348
  const mathPattern =
349
349
  /^(Add|Subtract|Multiply|Divide|Sum|Avg|Min|Max|Round|Floor|Ceil|Abs)\b/i;
@@ -360,19 +360,19 @@ class RuntimeAPI {
360
360
  let resolversToRun = [];
361
361
 
362
362
  if (agentResolver && Array.isArray(agentResolver._chain)) {
363
- // Resolver chain mode
364
363
  resolversToRun = agentResolver._chain;
365
364
  } else if (Array.isArray(agentResolver)) {
366
- // Array of resolvers mode (what npx olang passes with -r flags)
367
365
  resolversToRun = agentResolver;
368
366
  } else if (agentResolver) {
369
- // Single resolver mode
370
367
  resolversToRun = [agentResolver];
371
368
  }
372
369
 
373
- // ✅ CORRECTED SAFETY: Try ALL resolvers before halting
370
+ // ✅ Track detailed resolver outcomes for diagnostics
371
+ const resolverAttempts = [];
372
+
374
373
  for (let idx = 0; idx < resolversToRun.length; idx++) {
375
374
  const resolver = resolversToRun[idx];
375
+ const resolverName = resolver?.resolverName || resolver?.name || `resolver-${idx}`;
376
376
  enforceResolverPolicy(resolver, step);
377
377
 
378
378
  try {
@@ -393,22 +393,110 @@ class RuntimeAPI {
393
393
  this.context[`__resolver_${idx}`] = result;
394
394
  return result;
395
395
  }
396
- // ❌ Resolver skipped this action (returned undefined/null) — continue to next resolver
397
- // This is NORMAL resolver chaining behavior — NOT an error
396
+
397
+ // Resolver skipped this action (normal behavior)
398
+ resolverAttempts.push({
399
+ name: resolverName,
400
+ status: 'skipped',
401
+ reason: 'Action not recognized'
402
+ });
398
403
 
399
404
  } catch (e) {
400
- // ❌ Resolver threw an errorlog warning but CONTINUE to next resolver
401
- this.addWarning(`Resolver "${resolver?.resolverName || resolver?.name || idx}" threw error for action "${action}": ${e.message}`);
402
- // Continue loop to try next resolver (don't halt yet)
405
+ // ❌ Resolver attempted but failedcapture structured diagnostics
406
+ const diagnostics = {
407
+ error: e.message || String(e),
408
+ requiredEnvVars: e.requiredEnvVars || [], // Resolver can attach this
409
+ missingInputs: e.missingInputs || [], // Resolver can attach this
410
+ documentationUrl: resolver?.documentationUrl ||
411
+ (resolver?.manifest?.documentationUrl) || null
412
+ };
413
+
414
+ resolverAttempts.push({
415
+ name: resolverName,
416
+ status: 'failed',
417
+ diagnostics
418
+ });
419
+
420
+ // Log for verbose mode but continue chaining
421
+ this.addWarning(`Resolver "${resolverName}" failed for action "${action}": ${diagnostics.error}`);
422
+ }
423
+ }
424
+
425
+ // ✅ BUILD DYNAMIC, ACTIONABLE ERROR MESSAGE
426
+ let errorMessage = `[O-Lang SAFETY] No resolver handled action: "${action}"\n\n`;
427
+ errorMessage += `Attempted resolvers:\n`;
428
+
429
+ resolverAttempts.forEach((attempt, i) => {
430
+ const namePad = attempt.name.padEnd(30);
431
+ if (attempt.status === 'skipped') {
432
+ errorMessage += ` ${i + 1}. ${namePad} → SKIPPED (not applicable to this action)\n`;
433
+ } else {
434
+ errorMessage += ` ${i + 1}. ${namePad} → FAILED\n`;
435
+ errorMessage += ` Error: ${attempt.diagnostics.error}\n`;
436
+
437
+ // ✅ DYNAMIC HINT: Resolver-provided env vars
438
+ if (attempt.diagnostics.requiredEnvVars?.length) {
439
+ errorMessage += ` Required env vars: ${attempt.diagnostics.requiredEnvVars.join(', ')}\n`;
440
+ }
441
+
442
+ // ✅ DYNAMIC HINT: Resolver-provided docs link
443
+ if (attempt.diagnostics.documentationUrl) {
444
+ errorMessage += ` Docs: ${attempt.diagnostics.documentationUrl}\n`;
445
+ }
446
+ }
447
+ });
448
+
449
+ // ✅ DYNAMIC REMEDIATION (no hardcoded resolver names)
450
+ const failed = resolverAttempts.filter(a => a.status === 'failed');
451
+ const allSkipped = failed.length === 0;
452
+
453
+ errorMessage += `\n💡 How to fix:\n`;
454
+
455
+ if (allSkipped) {
456
+ // Likely action syntax mismatch
457
+ errorMessage += ` • Action syntax may not match resolver expectations\n`;
458
+ errorMessage += ` → Try removing "Action" keyword: bank-account-lookup "{id}"\n`;
459
+ errorMessage += ` → NOT: Action bank-account-lookup "{id}"\n`;
460
+ errorMessage += ` • Verify correct resolver is installed for this action type\n`;
461
+ } else {
462
+ // At least one resolver attempted but failed
463
+ errorMessage += ` • Check resolver requirements:\n`;
464
+ errorMessage += ` → Run workflow with --verbose flag for detailed logs\n`;
465
+ errorMessage += ` → Ensure required environment variables are set\n`;
466
+
467
+ // Pattern-based hints (generic, not hardcoded)
468
+ const envVarPattern = /environment variable|env\.|process\.env|missing.*path/i;
469
+ if (failed.some(f => envVarPattern.test(f.diagnostics.error))) {
470
+ errorMessage += ` → Example (PowerShell): $env:VARIABLE="value"\n`;
471
+ errorMessage += ` → Example (Linux/macOS): export VARIABLE="value"\n`;
472
+ }
473
+
474
+ const dbPattern = /database|db\.|sqlite|postgres|mysql|mongodb/i;
475
+ if (failed.some(f => dbPattern.test(f.diagnostics.error))) {
476
+ errorMessage += ` → Ensure database file/connection exists and path is correct\n`;
477
+ }
478
+
479
+ const authPattern = /auth|api key|token|credential/i;
480
+ if (failed.some(f => authPattern.test(f.diagnostics.error))) {
481
+ errorMessage += ` → Verify API keys/tokens are set in environment variables\n`;
403
482
  }
404
483
  }
405
484
 
406
- // ✅ SAFETY GUARD: ALL resolvers skipped/failed — HALT workflow
407
- throw new Error(
408
- `[O-Lang SAFETY] No resolver successfully handled action: "${action}". ` +
409
- `Attempted resolvers: ${resolversToRun.map(r => r.resolverName || r.name || 'anonymous').join(', ')}. ` +
410
- `Workflow execution halted to prevent unsafe data propagation.`
411
- );
485
+ // ✅ Always show generic troubleshooting path
486
+ errorMessage += `\n • Resolver documentation:\n`;
487
+ let hasDocs = false;
488
+ resolverAttempts.forEach(attempt => {
489
+ if (attempt.diagnostics?.documentationUrl) {
490
+ errorMessage += ` → ${attempt.name}: ${attempt.diagnostics.documentationUrl}\n`;
491
+ hasDocs = true;
492
+ }
493
+ });
494
+ if (!hasDocs) {
495
+ errorMessage += ` → Search "@o-lang/<resolver-name>" on npmjs.com\n`;
496
+ }
497
+
498
+ errorMessage += `\n🛑 Workflow halted to prevent unsafe data propagation to LLMs.`;
499
+ throw new Error(errorMessage);
412
500
  };
413
501
 
414
502
  switch (stepType) {