Skip to content

Support async handlers for tool_result_persist hook #58558

Description

@saurabhjain1592

Summary

The tool_result_persist hook currently has a synchronous-only signature:

tool_result_persist: (event: PluginHookToolResultPersistEvent, ctx: PluginHookToolResultPersistContext) 
  => PluginHookToolResultPersistResult | void;

This prevents plugins from making async operations (HTTP calls, database queries) during tool result persistence. Other hooks like before_tool_call, message_sending, after_tool_call, session_start, and session_end all support Promise returns.

Use Case

Governance plugins need to scan tool results for PII, secrets, and policy violations before they are persisted to the session transcript. This requires an async HTTP call to a policy server. With the current sync-only signature, the plugin cannot:

  • Send tool results to an external policy engine for PII/secrets detection
  • Redact sensitive data before it enters the LLM context window
  • Block results that violate data governance policies

The message_sending hook (which IS async) catches PII before it reaches the end user, but the gap is tool results entering the LLM context window unscanned.

Proposed Change

Add Promise support to the tool_result_persist return type, matching the pattern used by other hooks:

tool_result_persist: (event: PluginHookToolResultPersistEvent, ctx: PluginHookToolResultPersistContext) 
  => Promise<PluginHookToolResultPersistResult | void> | PluginHookToolResultPersistResult | void;

Similarly, before_message_write has the same sync-only limitation and would benefit from async support.

Current Workaround

Plugins can use before_tool_call for input governance and message_sending for outbound message scanning, but there is no async-capable hook for scanning tool results before transcript persistence.

Context

We encountered this while building @axonflow/openclaw-plugin, a governance plugin that enforces policies across tool inputs and outbound messages. The tool_result_persist hook was the intended point for output scanning, but the sync-only constraint makes it impossible to call an external policy server.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions