Skip to main content

Type system

Types are Agent Flow's contract layer: an expect type compiles into a JSON Schema enforced at runtime; field access, projections and return values are all checked at compile time.

Type expressions

TypeRef := PrimaryTypeRef Suffix*
PrimaryTypeRef := 'member' '<' Id '>' | Id
Suffix := '[' RangeBounds? ']' // a suffix makes it an array
RangeBounds := NUMBER '..' NUMBER // closed interval

Examples: text, path[], WorkItem[1..6], path[][], and member<engineering>[]. Optional fields use field?: Type. Reading one produces internal T?, which must be resolved with value ?? fallback; null is the explicit empty value.

Type kinds

TypeSyntaxJSON SchemaNotes
texttextstring
numbernumbernumber
boolboolboolean
pathpathstringstructurally text; semantically for permissions / write scopes
durationdurationnumber (ms)number <: duration both ways
objecttype Name { ... }objectnamed field set with types and optionality
arrayT[], T[min..max]array (optional minItems/maxItems)bounds enforced at runtime only; static assignability ignores them
optional field/valuefield?: T / expr ?? fallbackomitted from requiredreads cannot be used directly as T
membermember<Team>string + x-flow-member annotationteam member id; membership validated by the Runtime when used in calls
verificationVerification (builtin){passed, failed, checks}only producible by verify
externalundeclared named typeanything (accepted)only allowed at workflow input/output/context boundaries; info diagnostic

Name resolution order

  1. Primitive type names (text/number/bool/path/duration)
  2. Builtin Verification
  3. type declarations in this workflow (order-independent, forward refs ok)
  4. At input/output/context boundaries, an external type; elsewhere unknown-type

Undeclared types in expect, stage returns, or fields are errors, so contracts cannot silently degenerate.

Assignability

Rules for S <: T:

  • any (context) and external types: assignable both ways with anything
  • Primitives: same name; number <: duration and duration <: number
  • member<T> <: text; member<T> <: member<T> only for the same alias
  • Arrays: element assignability (min/maxItems not compared — bounds are runtime Ajv business)
  • Objects are structural: S <: T iff every required field of T exists in S with assignable type; optional fields may be absent; extra fields are fine (runtime validation is Schema-based with additionalProperties: false)
  • Verification<S_checks> <: Verification<T_checks>: checks assignable
  • Recursive types are rejected: recursive-type

Common patterns

Bounds as business constraints

type Plan {
summary: text
work: WorkItem[1..6] // compiled into the Schema: Ajv enforces 1..6
}

Bounds are runtime semantics (static checks ignore them), so they complement require ... in 1..6: bounds gate bad data at the Schema; the require states a workflow assertion with a readable message and a require.failed event.

member<Team>: people as types

use team "engineering-team" as engineering

type WorkItem {
id: text
owner: member<engineering> // value must be a member id of that team
writes: path[]
}

member<engineering> compiles to {"type":"string","x-flow-member":"engineering"}. When used as the argument of team.member(expr), the Runtime validates membership — assigning work to a person who actually exists on the team is a runtime guarantee.

Structural object assignability

A parallel's anonymous object result can be returned directly to a structurally identical named type:

stage evidence -> Evidence { // type Evidence { style: Findings ... }
let found = parallel {
style = agent(styler) { /*...*/ expect Findings }
accuracy = agent(facter) { /*...*/ expect Findings }
}
return found // {style: Findings, accuracy: Findings} <: Evidence
}

JSON Schema mapping

Flow typeJSON Schema
text / path{"type":"string"}
number / duration{"type":"number"}
bool{"type":"boolean"}
member<T>{"type":"string","x-flow-member":"<teamId>"}
T[]{"type":"array","items":<T>}
T[min..max]the above + minItems/maxItems
object (named, nested)properties + required + additionalProperties:false, nested named types inlined
Verification / external / any{} (accepts anything)

expect Verification is forbidden (expect-invalid-type): verification verdicts must be computed by the Runtime, never produced by an agent — see verify.

Next steps