Atomic Behaviours
What you'll learn: The 12 behaviour types that form Kyvvu's canonical vocabulary, how step_type + verb classifies them, and where properties fit in.
The vocabulary
Kyvvu's vocabulary contains 12 atomic behaviour types across two namespaces. The four task.* types mark the boundaries of a task; the eight step.* types describe what the agent does inside one. The split names a real asymmetry rather than a routing difference: a task.* boundary marker carries no verb and neither input nor output, while a step.* behaviour carries the full {properties, input, output} triplet — which is what lets an absent output mean intended rather than no result yet. Together with an HTTP-style verb (GET, POST, PATCH, DELETE, or none), they form the vocabulary the engine operates on.
Each task-boundary behaviour follows the step lifecycle: it is emitted through the normal step path, evaluated at step_execution, and matched by step_type. The SDK exports the four task-boundary types as TASK_BOUNDARY_STEP_TYPES. Test for a boundary with that frozenset, not with a startswith("task.") prefix check — the prefix is correct today, which is exactly why it spreads; the frozenset is the one place that changes when the vocabulary does.
task.start
--
A task begins.
task.end
--
A task completes normally.
task.error
--
A task terminates with an error.
task.idle
--
The agent is idle within a task (heartbeat / keepalive).
step.resource
GET/POST/PATCH/DELETE
Read or mutate an external resource (DB, file, API).
step.message
GET/POST
Receive (GET) or send (POST) a message.
step.self
GET/POST/PATCH/DELETE
Read/write the agent's own internal state (memory, scratchpad, plan).
step.model
--
Send a prompt to an LLM and receive a completion; no verb.
step.credential
GET/POST/PATCH/DELETE
Read, issue, rotate, or revoke a secret, token, or credential.
step.exec
--
Execute code (run a script, call a function, shell out).
step.gate
--
Cross a gate — human approval, policy check, guardrail.
step.unknown
--
Uncategorisable behaviour (template fallback).
SDK and Claude task.start events declare aag_version: "0.5.0-partial", matching the pinned AAG vocabulary version. The -partial suffix records remaining differences from that grammar; it is a conformance declaration, not a guarantee that every AAG field is present. Legacy starts without the field remain valid. Other step types cannot carry aag_version.
These combinations are enforced by the Behavior model validator, which checks (step_type, verb) pairs. Any pair outside the valid set raises on construction. Behavior also forbids input keys it does not define, so an unexpected field raises there too rather than being silently dropped.
Valid step_type and verb pairs
A behaviour is a step_type plus, for some types, a verb. Exactly 22 pairs are valid; any other pair is refused when the behaviour is built (in Python, as a ValueError; over REST, as HTTP 422; in a kyvvu try steps file, as an error naming the step). The table is generated from VALID_COMBINATIONS in kyvvu_engine/schemas.py, so it is the whole list.
step_type
Valid verb values
task.start, task.end, task.error, task.idle
none: leave verb out (or null)
step.resource
GET, POST, PATCH, DELETE
step.self
GET, POST, PATCH, DELETE
step.message
GET (receive), POST (send)
step.model
none: leave verb out (or null)
step.credential
GET (read), POST (issue), PATCH (rotate), DELETE (revoke)
step.exec, step.gate, step.unknown
none: leave verb out (or null)
Verbs are upper case and exact: post is not POST. step.model, step.exec, step.gate and step.unknown take no verb, and neither do the four task.* types. Giving one a verb is an error, and so is leaving the verb out of a type that needs one (a step.resource without a verb). Model calls send context and receive a result, so policies with a verb filter alone no longer match them; filter on step.model explicitly. Historical model rows with POST remain in stored logs. The error says what the type takes:
Policy enforcement points
Policies are evaluated at two enforcement points:
Agent registration — once, before the first task begins. Policies with
enforcement_point: agent_registrationrun against the agent's metadata (purpose, tools, classification).Every step — before each step executes. Policies with
enforcement_point: step_executionrun against the intended Behavior and the task's history.
At step_execution, all 12 behaviour types follow the policy-evaluation path. A rule without a step-type filter sees task-boundary behaviours as well as action behaviours. working_hours_only, for instance, checks the clock on every behaviour it sees, and task.idle keeps that check active during a long pause.
Properties
Everything beyond the (step_type, verb) pair lives in properties — a nested dict that policies can inspect. Properties distinguish a step.resource reading customer-data from one reading product-data. The type is the same; the property is different.
Standard property groups:
target
The thing being acted on
domain, table, object_id, system, trust
auth
Authentication access level
access_level (read/write/admin), principal
data
Payload classification
classification (pii, public), fields
model
LLM details (for step.model)
provider, name, parameters
exec
Execution details (for step.exec)
runtime, isolation, side_effect_class
guard
Gate details (for step.gate)
check_type (human_approval, policy_check), result (pass or fail; only a pass satisfies a gate requirement)
message
Message details (for step.message)
channel, sender, recipient
usage
Usage metrics (for step.model outputs)
prompt_tokens, completion_tokens, cost_usd
Custom property groups are permitted. The engine passes them through unchanged, and rule functions read them via dot-path accessors (e.g. target.table, data.classification).
Worked example
A step.resource GET reading customer data:
A policy using the domain_allowlist rule, with step.resource in its target_step_types, would check target.host. A policy using pii_in_request could scan the input for sensitive patterns. The history_contains rule could check whether this behaviour has already occurred in the task.
Mapping from frameworks
Templates bridge framework-specific events to the behavior vocabulary above. Each template is a YAML file with ordered rules that map event types (e.g. LangChain's on_llm_start, or a decorator's step_type) to (step_type, verb) pairs with property extraction. See Templates for how this works, and the Template Authoring Guide for how to write your own.
Next steps
Policies and Rules — how policies use behaviour types and properties to make decisions
Templates — how the SDK maps framework events to this vocabulary
Built-in Rules Reference — all 27 rules and which behaviour types they operate on
Last updated
