Context
Context is the durable system framing the model sees before the conversation. You write it as composable blocks at one of two scopes. The scope you pick is the only real decision; caching follows from it.
The primitive
type ContextBlock = { id?: string; content: string };
type ContextInput = string | ContextBlock[];
A block is just text. The SDK never interprets content; it places it verbatim into the model's cached system prefix. id is an opaque handle (like a map key): it lets you address or replace one block on a later update and keeps diffs stable. The SDK never branches on its value.
There is no block vocabulary. "rules", "persona", "memory" are ids you choose for your own bookkeeping, not SDK concepts. The only built-in distinction is which object you attach the context to.
Anywhere a layer takes context you can pass a bare string as shorthand for a single unkeyed block:
await novo.agents.create({ context: 'You are a refund triage assistant.' });
Two scopes
| | Agent context | Thread context |
|---|---|---|
| Attach point | agents.create / agents.update | threads.create / threads.update |
| Shared across | every thread of the agent | one conversation |
| On edit | rebuilds the prompt cache next run | folds in at the next cache rebuild (deferred) |
| Good for | role, operating rules, persona | per-user profile, a memory manifest |
Agent context is shared, durable framing: the same for every conversation. Editing it busts the prompt cache, which is cheap and rare. The value is captured (frozen) when each run is created, so an edit applies cleanly on the next run with no race against an in-flight one.
Thread context is per-conversation framing: a user's profile, the facts a given conversation should always carry. It is deferred: on a warm turn Novo replays the exact bytes already in the cache, so an edit does not bust a warm cache. The edit folds into the prefix the next time the prefix is rebuilt anyway (a new thread, a band change, forceRefresh, or the cache going cold on its own).
Separate blocks give structure
Because each block is independently addressable, you can keep authority structural. Put immutable operating rules in one block and a user-editable persona in another:
await novo.agents.create({
context: [
{ id: 'rules', content: 'Never issue a refund over $500 without approval.' },
{ id: 'persona', content: 'You are warm, concise, and a little witty.' },
],
});
// Later: replace only the persona. The rules block is untouched.
await novo.agents.update(agentId, {
context: [
{ id: 'rules', content: 'Never issue a refund over $500 without approval.' },
{ id: 'persona', content: 'You are formal and terse.' },
],
});
Editing one block can't drop another. (Placement is defense-in-depth, not enforcement: a rules block should still state its precedence in its own words, and real guardrails live on the approval side.)
The one rule
Agent edits take effect now (a cache rebuild on the next run).
Thread edits take effect at the next refresh (deferred, cache-preserving).
Need it seen right now? Say it in the conversation: put it in the run input.
The deferred thread layer trades immediacy for cache warmth, and that trade is almost always free: the reason you're editing thread context is usually something the user just said, so the model already sees it in the message tail before it folds into the prefix.
When you genuinely need a staged thread-context edit applied this turn, pass the escape hatch:
await novo.threads.update(threadId, {
context: 'Account tier: enterprise. Region: EU.',
forceRefresh: true, // fold now, at the cost of one cache write
});
What does not belong in context
- Current date/time. Novo gives the agent the current date and time for each turn, so don't bake it into a block (it would change the bytes every turn and bust the cache). The default timezone is
UTC; settimeZoneon a thread or per run when the agent should reason in a specific IANA timezone such asAmerica/New_York. - Per-turn facts and attachments. Put them in the run
input; they're part of the conversation, not durable framing. - Volatile state the agent can fetch. Give the agent a tool and let it pull just-in-time.
- Subagent-specific instructions. When
subagentsare enabled, delegated subagents inherit the effective agent and thread context. Keep durable context narrow and role-neutral; put task-specific subagent instructions in the delegated prompt.
See also
- Agents API:
contexton create/update. - Threads API: thread
contextandforceRefresh. - Agents, threads, runs: how the three nouns fit together.