Skip to main content

agent() calls & output contracts

agent() is the language's only point of non-determinism. All model capability flows through it, and every output carries a contract. This page covers all options and runtime behavior.

Full form

agent(target) {
task "..." // required, unique: natural-language task
input { ... } // structured input (object literal)
tools [read_file, grep] // requested tool capabilities (tools none = empty)
write item.writes // write scope, must be exactly path[]
expect WorkItem // required, unique: output contract
timeout 5m // positive duration
retry { attempts: 1, on: [timeout, rate_limit, transient] }
}

The static type of an agent() expression equals its expect type — so accesses like result.work[*].owner are checked at compile time.

The three target forms

FormCompiles toMeaning
agent(coder) (alias from use agent){kind:'alias'}a fixed logical agent
agent(team.main){kind:'team.main'}the team's main agent (e.g. the lead)
agent(team.member(expr)){kind:'team.member', member: <expr>}dynamic member; expr must be text/member — membership validated at runtime

Anything else is invalid-agent-target; team.main(...) is not callable.

Dynamic routing is the payoff of this design:

// Route each work item to its owner — routing as data, not hardcoding
return parallel map plan.work as item {
agent(crew.member(item.owner)) {
task "Implement the work item"
write item.writes
expect ChangeResult
}
}

expect: from type to enforced contract

The expect type must be Schema-generatable (primitives, member, objects, arrays, composable). Verification and external types are not allowed.

Runtime chain:

Flow type → JSON Schema → OutputContract{typeName, schema, repairAttempts}
→ AgentRuntime invocation → structured result → Ajv validation
→ failed? re-invoke with {previousOutput, errors} (default 1 repair)
→ still failing → OutputValidationError

Key points:

  • The repair loop is default behavior (repairAttempts = 1): on the first invalid output the Runtime re-invokes the agent carrying the validation errors rather than failing immediately
  • Validation failures are structured — the Ajv error list feeds the repair prompt

tools and write: requests, not grants

tools [read_file, list_dir, grep] // request these tools
tools none // request the empty set
write item.writes // declare the write scope (path[] type)

Two things to internalize:

  1. Request ∩ host policy. The tools list is what the workflow wants; the effective set = request ∩ host policy.allowedTools. Declaring more never yields more.
  2. write must be statically path[]. Array literals infer a common element type, so [input.kb_path] is valid when that field is a path; mixing non-path elements fails write-not-path-array.

retry: by error code, not message

retry { attempts: 1 } // on omitted = ['transient']
retry { attempts: 2, on: [timeout, rate_limit] }
  • attempts counts extra tries: attempts: 1 executes at most twice
  • Retrying is decided by error code (timeout / rate_limit / transient), never by parsing the message — messages change, codes don't
  • Each attempt is still bounded by timeout and the workflow's remaining deadline

timeout and deadlines

timeout 3m caps a single invocation. The Runtime races the call against min(timeout, remaining workflow deadline) via AbortSignal: a late result never takes effect. Short values like 90s are perfectly legal.

A complete example

Excerpted from examples/bug-triage.flow:

use team "dev-team" as dev
use agent "repo-investigator" as investigator

stage reproduce -> Reproduction {
let result = agent(investigator) {
task """
Locate the suspect code for the reported bug.
Quote the decisive lines in hypothesis.
"""
input {
bug: input
logs: input.logs
}
tools [read_file, grep, list_dir] // request read-only tools
expect Reproduction // {reproduced, suspect_files: path[], hypothesis}
timeout 3m
retry { attempts: 1, on: [transient] }
}

require result.suspect_files.count in 0..10
else fail "suspicion list must stay focused"
return result
}

Deterministic judgment (require) sits outside the agent; the non-deterministic output is fenced by expect; tools are requests; timeout and retry are declarations. This is the canonical Agent Flow posture.

Next steps