v0.5.0¶
This release makes an agent's turn composable and game-driven: a per-turn slot blackboard lets any node's output feed another node or template later in the same turn, agent variables let game code push live state (level, mood, inventory) straight into prompts, retrieval filters, and generated text, and tool results can now flow back into the model mid-turn instead of being faked with an acknowledgement. It also adds GBNF grammar constraints for output your game code has to parse, dialog-history editing for scripted openers and rollbacks, explicit KV-cache control, and a visual workflow graph editor for Unreal.
Several node params were replaced, a node was renamed, and the wire protocol changed — existing integrations must be updated and rebuilt. See Breaking changes and behavior changes before upgrading.
Highlights¶
- Agent variables — a typed per-agent key→value store the game writes over the wire and the server reads from prompt templates (
{{var.<name>}}), retrieval filters, and (opt-in) markers in generated text. See Agent Variables. - Slots and inter-node value passing — every producing node writes into a named slot any later node can consume via
inputor{{slot.<name>}}, plus a model-freeTransformnode for composing text. See Slots and inter-node value passing. - Model-visible tool results — pause on a tool call, run it client-side, and hand the real payload back so a downstream
Generateanswers from it. See Pause and resume a turn. - Constrained output (GBNF) — give
Generatea grammar and every reply is guaranteed to be one of your legal strings: no parse failures, no preamble to strip. See Constrain output with a grammar. - Seed and edit dialog history — append scripted user/assistant turns or tail-remove whole interactions without running the graph. See Seed and edit dialog history.
- KV-cache control — prefill an agent's caches at creation or on demand, evict them to reclaim memory, and query residency. See Manage an agent's KV cache.
- Unreal workflow graph editor — author workflows visually in Unreal, as Unity has since v0.3.0. See Edit workflows in the Unreal graph editor.
Agent variables¶
- Declare once, mutate freely.
CreateAgentgains avariablesfield — the agent's complete, type-locked declaration (int64/double/string/bool/set<string>), each with an initial value. Writes to an undeclared name are rejected. See Agent Parameters. - Read from three places, server-side. Mustache templates via
{{var.<name>}}(see Use Mustache templates), the Retrieve / ClassifyIntent filter grammar via a{"var": "<name>"}operand (see Retrieve filter grammar), and — opt-in —__NAME__markers in generated text. All three re-read the current value on every use, so a mutation lands on the very next turn with noChangeParamround-trip and no recompile. - A typed mirror on every client.
Variables()(C++) /.variables(Python) /.Variables(Unity) / Get Variables (Unreal Blueprint) offerSetInt/SetFloat/SetString/SetBool/SetStringSet/AddToSet/RemoveFromSet/Resetand matching getters. Writes validate locally against the declaration — an unknown name or wrong type fails immediately with no round-trip — update the local mirror synchronously, and are batched into one update flushed just before the next send, resume, or param change. - Output substitution. With
allow_output_substitutionon the declaration andsubstitute_agent_variableson aGenerate/GenerateAndSpeaknode,__PRICE__-style markers in the model's streamed output are replaced with the live variable value before the slot, the wire answer, history, and TTS see the text — so game code owns the number while the model writes the sentence. See Substitute agent variables in LLM output and Keep merchant prices deterministic. - Editor authoring. Declare variables on the Unity
TryllWorkflowAsset/ Unreal workflow asset, then override initial values per instance on the agent component (a Material-Instance-style Inspector row per declaration). Override edits update a live Play-mode or editor-Chat agent immediately, with a Flush Now button for eager sends. - Guide: Drive prompts and retrieval from game state.
Slots and inter-node value passing¶
- Slots replace fixed message roles. Every producing node (
Generate,GenerateAndSpeak,CannedResponse,Transform,Instruction/IntentToInstruction) writes its output into a named slot on the turn's shared blackboard. Any later node in the same turn can read it viainput; any template can reference it via{{slot.<name>}}. See Slots and inter-node value passing. - New
Transformnode. A model-free node that renders a Mustache template into its own slot — no inference, no wire emission. The flagship use is query rewriting for RAG: fold the conversation into a standalone search query beforeRetrieveruns. See Query rewriting for RAG. inputselector.Generate,GenerateAndSpeak,ToolCall,Retrieve,ClassifyIntent,ClassifyIntentLLM,RegexGuardrail, andSpeakgain aninputparam naming the slot to consume (empty = the reserveduser_messageslot), replacing the oldPlacement.InPlaceOfUserand "voice the latest character message" behaviors.output_nameoverride. Producing nodes can name their slot explicitly instead of using the node's own name, so a graph can be rewired without renaming nodes.sendreplacesstream. A three-waySendAnswerenum (None,Whole,Streamed) on generation / canned-response nodes controls wire delivery;Nonelets a node compute a value used only internally this turn (e.g. a query rewrite) without ever emittingAnswerText.history_rolecontrols cross-turn replay. AHistoryRoleenum (None,Assistant) on the same nodes controls whether a slot's text is replayed as history on later turns, independent of whether it was sent on the wire this turn. Together withsend, this is how you add a hidden draft or reasoning pass — see Add a hidden reasoning or draft step.- Multi-sender
AnswerText.node_name. EveryAnswerTextframe now carries the name of the node that produced it, so a client can attribute chunks when a turn routes through more than one text-producing node — two NPCs speaking, or a thinking channel plus a reply. Each producing node emits its own final chunk. See Send multiple answers in one turn. - Slot-aware editor authoring. The Unity and Unreal graph editors render
inputas a dropdown of only the slots reachable upstream of that node (no free text), and renaming a node or itsoutput_nameoverride now automatically rewrites every exit,inputselector, and{{slot.<name>}}template reference to the old name in one step — see Slots and inter-node value passing.
Tool calling¶
- One
dispositionparam replaces the notify/pause/history trio. A single seven-valueToolCallDispositionenum (RouteOnly,Acknowledge(default),Notify,NotifyAndAcknowledge,Pause,PauseAndAcknowledge,AwaitResult) now controls whether aToolCallnode fires atool_callevent, pauses the turn, and how the call projects into later-turn history. The full matrix is in the ToolCall reference; the history contract is in Tool-call history: complete pairs, never dangling. - Model-visible tool results (
AwaitResult).ResumeAgentRequestgainstool_results: [ToolResult {call_id, result}]. AnAwaitResultpause is resumed with a complete, atomically-applied batch — one entry per pendingcall_id, taken from thetool_callevent — so a downstreamGeneratecan condition on the real payload instead of a synthetic acknowledgement. Invalid batches (unknown, duplicate, or missingcall_id; a batch on a non-AwaitResultpause; or over the 64-entry / 128-byte-call_id/ 256 KiB-result / 512 KiB-aggregate limits) return the new3016 InvalidToolResultsand leave the turn paused for retry. See Pause and resume a turn. acknowledge_text. The acknowledgement the server writes into history for theAcknowledgedispositions is now configurable per node (empty ="ok").- New client APIs for the result-bearing resume. C++:
ResumeWithToolResult(s)/*Async,SetOnToolCallWithId, and the preferred owning-payloadSetOnToolCallEvent(safe to capture beyond the callback). Python:resume(..., tool_results=)plus the non-blockingresume_async(...), which is required from a reader-thread tool-call or paused callback — the blockingresume()/change_params()deadlock the sole reader thread if called from there. Unity / Unreal:ResumeWithToolResult(s), plusRegisterToolauto-resume sugar that now retains the paused batch (and any already-succeeded handler results) across a failed attempt — seeHasPausedToolBatch/RetryPausedToolBatch. - Pick a tool-capable model before downloading it.
models.jsonvariants now declaretool_call_support("supported"/"unsupported"), surfaced byListModelsand in the Unity / Unreal Model Manager detail pane. It is advisory — whether aToolCallnode accepts a model is still decided at agent creation from the model's own chat template. See Model Management.
Constrained output (GBNF)¶
grammaronGenerateandGenerateAndSpeak. Supply a GBNF grammar and output is constrained at every decode step — malformed output becomes unsampleable. Ideal for closed-set command parsing, dialogue-choice enums, emotion tags, or anything consumed by game code rather than read by a human. See Generate and the concept page Constrained output (GBNF).- Fail fast, flip at runtime. A grammar must contain a
rootrule; an invalid one is rejected at agent creation, and an invalid mutation is rejected with3007 InvalidParamValuewhile the previous grammar is kept.grammaris mutable and rebuilt per turn, so a client can flip a node between a strict "command turn" and free chat with a param change. See Constrain output with a grammar.
Dialog history and turn control¶
- Append or trim history without running a turn. Two new operations —
AppendInteractions(batches of{user_message, assistant_message}; either side may be empty) andRemoveInteractionsFromEnd(saturating whole-interaction tail removal) — let you seed a scripted opener, restore a save game, or roll back the last exchange. Both return applied/removed counts, emit noTurnComplete, and require a strictly idle agent (rejected with3004 AgentBusywhile a turn is running or paused, which is stricter thanChangeAgentParam). Exposed on every client (AppendInteractions/RemoveInteractionsFromEnd; Pythonappend_interactions/remove_interactions_from_end). See Seed and edit dialog history. - Stateless agents can be seeded. With
maintain_dialogue_history = false, appended history acts as a one-shot seed for a single turn and clears when that turn completes. - Cancelling a turn now has a guide. Cancel a turn covers the
StopAndKeep/StopAndDiscardmodes on every client, and how a discard differs from an explicit tail removal.
KV-cache control¶
- Choose how caches are prepared at creation.
CreateAgentgainskv_cache_initialization:AllocateOnly(default),Prefill(allocate and decode the reusable prefix up front, so the first turn skips that cost), orDeferAllocation(defer until the first send). See Agent Parameters. - Prefill, evict, and inspect on demand. Three new operations report aggregate residency (
Evicted/Resident), reusable-prefix status (NotCurrent/Current), eligible node count, and — for prefill — contexts created plus tokens decoded and reused. Exposed asPrefillKvCache/EvictKvCache/GetKvCacheStatus(C++ and Unity, with async variants),prefill_kv_cache()/evict_kv_cache()/get_kv_cache_status()(Python), and Prefill KV Cache / Evict KV Cache / Get KV Cache Status with matching result events (Unreal Blueprint). - Eviction is safe. Evicted eligible contexts are restored automatically before the next send — there is no restore call to make. Cache operations are idle-only; a conflict with a running or paused turn, or another cache operation, returns
3004 AgentBusy. Only language-model contexts are affected. See Manage an agent's KV cache.
Editor tooling¶
- Unreal workflow graph editor. Unreal now ships its own visual workflow graph editor with an asset factory, matching the Unity window: add nodes, wire exits, edit params, validate. See Edit workflows in the Unreal graph editor.
- Unreal Chat window. The editor Chat panel is a dockable tab that targets a scene actor's agent component, creating its own preview agent from that component's resolved graph, variable declarations, and overrides — so you can iterate on a workflow without entering PIE. Variable-value edits stay live; structural changes need a restart. See Test an agent in the editor.
- Variable overrides Inspector. Both editors draw one optional override row per declared variable — checkbox, read-only name, typed value — rather than a free-form list, and warn about stale entries left behind by a renamed or removed declaration.
Models, memory, and server configuration¶
- Pinning and sweeping now cover every model kind.
LoadModel/UnloadModelretention applies to language, embedding, STT, and TTS caches alike, and unused on-demand models are swept across all four caches immediately before the next load — not just afterDestroyAgent— so idle models stop lingering. VAD is catalog/download-only:LoadModelfor a VAD entry returns6005 ModelResolutionFailed. See Pin and unpin models and Model Management. models.jsonreference filled in. The catalog reference now documentsmodel_type,audience,hidden,default_sampling, the requiredtool_call_support, the llama.cpp knobs (disable_thinking,use_jinja,reasoning_budget,chat_template_file), and the STT / TTS variant fields (stt_family,tts_family,tts_files,tts_lang,device_preference,num_threads).- New server-config fields.
include_engine_diagnostics(per-node engine metrics indebug_info; never prompt or generated text),default_vad_model,stt_debug_dump_dir,idle_shutdown_timeout(a managed server self-exits after this many idle seconds), plus documentedcrash_dump,monitor, anddebug_commandsgroups. See Server Configuration. - Managed-server shutdown documented. Stopping a managed server closes the client side and lets the server self-exit once idle (default 60 s) rather than hard-killing it, and the client's port setting is passed on the command line, so it overrides
server-config.json. See Auto-launch the server.
New and expanded documentation¶
- New concept pages: Agent Variables, Slots and inter-node value passing, Constrained output (GBNF), and STT and voice input.
- New how-tos: Query rewriting for RAG, Add a hidden reasoning or draft step, Send multiple answers in one turn, Drive prompts and retrieval from game state, Substitute agent variables in LLM output, Keep merchant prices deterministic, Constrain output with a grammar, Seed and edit dialog history, Manage an agent's KV cache, Cancel a turn, and Edit workflows in the Unreal graph editor.
- Reference correctness pass. Every getting-started tutorial, node page, and client-API page was reconciled against the shipped code — enum defaults are now shown by name rather than by number, mutable-vs-structural param tables are complete, and the STT and voice-input error codes (
4100–4105),5005/5006, and6005are documented for the first time. Snippets that used removed APIs were rewritten.
Breaking changes and behavior changes¶
HumanMessageGuardrailis renamedRegexGuardrail. The node type, params type, and builder method are renamed on every client (AddRegexGuardrail/RegexGuardrailParams/TryllRegexGuardrailParams/UTryllRegexGuardrailParams), and the node gains aninputparam so it can guardrail any slot, not just the user's message. See Regex Guardrail.- Fixed message components are removed.
HumanMessageComponent,CharacterMessageComponent, andInstructionComponentare replaced by a singleSlotComponent {name, text, kind, history_role, producer}, andTurnDiagnostics.human_message/character_messageare gone; slot text appears underdebug_info.interaction.components[]asSlotComponententries. Update any tooling that parsesdebug_infoJSON. GenerateParams.stream/CannedResponseParams.stream(bool) are removed — replaced bysend: SendAnswer.stream=True→send=SendAnswer.Streamed;stream=False→send=SendAnswer.Whole(the oldstream=Falsepath still emitted one whole-textAnswerTextframe — it just skipped per-token deltas; it never meant "don't send"). Usesend=SendAnswer.None_only for a node whose text should never reach the wire at all (e.g. an internal query-rewrite step), which has no equivalent in the old boolean.GenerateAndSpeakhas nosendfield — it always streams.Placement.InPlaceOfUseris removed, and the Unity enum is renamedTryllKnowledgePlacement→TryllPlacement. Compose the replacement text with aTransformnode and have the downstream node consume it viainputinstead. See Query rewriting for RAG for the migration pattern.Speak's implicit input changed. It no longer voices "the latest character message" — it voices itsinput-resolved slot, and an emptyinputdefaults touser_message(the same default as every other input-bearing node). ACannedResponse → Speakgraph must therefore setSpeak'sinputto the upstream canned node's slot; leaving it empty voices the user's message instead. Graphs relying onPlacement.InPlaceOfUserupstream need theTransformmigration above.- Instruction lookup tag renamed.
{{instruction_<name>}}is removed; use{{slot.<name>}}.{{#instructions}}is unchanged. ToolCall'snotify_client,pause_after_tool_call, andhistory_policyare removed — setdisposition: ToolCallDispositioninstead (notify_client=true→NotifyorNotifyAndAcknowledge;pause_after_tool_call=true→Pause,PauseAndAcknowledge, orAwaitResult). Thetool_callevent now carries adispositionvalue in place ofhistory_policy. Rewrite any graph or config that set the old fields — see Tool Calling.on_answer_text/OnAnswerTextcallback signature changed on every client — it gains a leadingnode_nameparameter (Python:Callable[[str, str, bool, bool], None]; C++ / Unity / Unreal equivalents updated to match). Update callback signatures when upgrading the client libraries.AnswerText.is_finalis now per node, not per turn. A turn that routes through more than one text-producing node emits one final chunk per producing node. A client that treats the firstis_finalas "the turn is over" must key offnode_name, or wait forTurnComplete.tool_call_supportis required inmodels.json. Every language-model variant must declare"supported"or"unsupported"; the server rejects a catalog that omits it, and the field is not allowed on other model types. Add it to any custom catalog before upgrading.- Breaking wire-protocol change. Rebuild your integration against the new schema; a client built against the v0.4.0 schema is rejected at connection time with
5004 ProtocolVersionMismatch.
New error codes¶
3013 UnknownVariable— a variable update targeted a name not declared inCreateAgent.variables.3014 VariableTypeMismatch— an update's value type did not match the variable's declared type.3015 InvalidVariableValue— an invalid variable declaration (bad name, duplicate, or over a size limit), an unknown operation, or a set that would exceed its 4096-element cap.3016 InvalidToolResults—tool_resultson a resume is not a complete, unique batch for the agent's currently pausedAwaitResultcalls. The turn stays paused for retry.4004 KvCacheOperationFailed— an explicit prefill, or agent creation withkv_cache_initialization = Prefill, failed while allocating, formatting, or decoding the reusable prefix.4005 KvCacheManagementUnsupported— the selected inference backend does not support the requested KV-cache operation.
See Error Codes for the full list.