For the complete documentation index, see llms.txt. This page is also available as Markdown.

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.

Step type
Valid verbs
What it represents

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:

  1. Agent registration — once, before the first task begins. Policies with enforcement_point: agent_registration run against the agent's metadata (purpose, tools, classification).

  2. Every step — before each step executes. Policies with enforcement_point: step_execution run 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:

Group
Purpose
Example keys

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

Last updated