> For the complete documentation index, see [llms.txt](https://adaptix-framework.gitbook.io/adaptix-framework/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://adaptix-framework.gitbook.io/adaptix-framework/development/teamserver-interface/events-and-hooks.md).

# Events and Hooks

The AdaptixC2 TeamServer event system allows plugins to react to various actions in the system: agent connections, task execution, tunnel creation, etc.&#x20;

## Architecture

<figure><img src="/files/m6fhvaMT3POytyaea9SE" alt=""><figcaption></figcaption></figure>

```
┌─────────────────────────────────────────────────────────────────────────────┐
│                           EVENT SYSTEM ARCHITECTURE                         │
├─────────────────────────────────────────────────────────────────────────────┤
│                                                                             │
│  ┌────────────────────────────────────────────────────────────────────────┐ │
│  │                         EVENT MANAGER                                  │ │
│  │                                                                        │ │
│  │   ┌─────────────────────────────────────────────────────────────────┐  │ │
│  │   │                      HOOK REGISTRY                              │  │ │
│  │   │                                                                 │  │ │
│  │   │  EventType: "agent.new"                                         │  │ │
│  │   │  ├── Hook[0]: {Name: "validator", Phase: Pre, Priority: 10}     │  │ │
│  │   │  ├── Hook[1]: {Name: "logger", Phase: Pre, Priority: 100}       │  │ │
│  │   │  ├── Hook[2]: {Name: "notifier", Phase: Post, Priority: 50}     │  │ │
│  │   │  └── Hook[3]: {Name: "analytics", Phase: Post, Priority: 100}   │  │ │
│  │   │                                                                 │  │ │
│  │   │  EventType: "task.complete"                                     │  │ │
│  │   │  ├── Hook[0]: {Name: "logger", Phase: Post, Priority: 100}      │  │ │
│  │   │  └── ...                                                        │  │ │
│  │   └─────────────────────────────────────────────────────────────────┘  │ │
│  │                                                                        │ │
│  │   ┌─────────────────────────────────────────────────────────────────┐  │ │
│  │   │                      WORKER POOL                                │  │ │
│  │   │                                                                 │  │ │
│  │   │   Worker[0] ─┬─> Process async Post hooks                       │  │ │
│  │   │   Worker[1] ─┤                                                  │  │ │
│  │   │   Worker[2] ─┤                                                  │  │ │
│  │   │   Worker[3] ─┘                                                  │  │ │
│  │   │                                                                 │  │ │
│  │   └─────────────────────────────────────────────────────────────────┘  │ │
│  └────────────────────────────────────────────────────────────────────────┘ │
│                                                                             │
│  ═══════════════════════════════════════════════════════════════════════    │
│                                                                             │
│                              EVENT FLOW                                     │
│                                                                             │
│  Action Triggered                                                           │
│         │                                                                   │
│         V                                                                   │
│  ┌─────────────┐     ┌─────────────┐     ┌─────────────┐                    │
│  │  PRE HOOKS  │────>│   ACTION    │────>│ POST HOOKS  │                    │
│  │ (sync)      │     │  EXECUTED   │     │ (async)     │                    │
│  └─────────────┘     └─────────────┘     └─────────────┘                    │
│         │                                        │                          │
│         │ Can cancel                             │ Cannot cancel            │
│         │ Timeout: 5s                            │ Timeout: 30s             │
│         V                                        V                          │
│  event.Cancel(err)                        Notifications, logging, etc       │
│                                                                             │
└─────────────────────────────────────────────────────────────────────────────┘
```

<table><thead><tr><th width="154">Concept</th><th>Description</th></tr></thead><tbody><tr><td><strong>Event</strong></td><td>Notification about an action in the system</td></tr><tr><td><strong>Hook</strong></td><td>Event handler function</td></tr><tr><td><strong>Phase</strong></td><td>Execution phase: Pre (before) or Post (after)</td></tr><tr><td><strong>Priority</strong></td><td>Execution priority (lower = earlier)</td></tr><tr><td><strong>Timeout</strong></td><td>Maximum hook execution time</td></tr></tbody></table>

## Hook **Phases**

The event system supports two hook phases with fundamentally different capabilities.

### **Pre Hooks (Phase: Pre)**

Pre hooks execute **before** the main action is performed. They have full control over the event:

<table><thead><tr><th width="144">Capability</th><th>Description</th></tr></thead><tbody><tr><td><strong>Modify data</strong></td><td>Can modify event data before the action executes. Changes are passed to subsequent hooks and the action itself</td></tr><tr><td><strong>Cancel event</strong></td><td>Can completely cancel the event by returning an error. The action will not be executed</td></tr><tr><td><strong>Validate</strong></td><td>Can validate input data and reject invalid requests</td></tr><tr><td><strong>Synchronous</strong></td><td>Execute sequentially in priority order. Block the action until all Pre hooks complete</td></tr><tr><td><strong>Timeout</strong></td><td>5 seconds maximum execution time per hook</td></tr></tbody></table>

**Use cases:**

* Input validation (reject agents from specific IP ranges)
* Data enrichment (add tags, modify parameters)
* Access control (block certain operations)
* Data transformation before processing

```go
// Example: Pre hook that can cancel the event
Ts.TsEventHookRegister("agent.new", "ip_filter", HookPre, 10, func(event any) error {
    e := event.(*adaptix.EventDataAgentNew)
    
    // Modify data
    e.AgentData.Tag = "auto-tagged"
    
    // Cancel event if condition met
    if isBlockedIP(e.AgentData.ExternalIP) {
        return fmt.Errorf("blocked IP: %s", e.AgentData.ExternalIP)
    }
    
    return nil // Allow event to proceed
})
```

### **Post Hooks (Phase: Post)**

Post hooks execute **after** the main action has completed. They are read-only observers:

<table><thead><tr><th width="169">Capability</th><th>Description</th></tr></thead><tbody><tr><td><strong>Read-only</strong></td><td>Receive a copy of the event data. Modifications have no effect on the system</td></tr><tr><td><strong>Cannot cancel</strong></td><td>The action has already been executed. Returning an error only logs a warning (exception: <code>agent.generate</code> — Post hooks can cancel generation and modify <code>FileName</code>/<code>FileContent</code>)</td></tr><tr><td><strong>Asynchronous</strong></td><td>Execute in parallel via worker pool. Do not block the main flow</td></tr><tr><td><strong>Timeout</strong></td><td>30 seconds maximum execution time per hook</td></tr></tbody></table>

**Use cases:**

* Notifications (Telegram, Slack, email)
* Logging and analytics
* External integrations
* Metrics collection

```go
// Example: Post hook for notifications (read-only)
Ts.TsEventHookRegister("agent.new", "telegram_notify", HookPost, 50, func(event any) error {
    e := event.(*adaptix.EventDataAgentNew)
    
    // Data is read-only, modifications are ignored
    message := fmt.Sprintf("New agent: %s from %s", e.AgentData.Id, e.AgentData.ExternalIP)
    sendTelegramMessage(message)
    
    return nil // Errors are logged but don't affect the system
})
```

#### Comparison Table

<table><thead><tr><th width="196">Feature</th><th width="276">Pre Hook</th><th>Post Hook</th></tr></thead><tbody><tr><td>Execution time</td><td>Before action</td><td>After action</td></tr><tr><td>Can modify data</td><td>✅ Yes</td><td>❌ No (read-only copy)</td></tr><tr><td>Can cancel event</td><td>✅ Yes (return error)</td><td>❌ No</td></tr><tr><td>Execution mode</td><td>Synchronous</td><td>Asynchronous</td></tr><tr><td>Blocks main flow</td><td>✅ Yes</td><td>❌ No</td></tr><tr><td>Timeout</td><td>5 seconds</td><td>30 seconds</td></tr><tr><td>Error handling</td><td>Cancels event</td><td>Logs warning</td></tr></tbody></table>

### Priorities

Hooks are executed in order of priority (lower value = earlier):

<table><thead><tr><th width="170">Priority</th><th>Recommended usage</th></tr></thead><tbody><tr><td>1-10</td><td>Critical validators</td></tr><tr><td>10-50</td><td>Data modifiers</td></tr><tr><td>50-100</td><td>Standard processing</td></tr><tr><td>100+</td><td>Logging, monitoring</td></tr></tbody></table>

## Event Types

### Client Events

<table><thead><tr><th width="203">Event Type</th><th width="239">Description</th><th>Data</th></tr></thead><tbody><tr><td><code>client.connect</code></td><td>Client GUI connection</td><td><code>EventDataClientConnect</code></td></tr><tr><td><code>client.disconnect</code></td><td>Client GUI disconnection</td><td><code>EventDataClientDisconnect</code></td></tr></tbody></table>

### Agent Events

<table><thead><tr><th width="175">Event Type</th><th>Description</th><th>Data</th></tr></thead><tbody><tr><td><code>agent.new</code></td><td>New agent connected</td><td><code>EventDataAgentNew</code></td></tr><tr><td><code>agent.generate</code></td><td>Agent payload generation</td><td><code>EventDataAgentGenerate</code></td></tr><tr><td><code>agent.checkin</code></td><td>Agent check-in (heartbeat)</td><td><code>EventDataAgentCheckin</code></td></tr><tr><td><code>agent.activate</code></td><td>Agent activated (from Inactive)</td><td><code>EventDataAgentActivate</code></td></tr><tr><td><code>agent.update</code></td><td>Agent data update</td><td><code>EventDataAgentUpdate</code></td></tr><tr><td><code>agent.terminate</code></td><td>Agent terminated</td><td><code>EventDataAgentTerminate</code></td></tr><tr><td><code>agent.remove</code></td><td>Agent removed</td><td><code>EventDataAgentRemove</code></td></tr></tbody></table>

### Listener Events

<table><thead><tr><th width="170">Event Type</th><th>Description</th><th>Data</th></tr></thead><tbody><tr><td><code>listener.start</code></td><td>Listener started (start/resume)</td><td><code>EventDataListenerStart</code></td></tr><tr><td><code>listener.stop</code></td><td>Listener stopped (stop/pause)</td><td><code>EventDataListenerStop</code></td></tr></tbody></table>

### Task Events

| Event Type        | Description            | Data                     |
| ----------------- | ---------------------- | ------------------------ |
| `task.create`     | Task creation          | `EventDataTaskCreate`    |
| `task.start`      | Task start             | `EventDataTaskStart`     |
| `task.update_job` | Background task update | `EventDataTaskUpdateJob` |
| `task.complete`   | Task completion        | `EventDataTaskComplete`  |

### Credentials Events

| Event Type           | Description         | Data                     |
| -------------------- | ------------------- | ------------------------ |
| `credentials.add`    | Credentials added   | `EventCredentialsAdd`    |
| `credentials.edit`   | Credentials edited  | `EventCredentialsEdit`   |
| `credentials.remove` | Credentials removed | `EventCredentialsRemove` |

### Download Events

| Event Type        | Description       | Data                      |
| ----------------- | ----------------- | ------------------------- |
| `download.start`  | Download started  | `EventDataDownloadStart`  |
| `download.finish` | Download finished | `EventDataDownloadFinish` |
| `download.remove` | Download removed  | `EventDataDownloadRemove` |

### Screenshot Events

| Event Type          | Description        | Data                        |
| ------------------- | ------------------ | --------------------------- |
| `screenshot.add`    | Screenshot added   | `EventDataScreenshotAdd`    |
| `screenshot.remove` | Screenshot removed | `EventDataScreenshotRemove` |

### Tunnel Events

| Event Type     | Description    | Data                   |
| -------------- | -------------- | ---------------------- |
| `tunnel.start` | Tunnel started | `EventDataTunnelStart` |
| `tunnel.stop`  | Tunnel stopped | `EventDataTunnelStop`  |

### Target Events

| Event Type      | Description    | Data                    |
| --------------- | -------------- | ----------------------- |
| `target.add`    | Target added   | `EventDataTargetAdd`    |
| `target.edit`   | Target edited  | `EventDataTargetEdit`   |
| `target.remove` | Target removed | `EventDataTargetRemove` |

### Pivot Events

| Event Type     | Description              | Data                   |
| -------------- | ------------------------ | ---------------------- |
| `pivot.create` | Pivot connection created | `EventDataPivotCreate` |
| `pivot.remove` | Pivot connection removed | `EventDataPivotRemove` |

***

## Hook Registration

### TsEventHookRegister

Full registration with all parameters specified.

```go
hookId := Ts.TsEventHookRegister(
    eventType string,           // Event type
    name string,                // Hook name (for identification)
    phase int,                  // 0 = Pre, 1 = Post
    priority int,               // Execution priority
    handler func(event any) error,  // Handler function
) string  // Returns a unique hook ID
```

**Example:**

```go
const (
    HookPre  = 0
    HookPost = 1
)

hookId := Ts.TsEventHookRegister("agent.new", "my_handler", HookPost, 50, 
    func(event any) error {
        data := event.(*eventing.EventDataAgentNew)
        fmt.Printf("New agent: %s\n", data.Agent.Id)
        return nil
    })
```

### TsEventHookOnPre / TsEventHookOnPost

Simplified registration for Pre/Post hooks.

```go
// Pre hook with default priority
hookId := Ts.TsEventHookOnPre("agent.new", "validator", func(event any) error {
    // ...
    return nil
})

// Post hook with default priority
hookId := Ts.TsEventHookOnPost("agent.terminate", "notifier", func(event any) error {
    // ...
    return nil
})
```

### TsEventHookUnregister

Unregister a hook by ID.

```go
success := Ts.TsEventHookUnregister(hookId) // bool
```

### TsEventHookUnregisterByName

Unregister all hooks with the specified name.

```go
count := Ts.TsEventHookUnregisterByName("my_handler") // int - number removed
```

***

## Event Data Structures

### BaseEvent

Base structure embedded in all events.

```go
type BaseEvent struct {
    Type      EventType   // Event type
    Phase     HookPhase   // Phase (Pre/Post)
    Cancelled bool        // Cancellation flag
    Error     error       // Error (if any)
}

// Method to cancel an action (Pre hooks only)
func (e *BaseEvent) Cancel(err error) {
    e.Cancelled = true
    e.Error = err
}
```

### Client Events

```go
type EventDataClientConnect struct {
    BaseEvent
    Username string   // Username
}

type EventDataClientDisconnect struct {
    BaseEvent
    Username string
}
```

### Agent Events

```go
type EventDataAgentNew struct {
    BaseEvent
    Agent   adaptix.AgentData   // Agent data
    Restore bool                // true if restored from DB
}

type EventDataAgentGenerate struct {
    BaseEvent
    BuilderId     string   // Builder ID (for logging and cancellation)
    AgentName     string   // Agent type name
    ListenersName []string // List of listeners
    Config        string   // Configuration (JSON)
    FileName      string   // File name
    FileContent   []byte   // File content
}

type EventDataAgentCheckin struct {
    BaseEvent
    Agent adaptix.AgentData
}

type EventDataAgentActivate struct {
    BaseEvent
    Agent adaptix.AgentData   // Activated agent data
}

type EventDataAgentUpdate struct {
    BaseEvent
    Agent adaptix.AgentData
}

type EventDataAgentTerminate struct {
    BaseEvent
    AgentId string   // Agent ID
    TaskId  string   // Terminate task ID
}

type EventDataAgentRemove struct {
    BaseEvent
    Agent adaptix.AgentData
}
```

### Listener Events

```go
type EventDataListenerStart struct {
    BaseEvent
    ListenerName string   // Listener name
    ListenerType string   // Listener type
    Config       string   // Configuration (JSON)
}

type EventDataListenerStop struct {
    BaseEvent
    ListenerName string   // Listener name
    ListenerType string   // Listener type
}
```

### Task Events

```go
type EventDataTaskCreate struct {
    BaseEvent
    AgentId string
    Task    adaptix.TaskData
    Cmdline string
    Client  string   // Operator name
}

type EventDataTaskStart struct {
    BaseEvent
    AgentId string
    Task    adaptix.TaskData
}

type EventDataTaskUpdateJob struct {
    BaseEvent
    AgentId string
    Task    adaptix.TaskData
}

type EventDataTaskComplete struct {
    BaseEvent
    AgentId string
    Task    adaptix.TaskData
}
```

### Credentials Events

```go
type EventCredentialsAdd struct {
    BaseEvent
    Credentials []adaptix.CredsData
}

type EventCredentialsEdit struct {
    BaseEvent
    CredId  string
    OldCred adaptix.CredsData
    NewCred adaptix.CredsData
}

type EventCredentialsRemove struct {
    BaseEvent
    CredIds []string
}
```

### Download Events

```go
type EventDataDownloadStart struct {
    BaseEvent
    AgentId  string
    FileId   string
    FileName string
    FileSize int
}

type EventDataDownloadFinish struct {
    BaseEvent
    Download adaptix.DownloadData
    Canceled bool
}

type EventDataDownloadRemove struct {
    BaseEvent
    FileIds []string
}
```

### Screenshot Events

```go
type EventDataScreenshotAdd struct {
    BaseEvent
    AgentId string
    Note    string
    Content []byte
}

type EventDataScreenshotRemove struct {
    BaseEvent
    ScreenId string
}
```

### Tunnel Events

```go
type EventDataTunnelStart struct {
    BaseEvent
    AgentId    string
    TunnelId   string
    TunnelType int
    Port       int
    Info       string
}

type EventDataTunnelStop struct {
    BaseEvent
    AgentId    string
    TunnelId   string
    TunnelType int
    Port       int
}
```

### Target Events

```go
type EventDataTargetAdd struct {
    BaseEvent
    Targets []adaptix.TargetData
}

type EventDataTargetEdit struct {
    BaseEvent
    Target adaptix.TargetData
}

type EventDataTargetRemove struct {
    BaseEvent
    TargetIds []string
}
```

### Pivot Events

```go
type EventDataPivotCreate struct {
    BaseEvent
    PivotId       string
    ParentAgentId string
    ChildAgentId  string
    PivotName     string
}

type EventDataPivotRemove struct {
    BaseEvent
    PivotId string
}
```

***

## Usage Examples

### Example 1: Event Logger

```go
package main

import (
    "fmt"
    "time"
)

const (
    HookPre  = 0
    HookPost = 1
)

var AllEventTypes = []string{
    "client.connect", "client.disconnect",
    "agent.new", "agent.activate", "agent.terminate", "agent.remove",
    "listener.start", "listener.stop",
    "task.create", "task.complete",
    "download.start", "download.finish",
    "tunnel.start", "tunnel.stop",
    "pivot.create", "pivot.remove",
}

func InitPlugin(ts any, moduleDir string, serviceConfig string) adaptix.PluginService {
    Ts = ts.(Teamserver)
    
    for _, eventType := range AllEventTypes {
        et := eventType
        
        // Pre hook
        Ts.TsEventHookRegister(et, "event_logger_pre", HookPre, 100, 
            func(event any) error {
                logEvent("PRE", et, event)
                return nil
            })
        
        // Post hook
        Ts.TsEventHookRegister(et, "event_logger_post", HookPost, 100, 
            func(event any) error {
                logEvent("POST", et, event)
                return nil
            })
    }
    
    fmt.Println("[EventLogger] All event hooks registered")
    return &PluginService{}
}

func logEvent(phase string, eventType string, event any) {
    timestamp := time.Now().Format("2006-01-02 15:04:05.000")
    fmt.Printf("[%s] [%s] %s: %+v\n", timestamp, phase, eventType, event)
}
```

### Example 2: IP Blacklist Validator (Pre Hook)

```go
package main

import (
    "errors"
    "strings"
)

const HookPre = 0

var blacklistedIPs = []string{
    "10.0.0.1",
    "192.168.1.100",
}

var blacklistedSubnets = []string{
    "172.16.",
}

func InitPlugin(ts any, moduleDir string, serviceConfig string) adaptix.PluginService {
    Ts = ts.(Teamserver)
    
    // Register Pre hook for validation
    Ts.TsEventHookRegister("agent.new", "ip_validator", HookPre, 10, 
        func(event any) error {
            data := event.(*EventDataAgentNew)
            
            // Skip restore from DB
            if data.Restore {
                return nil
            }
            
            ip := data.Agent.ExternalIP
            
            // Check exact IPs
            for _, blocked := range blacklistedIPs {
                if ip == blocked {
                    data.Cancel(errors.New("IP is blacklisted"))
                    return errors.New("blocked IP: " + ip)
                }
            }
            
            // Check subnets
            for _, subnet := range blacklistedSubnets {
                if strings.HasPrefix(ip, subnet) {
                    data.Cancel(errors.New("IP subnet is blacklisted"))
                    return errors.New("blocked subnet: " + ip)
                }
            }
            
            return nil
        })
    
    return &PluginService{}
}
```

### Example 3: Auto-Tagger

```go
package main

const HookPost = 1

func InitPlugin(ts any, moduleDir string, serviceConfig string) adaptix.PluginService {
    Ts = ts.(Teamserver)
    
    // Automatic tagging of new agents
    Ts.TsEventHookRegister("agent.new", "auto_tagger", HookPost, 50, 
        func(event any) error {
            data := event.(*EventDataAgentNew)
            agent := data.Agent
            
            var tag string
            
            // Determine tag by OS
            switch agent.Os {
            case 0: // Windows
                if agent.Elevated {
                    tag = "windows-admin"
                } else {
                    tag = "windows-user"
                }
            case 1: // Linux
                tag = "linux"
            case 2: // macOS
                tag = "macos"
            }
            
            // Additional tags by domain
            if strings.Contains(strings.ToLower(agent.Domain), "dc") {
                tag += ",domain-controller"
            }
            
            // Update tags
            if tag != "" {
                Ts.TsAgentUpdateDataPartial(agent.Id, map[string]interface{}{
                    "tags": tag,
                })
            }
            
            return nil
        })
    
    return &PluginService{}
}
```

### Example 4: Webhook Integration

```go
package main

import (
    "bytes"
    "encoding/json"
    "net/http"
    "time"
)

const HookPost = 1

var webhookURL = "https://example.com/webhook"

type WebhookPayload struct {
    EventType string      `json:"event_type"`
    Timestamp int64       `json:"timestamp"`
    Data      interface{} `json:"data"`
}

func InitPlugin(ts any, moduleDir string, serviceConfig string) adaptix.PluginService {
    Ts = ts.(Teamserver)
    
    events := []string{"agent.new", "agent.terminate", "task.complete"}
    
    for _, et := range events {
        eventType := et
        Ts.TsEventHookRegister(eventType, "webhook", HookPost, 50, 
            func(event any) error {
                go sendWebhook(eventType, event)
                return nil
            })
    }
    
    return &PluginService{}
}

func sendWebhook(eventType string, event any) {
    payload := WebhookPayload{
        EventType: eventType,
        Timestamp: time.Now().Unix(),
        Data:      event,
    }
    
    jsonData, err := json.Marshal(payload)
    if err != nil {
        return
    }
    
    client := &http.Client{Timeout: 10 * time.Second}
    req, _ := http.NewRequest("POST", webhookURL, bytes.NewBuffer(jsonData))
    req.Header.Set("Content-Type", "application/json")
    req.Header.Set("X-Webhook-Event", eventType)
    
    resp, err := client.Do(req)
    if err != nil {
        return
    }
    defer resp.Body.Close()
}
```

***

## Best Practices

### 1. Error Handling

```go
Ts.TsEventHookRegister("agent.new", "my_hook", HookPost, 50, 
    func(event any) error {
        // Safe type assertion
        data, ok := event.(*EventDataAgentNew)
        if !ok {
            return errors.New("invalid event type")
        }
        
        // Recover from panic
        defer func() {
            if r := recover(); r != nil {
                fmt.Printf("Hook panic recovered: %v\n", r)
            }
        }()
        
        // ... main logic ...
        
        return nil
    })
```

### 2. Asynchronous Processing in Post Hooks

```go
Ts.TsEventHookRegister("agent.new", "notifier", HookPost, 50, 
    func(event any) error {
        data := event.(*EventDataAgentNew)
        
        // Async send — do not block the event
        go func() {
            sendNotification(data.Agent)
        }()
        
        return nil  // Return nil immediately
    })
```

### 3. Correct Usage of Pre Hooks

```go
// Correct: validation and cancellation
Ts.TsEventHookRegister("agent.new", "validator", HookPre, 10, 
    func(event any) error {
        data := event.(*EventDataAgentNew)
        
        if !isAllowed(data.Agent.ExternalIP) {
            data.Cancel(errors.New("not allowed"))
            return errors.New("blocked")
        }
        
        return nil
    })

// Incorrect: long-running operations in Pre hook
Ts.TsEventHookRegister("agent.new", "slow_hook", HookPre, 10, 
    func(event any) error {
        time.Sleep(10 * time.Second)  // Blocks everything for 10 seconds!
        return nil
    })
```

### 4. Unique Hook Names

```go
// Use unique names to allow unregistration
hookId := Ts.TsEventHookRegister("agent.new", "my_service_agent_hook", ...)

// Later you can remove all service hooks
Ts.TsEventHookUnregisterByName("my_service_agent_hook")
```

### 5. Priorities

```go
// Validators — low priority (executed first)
Ts.TsEventHookRegister("agent.new", "validator", HookPre, 10, ...)

// Modifiers — medium priority
Ts.TsEventHookRegister("agent.new", "modifier", HookPre, 50, ...)

// Loggers — high priority (executed last)
Ts.TsEventHookRegister("agent.new", "logger", HookPre, 100, ...)
```

### 6. Timeouts

* Pre hooks: 5 seconds (short operations)
* Post hooks: executed in a separate thread, 30 seconds (you can do external requests)

If a hook exceeds the timeout, it is interrupted with an error.

***

### Constants

```go
// Execution phases
const (
    HookPre  = 0   // Before action (sync)
    HookPost = 1   // After action (async)
)

// Default timeouts
const (
    DefaultPreHookTimeout  = 5 * time.Second
    DefaultPostHookTimeout = 30 * time.Second
)
```
