Intercepting AI Events Using Hooks

The Oxygen AI Positron exposes a hook system that lets developers intercept events fired during the AI lifecycle. A hook handler can observe fire-and-forget events for auditing or analytics, or veto blocking events to prevent an action from running.

Typical use cases include:

  • Auditing every AI action, tool call, or shell command to an external log or compliance system.
  • Gating risky operations (for example, blocking shell commands that modify production files).
  • Controlling sub-agent execution to enforce organizational policies on autonomous AI tasks.
  • Collecting token usage metrics after each assistant turn.

Implementing a Hook Handler

To create a hook handler add-on, follow these steps:
  1. Add oxygen-ai-positron-api as a provided dependency in your plugin project's pom.xml:
    <dependency>
      <groupId>com.oxygenxml</groupId>
      <artifactId>oxygen-ai-positron-api</artifactId>
      <version>8.4.0</version>
      <scope>compile</scope>
    </dependency>
    The artifact is published at https://oxygenxml.com/maven/. Add the repository to your pom.xml (if it is not already present):
    <repository>
      <id>oxygen-public-repo</id>
      <url>https://www.oxygenxml.com/maven/</url>
    </repository>
  2. Implement the com.oxygenxml.positron.api.hook.AIHooksHandler interface in your plugin's libraries. The single method eventOccurred(String eventName, String payloadJson) is called for every hook event. eventName is the string identifier of the event (for example, beforeShellExecution); payloadJson is the JSON serialization (snake_case keys) of the event-specific payload, which you can deserialize into the matching BaseHookPayload subclass using Jackson to access its fields. Throw an AIHookVetoException from this method to veto a blocking event and prevent the associated action from running.
    package my.pack;
    
    import java.util.List;
    
    import com.fasterxml.jackson.databind.ObjectMapper;
    import com.oxygenxml.positron.api.hook.AIHookEvent;
    import com.oxygenxml.positron.api.hook.AIHookVetoException;
    import com.oxygenxml.positron.api.hook.AIHooksHandler;
    import com.oxygenxml.positron.api.hook.payload.BeforeShellExecutionPayload;
    
    /**
     * Hook handler that blocks shell commands considered dangerous for
     * a documentation project (destructive file operations, package
     * installations, and network-fetching utilities).
     */
    public class ShellGuardHooksHandler implements AIHooksHandler {
    
      private static final ObjectMapper MAPPER = new ObjectMapper();
    
      private static final List<String> BLOCKED_PATTERNS = List.of(
          "rm ",  "rmdir", "del ",  "rd ",
          "npm install", "pip install", "mvn install",
          "curl", "wget"
      );
    
      @Override
      public void eventOccurred(String eventName, String payloadJson) throws AIHookVetoException {
        if (AIHookEvent.BEFORE_SHELL_EXECUTION == AIHookEvent.fromEventName(eventName)) {
          try {
            BeforeShellExecutionPayload data =
                MAPPER.readValue(payloadJson, BeforeShellExecutionPayload.class);
            blockDangerousCommands(data);
          } catch (Exception e) {
            // log and continue — deserialization failures must not block the action
          }
        }
      }
    
      private void blockDangerousCommands(BeforeShellExecutionPayload data) throws AIHookVetoException {
        String command = data.getCommand();
        if (command == null) {
          return;
        }
        String lowerCommand = command.toLowerCase();
        for (String pattern : BLOCKED_PATTERNS) {
          if (lowerCommand.contains(pattern)) {
            throw new AIHookVetoException(
                "Shell command blocked by policy: '" + command + "'. "
                + "Matched restricted pattern: '" + pattern + "'."
            );
          }
        }
      }
    }
  3. In the plugin descriptor file, declare the extension point that points to your implementation:
    <extension type="AIHooksHandler" class="my.pack.ShellGuardHooksHandler"/>

A ready-to-use implementation is available via the Oxygen AI Positron Hooks add-on. It implements this extension point and dispatches hook events to external scripts configured in a JSON file, without requiring any Java code.

You can use the sample plugin available on GitHub as a reference when building your own hook handler: https://github.com/oxygenxml/oxygen-ai-positron-hooks-handler-sample-plugin.

Hook Events Overview

The following tables list all hook events grouped by category. Blocking events can be vetoed by throwing an AIHookVetoException; fire-and-forget events are informational only.

