Hinting (a note for one reply)
Steer one reply with a note the agent must not remember — send the player's message and the note in a transient send, then keep just the message and the reply with `addMessages(user, reply)`. Covers why the note stays out of history, the `[System]` prefix, how the note rides every round of the send, what `messages` cost against kept history, and the anti-pattern of keeping every send transient and rebuilding the transcript yourself. There is no `hint` option; this is how to hint.
Some replies need a nudge the character shouldn't remember being given: "the player is close — steer them toward the cellar", "wrap the scene up now", this turn's dice roll. Post the note after the player's message in a send that keeps nothing, then keep what should be remembered:
import type { UserMessage } from '@aichatgames/sdk'
const user: UserMessage = { role: 'user', content: input }
const note: UserMessage = { role: 'user', content: '[System] The player is close. Steer them toward the cellar without naming it.' }
const reply = await gm.send(`reply-${turn}`, {
messages: [user, note],
transient: true,
onText: text => emit({ type: 'narration', text }),
})
gm.addMessages(user, reply)Why this shape
- The note guides this reply and is then gone.
transient: truekeeps nothing from the send — neithermessagesnor the reply. The next line keeps the player's message and the reply, in order, without the note, so the agent's history reads as if the player spoke and it answered. A note left in history would keep steering every later reply. replyis already a message.addMessagestakes exactly whatsendreturns, so the reply's tool calls and memory work are kept with it (seeagent-memory).addMessages(user, reply)is only for a transient send. A kept send has already put its messages and reply in history; adding the reply again throws.- Notes are
usermessages with a[System]prefix. There is nosystemrole for messages; the prefix is how an agent tells direction from dialogue. - Everything without a note is a plain send (no
transient), which keeps its messages and reply on its own.
The note rides the whole send
messages are posted after kept history and before the reply — on every model call of the
send. If the agent calls a tool, the call after the tool result still sees the note, and the
prompt's prefix stays the same from call to call, so it caches.
What messages cost
Messages passed in messages can cost up to 10× the same content in kept history. Kept
history is a stable prefix the platform caches and compacts; a list rebuilt for each send is
neither. A one-line note is nothing; a transcript is a lot.
Anti-pattern: keeping the transcript yourself
Don't make every send transient and pass the conversation back in each time:
// Don't: the transcript lives in game state and is re-posted on every send.
const reply = await gm.send(`reply-${turn}`, {
messages: [...state.get().transcript, user],
transient: true,
})Every send pays for the whole transcript again at the uncached rate, compaction can't shrink
it (it only compacts kept history), and the agent's memory work is dropped with every reply.
Keep the transcript in the agent's history, and put only what is new — plus any note — in
messages.