You've already forked agentic-coding-workflow
90 lines
3.0 KiB
Go
90 lines
3.0 KiB
Go
package handlers
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"strings"
|
|
)
|
|
|
|
// PermissionHandler handles session/request_permission (spec §5.1).
|
|
//
|
|
// 行为分两层:
|
|
// - fs.* 工具调用由服务端直接应答(不经过本 handler)。
|
|
// - 其他工具调用:若注入了 PermissionDecider(生产路径),交由其裁决——命中
|
|
// agent kind 白名单则自动放行,否则挂起等待人工审批;未注入 decider 时
|
|
// (部分单测/向后兼容)回退到「默认拒绝」的纯本地逻辑。
|
|
type PermissionHandler struct {
|
|
decider PermissionDecider
|
|
}
|
|
|
|
// PermOption 是 session/request_permission 提供的一个候选选项。
|
|
type PermOption struct {
|
|
ID string `json:"id"`
|
|
Name string `json:"name,omitempty"`
|
|
}
|
|
|
|
// PermissionDecider 是权限裁决器契约。实现放在 internal/acp 主包(PermissionService),
|
|
// 以接口注入避免 handlers 子包反向 import 主包形成循环。返回选中的 option id;
|
|
// 空串表示拒绝。
|
|
type PermissionDecider interface {
|
|
Decide(ctx context.Context, sess SessionContext, agentRequestID string,
|
|
toolCall json.RawMessage, options []PermOption) (chosenOptionID string)
|
|
}
|
|
|
|
// NewPermissionHandler 构造 PermissionHandler。decider 可为 nil(回退到本地默认拒绝)。
|
|
func NewPermissionHandler(decider PermissionDecider) *PermissionHandler {
|
|
return &PermissionHandler{decider: decider}
|
|
}
|
|
|
|
type permissionParams struct {
|
|
SessionID string `json:"sessionId"`
|
|
ToolCall json.RawMessage `json:"toolCall"`
|
|
Options []PermOption `json:"options"`
|
|
}
|
|
|
|
type permOutcomeSelected struct {
|
|
ID string `json:"id"`
|
|
}
|
|
|
|
type permOutcome struct {
|
|
Selected *permOutcomeSelected `json:"selected,omitempty"`
|
|
}
|
|
|
|
type permResult struct {
|
|
Outcome permOutcome `json:"outcome"`
|
|
}
|
|
|
|
// Handle 解析参数后委托 decider 裁决;无 decider 时回退到本地默认拒绝逻辑。
|
|
// agentRequestID 是代理侧 JSON-RPC request id,用于持久化与排障。
|
|
func (h *PermissionHandler) Handle(ctx context.Context, sess SessionContext, agentRequestID string, paramsJSON json.RawMessage) FsResult {
|
|
var p permissionParams
|
|
if err := json.Unmarshal(paramsJSON, &p); err != nil {
|
|
return FsResult{Err: &FsError{Code: -32602, Message: "invalid params: " + err.Error()}}
|
|
}
|
|
|
|
var chosen string
|
|
if h.decider != nil {
|
|
chosen = h.decider.Decide(ctx, sess, agentRequestID, p.ToolCall, p.Options)
|
|
} else {
|
|
chosen = defaultRejectChoice(p.Options)
|
|
}
|
|
|
|
if chosen == "" {
|
|
out, _ := json.Marshal(permResult{Outcome: permOutcome{}})
|
|
return FsResult{OK: out}
|
|
}
|
|
out, _ := json.Marshal(permResult{Outcome: permOutcome{Selected: &permOutcomeSelected{ID: chosen}}})
|
|
return FsResult{OK: out}
|
|
}
|
|
|
|
// defaultRejectChoice 是无 decider 时的回退:优先选 reject 类选项,否则返回空(拒绝)。
|
|
func defaultRejectChoice(options []PermOption) string {
|
|
for _, opt := range options {
|
|
lc := strings.ToLower(opt.ID)
|
|
if strings.Contains(lc, "reject") || strings.Contains(lc, "deny") || strings.Contains(lc, "no") {
|
|
return opt.ID
|
|
}
|
|
}
|
|
return ""
|
|
}
|