Session

Event Type Description
sessionStart Fire-and-forget Fired when a new AI session is created.
sessionEnd Fire-and-forget Fired when an AI session ends.
sessionReload Fire-and-forget Fired when the user restores a previous conversation from chat history.

Actions and prompts

Event Type Description
beforeStartAction Blocking Fired before an AI action is launched. Veto to prevent it from running.
beforeSubmitPrompt Blocking Fired before a user prompt is submitted to the AI model from the chat UI.
afterAgentThought Fire-and-forget Fired after the agent completes a thinking block (extended thinking mode).
afterAgentResponse Fire-and-forget Fired after the agent completes an assistant turn.
agentFinish Fire-and-forget Fired when an AI agent interaction finishes (completed, aborted, or error).
subagentStart Blocking Fired before a sub-agent is started. Veto to prevent it from being created.
subagentStop Fire-and-forget Fired when a sub-agent finishes, errors, or is abandoned.

Tool, shell, and MCP execution

Event Type Description
preToolUse Blocking Fired before any AI tool is invoked. Veto to prevent the tool from running.
postToolUse Fire-and-forget Fired after a tool completes successfully.
postToolUseFailure Fire-and-forget Fired after a tool invocation fails, times out, or is denied.
beforeShellExecution Blocking Fired before a shell command is executed. Use this to enforce shell command policies.
afterShellExecution Fire-and-forget Fired after a shell command finishes executing.
beforeMCPExecution Blocking Fired before an MCP tool is invoked. Veto to prevent it from running.
afterMCPExecution Fire-and-forget Fired after an MCP tool execution completes, whether successfully or with an error.

File and skill operations

Event Type Description
beforeReadFile Blocking Fired before the agent reads a file. Veto to block file access.
afterReadFile Fire-and-forget Fired after the agent successfully reads a file.
beforeEditFile Blocking Fired before the agent edits or creates a file. Veto to prevent the write.
afterEditFile Fire-and-forget Fired after the agent attempts to edit or create a file.
beforeLoadSkill Blocking Fired before the agent loads a skill. Veto to prevent the skill from being loaded.
afterLoadSkill Fire-and-forget Fired after the agent successfully loads a skill.

User actions

Event Type Description
afterUserInsertResponse Fire-and-forget Fired after the user inserts or replaces an AI suggestion in the document.
afterUserPreviewResponse Fire-and-forget Fired after the preview dialog box closes or when an error occurs while applying the changes.
afterCopyResponse Fire-and-forget Fired after the user copies an AI response to the clipboard.
afterUserAcceptChanges Fire-and-forget Fired after the user accepts AI-proposed file changes.
afterUserRejectChanges Fire-and-forget Fired after the user rejects AI-proposed file changes.

Hook Events Reference

The following tables list all events defined in AIHookEvent, grouped by lifecycle category. Events marked as Blocking can be vetoed by throwing an AIHookVetoException inside eventOccurred. Events marked as Fire-and-forget are informational (throwing an exception has no effect on the AI action).

Every event delivers its data as a JSON string (payloadJson) with snake_case keys. Deserialize it with Jackson into the event-specific BaseHookPayload subclass (listed under each event below) to access its fields. All payload types share the following base fields:

