Files
agentic-coding-workflow/internal/acp/handlers/server_permission.go
T

68 lines
2.0 KiB
Go

package handlers
import (
"context"
"encoding/json"
"strings"
)
// PermissionHandler handles session/request_permission (spec §5.1).
//
// MVP behaviour (decision §1 #8 + §10 misc #1): fs.* tool calls are answered
// directly by the server (so they never reach this handler). Other tool calls
// default to "reject_once" — fully automated, no human-in-the-loop.
type PermissionHandler struct{}
// NewPermissionHandler constructs a PermissionHandler.
func NewPermissionHandler() *PermissionHandler { return &PermissionHandler{} }
type permissionParams struct {
SessionID string `json:"sessionId"`
ToolCall json.RawMessage `json:"toolCall"`
Options []permOption `json:"options"`
}
type permOption struct {
ID string `json:"id"`
Name string `json:"name,omitempty"`
}
type permOutcomeSelected struct {
ID string `json:"id"`
}
type permOutcome struct {
Selected *permOutcomeSelected `json:"selected,omitempty"`
}
type permResult struct {
Outcome permOutcome `json:"outcome"`
}
// Handle picks the first option whose ID looks like a "reject" choice; if none
// matches, falls back to the first option. Aligns with spec §10 misc #4
// ("default reject").
func (h *PermissionHandler) Handle(_ context.Context, _ SessionContext, 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()}}
}
chosen := ""
for _, opt := range p.Options {
lc := strings.ToLower(opt.ID)
if strings.Contains(lc, "reject") || strings.Contains(lc, "deny") || strings.Contains(lc, "no") {
chosen = opt.ID
break
}
}
if chosen == "" && len(p.Options) > 0 {
chosen = p.Options[0].ID
}
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}
}