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 "" }