Syntax Reference
Transform lists and compose text
| Expression | Result |
|---|---|
[x * 2 for x in values if x > 0] | A new list containing doubled positive values |
concat(left, right) | A new list, left items followed by right items |
join(labels, "; ") | Text assembled from a List of Text |
The collection is evaluated once. Each item's filter runs before its
transformation. The named item is local to the comprehension; input lists are
preserved. Element types must be compatible. Empty lists receive their type
from context, for example bind labels as List of Text = [].
Comprehensions respect the host iteration limit, including filtered items, and cancellation and duration checks. See the pure functions tutorial.
This page is a practical reference for the visible Touba syntax.
Files and indentation
.tb files accept both LF (Linux/macOS) and CRLF (Windows) line endings.
Indent each level with four spaces, without tabs. Diagnostics refer to the
lines and positions in the original file; no manual conversion to LF is needed.
A lone carriage return (CR without LF) is not a line ending.
Use # for a line comment, including after an instruction.
Compilation Front End
Tree-sitter support is used for editor tooling and CST construction. The Touba
compiler rejects ERROR and MISSING recovery nodes before AST lowering. A
file that Tree-sitter can partially display in an editor is therefore not
accepted as a Touba program when the grammar had to recover from an error.
Text, types and binding scope
A Text literal is double-quoted on one source line. JSON escapes are decoded exactly once: \n (newline), \r, \t, \b, \f, \", \\, \/ and \uXXXX (Unicode, including valid surrogate pairs). "a\n\nb" contains two newlines; "\\n" retains a backslash followed by n. Invalid escapes and raw control characters are refused. Multiline source literals remain unsupported; no implicit interpolation or JavaScript evaluation.
Int / Int returns Float. Int // Int returns an Int truncated toward zero: (0 - 7) // 2 is -3. A zero divisor is refused. Currency formatting and locale conventions belong in a package.
Templates are explicit: f"Case {reference}, quantity {text(quantity)}". Every expression inside braces must produce Text. Use {{ and }} for literal braces. Ordinary JSON text escapes still apply; expressions are pure and no host code is evaluated.
A binding belongs to its block. Sibling branches may reuse a name; shadowing a visible name or using a name outside its scope is rejected. if, for and while can nest in functions, skills and strategies. List items retain their type, including List of List of Int.
Module
module examples.name
In a Touba project, the recommended path mirrors the logical module name:
src/examples/name.tb
Run limits
module examples.bounded
limits run
max_run_ms as 30000
max_steps as 100
max_tool_calls as 20
max_agent_invocations as 5
max_provider_calls as 10
A single limits run block, after imports and before declarations, bounds the entire run. Each value is a positive Int literal no greater than 9007199254740991. The compiler retains its source location in AIR; no manual JSON editing is needed.
An authorized capability may be reused while authority and budget allow it. Subagents share the run counters; a human-pause resume preserves prior consumption. Linking combines the limits of the entry module and modules whose executable declarations are linked, taking the minimum. Importing only a type, constant or memory does not transfer its module's run limits.
Imports
The documented imports cover two families:
- pure imports (IM-1):
type, non-sensitivedefine, and purefunction; - effectful imports (IM-2): local
tool,skill,strategy,agent, andmemoryfrom the same project.
module examples.search
import type SearchRequest from examples.common
import define DefaultRows from examples.common
import function NormalizeQuery from examples.common
import tool SearchDatasets from examples.data_tools
import skill BuildBrief from examples.shared_flow
import strategy SharedRun from examples.shared_flow
import agent Reviewer from examples.agent_pool
import memory ProjectKnowledge from examples.shared_memory
Imports must appear after module and before other declarations.
Effectful imports (IM-2) make local effectful components available for
composition, but they grant no execution authority. The manifest, grants,
secrets, handlers, memory scopes, callable_by, constraints, supply-chain lock,
and runtime refusals remain explicit.
Exports from an installed package use the same import syntax. Aliases, wildcard imports, and re-exports remain excluded. The compiler resolves these declarations during project AIR assembly.
Complete modules required by the import example
Save these files alongside the importing module. Imports resolve source contracts, not runtime permissions.
module examples.common
type SearchRequest
query is Text
rows is Int
define DefaultRows as Int = 5
function NormalizeQuery
takes query as Text
gives Text
do
give query
module examples.data_tools
import type SearchRequest from examples.common
tool SearchDatasets
description "Find public datasets matching the request."
takes request as SearchRequest
gives Text
requires network
module examples.shared_flow
skill BuildBrief
description "Return a typed brief supplied by the caller."
takes brief as Text
gives Text
do
give brief
strategy SharedRun
takes brief as Text
gives Text
uses BuildBrief
do
BuildBrief
with brief as brief
into result
give result
module examples.agent_pool
agent Reviewer
responsibility "Review the supplied brief for completeness using only authorized capabilities."
callable_by strategy examples.shared_flow.SharedRun
module examples.shared_memory
memory ProjectKnowledge
Types
Int represents integers exactly from −9,007,199,254,740,991 to
9,007,199,254,740,991. Inputs and results outside that range are refused.
Text length counts Unicode code points: 😀 counts as 1, precomposed é
as 1, and e followed by a combining accent as 2. Visible graphemes and UTF-8
bytes use different measures.
type Request
query is Text
rows is Int
Primary builtins: Text, Boolean, Int, Float, List of <Type>.
Business enumerations
An enumeration declares a closed set of values. Each declaration is a distinct
nominal type: Priority.high is not a Text.
module priority
enum Priority
low
normal
high
define DefaultPriority as Priority = Priority.normal
function IsUrgent
takes priority as Priority
gives Boolean
do
give priority == Priority.high
Equality operators == and != require the same nominal type. Ordered
comparisons and implicit conversion from text are rejected. Records, lists
and signatures may use this type. Import it with
import type Priority from priority.
At JSON boundaries (handlers, host inputs and agent responses), the value is
the exact member name, such as "high", checked against the expected type.
Unknown values are rejected, never replaced by a default member.
Use a define constant for a static trigger argument.
Variants have no associated payload. match value followed by indented case Enum.member blocks selects one exhaustive branch: every member must occur exactly once, with no default case. See Enumerations.
Explicit optional values
Optional of T distinguishes absence from an empty value. some(value)
constructs presence; none(T) constructs typed absence.
module optional_contact
function ContactLabel
takes contact as Optional of Text
gives Text
do
if present contact as address
give address
else
give "No contact"
function EmptyContact
gives Optional of Text
do
give none(Text)
function KnownContact
takes address as Text
gives Optional of Text
do
give some(address)
address exists only inside the if present branch. Direct access through
contact.value, use as Text, and comparison of optional envelopes are
rejected: handle presence, then compare the extracted value.
Presence guards compose with else if and loops.
List of Optional of Contact and Optional of List of Contact express
different, supported types.
At JSON boundaries use {"present":false} or
{"present":true,"value":"person@example.test"}, without extra fields.
null is not Touba absence; an Optional field remains required in a record.
{"present":true,"value":""} is a present, empty text.
Handlers and providers must preserve the same envelope.
some and none are expressions, not module define constants.
Module Constants
define names an immutable, non-sensitive module value.
define Name as Type = literal
define DefaultTopic as Text = "transport"
define ScoreLimit as Float = 0.75
define MaxRows as Int = 10
define ReviewsEnabled as Boolean = true
A constant can be used by functions, skills, or strategies in the same module.
- Only
Text,Boolean,Int,Floatliterals and qualified enum members are accepted - No composed expression in
define - No secret, environment variable, or local path in
define
Function
A function computes a pure value.
function AverageScore
takes score as Float
takes bonus as Float
gives Float
do
give (score + bonus) / 2.0
Immutable local value inside a block:
define ScoreLimit as Float = 0.75
function ReviewScore
takes score as Float
takes bonus as Float
gives Text
do
bind adjusted as Float = (score + bonus) / 2.0
bind accepted as Boolean = adjusted >= ScoreLimit
if accepted
give "accepted"
else
give "review"
bind names a local calculation. It does not allow the value to be modified
afterward.
Expressions:
- Arithmetic:
+,-,*,/,//,% - Booleans:
and,or,not - Comparisons:
==,!=,<,>,<=,>= - Text concatenation:
++ - Explicit text rendering:
text(expr)forText,Int,Float, andBoolean - Field access:
value.field
A nominal record is constructed with record TypeName { field as expression, ... } on one source line. Every field is supplied once or has a declared default. copy original { field as expression } creates a new record with explicit replacements. The original stays unchanged; the runtime validates the complete result.
Date is an ISO civil date (YYYY-MM-DD); date("2026-10-21") constructs it and text(date_value) renders it. Dates support comparison. [a, b] constructs a list; an empty list needs an expected type, for example bind items as List of Text = [].
Record fields may declare format "digits" for ASCII digits, length 3 or length 1..40 for Unicode code points or list items, and inclusive numeric min/max bounds. default accepts scalar literals, date("…"), none(Type) and []. Defaults apply to source construction; handlers and models provide complete records. See Typed data.
Tool
A tool declares an external effect. The actual implementation lives in a host
handler.
tool SearchDatasets
description "Search approved dataset sources for records matching the request."
takes request as DatasetSearchRequest
gives DatasetSearchResult
requires network
Supported clauses: requires <capability>, secrets HANDLE,
uses memory <Name> read, uses memory <Name> write.
Skill
A skill orchestrates supported functions, tools, skills, interactions, and
human validations.
skill Search
description "Run the governed dataset search workflow and return normalized results."
takes request as DatasetSearchRequest
gives DatasetSearchResult
uses SearchDatasets
do
SearchDatasets
with request as request
into result
give result
A fallback declares the single failure family it catches:
tool Primary
gives Text
skill Backup
gives Text
do
give "backup"
skill Run
gives Text
uses Primary
uses Backup
do
Primary
give "primary"
on failure handler
fallback Backup
Catchable families are business, authority, provider and handler. Cancellation and human stops are never caught.
Composable conditions and iterations
else if selects the first matching case; the final else is optional.
If no case matches, execution continues after the chain. Each for visits
its list in order; an empty list skips its body. Inner loops start over on
each new entry.
module examples.control
function FirstPositive
takes rows as List of List of Int
gives Int
do
for row in rows
for item in row
if item > 0
give item
give 0
function Category
takes value as Int
gives Text
do
if value < 0
give "negative"
else if value == 0
give "zero"
else
give "positive"
give exits the invocation, including from an inner loop. Values remain
immutable; there is no assignment, break or continue. while reevaluates
its condition and remains bounded: an unchanged condition can reach the
limit. Literal while true and while false are rejected at compilation.
The host's max_loop_iterations limit (100 by default) applies per loop
activation. Exiting after exactly that many iterations is allowed; starting
another iteration is refused. Nesting bypasses neither run-wide budgets nor
call authority. A human approval inside a loop requires a new decision for
each occurrence.
Agent
An agent exposes a bounded capability. callable_by declares authorized
callers.
agent Researcher
description "Research agent that selects an authorized dataset-search capability."
takes request as DatasetSearchRequest
gives DatasetSearchResult
responsibility "Select the declared research capability that matches the request."
uses Search
callable_by strategy RunSearch
Every capability exposed to a decision provider through agent.uses must
declare an explicit source description. The runtime refuses before the
provider call when that signal is missing.
Typed calls to imported agents use the same named with inputs and into
result binding as local calls. The compiler must receive the source modules
needed to resolve their signatures. Declaring type identities and callable_by
checks are preserved. An authorized capability may be invoked repeatedly within
the applicable budgets; the caller does not acquire the specialist’s powers.
The explicit inter-module form is accepted for imported agents:
agent Reviewer
responsibility "Fulfill the declared Reviewer responsibility using only authorized capabilities."
callable_by strategy examples.agent_app.RunReview
Typed agent invocation uses the same explicit bindings:
Researcher
with request as request
into result
Every declared input requires with, and into receives the final value checked against gives. Interfaces are not inferred from uses. Imported agents with explicit signatures use the same bindings once their source modules are resolved; agents without signatures accept only simple calls.
Strategy
A strategy is a runtime-executable orchestration entry point.
strategy RunSearch
uses Researcher
do
Researcher
A local or imported strategy with gives can also be called from a skill or
strategy using with / into. Every parameter is explicit. Local values are
isolated; budgets and cancellation stay shared across the run. A pure function
cannot call a strategy. A bounded revision can pass typed state into another
invocation without implicit shared memory.
Trigger
A trigger declares a governed event entry point.
trigger DailyResearchSchedule
on schedule "every 1s"
runs strategy RunResearch
limits
max_runs_per_day as 1
timeout_ms as 30000
trigger WebhookResearch
on request POST "/webhook/research"
runs strategy RunResearch
limits
max_runs_per_day as 10
timeout_ms as 30000
Touba accepts schedule -> strategy and request -> strategy. The schedule
runner supports every <n>s, every <n>m, and every <n>h. The webhook
service routes request entries by GET, POST, PUT, PATCH, or DELETE
method, absolute path, manifest payload contract, and TD-1 state. Other event
families are outside this syntax.
User Interactions
show and ask are interaction nodes, not functions.
show
with message as "age is: " ++ text(age)
ask
with prompt as "Search query?"
into query
Human Validation
human.approve
with context as change
into approval
Logical Memories
A memory is a logical resource. Actual access goes through a tool, a manifest,
and a handler.
memory ProjectKnowledge
tool ReadProjectKnowledge
uses memory ProjectKnowledge read
takes query as Query
gives KnowledgeResult
requires memory_read
Visible Syntax Boundaries
- No inline handler code in
.tb - No
handled_by - Enriched agent calls use
with/intowith an explicit resolved signature, local or imported. No contract is inferred fromuses. - No host method call such as
title.toLowerCase() - No
stdinread - No
showoraskinfunction - No
usesclause in a purefunction; call pure functions directly. on failurebelongs to askill, not astrategy.callable_bybelongs to anagent, not askill.- No reassignment or mutable variable
- No module constant based on a call or composed expression
- Package imports target declared exports; aliases, wildcard imports, and re-exports are excluded.
- No
runs agentin a trigger
Float uses binary floating-point arithmetic. text() preserves its numeric value and may expose decimal noise such as 0.7000000000000001; it does not silently round. Use Int minor units for exact fixed-scale amounts, and perform display formatting explicitly in the host application.
Structured parallelism
A skill or strategy distributes a typed list to a declared callable:
parallel Diagnose
each incident in incidents
with request as incident
max_concurrency 4
max_items 32
into diagnoses
Place this fragment inside do with declared variables and target. Agents,
strategies, skills, functions and tools with explicit output R are callable.
The result is List of R, in input order. Both positive integer bounds are
required, with concurrency no greater than cardinality. All arguments are
checked before admission. No partial result is published. Existing authority
rules apply; pure functions cannot contain this orchestration.
See the multi-agent tutorial.