Skip to content

Language Reference ​

A .halo file is a list of declarations in any order. Comments are // and /* … */. Whitespace and newlines don't matter. The full grammar is grammars/Halo.g4.

halo
policy Safe : Block(PromptInjection)
tool   Api  : REST("api.example.com/v1", apiKey: Env.API_KEY)
store  Docs : VectorStore(namespace: "docs")
schema Answer { text: String, sources: Array<String> }
agent  Bot [openai/gpt-4o-mini, temp: 0.3]
  equips Docs
  requires Safe
pipeline Ask(question: String) -> Answer:
  question |> Bot.answer("""Reply as {"text": ..., "sources": [...]}""")

agent ​

@directive(...)*
agent Name [model, param: value, ...]
  system "..." | """..."""
  equips Tool, Store, ...
  requires Policy, ...
  • model: a model id. Its prefix chooses the provider: openai/, anthropic/, google/ or gemini/, openrouter/, together/, replicate/, ollama/ or local/. Bare gpt-*, claude-*, and gemini-* ids also work. See Quickstart for each provider's key variable.
  • params: temp / temperature, max_tokens, and top_p are sent to the model. system: or role: here sets the system prompt when there is no system modifier. tools: auto, max_steps, and tool_result_chars control tool calls the agent chooses.
  • system: the system prompt.
  • equips: tools and stores the agent may use with tool: or tools:. Equipped VectorStores and File stores also add context automatically (see Stores).
  • requires: policies checked on every call (see Policies).
  • directives: @timeout, @retry, @shadow, @correct (see Directives).

tool and store ​

tool  Name : Provider(args...)
store Name : Provider(args...)

tool and store mean the same thing. Use store for things that hold data. The providers are REST/GET, POST, PostgreSQL/SQL, VectorStore, File, Bash, Browser, Chrome, Workspace, KV, Code, Documents, MCP, Slack, Email, GitHub, Linear, and Figma (see Built-in Tools). You can add more with tool packages or runtime.registerToolProvider.

Env.NAME reads an environment variable. It works only in tool and store declarations, written either bare (Env.GITHUB_TOKEN) or as a string ("Env.GITHUB_TOKEN"). In a pipeline it is an error, and {{Env.X}} is never filled in, so secrets can't reach prompts.

policy ​

policy Name : Rule(args) & Rule(args) & ...

See Policies for each rule and which ones are enforced.

schema ​

schema Name { field: Type, other: Array<Type> }

Field types: String, Number, Integer, Boolean, Array/List (optionally Array<T>), Object/JSON/Any, or another schema. When a pipeline declares -> SchemaName, its output is checked against that schema. See Types & Schemas.

pipeline ​

@directive(...)*
pipeline Name(param: Type, ...) -> ReturnType:
  start-value
    |> Stage() as name
    ...

A pipeline starts with an expression (a parameter, a literal, or a context.… path) or directly with an operator. Parameter types are labels and are not checked at runtime. The return type is checked only when it names a declared schema. Result is a common placeholder.

Calls ​

FormMeaning
Agent.method(args)Prompt an agent. The method name becomes the task.
Tool.method(args)Call a tool provider method.
System.Method(args)A built-in (Print, Log, Sleep, Fail, Pick) or an event (Publish, Notify, …).
Pipeline(args)Run another pipeline. Positional args bind to its parameters in order, named args by name.

Arguments are positional or named (key: value). A value is a string ("…", '…', """…"""), a number, a duration (500ms, 30s, 5m, 1h, 2d), true/false/null, a list [a, b], a parameter or result name, a dotted path, or context.….

On an agent call:

  • positional strings are instructions
  • other positional values are inputs
  • named args are listed as parameters
  • tool: T runs the equipped tool first
  • tools: [A, B] (or auto, or none) lets the agent choose its own calls to those tools; max_steps: n limits the requests
  • temperature, max_tokens, top_p, and system override the agent's settings

If an agent call passes no inputs and its instructions have no {{…}} placeholders, the flowing value is sent as the input.

Operators ​

OperatorStage
|> Call() as xpipe
|? (cond)gate: halt if false
|? { (cond) => Call(), else => Call() } as xbranch
|* { CallA(), CallB() as b } as allfan-out in parallel
|* each(item in list, concurrency: n) => Call() as xsmap over a list
|! yield(msg, ...) as answerask a human
~> listen(Source.Event) as eventwait for an event
|<> loop(Agent, "cond", max: n) as triesretry an earlier agent stage

Conditions support ==, !=, >, >=, <, <=, &&, ||, !, and parentheses. Details are in Pipe Operators.

Templates ​

{{name}}, {{name.field}}, {{context.data.x}}, and {{current}} are filled in inside strings. They resolve parameters, results, and bindings. If a placeholder doesn't resolve, it is left as written, and the validator warns.

Keywords ​

policy tool store schema agent pipeline equips requires system context yield listen loop each in else as, plus true false null. The reserved names System and Env cannot be declared.

curl -fsSL https://halolang.com/install.sh | sh