Language Reference

O-Lang Syntax

The complete, accurate reference for .ol files — derived directly from the kernel parser and runtime.

Overview

O-Lang uses a format called Linear Declarative Workflow Form (LDWF). Workflows are read and executed top-to-bottom. Each statement expresses intent — the kernel handles execution, validation, resolver dispatch, and cryptographic audit logging.

linear

top-to-bottom execution

declarative

intent, not mechanics

.ol

file extension

This reference is derived directly from the kernel parser and runtime source. Every keyword and syntax pattern shown here is verified against actual parser regex and handler logic.

File structure

Every .ol file has three regions in this order. Blank lines between steps are required for readability and encouraged by the spec. Indentation is semantic-neutral — execution never depends on it.

1Declaration

Workflow name and input variables. Parsed first.

2Allow resolvers

Explicit allowlist. Only listed resolvers may be invoked. Required.

3Steps

Execution steps in monotonically increasing numeric order.

structure.ol
# ── 1. DECLARATION ────────────────────────────────────
Workflow "Workflow Name" with input_a, input_b

# ── 2. RESOLVER ALLOWLIST ─────────────────────────────
Allow resolvers:
  ToolA
  ToolB

# ── 3. STEPS ──────────────────────────────────────────
Step 1: Ask ToolA "{input_a}"
Save as result_a

Step 2: Ask ToolB "{result_a.field} and {input_b}"
Save as result_b

Return result_b

Keyword reference

All keywords verified against the kernel parser. Keywords are case-insensitive in the parser (regex uses /i flag throughout) but the convention is to write them with capital first letters as shown.

Keyword / StatementRegionPurpose
Workflow ... with ...HeaderDeclares workflow name and comma-separated input variable names.
Allow resolvers:HeaderOpens the resolver allowlist block. Required. Only resolvers listed here may run.
- ResolverNameAllowlistAdds a resolver to the allowlist. Parser accepts both '- Name' and bare 'Name' entries.
Constraint: key = valueHeaderWorkflow-level constraint. Currently: max_generations = N.
Step N: Ask Resolver "..."StepsNumbered step. Calls a resolver with an interpolated prompt. N must increase monotonically.
Save as variableStepsBinds the step output to a named context variable. Available to all subsequent steps.
Return var1, var2StepsDeclares workflow output(s). Comma-separated. Must be the last statement.
If {var} equals "val" thenStepsConditional block. Condition uses {curly braces} and keyword operators.
End IfStepsCloses an If block. Required — scope is keyword-defined, not indentation-defined.
Run in parallelStepsOpens a parallel execution block. All inner steps run concurrently.
Run in parallel for NsStepsParallel block with timeout. N is a number, s/m/h/d is the time unit.
Run in parallel with escalation:StepsOpens an escalation block. Levels execute sequentially with timeouts.
Level N: descriptionEscalationDeclares an escalation level with optional timeout parsed from description.
EndStepsCloses a parallel or escalation block.
Use ToolNameStepsInvokes a tool without a prompt string. Distinct from Ask.
Debrief Agent with "msg"StepsSends a debrief message to a named agent. Emits a 'debrief' event.
Evolve @resolver using feedback: "..."StepsRequests resolver evolution with feedback. Emits evolution metadata.
Prompt user to "question"StepsDeclares a user prompt step. Output not automatically saved.
Persist var to "path"StepsWrites a context variable to a file. JSON files serialized automatically.
Emit "event" with payloadStepsEmits a named event with an interpolated payload string.
Constraint: key = valueStepPer-step constraint (e.g. max_time_sec, cost_limit, allowed_resolvers).

Variables & context

The kernel maintains a context object that grows throughout execution. Variables declared with with are seeded at start. Each Save as appends to context — nothing is ever overwritten. Dot-path notation accesses nested fields inside saved objects.

{input_name}

Runtime input

Declared in the with clause. Available from Step 1.

{saved_var}

Saved output

The full output object of a previous Save as step.

{saved_var.field}

Dot-path access

A specific field inside a saved output. Supports multiple levels: {a.b.c}.

Object interpolation is blocked by the kernel. You cannot use {account} directly in a prompt if account is an object — the kernel throws a safety error to prevent data corruption into LLM prompts. Always use dot-path: {account.balance}.

variables.ol
Workflow "Invoice Check" with invoice_id, question

Allow resolvers:
  InvoiceLookup
  VendorLookup
  GroqLLM

Step 1: Ask InvoiceLookup "{invoice_id}"
Save as invoice

# {invoice.vendor_id} — dot-path into the Step 1 output object
Step 2: Ask VendorLookup "{invoice.vendor_id}"
Save as vendor

# {question} — runtime input   {invoice.amount} — dot-path   {vendor.name} — dot-path
Step 3: Ask GroqLLM "{question}. Amount: {invoice.amount}. Vendor: {vendor.name}"
Save as answer

Return answer

