1package tools
2
3import (
4 "context"
5
6 "github.com/charmbracelet/crush/internal/permission"
7)
8
9type (
10 sessionIDContextKey string
11 messageIDContextKey string
12 hookPermissionContextKey string
13)
14
15const (
16 SessionIDContextKey sessionIDContextKey = "session_id"
17 MessageIDContextKey messageIDContextKey = "message_id"
18 HookPermissionContextKey hookPermissionContextKey = "hook_permission"
19)
20
21func GetSessionFromContext(ctx context.Context) string {
22 sessionID := ctx.Value(SessionIDContextKey)
23 if sessionID == nil {
24 return ""
25 }
26 s, ok := sessionID.(string)
27 if !ok {
28 return ""
29 }
30 return s
31}
32
33func GetMessageFromContext(ctx context.Context) string {
34 messageID := ctx.Value(MessageIDContextKey)
35 if messageID == nil {
36 return ""
37 }
38 s, ok := messageID.(string)
39 if !ok {
40 return ""
41 }
42 return s
43}
44
45// GetHookPermissionFromContext gets the hook permission decision from context.
46// Returns: permission string ("approve" or "deny"), found bool
47func GetHookPermissionFromContext(ctx context.Context) (string, bool) {
48 permission := ctx.Value(HookPermissionContextKey)
49 if permission == nil {
50 return "", false
51 }
52 s, ok := permission.(string)
53 if !ok {
54 return "", false
55 }
56 return s, true
57}
58
59// SetHookPermissionInContext sets the hook permission decision in context.
60func SetHookPermissionInContext(ctx context.Context, permission string) context.Context {
61 return context.WithValue(ctx, HookPermissionContextKey, permission)
62}
63
64// CheckHookPermission checks if a hook has already made a permission decision.
65// Returns true if execution should proceed, false if denied.
66// If hook approved, skips the permission service call.
67// If hook denied, returns ErrorPermissionDenied.
68// If hook said "ask" or no decision, calls the permission service.
69func CheckHookPermission(ctx context.Context, permissionService permission.Service, req permission.CreatePermissionRequest) (bool, error) {
70 hookPerm, hasHookPerm := GetHookPermissionFromContext(ctx)
71
72 if hasHookPerm {
73 switch hookPerm {
74 case "approve":
75 // Hook auto-approved, skip permission check
76 return true, nil
77 case "deny":
78 // Hook denied, return error
79 return false, permission.ErrorPermissionDenied
80 }
81 }
82
83 // No hook decision or hook said "ask", use normal permission flow
84 granted := permissionService.Request(req)
85 if !granted {
86 return false, permission.ErrorPermissionDenied
87 }
88 return true, nil
89}