Skip to content

[WIP] feat: Add tool hooks bridge for external UI visibility#2908

Closed
JustMaier wants to merge 3 commits intoopenclaw:mainfrom
JustMaier:feature/tool-hooks
Closed

[WIP] feat: Add tool hooks bridge for external UI visibility#2908
JustMaier wants to merge 3 commits intoopenclaw:mainfrom
JustMaier:feature/tool-hooks

Conversation

@JustMaier
Copy link

Summary

This PR adds a bridge that connects agent runtime tool events to the internal hooks system, enabling workspace hooks to react to tool execution lifecycle events.

Closes #2904

Problem

When an agent session is churning through tool calls (long-running exec, polling background processes, etc.), external UIs like Discord go silent even though the agent is actively working. There was no visibility into what the agent was doing.

Solution

Introduces a Tool Hook Bridge (src/infra/agent-hook-bridge.ts) that:

  1. Subscribes to agent events via onAgentEvent()
  2. Filters for stream: "tool" events
  3. Translates them to internal hook events (type: "tool")
  4. Triggers hooks with tool:start, tool:update, tool:result actions

Hook Event Structure

{
  type: "tool",
  action: "start" | "update" | "result",
  sessionKey: "agent:main:discord:channel:123456",
  timestamp: Date,
  context: {
    runId: string,
    toolName: string,
    toolCallId: string,
    args?: unknown,           // on start
    partialResult?: unknown,  // on update
    result?: unknown,         // on result
    isError?: boolean,        // on result
    meta: object,
    sessionMeta: {            // Parsed for convenience
      agentId: string,
      platform: string,       // discord, telegram, etc.
      channelType: string,
      channelId: string,
    }
  }
}

Key Design Decisions

  1. Update events NOT throttled at bridge level — Hook scripts handle their own rate limiting. This allows both throttled Discord updates AND real-time WebSocket streaming.

  2. Fire-and-forgetvoid triggerInternalHook() so tool execution isn't blocked by slow hooks.

  3. Reuses existing utilities — Uses parseAgentSessionKey from session-key-utils.js

  4. Dedicated module — Keeps gateway startup clean, follows existing patterns.

Changes

  • NEW: src/infra/agent-hook-bridge.ts - The bridge implementation
  • NEW: src/infra/agent-hook-bridge.test.ts - Comprehensive tests
  • MODIFIED: src/hooks/internal-hooks.ts - Adds "tool" to InternalHookEventType
  • MODIFIED: src/gateway/server-startup.ts - Starts bridge when hooks are enabled

Testing

Added tests for:

  • tool:start, tool:update, tool:result events
  • sessionMeta parsing (including colons in IDs like Matrix)
  • Malformed/empty session keys
  • Missing required fields (toolCallId, name)
  • Non-tool streams ignored
  • Unknown phases ignored
  • Double-subscription prevention
  • Stop functionality

Example Hook Script

// hooks/tool-visibility/handler.js
const activeTools = new Map();

export default async function(event) {
    if (event.type !== 'tool') return;
    
    const { sessionMeta, toolCallId, toolName } = event.context;
    if (sessionMeta.platform !== 'discord') return;
    
    if (event.action === 'start') {
        const msgId = await postEmbed({ title: `⚙️ ${toolName}` });
        activeTools.set(toolCallId, { msgId, startTime: Date.now() });
    }
    
    if (event.action === 'result') {
        const tracked = activeTools.get(toolCallId);
        if (tracked) {
            await updateEmbed(tracked.msgId, { title: `✅ ${toolName}` });
            activeTools.delete(toolCallId);
        }
    }
}

Review Notes

  • Architecture reviewed by external agent (Gemini 3 Pro) - approved with minor suggestions which have been addressed
  • This is a shallow clone, so diff against main might show false conflicts

Adds a bridge that connects agent runtime tool events to the internal
hooks system, enabling workspace hooks to react to tool execution
lifecycle events (start, update, result).

This enables use cases like:
- Ephemeral status messages in Discord showing tool progress
- Typing indicators during tool execution
- Real-time dashboards via WebSocket
- Audit logging of tool usage

Key changes:
- New src/infra/agent-hook-bridge.ts with startAgentHookBridge()
- Adds 'tool' to InternalHookEventType
- Integrates bridge into gateway startup when hooks are enabled
- Comprehensive test coverage for edge cases

Design decisions:
- Update events are NOT throttled at bridge level - hooks handle their
  own rate limiting (allows both throttled Discord and real-time WebSocket)
- Fire-and-forget pattern so tool execution isn't blocked
- Session key parsed into sessionMeta for hook convenience

Closes openclaw#2904
@openclaw-barnacle openclaw-barnacle bot added the gateway Gateway runtime label Jan 27, 2026
@JustMaier JustMaier changed the title feat: Add tool hooks bridge for external UI visibility [WIP] feat: Add tool hooks bridge for external UI visibility Jan 27, 2026
@JustMaier JustMaier marked this pull request as draft January 27, 2026 18:33
- Remove unused imports (vi, ToolHookEvent) from test file
- Apply oxfmt formatting
The test expects startAgentHookBridge() to return the same function
instance when called multiple times. Fixed by storing the cleanup
function separately from the event unsubscribe function.
@sebslight
Copy link
Member

Closing: This PR is marked as WIP/Draft and has been open without completion. Please reopen when the work is ready for review.

@sebslight sebslight closed this Jan 28, 2026
@JustMaier
Copy link
Author

The implementation is complete - I had forgotten to change the status from Draft to Ready for Review. Apologies for the confusion!

This PR implements the tool hooks feature requested in #2538 (and duplicate #2904), adding:

  • tool:start - fires when agent begins a tool call
  • tool:update - fires during streaming output
  • tool:result - fires when tool call completes

All CI checks passed. Ready for review when reopened.

@JustMaier
Copy link
Author

JustMaier commented Jan 28, 2026

@sebslight this should be ready for review and solves a real issue. I'm unable to change the status of the PR from draft.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

gateway Gateway runtime

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Feature: Tool Hooks for External UI Visibility

2 participants