Skip to main content
This page documents two related ABL constructs:

MEMORY

Memory in ABL defines how an agent stores, retrieves, and persists information across conversation turns, sessions, and users. The MEMORY: block declares four categories: session variables, persistent variables, remember triggers, and recall instructions.

Overview

Every agent has access to a memory configuration that controls data lifecycle:
  • Session variables exist for the duration of a single conversation session.
  • Persistent variables survive across sessions, scoped to a user or project.
  • Remember triggers automatically store values when conditions are met.
  • Recall instructions load stored facts at specific lifecycle events.

Session variables

Session variables hold data that is relevant only during a single conversation. They are created when the session starts and discarded when the session ends (unless RESET: never is specified).

Syntax

You can declare a session variable with a bare name (minimal form) or with additional metadata.

Properties

Reset behavior

Example: typed session variables

Write authority, format and derived values

A session variable can also declare who may write it, what kind of caller-supplied value it holds, and an expression the platform computes for it — together these are the building blocks for identity-verification flows (confirming a caller against a stored profile).
(Legacy .agent.abl format: SOURCE: user, FORMAT: email, DERIVED: MATCHES(supplied_email, profile_email) are written the same way, uppercase, directly under the variable.)
  • SOURCE: user — the variable is stored only when the value is something the caller actually said; a model write of a value the caller never said is silently refused (the value is not stored) and traced by field name only, never by value. A close spelling is stored as the caller said it — for example the caller spells “Riviera” but the model writes “Rivera”; the stored value is the caller’s own spelling.
  • SOURCE: tool — only a tool binding (via on_result/on_error mappings) can set the variable; a model write is refused the same way. SET and other FLOW-authored writes still follow the active agent’s own declarations.
  • FORMAT — one of email, phone, digits, address, person_name. It governs how the caller’s spoken/typed words are read when grounding a SOURCE: user value (for example, “at the rate” → @, spelled-out digits, spoken addresses), and supplies the default kind for a two-argument MATCH_SCORE/MATCHES/MATCH_RESULT call whose operands are both FORMAT-declared fields — but only inside a DERIVED expression. Elsewhere (CONSTRAINTS, WHEN, hooks, and other expressions), a two-argument call is scored as kind: text unless you pass the kind explicitly: MATCHES(a, b, "email").
  • DERIVED — an expression (typically built from MATCH_SCORE/MATCHES/MATCH_RESULT, or a boolean combination of other fields) that the platform recomputes after every session write. The model can never set a DERIVED field directly. A derived field may reference another derived field in any declaration order. If two or more derived fields depend on each other in a cycle, all of them are left unset — so a rule like verified == true fails closed rather than evaluating against a stale or undefined value (compiler warning DERIVED_CYCLE). A computed value that doesn’t match the field’s declared TYPE is also left unset. A DERIVED expression must be valid CEL with no trailing // comment (CEL has no comments) and no call to a function the evaluator has no overload for — any of those is a compiler warning (DERIVED_EXPRESSION_INVALID); the agent still compiles, but the field never computes correctly.
  • READONLY: true — a lighter-weight opt-in than SOURCE/DERIVED: it only blocks the model’s own __set_context__ writes. SET, on_result, and hooks can still write it. Only the literal value true opts in.
MATCH_SCORE(said, stored, kind?) returns 0-100 (or -1 when there’s nothing to judge — a missing value, a PII vault token, or an input over 1,024 characters; -1 is below every threshold, so a rule written as MATCH_SCORE(...) >= 70 fails closed). MATCHES(said, stored, kind?) is the pass/fail form on the platform’s own match bands, and MATCH_RESULT(said, stored, kind?) returns "match", "retry", "mismatch", or "missing". These are a said-versus-stored identity comparison, not a regex test — the infix a MATCHES "pattern" operator is the regex form. See Built-in function reference for the full signatures and match bands.

Persistent variables

Persistent variables survive across sessions. They are stored in a fact store and scoped to either a specific user or to the entire project.

Syntax

Persistent variable paths use dot notation. The first segment typically indicates the scope (user.* or project.*), but you can override this with the SCOPE property.

Properties

Scoping rules

  • User scope (SCOPE: user): Values are unique per authenticated user. Two users in the same project see different values for the same path.
  • Project scope (SCOPE: project): Values are shared across all users within the project. Useful for reference data, feature flags, or global configuration.
  • Execution-tree scope (SCOPE: execution_tree): Values are shared across one handoff tree / long-running execution — visible to the agents participating in that orchestration but not persisted per-user or per-project.
As shorthand, a READS: sub-block declares persistent paths with ACCESS: read, and a WRITES: sub-block declares them with ACCESS: write — an alternative to listing each path under persistent: with an explicit ACCESS.

Remember triggers

Remember triggers define rules for automatically storing values into persistent memory when a condition is met during conversation.

Syntax

Array targets can accumulate values instead of being overwritten, using the block form of STORE:
The modifiers are only read from the block form; the inline value -> target arrow form always uses overwrite. append, unique, and maxSize apply to array-typed targets in user, project, and execution_tree scopes.

Properties

TTL format

TTL values use a duration string: Example: "90d" means the stored fact expires 90 days after it was written.

Recall instructions

Recall instructions define when and how to load persistent facts back into the session context. They execute at specific lifecycle events.

Syntax

Properties

Recall events

ON: accepts these canonical lifecycle event patterns:
Legacy event names such as search:before, ON_START, or ON_SEARCH are no longer supported and are rejected during compilation — use the canonical patterns above. Note that tool:<name>:before is not a valid recall event (only tool:<name>:after).

Recall actions

Accessing memory in expressions

Session variables are accessed by name in expressions and template strings:
Persistent variables are accessed through their full dot-notation path:
Variables set via SET assignments in flow steps, ON_START, or hooks are written to session memory and available for the remainder of the session.

CONSTRAINTS

Constraints are deterministic business rules that the runtime evaluates against the current session state. Authors can still group constraints under named phases for readability, but phase names are currently labels only: the compiler flattens constraints into a single runtime list unless a constraint explicitly gates itself with WHEN or a structural checkpoint construct.

Overview

Constraints enforce guardrails on agent behavior that are separate from LLM instructions. They are deterministic checks evaluated by the runtime, not suggestions to the model. A constraint that fails blocks the current operation and triggers an ON_FAIL action.

Phases

Named phases are retained as authoring labels for readability, review, and organization. They do not currently create separate runtime execution phases by themselves.

Syntax

Use labels like always, pre_booking, or eligibility_check when they help readers understand intent, but use WHEN to gate applicability and BEFORE only for explicit structural checkpoint targets.

Requirement rules

Each requirement within a phase uses one of three keywords: REQUIRE, LIMIT, or RESTRICT.

REQUIRE

The most common form. Asserts that a condition must be true. If the condition evaluates to false, the ON_FAIL action is triggered.

LIMIT

Expresses a numeric boundary. Semantically equivalent to REQUIRE with a comparison, but communicates intent more clearly.

RESTRICT

Expresses a prohibition. The condition describes what is forbidden; when it evaluates to true, the constraint fails.

WHEN

Use WHEN: to make a constraint apply only in a specific context without overloading the phase label.

BEFORE

Use BEFORE for structural checkpoints that the runtime knows how to activate.
Supported structural targets today:
  • BEFORE calling <tool_name>
  • BEFORE returning results
Non-structural BEFORE targets are retained for compatibility, but they are warning-only and have no runtime effect. Prefer IMPLIES or WHEN for that form.
A BEFORE calling <tool_name> rule’s condition (REQUIRE/LIMIT/RESTRICT) or WHEN can read the about-to-be-sent arguments via tool_args, but only when the agent sets EXECUTION: guardrail_builtins: true. Without it, tool_args is unbound: the rule cannot read the arguments, so it never holds, and the tool call is refused. The compiler warns GUARDRAIL_BUILTIN_NOT_BOUND for this case.

IMPLIES

IMPLIES is an inline condition operator (not a top-level keyword) for expressing conditional requirements. A IMPLIES B is lowered to NOT (A) OR (B) — that is, “if A holds, then B must also hold.” Use it inside the condition of a REQUIRE/LIMIT/RESTRICT rule:

ON_FAIL actions

ON_FAIL accepts a single-line action — a message string, HANDOFF <Agent>, or ESCALATE — or a structured block. The block form can respond, reset state, and route in one place:
The CLEAR key lets an ON_FAIL block reset dependent state directly — you no longer need a separate CLEAR-only step solely to clear variables before a retry.
An ON_FAIL RESPOND message may use {{_abl_constraint_fail_count}} — this failure’s 1-based ordinal for the constraint within the current conversation. Pair it with MAX_REPEATS/ON_EXHAUSTED to vary the message as attempts run out, then hand off once the cap is reached:

Constraint properties

ON_FAIL actions

The ON_FAIL value determines what happens when a constraint fails.

String message (respond)

The most common form. Sends a message to the user and halts the current operation.

HANDOFF

Transfers control to another agent. Active flow and reasoning runtime checkpoints execute the handoff immediately when the constraint fails.

ESCALATE

Triggers human escalation.

Block

Silently blocks the operation without a user-facing message.

Structured ON_FAIL block

For more control, use a structured block that combines multiple actions.
Structured ON_FAIL properties

Collect and retry example

GOTO example

Severity levels

Constraint evaluation order

  1. Constraint phase names such as always or pre_booking are labels only; the compiler flattens all constraints into one runtime list.
  2. WHEN gates a constraint to a specific context, and structural BEFORE gates it to supported checkpoints such as tool calls or final responses.
  3. Inline flow-step CHECK expressions are separate from CONSTRAINTS and evaluate directly against step.check.
  4. Declaration order is preserved in the flattened list.
  5. Evaluation stops at the first error-severity failure. Warnings do not halt evaluation.

Template interpolation in messages

ON_FAIL message strings support {{variable}} interpolation using the current session context:

Related articles: