Repository navigation
Expand file tree
/
Copy pathapproval_waterfall.go
More file actions
58 lines (51 loc) · 1.97 KB
/
Copy pathapproval_waterfall.go
File metadata and controls
58 lines (51 loc) · 1.97 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
package engine
import (
"context"
"fmt"
)
// ApprovalDecider is one link in the approval waterfall. It returns the response and
// true when it has an authoritative answer, or (default, false) to abstain and pass
// control to the next decider.
type ApprovalDecider func(ctx context.Context, req ApprovalRequest) (ApprovalResponse, bool)
// ApprovalWaterfall answers approval requests through an ordered chain of deciders.
// It is the deepseek-harness approval/request waterfall port: the first decider
// that answers wins; a decider that abstains delegates to the next. If every
// decider abstains — including the empty chain — the gate is fail-closed and the
// action is denied. Deciders run synchronously in registration order.
type ApprovalWaterfall struct {
deciders []ApprovalDecider
}
// NewApprovalWaterfall constructs an empty waterfall.
func NewApprovalWaterfall() *ApprovalWaterfall {
return &ApprovalWaterfall{}
}
// Add appends a decider and returns a disposer that removes exactly it.
func (w *ApprovalWaterfall) Add(fn ApprovalDecider) func() {
if w == nil || fn == nil {
return func() {}
}
w.deciders = append(w.deciders, fn)
idx := len(w.deciders) - 1
return func() { w.remove(idx) }
}
func (w *ApprovalWaterfall) remove(idx int) {
if w == nil || idx < 0 || idx >= len(w.deciders) {
return
}
w.deciders = append(w.deciders[:idx], w.deciders[idx+1:]...)
}
// Decide runs the waterfall. It returns (ApprovalReject, denyMessage) when no
// decider answers, implementing the fail-closed guarantee.
func (w *ApprovalWaterfall) Decide(ctx context.Context, req ApprovalRequest) (ApprovalResponse, string) {
if w != nil {
for _, fn := range w.deciders {
if resp, ok := fn(ctx, req); ok {
if resp == ApprovalReject {
return resp, fmt.Sprintf("approval denied for %s (%s)", req.Category, req.ToolName)
}
return resp, ""
}
}
}
return ApprovalReject, fmt.Sprintf("approval required for %s (%s) but no answerer is configured", req.Category, req.ToolName)
}