Assivy

Turn hooks

Run code before the turn — fetch context, ground the prompt, switch model, or refuse.

A turn hook is JavaScript attached to an agent that runs before the model's turn. Today there is one hook: beforeTurn. It runs after the user sends a message and before the model sees it — the place to fetch context, inject grounding, or decide the turn shouldn't run at all. It runs on every turn, including turns from embedded and public chats.

Add it in the agent editor under Turn code → Before turn (stored as the agent's beforeTurn).

The contract

async function beforeTurn(data, runtime) {
  log('checking request', { model: data.model });
  if (data.messages.length > 200) {
    return { block: true, reason: 'This conversation is too long. Start a new chat.' };
  }
  // Return only what you change.
  return { system: (data.system ?? '') + '\nAnswer in French.' };
}

The function must be named beforeTurn: if the code declares functions and none has that name, the hook is skipped.

data
messagesThe conversation the model is about to see.
systemThe agent's instructions.
modelThe model this turn runs on.
temperatureThe agent's temperature.
runtime
user{ id, email, isAnonymous, roles, metadata } of whoever is chatting.
tenantIdThe organization.
agentId, threadIdThis agent and thread.
modelThe model this turn runs on.

data arrives as a JSON copy. Return nothing (or null) to leave the turn alone; editing data in place and returning nothing also works. Fields left out of a returned object stay as they were.

What a return can change

ReturnEffect
{ block: true, reason }Refuses the turn before any model call; reason is streamed as the reply (a generic message without one). Wins over everything else.
systemReplaces the instructions only — the platform's knowledge, tool, and memory instructions are still added. Append to the system you were given to add rather than replace.
messagesReplaces what the model sees for this turn. The stored history and the trace keep what the user actually sent. An invalid array is ignored.
modelSwitches model. One the organization can't use is ignored, with a warning.
temperatureAny finite number.

Failure and limits

Hooks fail open: a throw, a timeout, a misnamed function, or an oversized return leaves the turn as it was. The cause is recorded on the turn's trace as beforeTurnHookError, so a policy hook that never ran is visible.

Limit
Run time30 seconds
Return value128 KB
Logs50 log() lines per run, 4 KB each

Return only the fields you change: echoing the whole data back on a long conversation can pass the 128 KB cap.

The hook runs in the same sandbox as function tools. fetch goes out only through the organization's outbound-access rules in Settings.

Logging from a hook

Hooks use the same log(message, data?, level?) as function tools — level is debug, info (default), warn, or error. Lines land in the Logs tab, tagged with the hook that wrote them:

[tool:hook:beforeTurn] checking request
[tool:hook:beforeTurn] attached 3 messages of history

Logging is opt-in: a hook that never calls log() emits nothing.

What hooks are for

  • Grounding. Pull the thread history, the customer record, the relevant docs — and attach them before the model reasons.
  • Policy. Reject or reshape turns that shouldn't run as asked.
  • Routing. Move a turn to a cheaper or stronger model.
  • Annotation. Leave a record of what context the turn had, so the trace explains not just what the model did but what it knew.

On this page