Skip to content

feat(hooks): add async_mode support for non-blocking hook execution#1154

Merged
ErikBjare merged 2 commits intomasterfrom
async-hooks
Jan 26, 2026
Merged

feat(hooks): add async_mode support for non-blocking hook execution#1154
ErikBjare merged 2 commits intomasterfrom
async-hooks

Conversation

@TimeToBuildBob
Copy link
Copy Markdown
Member

Summary

Adds async_mode parameter to hooks that allows them to run in background threads without blocking the main execution flow.

This is inspired by Claude Code's async hooks feature:

Hooks can now run in the background without blocking Claude Code's execution. Just add async: true to your hook config.

Use Cases

Async hooks are ideal for:

  • Logging and telemetry - Send data to external services without blocking
  • Notifications - Trigger alerts/webhooks in background
  • External service calls - API calls that don't need to block execution
  • Any non-blocking side effects - Operations where the result isn't needed immediately

Changes

  • Add async_mode field to Hook dataclass (default: False)
  • Update HookRegistry.register to accept async_mode parameter
  • Modify HookRegistry.trigger to run async hooks in daemon threads
  • Add _run_async_hook method for background execution with error handling
  • Update register_hook function and all type overloads to support async_mode
  • Add comprehensive tests for async hook functionality

Usage

from gptme.hooks import register_hook, HookType

def my_logging_hook(manager):
    # This runs in background, won't block execution
    send_telemetry(manager.log)
    yield Message("system", "Logged")

# Register as async hook
register_hook(
    "my_logger",
    HookType.STEP_POST,
    my_logging_hook,
    async_mode=True  # <-- Runs in background thread
)

Testing

Added tests covering:

  • Async hooks run without blocking sync hooks
  • Error handling in async hooks doesn't crash the system
  • async_mode is properly stored in hook registration

Closes ErikBjare/bob#264

Adds async_mode parameter to hooks that allows them to run in background
threads without blocking the main execution flow. This is useful for:

- Logging and telemetry
- Notifications
- External service calls
- Any non-blocking side effects

Based on Claude Code's async hooks feature (add async: true to hook config).

Closes ErikBjare/bob#264

Changes:
- Add async_mode field to Hook dataclass (default: False)
- Update HookRegistry.register to accept async_mode parameter
- Modify HookRegistry.trigger to run async hooks in daemon threads
- Add _run_async_hook method for background execution
- Update register_hook function and all overloads to support async_mode
- Add tests for async hook functionality

Co-authored-by: Bob <bob@superuserlabs.org>
@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps Bot commented Jan 26, 2026

Greptile Overview

Greptile Summary

Added async_mode parameter to the hook system that enables hooks to run in background daemon threads without blocking the main execution flow.

Key changes:

  • Added async_mode field to Hook dataclass (default: False)
  • Modified HookRegistry.trigger to separate sync and async hooks, launching async ones in daemon threads
  • Implemented _run_async_hook method with proper error handling and logging
  • Added async_mode parameter to all register_hook type overloads
  • Async hooks have their messages logged but not yielded to the caller
  • Daemon threads are used intentionally for non-blocking side effects (logging, telemetry) where completion guarantees aren't required
  • Comprehensive test coverage for async execution, error handling, and registration

Confidence Score: 4/5

  • This PR is safe to merge with minimal risk
  • The implementation is well-designed with proper error handling, comprehensive tests, and addresses all previously raised review concerns. The use of daemon threads is appropriately documented for the intended use cases (logging, telemetry). The changes are backward compatible and follow the existing codebase patterns.
  • No files require special attention

Important Files Changed

Filename Overview
gptme/hooks/init.py Added async_mode parameter to hooks with background thread execution, proper error handling, and comprehensive documentation
tests/test_hooks.py Added comprehensive tests for async hook functionality including non-blocking execution, error handling, and registration verification

Sequence Diagram

sequenceDiagram
    participant Client
    participant HookRegistry
    participant SyncHook
    participant AsyncHook
    participant Thread

    Client->>HookRegistry: trigger(hook_type, args, kwargs)
    HookRegistry->>HookRegistry: Separate sync and async hooks
    
    par Async Hooks (Fire-and-Forget)
        loop For each async hook
            HookRegistry->>Thread: Start daemon thread
            Thread->>AsyncHook: _run_async_hook(hook, args, kwargs)
            AsyncHook->>AsyncHook: Execute hook function
            AsyncHook->>AsyncHook: Log messages (not yielded)
            AsyncHook->>AsyncHook: Handle errors (caught, logged)
            Note over AsyncHook: Runs in background<br/>without blocking
        end
    and Sync Hooks (Sequential)
        loop For each sync hook
            HookRegistry->>SyncHook: hook.func(args, kwargs)
            SyncHook-->>HookRegistry: Yield messages
            Note over HookRegistry,SyncHook: Check for StopPropagation
            alt StopPropagation
                SyncHook-->>Client: Stop and return
            end
        end
        HookRegistry-->>Client: Yield all sync messages
    end
    
    Note over Thread,AsyncHook: Async hooks continue<br/>in background after<br/>client receives response
Loading

Copy link
Copy Markdown
Contributor

@greptile-apps greptile-apps Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

4 files reviewed, 4 comments

Edit Code Review Agent Settings | Greptile

Comment thread gptme/hooks/__init__.py Outdated
Comment thread gptme/hooks/__init__.py Outdated
Comment thread gptme/hooks/__init__.py
Comment thread gptme/hooks/__init__.py
@codecov
Copy link
Copy Markdown

codecov Bot commented Jan 26, 2026

Codecov Report

❌ Patch coverage is 27.77778% with 26 lines in your changes missing coverage. Please review.
✅ All tests successful. No failed tests found.

Files with missing lines Patch % Lines
gptme/hooks/__init__.py 27.77% 26 Missing ⚠️

📢 Thoughts on this report? Let us know!

- Add safety checks for string slicing on msg.content/result.content
- Handle empty/non-string content gracefully with preview helper
- Add SessionCompleteException handling in async hooks (logged, not propagated)
- Add comment explaining daemon=True is intentional for non-blocking side effects
@TimeToBuildBob
Copy link
Copy Markdown
Member Author

✅ All Greptile Review Comments Addressed

Pushed fix in commit 6dbee5e:

Fixes Applied:

  1. String slicing safety (x2): Added helper that handles empty/non-string content gracefully with preview truncation
  2. SessionCompleteException handling: Added special-case handling in async hooks (logs info message, cannot propagate from background thread)
  3. daemon=True documentation: Added comment explaining this is intentional for non-blocking side effects

All review threads have individual replies. Ready for re-review!

@ErikBjare
Copy link
Copy Markdown
Member

@greptileai review

Copy link
Copy Markdown
Contributor

@greptile-apps greptile-apps Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No files reviewed, no comments

Edit Code Review Agent Settings | Greptile

@ErikBjare ErikBjare merged commit a2b4b70 into master Jan 26, 2026
11 checks passed
@TimeToBuildBob TimeToBuildBob deleted the async-hooks branch February 28, 2026 20:55
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants