- Apply inbound
set:button taps (when the user text is a valid payload from the latest agent message). The engine writes that memory field and runs its memory validation tool before any LLM call. Stale or non-writable taps utterutter_set_memory_button_skippedand skip the rest of this loop. See Set-memory payloads. - Execute deterministic steps first: ordered-block steps that need no
model call (
execute_tool:,noop:,set_memory:,action:, andcollect:steps whose value is already known, including a field just written by aset:tap). The framework also narrows what the LLM may do next, omittinghidden/disabled/ engine-managed skills and hiding gated tools, though the routing choice itself is made by the LLM in step 4. - Build a scoped prompt from live state: the active skill’s instructions
filtered by
if:markers, the tools whoserequires:conditions currently hold, and the memory values readable in scope. A suppressedset:payload is omitted from this prompt. - Call the LLM.
- If the LLM returns a tool call: dispatch it, apply any memory writes the tool makes as it runs, check for newly-available deterministic steps, and loop back to step 2.
- Otherwise: send any text. Then either wait for the user or loop from step 2. See When the engine waits.
When the engine waits
The engine waits for the next user message when:- The stack is empty after a delivered response (the skill or block finished).
- An engaged
collect:needs the user’s answer. - An ordered-block
action: action_listenstep runs. - The LLM calls
listen()alone (not mixed with text or other tools).
utter_*, leftover hybrid prose or further
deterministic steps can still run in the same turn. On that follow-up LLM call,
a
canned-response reminder
tells the model not to repeat or paraphrase the canned reply. See
listen.
When the engine ends the conversation
Hangup is destructive and is not offered as an LLM tool. A skill must explicitly executeaction: hangup in an ordered block to end the conversation. Place an
utter_* goodbye before that step when a farewell is needed.
State tracked per control level
These coexist. A hybrid skill, meaning prose with
if: markers that also
references an ordered block, tracks both a step pointer and memory state at
once.
Tool execution
A tool may be invoked by the LLM (a tool call) or by the framework (anexecute_tool: step). The function does not know or care which.
tool_constraints are checked in both cases, and in two places:
- when the tool schema is built, deciding whether the LLM sees the tool at all
- again at dispatch, before the function runs
requires: condition that cannot be evaluated hides
and blocks the tool rather than allowing it.
When the LLM issues a local or MCP tool call, Mantle validates the arguments
against the JSON schema offered to the model before confirmation or execution.
Missing, extra, or mistyped arguments are returned as a retryable error so the
model can correct the call in the same turn. Because the tool did not run, this
does not trigger its on_failure response. An imported MCP tool without a
usable object schema is treated the same way: the remote tool is not called.
Ordered execute_tool steps use the same check after memory fill. Those
arguments come from the step’s parameters:, not from the model, so the LLM
cannot repair a schema-invalid payload. The engine stays on the step, does not
run the tool or on_failure, and yields so a later deterministic pass can
retry if missing memory has been filled. If the parameters themselves stay
invalid, the tool cannot run.
Knowledge search changes the loop
Whensearch_knowledge runs during a turn, the tool list for the remainder of
that turn collapses to activate, search_knowledge, and cannot_help, plus
resolve_tool_confirmation when a confirmation is pending so the pending gate
is never stranded. The active skill’s lifecycle, collect, builder, and listen
tools are dropped so a grounded answer neither re-drives the skill nor ends
silently before presenting the retrieved information. They return on the next
turn.
Relatedly, cannot_help is withheld until a search has run whenever a knowledge
base is configured, so the model cannot decline before retrieval could have
answered.
If the model still mixes listen with a user-facing answer after a search, the
engine ignores listen and treats the text as the grounded reply: the
post-search trim ends so the skill can be parked and resume offered.
Where conditions are evaluated
See Conditions for expression grammar, and
skill.md
complete_when for the skill-level
mapping.
Tracker events
Mantle appends these events to the conversation tracker. They are what Inspector and Copilot read; you do not author them in YAML.
These are the only lifecycle, tool, and memory events Mantle writes. Skill
lifecycle is
skill_*, block lifecycle is ordered_block_*, tool runs are
tool_executed, and memory writes are memory_set / memory_cleared.
block_id is the authored id (submit, main).
Inspector shows each ordered_block_* as a Block row.
Session-start channel metadata is recorded as memory_set (key
session_started_metadata, after the session starts). A later session start
without channel metadata does not reuse the previous session’s value.
See also
- The Runtime Loop: the same loop, explained
- Skills & Routing: how Inspector shows these events
- Constraint Table: framework vs. LLM at each level
- Tools reference: the built-in framework tools