Allow resolvers

The Allow resolvers: block is an explicit allowlist. The kernel loads it before Step 1 and enforces it on every resolver call. Any resolver not in this list is blocked, logged to logs/disallowed_resolvers.json, and written to the cryptographic audit trail as a security_violation event.

Both formats are accepted by the parser

Allow resolvers:
  - GroqLLM
  - Extractor

With leading dash (recommended)

Bare names also valid

Allow resolvers:
  GroqLLM
  Extractor

Without leading dash

The built-in math resolver (builtInMathResolver) is added to the allowlist automatically when the kernel detects math keywords (Add, Sum, Avg, etc.) in step actions. You do not need to list it manually.

Steps & Ask

Steps execute in ascending numeric order. Step numbers must be monotonically increasing (gaps are allowed but discouraged). Each step makes one resolver call — either via Ask (with a prompt string) or Use (tool invocation without prompt).

Step N: Ask ResolverName "prompt with {variable} interpolation"
Save as variable_name
Step N:

N is a positive integer. Steps execute in order. Numbers must only increase.

Ask

Calls the named resolver with the prompt. The kernel prepends 'Action' internally before dispatch.

ResolverName

Must appear in the Allow resolvers: block. Blocked and audit-logged if not listed.

"prompt"

Prompt string. {variable} references are interpolated from context before the call is made.

Save as name

Saves the resolver's unwrapped output to context as 'name'. Available to all subsequent steps.

Use — invoke a tool without a prompt string:

use-example.ol
Step 3: Use NotifySystem
Save as notification_result

Control flow

O-Lang supports explicit conditional branching via If / End If. Scope is defined by keywords, never by indentation. The condition must reference a context variable in curly braces and use one of three comparison operators.

If {variable.field} equals "string value" then
If {variable.score} greater than 80 then
If {variable.count} less than 5 then
equals "value"

String equality check. Value must be quoted.

greater than N

Numeric greater-than. N is an unquoted number.

less than N

Numeric less-than. N is an unquoted number.

End If

Closes the block. Required. No implicit closing by indentation.

There is no Else branch. The parser supports If / End If only. For two-path branching, use two sequential If blocks with inverse conditions.

control-flow.ol
Workflow "Risk Triage" with account_id

Allow resolvers:
  RiskClassifier
  ComplianceAlert
  GroqLLM

Step 1: Ask RiskClassifier "{account_id}"
Save as risk

# Condition: {curly braces} + operator keyword + value
If {risk.score} greater than 80 then
  Step 2: Ask ComplianceAlert "Flag {account_id} — score {risk.score}"
  Save as alert_result
End If

If {risk.level} equals "low" then
  Step 3: Ask GroqLLM "Summarise low-risk profile for {account_id}"
  Save as summary
End If

Return risk

Parallel & escalation

Three parallel execution forms are supported. All are closed with End. Time units for timeouts: s seconds, m minutes, h hours, d days.

1 — Basic parallel (no timeout)

parallel-basic.ol
Run in parallel
  Step 1: Ask GroqLLM "Analyse {text}"
  Save as analysis

  Step 2: Ask Extractor "{text}"
  Save as entities
End

2 — Timed parallel (with timeout)

parallel-timed.ol
# All steps must complete within 30 seconds or timed_out = true in context
Run in parallel for 30s
  Step 1: Ask GroqLLM "Analyse {text}"
  Save as analysis

  Step 2: Ask Extractor "{text}"
  Save as entities
End

After a timed parallel block, { timed_out } is set in context: true if the deadline was exceeded.

3 — Escalation (sequential levels with timeouts)

escalation.ol
# Levels execute sequentially. First level to complete stops escalation.
Run in parallel with escalation:

  Level 1: immediately
    Step 1: Ask AutoResponder "Handle {incident_id}"
    Save as response

  Level 2: within 5m
    Step 2: Ask OnCallEngineer "Escalation for {incident_id}"
    Save as response

  Level 3: within 30m
    Step 3: Ask IncidentCommander "Critical: {incident_id}"
    Save as response

End

Return response

After escalation, context contains escalation_completed, escalation_level, and timed_out.

Advanced statements

These statements cover side effects, persistence, agent communication, and event emission. All are parsed and executed by the kernel.

Debrief — send a message to an agent

Emits a debrief event. Variable references in the message are validated before emission.

debrief.ol
Debrief System with "High risk detected for {account_id} — score: {risk.score}"

Evolve — request resolver improvement

Records an evolution request with feedback. When OLANG_EVOLUTION_API_KEY is set, advanced evolution is enabled.

evolve.ol
Evolve Extractor using feedback: "Entity extraction missed subsidiary company names"
Save as evolution_result

Prompt user — declare a user input step

Declares that user input is needed at this point. The kernel logs it; actual input collection depends on the runtime environment.

prompt.ol
Prompt user to "Please confirm the account ID before proceeding"

Persist — write context variable to file

Writes a saved variable to a file path. JSON files are serialised automatically. The variable must exist in context or the step is skipped with a warning.

persist.ol
# Writes aggregated_risk as formatted JSON
Persist aggregated_risk to "./logs/aggregated_risk.json"

Emit — fire a named event

Emits a runtime event with an interpolated payload. All {variable} references in the payload are validated before emission.

emit.ol
Emit "risk_flagged" with {account_id}

Constraints

Constraints can be declared at the workflow level or per-step. The parser recognises and stores them; enforcement varies by constraint type.

Workflow-level constraint

constraint-workflow.ol
Workflow "Agentic Loop" with task

# Prevents runaway agentic loops — enforced by the kernel at runtime
Constraint: max_generations = 5

Per-step constraints

constraint-step.ol
Step 1: Ask SlowLookup "{task}"
Save as result
Constraint: max_time_sec = 10
Constraint: cost_limit = 0.05

Per-step constraints are parsed and stored. The kernel currently warns when they are present — enforcement is in active development.

Full examples

Complete production-style workflows using verified syntax only.

Finance — AML screening with branching

aml-screening.ol
Workflow "AML Screening" with account_id, query

Allow resolvers:
  BankAccountLookup
  RiskClassifier
  ComplianceAlert
  GroqLLM

Step 1: Ask BankAccountLookup "{account_id}"
Save as account

Step 2: Ask RiskClassifier "{account.transactions}"
Save as risk

If {risk.score} greater than 80 then
  Step 3: Ask ComplianceAlert "Flag {account_id} — risk score {risk.score}"
  Save as alert
End If

Step 4: Ask GroqLLM "{query}. Risk: {risk.level}. Score: {risk.score}. Account: {account.id}"
Save as analysis

Persist risk to "./logs/aml_risk.json"

Return analysis, risk

Healthcare — parallel ICU assessment

icu-parallel.ol
Workflow "ICU Assessment" with patient_id, question

Allow resolvers:
  ICUAdmission
  LabResults
  GroqLLM

# Fetch admission record and lab results in parallel — 20s timeout
Run in parallel for 20s
  Step 1: Ask ICUAdmission "{patient_id}"
  Save as admission

  Step 2: Ask LabResults "{patient_id}"
  Save as labs
End

Step 3: Ask GroqLLM "{question}. Ward: {admission.ward}. Hb: {labs.haemoglobin}"
Save as summary

Return summary

Multi-agent — risk assessment with debrief and persist

risk-assessment.ol
Workflow "Risk Assessment Demo" with text, user_id

Allow resolvers:
  GroqLLM
  Extractor
  AggregateConfidence
  NotifySystem
  Persist

Step 1: Ask GroqLLM "Analyse the incident: {text}"
Save as analysis

Step 2: Ask Extractor "{analysis}"
Save as risks

Step 3: Ask AggregateConfidence "{risks}"
Save as aggregated_risk

If {aggregated_risk.score} greater than 80 then
  Debrief System with "High risk detected for user {user_id} — score {aggregated_risk.score}"
End If

Persist aggregated_risk to "./logs/aggregated_risk.json"

Return risks, aggregated_risk

Cheatsheet

Every verified keyword in one annotated file.

o-lang-cheatsheet.olverified against kernel parser
# ── DECLARATION ────────────────────────────────────────────────
Workflow "Name" with input_one, input_two
Constraint: max_generations = 5          # optional workflow constraint

# ── RESOLVER ALLOWLIST ─────────────────────────────────────────
Allow resolvers:
  ToolA
  ToolB

# ── STEPS ──────────────────────────────────────────────────────
Step 1: Ask ToolA "{input_one}"
Save as result_a
Constraint: max_time_sec = 10                # optional per-step constraint

Step 2: Ask ToolB "{input_two}. Field: {result_a.field}"
Save as result_b

Step 3: Use ToolA
Save as tool_result

# ── CONTROL FLOW ───────────────────────────────────────────────
If {result_a.score} greater than 80 then
  Step 4: Ask ToolA "{result_a.id} flagged"
  Save as flag_result
End If

If {result_a.status} equals "low" then
  Step 5: Ask ToolB "Summarise {result_b}"
  Save as summary
End If

# ── PARALLEL ────────────────────────────────────────────────────
Run in parallel for 30s
  Step 6: Ask ToolA "{input_one}"
  Save as p_result_a
  Step 7: Ask ToolB "{input_two}"
  Save as p_result_b
End

# ── ADVANCED STATEMENTS ────────────────────────────────────────
Debrief System with "Completed for {input_one}"
Evolve ToolA using feedback: "Improve accuracy on edge cases"
Prompt user to "Confirm before proceeding"
Persist result_b to "./logs/output.json"
Emit "workflow_done" with {result_b}

# ── RETURN ──────────────────────────────────────────────────────
Return result_a, result_b       # comma-separated, always last line