-
Notifications
You must be signed in to change notification settings - Fork 3
Expand file tree
/
Copy pathhooks.cpp
More file actions
205 lines (175 loc) · 7.08 KB
/
hooks.cpp
File metadata and controls
205 lines (175 loc) · 7.08 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
// Copyright (c) 2025 Elias Bachaalany
// SPDX-License-Identifier: MIT
/// @file hooks.cpp
/// @brief Example demonstrating the hooks system for tool lifecycle interception
///
/// This example shows how to:
/// 1. Register PreToolUse hooks to inspect/modify/deny tool calls
/// 2. Register PostToolUse hooks to inspect/modify tool results
/// 3. Register session lifecycle hooks (start, end, error)
/// 4. Combine multiple hooks for comprehensive observability
#include <atomic>
#include <chrono>
#include <condition_variable>
#include <copilot/copilot.hpp>
#include <iostream>
#include <mutex>
#include <string>
#include <vector>
struct HookLog
{
std::string hook_type;
std::string detail;
};
std::vector<HookLog> g_hook_log;
std::mutex g_log_mutex;
void log_hook(const std::string& type, const std::string& detail)
{
std::lock_guard<std::mutex> lock(g_log_mutex);
g_hook_log.push_back({type, detail});
std::cout << "[HOOK:" << type << "] " << detail << "\n";
}
int main()
{
try
{
copilot::ClientOptions options;
copilot::Client client(options);
std::cout << "=== Hooks System Example ===\n\n";
std::cout << "Demonstrates PreToolUse, PostToolUse, and session lifecycle hooks.\n\n";
client.start().get();
copilot::SessionConfig config;
// Set up hooks
config.hooks = copilot::SessionHooks{};
// ---- PreToolUse Hook ----
// Fires BEFORE every tool call. Can inspect args, allow/deny, or modify args.
config.hooks->on_pre_tool_use = [](const copilot::PreToolUseHookInput& input,
const copilot::HookInvocation&)
-> std::optional<copilot::PreToolUseHookOutput>
{
log_hook("PreToolUse", "Tool: " + input.tool_name);
// Example: deny any tool named "dangerous_tool"
if (input.tool_name == "dangerous_tool")
{
copilot::PreToolUseHookOutput output;
output.permission_decision = "deny";
output.permission_decision_reason = "This tool is blocked by policy";
log_hook("PreToolUse", "DENIED: " + input.tool_name);
return output;
}
// Allow all other tools (return nullopt = no modification)
return std::nullopt;
};
// ---- PostToolUse Hook ----
// Fires AFTER every tool call. Can inspect/modify the result.
config.hooks->on_post_tool_use = [](const copilot::PostToolUseHookInput& input,
const copilot::HookInvocation&)
-> std::optional<copilot::PostToolUseHookOutput>
{
std::string result_str = input.tool_result.has_value()
? input.tool_result->dump().substr(0, 100)
: "(no result)";
log_hook("PostToolUse", "Tool: " + input.tool_name + " => " + result_str);
// Example: add context to the result
copilot::PostToolUseHookOutput output;
output.additional_context = "Tool " + input.tool_name + " completed successfully";
return output;
};
// ---- SessionStart Hook ----
// Fires when the session starts.
config.hooks->on_session_start = [](const copilot::SessionStartHookInput&,
const copilot::HookInvocation&)
-> std::optional<copilot::SessionStartHookOutput>
{
log_hook("SessionStart", "Session is starting");
return std::nullopt;
};
// ---- SessionEnd Hook ----
// Fires when the session ends.
config.hooks->on_session_end = [](const copilot::SessionEndHookInput&,
const copilot::HookInvocation&)
-> std::optional<copilot::SessionEndHookOutput>
{
log_hook("SessionEnd", "Session is ending");
return std::nullopt;
};
// ---- ErrorOccurred Hook ----
// Fires on errors during the session.
config.hooks->on_error_occurred = [](const copilot::ErrorOccurredHookInput& input,
const copilot::HookInvocation&)
-> std::optional<copilot::ErrorOccurredHookOutput>
{
log_hook("ErrorOccurred", input.error + " (context: " + input.error_context + ")");
return std::nullopt;
};
// Permission handler is still needed for tool execution approval
config.on_permission_request = [](const copilot::PermissionRequest&)
-> copilot::PermissionRequestResult
{
copilot::PermissionRequestResult r;
r.kind = "approved";
return r;
};
auto session = client.create_session(config).get();
std::cout << "Session created: " << session->session_id() << "\n\n";
std::mutex mtx;
std::condition_variable cv;
std::atomic<bool> idle{false};
auto sub = session->on(
[&](const copilot::SessionEvent& event)
{
if (auto* msg = event.try_as<copilot::AssistantMessageData>())
std::cout << "\nAssistant: " << msg->content << "\n";
else if (event.type == copilot::SessionEventType::SessionIdle)
{
std::lock_guard<std::mutex> lock(mtx);
idle = true;
cv.notify_one();
}
}
);
// Interactive loop
std::cout << "Chat with hooks enabled. Type 'log' to see hook log, 'quit' to exit.\n\n> ";
std::string line;
while (std::getline(std::cin, line))
{
if (line == "quit" || line == "exit")
break;
if (line == "log")
{
std::lock_guard<std::mutex> lock(g_log_mutex);
std::cout << "\n=== Hook Log (" << g_hook_log.size() << " entries) ===\n";
for (const auto& entry : g_hook_log)
std::cout << " [" << entry.hook_type << "] " << entry.detail << "\n";
std::cout << "==============================\n\n> ";
continue;
}
if (line.empty()) { std::cout << "> "; continue; }
idle = false;
copilot::MessageOptions msg;
msg.prompt = line;
session->send(msg).get();
{
std::unique_lock<std::mutex> lock(mtx);
cv.wait(lock, [&idle]() { return idle.load(); });
}
std::cout << "\n> ";
}
// Final hook log
std::cout << "\n=== Final Hook Log ===\n";
{
std::lock_guard<std::mutex> lock(g_log_mutex);
for (const auto& entry : g_hook_log)
std::cout << " [" << entry.hook_type << "] " << entry.detail << "\n";
}
std::cout << "======================\n";
session->destroy().get();
client.stop().get();
return 0;
}
catch (const std::exception& e)
{
std::cerr << "Error: " << e.what() << "\n";
return 1;
}
}