When building an OMP extension that needs to filter or rewrite conversation history before it reaches a provider (e.g. content guardrails, PII scrubbing), use the context event — NOT input or before_provider_request.
Key behaviors discovered from source:
contextevent receives astructuredClone()deep copy of the fullAgentMessage[]array. Returning{ messages: modifiedArray }replaces what the LLM sees without touching persisted session history. This is the only event that gives read-write access to the full conversation context.inputevent fires before user text becomes a message — it can rewrite user input but cannot see or modify past messages. Use it for input-time filtering only.before_provider_requestgives the raw provider payload (already converted from AgentMessage to provider-specific format) — too late for semantic message-level filtering.Extensions cannot delete persisted session entries.
sessionManageris read-only fromExtensionContext.Agent.replaceMessages()andSessionManager.rewriteEntries()exist in core but are not exposed to extensions. Thecontextevent's copy-on-write design is intentional: the local session file is never sent to the provider, so scrubbing the LLM-bound copy is sufficient.ctx.modelin event handlers is a lazy getter that reflects/modelswitches without needing a separate listener. Gate on it to make extensions model-specific (e.g.ctx.model?.id?.includes('fable')).For assistant messages, content is an array of typed blocks (
TextContent,ThinkingContent,RedactedThinkingContent,ToolCall). Onlytextandthinkingblocks have scannable text —RedactedThinkingContentis opaque. User messages havecontent: string | (TextContent | ImageContent)[]— handle both shapes.