-
Notifications
You must be signed in to change notification settings - Fork 1.3k
Expand file tree
/
Copy pathhooks.rs
More file actions
133 lines (117 loc) · 4.5 KB
/
Copy pathhooks.rs
File metadata and controls
133 lines (117 loc) · 4.5 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
//! Session hooks for logging and auditing.
//!
//! Demonstrates `SessionHooks` to intercept lifecycle events — logging every
//! tool invocation, summarizing prompts, and recording session start/end
//! for audit purposes.
//!
//! ```sh
//! cargo run -p github-copilot-sdk --example hooks
//! ```
use std::sync::Arc;
use std::time::Duration;
use async_trait::async_trait;
use github_copilot_sdk::handler::ApproveAllHandler;
use github_copilot_sdk::hooks::{
HookEvent, HookOutput, PostToolUseOutput, PreToolUseOutput, SessionEndOutput, SessionHooks,
SessionStartOutput,
};
use github_copilot_sdk::types::{MessageOptions, SessionConfig};
use github_copilot_sdk::{Client, ClientOptions};
/// Hooks implementation that logs lifecycle events to stdout.
struct AuditHooks;
#[async_trait]
impl SessionHooks for AuditHooks {
async fn on_hook(&self, event: HookEvent) -> HookOutput {
match event {
HookEvent::SessionStart { input, ctx } => {
println!(
"[audit] session {} started (source={}, cwd={})",
ctx.session_id,
input.source,
input.working_directory.display(),
);
HookOutput::SessionStart(SessionStartOutput {
additional_context: Some("You are being audited. Be concise.".to_string()),
..Default::default()
})
}
HookEvent::PreToolUse { input, ctx } => {
println!(
"[audit] session {} — pre tool use: {} (args: {})",
ctx.session_id, input.tool_name, input.tool_args,
);
// Example: deny a specific tool by name.
if input.tool_name == "dangerous_tool" {
return HookOutput::PreToolUse(PreToolUseOutput {
permission_decision: Some("deny".to_string()),
permission_decision_reason: Some("blocked by audit policy".to_string()),
..Default::default()
});
}
HookOutput::None
}
HookEvent::PostToolUse { input, ctx } => {
println!(
"[audit] session {} — post tool use: {} (result: {})",
ctx.session_id, input.tool_name, input.tool_result,
);
HookOutput::PostToolUse(PostToolUseOutput::default())
}
HookEvent::UserPromptSubmitted { input, ctx } => {
println!(
"[audit] session {} — user prompt ({} chars)",
ctx.session_id,
input.prompt.len(),
);
HookOutput::None
}
HookEvent::SessionEnd { input, ctx } => {
println!(
"[audit] session {} ended (reason={})",
ctx.session_id, input.reason,
);
HookOutput::SessionEnd(SessionEndOutput {
session_summary: Some("Audited session complete.".to_string()),
..Default::default()
})
}
HookEvent::ErrorOccurred { input, ctx } => {
eprintln!(
"[audit] session {} — error in {}: {} (recoverable={})",
ctx.session_id, input.error_context, input.error, input.recoverable,
);
HookOutput::None
}
_ => HookOutput::None,
}
}
}
#[tokio::main]
async fn main() -> Result<(), github_copilot_sdk::Error> {
let client = Client::start(ClientOptions::default()).await?;
// hooks: true is set automatically when a hooks handler is provided.
let config = SessionConfig::default()
.with_permission_handler(Arc::new(ApproveAllHandler))
.with_hooks(Arc::new(AuditHooks));
let session = client.create_session(config).await?;
println!(
"Session {} with audit hooks. Sending a message...\n",
session.id()
);
let response = session
.send_and_wait(
MessageOptions::new("Say hello in three languages.")
.with_wait_timeout(Duration::from_secs(60)),
)
.await?;
if let Some(event) = response {
let text = event
.data
.get("content")
.and_then(|c| c.as_str())
.unwrap_or("<no response>");
println!("\n{text}");
}
session.disconnect().await?;
Ok(())
}