Field Type Description
session_id string Stable identifier for the current chat session, constant across all turns.
action_or_agent_id string Identifier of the AI action or agent (chat mode) that produced this event (e.g. agent, ask, or a custom chat mode ID).
generation_id string Identifier that changes with each user message/AI generation round.
connection_id string Identifier of the AI connection as configured by the user in Preferences.
model string The AI model currently in use (e.g. claude-opus-4-7).
hook_event_name string The name of the hook event that fired (e.g. beforeShellExecution).
positron_version string Running version of the AI Positron plugin (e.g. 8.3.0).
oxygen_version string Running version of the host application (e.g. 27.1.0).
oxygen_distribution string Host product distribution (e.g. Oxygen XML Editor).
workspace_root string Absolute path to the current project or workspace root.
user_id string System identifier of the user.
sessionStart: Fire-and-forget
Fired when a new AI session is created. This includes interactive chat conversations, batch AI refactoring sessions, and AI XPath function invocations.
Field Type Description
action_name string Human-readable name of the AI action that started the session.
session_type string Classifies the kind of session: chat for an interactive chat conversation, refactoring for a batch AI refactoring run, or xpath_function for an AI invocation triggered from an XPath extension function.
sessionEnd: Fire-and-forget
Fired when an AI session ends, whether a chat conversation is closed or cleared, a batch AI refactoring run finishes, or an AI XPath function invocation completes.
Field Type Description
reason string Reason the session ended: user_close (the user manually closed or cleared the chat), application_close (the host application is shutting down), completed (the session completed normally, for example, a refactoring batch run or an AI XPath function invocation finished successfully), aborted (the user pressed Stop during an invocation before it finished), or error (the invocation was terminated by an unexpected error).
session_type string Classifies the kind of session that ended: chat, refactoring, or xpath_function. Matches the value emitted in the corresponding sessionStart event.
sessionReload: Fire-and-forget
Fired when the user restores a previous conversation from the chat history.
Field Type Description
action_name string Human-readable name of the AI action that was active when this session was originally created.
session_type string Classifies the kind of session being reloaded: chat or refactoring.
beforeStartAction: Blocking
Fired before an AI action is launched. Throwing an exception vetoes the action and prevents it from running. The action identifier is available via the base field action_or_agent_id.
Field Type Description
input_content string The text content of the initial user input passed to the action.
document_url string URL of the document targeted by the action. Set when the action operates on a specific document, either an open editor (replace, insert, or analyze actions) or a file processed during an AI batch refactoring job. Absent when the action has no single document target (for example, new-document or prompt-user actions).
input_attachments string[] URLs of attachments included in the initial user input. Omitted when empty.
tools string[] IDs of the tools available to the model for this request. Omitted when empty.
action_expand_parameters map Expanded runtime parameters used to invoke the action (key-value pairs). Omitted when empty.
beforeSubmitPrompt: Blocking
Fired right before a user prompt is submitted to the AI model from the chat UI. Throwing an exception prevents the prompt from being sent.
Field Type Description
prompt string The full text of the prompt the user is about to submit.
attachments string[] URLs of attachments included by user. Omitted when empty.
tools string[] IDs of the tools available to the model for this request. Omitted when empty.
action_expand_parameters map Expanded runtime parameters used to invoke the action (key-value pairs). Omitted when empty.
document_url string URL of the document currently open in the editor when the prompt is submitted. Absent when no document is open.
agentFinish: Fire-and-forget
Fired when the AI agent interaction finishes, regardless of whether it completed successfully, was aborted by the user, or terminated by an error.
Field Type Description
status string Reason the interaction ended: completed, aborted, or error.
tool_call_rounds integer Number of tool-call rounds completed during the interaction (0 for plain Q&A with no tools).
error_message string Human-readable error description. Present only when status is error.
preToolUse: Blocking
Fired before any AI tool (function or action) is invoked. Throwing an exception prevents the tool from running.
Field Type Description
tool_id string ID of the tool about to be invoked (e.g. invoke_cmd_line).
tool_input JSON object Tool arguments as a raw JSON object.
tool_use_id string Unique identifier for this specific tool call, assigned by the AI model.
postToolUse: Fire-and-forget
Fired after a tool has completed successfully. Use it for auditing, analytics, or collecting tool execution metrics.
Field Type Description
tool_name string Name of the tool that was invoked.
tool_input JSON object Tool arguments as a raw JSON object.
tool_output string Output produced by the tool execution.
tool_use_id string Unique identifier for this specific tool call, assigned by the AI model.
postToolUseFailure: Fire-and-forget
Fired after a tool invocation has failed, timed out, or was denied.
Field Type Description
tool_name string Name of the tool that failed.
tool_input JSON object Tool arguments as a raw JSON object.
tool_use_id string Unique identifier for this specific tool call, assigned by the AI model.
error_message string Human-readable description of the failure.
subagentStart: Blocking
Fired before a sub-agent or sub-action is started. Throwing an exception prevents the sub-agent from being created.
Field Type Description
sub_action_or_agent_id string Identifier of the sub-action or sub-agent being started (e.g. improve-readability).
action_name string Human-readable name of the sub-action or sub-agent.
content string The document content or text fragment that the sub-agent will process.
parent_session_id string Session ID of the parent agent that spawned this sub-agent.
subagentStop: Fire-and-forget
Fired when a sub-agent finishes, errors, or is abandoned.
Field Type Description
sub_action_or_agent_id string Identifier of the sub-action or sub-agent that ended.
action_name string Human-readable name of the sub-action.
status string Outcome of the sub-agent: completed or failed.
duration_ms long Total wall-clock duration of the sub-agent execution in milliseconds.
beforeShellExecution: Blocking
Fired before a shell command is executed by the AI agent. Throwing an exception prevents the command from running. This is the primary hook to use when enforcing shell command policies.
Field Type Description
command string The full shell command string about to be executed.
timeout_seconds integer Maximum allowed execution time in seconds. Absent if no limit is set.
afterShellExecution: Fire-and-forget
Fired after a shell command has finished executing. Use it for auditing or collecting metrics from command output.
Field Type Description
command string The shell command that was executed.
output string Combined stdout/stderr output of the command.
exit_code integer Process exit code, or absent if the process was terminated abnormally.
duration_ms long Wall-clock duration of the command execution in milliseconds.
succeeded boolean true if the command ran without a system-level error, regardless of the process exit code; false if execution failed before the process could complete (e.g. IOException, unsupported platform).
beforeMCPExecution: Blocking
Fired before an MCP (Model Context Protocol) tool is invoked. Throwing an exception prevents the tool from running.
Field Type Description
server_name string Name of the MCP server hosting the tool.
tool_name string Name of the MCP tool to be invoked.
tool_input JSON object Tool arguments as a raw JSON object.
afterMCPExecution: Fire-and-forget
Fired after an MCP tool execution completes, whether successfully or with an error. Includes the tool's input parameters, the JSON result, and the execution outcome.
Field Type Description
server_name string Name of the MCP server that hosted the tool.
tool_name string Name of the MCP tool that was invoked.
tool_input JSON object Tool arguments as a raw JSON object.
result_json JSON value Result returned by the MCP tool as a raw JSON value. Absent when the execution failed.
succeeded boolean true if the MCP tool completed successfully; false if it threw an error.
error_message string Human-readable description of the failure. Absent when succeeded is true.
beforeReadFile: Blocking
Fired before the agent reads a file. Throwing an exception blocks the agent's access to that file. Use this hook to implement file-level access control (for example, to prevent the AI from reading files that contain secrets). Also fired before the agent reads the DITA map structure context (the Get DITA Map Structure Context tool), with file_path set to the URL of the DITA map currently open in the DITA Maps Manager.
Field Type Description
file_path string Absolute path or URL of the file about to be read.
afterReadFile: Fire-and-forget
Fired after the agent successfully reads a file. Use it to audit file-access patterns or log the content retrieved by the agent. Also fired after the Get DITA Map Structure Context tool completes successfully, with content set to the computed hierarchy JSON (not the raw DITA map XML).
Field Type Description
file_path string Absolute path or URL of the file that was read.
content string The content that was read from the file. For the Get DITA Map Structure Context tool, this is the computed hierarchy JSON returned to the AI model, not the raw DITA map XML.
beforeEditFile: Blocking
Fired before the agent edits or creates a file. Throwing an exception prevents the write from happening (the exception message is returned to the AI model as an error).
Note:
The event is fired only when the content to be written has been successfully computed. For replacement-based edits, this means the replacement strings were located in the existing file content. This event is also fired for DITA map structure additions (inserting a topic reference via the Add to DITA Map Structure tool).
Field Type Description
file_path string Absolute path or URL of the file about to be modified or created.
operation string Type of operation about to be performed: create or edit.
content string The full content that will be written to the file. null for DITA map structure additions, where the final content is computed internally by the Author API and is not available before the write.
fragments object[] The individual text replacements about to be applied. Each fragment contains an old_value and a new_value. Absent when the entire file content will be written at once. For DITA map structure additions, a single fragment is provided with old_value set to an empty string and new_value set to the topicref element about to be inserted (e.g. <topicref href='topic.dita'/>).
afterEditFile: Fire-and-forget
Fired after the agent attempts to edit or create a file, both on success and on failure. Also fired after a DITA map structure addition (inserting a topic reference). Use it to audit AI-generated file changes.
Field Type Description
file_path string Absolute path or URL of the file that was modified or created.
operation string Type of operation performed: create or edit.
content string The complete file content after a successful write, or null when the write failed. For fragment-based edits, this is the full document with all replacements already merged in.
tool_result string The result string returned to the AI model after the tool call completed (e.g. a success confirmation or an error message).
succeeded boolean true when the file was written to disk successfully, false when the save failed.
beforeLoadSkill: Blocking
Fired before the agent loads a skill by name. Throwing an exception prevents the skill from being loaded.
Field Type Description
skill_name string The name of the skill about to be loaded.
skill_url string The URL or path of the skill file. Absent when the skill location cannot be resolved before loading.
afterLoadSkill: Fire-and-forget
Fired after the agent successfully loads a skill. Use it to audit which skills are used during an AI session.
Field Type Description
skill_name string The name of the skill that was loaded.
skill_url string The URL or path of the skill file. Absent when the skill location cannot be resolved.
content string The full content of the skill that was loaded.
afterAgentResponse: Fire-and-forget
Fired after the agent completes an assistant turn, whether the turn contained only tool calls or produced a visible text response.
Field Type Description
response string The text content of the completed assistant turn. Empty string when the turn consisted solely of tool calls.
tool_call_names string[] Names of the tools called in this turn. Omitted when empty.
tokens_usage object Token usage information for this assistant turn. Contains prompt_tokens, completion_tokens, and total_tokens. Omitted when not reported by the backend.
afterAgentThought: Fire-and-forget
Fired after the agent completes a thinking block (extended thinking mode). Use it to observe the agent's reasoning process.
Field Type Description
text string The thinking/reasoning text. Depending on the model, it may be a summary or the complete text.
afterUserInsertResponse: Fire-and-forget
Fired after the user clicks the Insert/Replace button in the chat panel to apply an AI suggestion in the document.
Field Type Description
content string The content that was inserted or used to replace the selection.
editor_location string URL of the editor (document) where the content was inserted. Absent if the target editor could not be determined.
afterUserPreviewResponse: Fire-and-forget
Fired after the user clicks the Preview button in the chat panel and the preview dialog box closes, or when an error occurs while applying the changes. The preview opens a diff view comparing the current editor content with the result of applying the AI response, so the user can review the changes before deciding whether to apply them.
Field Type Description
content string The content that was sent to the preview dialog box.
applied boolean true if the user applied the changes from within the preview dialog box, false if the dialog box was closed without applying. Absent when an error occurred during the apply operation.
editor_location string URL of the editor (document) where the preview was shown. Absent if the target editor could not be determined.
error string Error message when applying the changes from the preview dialog box failed. Absent when no error occurred.
afterUserCopyResponse: Fire-and-forget
Fired after the user clicks the Copy button in the chat panel to copy an AI response to the clipboard.
Field Type Description
content string The text that was copied to the system clipboard.
afterUserAcceptChanges: Fire-and-forget
Fired after the user accepts AI-proposed file changes. This event is emitted both when the user accepts changes from the agentic chat flow and when the user accepts files in the batch AI refactoring diff dialog.
Field Type Description
documents string[] List of absolute paths or URLs of the documents whose changes were accepted.
afterUserRejectChanges: Fire-and-forget
Fired after the user rejects AI-proposed file changes. This event is emitted both when the user rejects changes from the agentic chat flow and when files are rejected in the batch AI refactoring diff dialog (including when the dialog is cancelled, in which case all files in the batch are treated as rejected).
Field Type Description
documents string[] List of absolute paths or URLs of the documents whose changes were rejected.

beforeInlineCompletion: Blocking
Fired before an inline autocomplete suggestion is requested from the AI model. Throwing an exception suppresses the suggestion — no AI call is made and no ghost text is shown.
Field Type Description
document_url string URL of the document open in the editor at the time the suggestion was triggered.
content string The text context used as input for the suggestion.
afterInlineCompletion: Fire-and-forget
Fired after an inline autocomplete suggestion has been generated by the AI model. Use it for auditing, token-usage tracking, or analytics on autocomplete activity. Note that this event fires regardless of whether the user accepts or dismisses the ghost text.
Field Type Description
document_url string URL of the document for which the suggestion was generated.
suggestion string The autocomplete suggestion text generated by the AI model. Absent when no suggestion was produced.
tokens_usage object Token usage for this completion request. Contains prompt_tokens, completion_tokens, and total_tokens. Absent when not reported by the backend.
Note:
The AIHooksHandler extension point is available starting with version 28.0 of Oxygen XML.