Sync behavior and timing
Understand how dynamic context flushes after debounce, how Wait Until Conversation Is Idle holds updates, and how offline queueing works.
Dynamic context updates do not send to Convai on every Blueprint call. By default, the plugin waits briefly so rapid changes can merge into one send, and each mutation function also carries a separate Delivery option that can hold the update for a natural pause in the conversation. This page explains when flushes happen, what each flush contains, when a held update is actually delivered, and when to bypass either wait.
When updates send
Default debounced call while connected, Delivery = Send Normally
Updates stage locally; one Replace context-update sends after ContextDebounceWindow (default 0.5 s) with no new updates in the window
Rapid burst of updates
All changes in the burst coalesce into one flush; timer resets on each call but cannot exceed ContextMaxDebounceWindow (default 3.0 s) from the first update
Delivery = Wait Until Conversation Is Idle, aggregate ShouldRespond is Auto/Always
The update is held — not staged into the debounce batch — until the conversation has stayed continuously quiet for Quiet Time Before Delivery (s) (default 2.0 s), then it flushes. See When a held update is actually delivered
Delivery = Wait Until Conversation Is Idle, aggregate ShouldRespond is Never
Holding does not apply — a silent update has nothing to interrupt with, so it sends normally through the regular debounce path
bFlushImmediately = true after connect, Delivery = Send Normally
FlushDynamicContext() runs in the current frame, bypassing debounce
bFlushImmediately = true after connect, Delivery = Wait Until Conversation Is Idle
Still waits for a quiet moment, but sends at the first quiet instant instead of waiting out the full Quiet Time Before Delivery (s) window
Updates before session connects
Changes accumulate in PendingContextBatch; first flush after connect and debounce deadline
Reset Dynamic Context
Drains staged content first, then sends a Reset context-update, then clears the local tracker
When a held update is actually delivered
Set Context State, Set Context States, and Add Context Event each expose a Delivery parameter (EConvaiContextDelivery). Debounce and delivery are two separate waits, and they apply in sequence:
Debounce (
ContextDebounceWindow,ContextMaxDebounceWindow) decides when a batch of staged updates is ready to leave the client.Delivery decides whether a ready update sends immediately or waits for the conversation to go quiet first.
An update with Delivery set to Wait Until Conversation Is Idle is held rather than staged into the normal debounce batch. It is released, and flushed as a Replace context-update, only after the conversation has stayed continuously quiet for Quiet Time Before Delivery (s) (the ConversationIdleSettleSeconds property, default 2.0 s). Any conversation activity — the character speaking, the user speaking — restarts that quiet window. There is deliberately no upper bound on the total hold, so a waiting update can never interrupt an ongoing exchange; it can wait for as long as the conversation lasts. A held Add Context Event lands at the end of the event history when it finally sends, so it still reads as the most recent thing that happened.
Holding only applies when the batch's aggregate ShouldRespond is Auto or Always. A silent (Never) update has nothing to interrupt with, so Delivery has no effect on it and it always sends through the regular debounce path.
bFlushImmediately = true changes what the hold waits for, not whether it holds: with Delivery set to Wait Until Conversation Is Idle, an immediate call still waits for a quiet moment, but sends as soon as that first quiet instant is detected instead of waiting out the full Quiet Time Before Delivery (s) window.
Transport message shape
UConvaiSubsystem::UpdateContext sends a context-update message with a JSON data payload:
mode
append, replace, reset
Present on every message
run_llm
"auto", "true", "false"
Present on every message — maps from EC_RunLLMOption (Auto, Always, Never)
text
Context string
Omitted on Reset when text is empty
current_attention_object
Object name string
When attention is folded into the same message
Blueprint enum display names (Append, Replace, Reset, Auto, Always, Never) differ from the wire strings above.
Flush payload structure
Each flush that includes staged states or events builds one text payload:
The first block is canonical context — states in insertion order ("{Key} is {Value}" per line) followed by events in chronological order. It is built by FConvaiDynamicContextTracker::BuildCanonicalContext().
When the batch's aggregate ShouldRespond is not Never and state delta lines exist, the plugin appends them on the next line after canonical context (one \n separator, not a blank line). Delta lines surface state changes at the tail of the prompt.
For state updates where the new value has more than three whitespace-separated words, the delta line uses "{Key} changed from {OldValue}" without repeating the long new value.
Scenarios during an active session
New state — aggregate ShouldRespond is Never
Triggering call: Set Context State or Set Context States with a key that has not been set before, and every staged item in the batch keeps aggregate ShouldRespond at Never.
The new key enters canonical immediately. No delta lines are added.
New state — aggregate ShouldRespond is Auto or Always
Triggering call: Set Context State or Set Context States with a new key, and at least one staged item raises aggregate ShouldRespond to Auto or Always.
On this flush, the new key is excluded from canonical. A delta line such as Health is 50 is appended after the canonical block.
On the next flush, the key enters canonical normally.
Update to existing state — aggregate ShouldRespond is Never
Triggering call: Set Context State with an existing key, and the batch keeps aggregate ShouldRespond at Never.
The updated value enters canonical. No delta lines are added.
Update to existing state — aggregate ShouldRespond is Auto or Always
Triggering call: Set Context State with an existing key, and at least one staged item raises aggregate ShouldRespond to Auto or Always.
The updated value enters canonical. A delta line describing the transition is appended.
Multiple states at once
Triggering call: Set Context States with a map of key-value pairs, potentially mixing new and existing keys.
All values are written to the tracker. First-appearance deferral applies per new key when aggregate is not Never. All changes share one flush — one Replace context-update.
Add context event
Triggering call: Add Context Event.
The event text is committed to the tracker's event list and appears in canonical. Regular context events are not duplicated into a separate delta line.
Identical event strings staged multiple times in the same debounce window are deduplicated — only the first occurrence is kept for that flush.
Remove context state
Triggering call: Remove Context State.
The key is removed from the tracker. bForceReplace is set so the flush sends canonical context without the removed key. No delta lines are added for removals.
Dynamic context reset
Triggering call: Reset Dynamic Context.
The plugin drains any staged context batch first, then sends a Reset context-update (mode: reset, run_llm: "false") with no text field for the empty Reset payload. The local tracker is cleared after the Reset message.
Queued triggers that already exist when Reset Dynamic Context is called are cleared immediately. Triggers queued after a pending offline Reset may still drain before the Reset during FlushDynamicContext().
When connected, Reset Dynamic Context calls the flush path immediately. When offline, the reset flag waits for the first post-connect flush.
Pre-session queueing
Default debounced mutation calls — Set Context State, Set Context States, Add Context Event, Remove Context State, and Reset Dynamic Context — accumulate in PendingContextBatch while IsChatbotConnected() is false.
TickDynamicContext() keeps accumulating until the session connects. When connected and the debounce deadline is reached, FlushDynamicContext() delivers the pending batch.
Offline updates collapse into the flush behavior described above — not replayed as a sequence of individual messages. After connection, on the first tick where the debounce deadline has elapsed, a batch whose aggregate ShouldRespond remains Never sends the current canonical snapshot from the tracker; batches with aggregate Auto or Always follow the canonical-plus-delta behavior described above. Any pending Reset clears context last.
If Reset Dynamic Context and subsequent updates are all pending offline, the flush sends staged content first, then Reset last.
Do not use bFlushImmediately = true before connection for context that must be delivered. Immediate flush bypasses the disconnected tick guard and can clear staged work before a valid session can send it.
bFlushImmediately
Each mutation function — Set Context State, Set Context States, Add Context Event, Remove Context State — exposes an Advanced parameter bFlushImmediately. When true, FlushDynamicContext() runs in the current frame.
Use bFlushImmediately for time-critical updates where the default debounce delay would cause perceptible lag. Each immediate call runs FlushDynamicContext() in the current frame and sends a context-update when the chatbot is connected with deliverable staged work. Avoid calling it every tick on continuously changing values.
Reset Dynamic Context calls the flush path immediately when connected. It has no bFlushImmediately parameter.
bFlushImmediately does not override Delivery. On an update with Delivery set to Wait Until Conversation Is Idle, bFlushImmediately still waits for the conversation to go quiet — see When a held update is actually delivered — it only skips the full Quiet Time Before Delivery (s) window once quiet is detected.
High-frequency use of bFlushImmediately — for example, syncing a value that updates every frame — can generate one context-update per call when connected. Use the default debounce behavior for continuously changing values.
Next steps
Troubleshoot dynamic contextDynamic context Blueprint referenceLast updated
Was this helpful?