Documentation
¶
Overview ¶
Package approval provides a unified interface for tool execution approval across multiple channels (Gateway WebSocket, Telegram, Discord, Slack, TTY).
Index ¶
- Constants
- Variables
- func ApprovalTargetFromContext(ctx context.Context) string
- func FormatToolExecutionError(toolName string, kind error) error
- func ProviderFromError(err error) string
- func RequestIDFromError(err error) string
- func TurnApprovalKey(toolName string, params map[string]interface{}) (string, string, error)
- func WithApprovalTarget(ctx context.Context, target string) context.Context
- func WithTurnApprovalState(ctx context.Context, state *TurnApprovalState) context.Context
- func WrapError(kind error, provider, requestID, message string) error
- type ApprovalRequest
- type ApprovalResponse
- type ApprovalViewModel
- type CompositeProvider
- func (c *CompositeProvider) CanHandle(_ string) bool
- func (c *CompositeProvider) Register(p Provider)
- func (c *CompositeProvider) RequestApproval(ctx context.Context, req ApprovalRequest) (ApprovalResponse, error)
- func (c *CompositeProvider) SetP2PFallback(p Provider)
- func (c *CompositeProvider) SetTTYFallback(p Provider)
- type DisplayTier
- type Error
- type GatewayApprover
- type GatewayProvider
- type GrantInfo
- type GrantStore
- func (s *GrantStore) CleanExpired() int
- func (s *GrantStore) Grant(sessionKey, toolName string)
- func (s *GrantStore) IsGranted(sessionKey, toolName string) bool
- func (s *GrantStore) List() []GrantInfo
- func (s *GrantStore) Revoke(sessionKey, toolName string)
- func (s *GrantStore) RevokeSession(sessionKey string)
- func (s *GrantStore) SetTTL(ttl time.Duration)
- type HeadlessProvider
- type HistoryEntry
- type HistoryStore
- type Provider
- type RiskIndicator
- type TTYProvider
- type TurnApprovalEntry
- type TurnApprovalState
- type TurnOutcome
Constants ¶
const MaxTurnApprovalTimeouts = 3
MaxTurnApprovalTimeouts bounds how many approval timeouts the same canonical action can accumulate in a single turn before later retries are blocked without opening another approval prompt.
Variables ¶
var ( ErrDenied = errors.New("approval denied") ErrTimeout = errors.New("approval timeout") )
Functions ¶
func ApprovalTargetFromContext ¶
ApprovalTargetFromContext retrieves the approval routing target from the context. Returns empty string if no target is set.
func FormatToolExecutionError ¶ added in v0.7.0
func ProviderFromError ¶ added in v0.7.0
func RequestIDFromError ¶ added in v0.7.0
func TurnApprovalKey ¶ added in v0.7.0
func WithApprovalTarget ¶
WithApprovalTarget sets an explicit approval routing target in the context. This overrides the session key for approval routing, allowing automation systems (cron, background) to route approval requests to the originating channel.
func WithTurnApprovalState ¶ added in v0.7.0
func WithTurnApprovalState(ctx context.Context, state *TurnApprovalState) context.Context
Types ¶
type ApprovalRequest ¶
type ApprovalRequest struct {
ID string
ToolName string
SessionKey string
Params map[string]interface{}
Summary string // Human-readable description of what the tool will do
CreatedAt time.Time
// Tier classification fields (optional, populated by approval middleware).
SafetyLevel string `json:",omitempty"` // "safe", "moderate", "dangerous"
Category string `json:",omitempty"` // tool capability category (e.g. "filesystem", "automation")
Activity string `json:",omitempty"` // tool capability activity (e.g. "execute", "write", "read")
}
ApprovalRequest represents a request for tool execution approval.
type ApprovalResponse ¶
ApprovalResponse carries the result of an approval request.
type ApprovalViewModel ¶ added in v0.7.0
type ApprovalViewModel struct {
Request ApprovalRequest
Tier DisplayTier
Risk RiskIndicator
DiffContent string // unified diff for fs_edit/fs_write, empty otherwise
RuleExplanation string // human-readable explanation of why approval is needed
}
ApprovalViewModel bridges ApprovalRequest data to TUI rendering.
func NewViewModel ¶ added in v0.7.0
func NewViewModel(req ApprovalRequest) ApprovalViewModel
NewViewModel creates an ApprovalViewModel from a request.
type CompositeProvider ¶
type CompositeProvider struct {
// contains filtered or unexported fields
}
CompositeProvider routes approval requests to the appropriate provider based on session key prefix. Falls back to TTY, then denies (fail-closed). P2P sessions ("p2p:..." keys) use a dedicated fallback and are never routed to HeadlessProvider to prevent remote peers from auto-approving.
func NewCompositeProvider ¶
func NewCompositeProvider() *CompositeProvider
NewCompositeProvider creates a new CompositeProvider.
func (*CompositeProvider) CanHandle ¶
func (c *CompositeProvider) CanHandle(_ string) bool
CanHandle always returns true; CompositeProvider accepts all requests and routes internally.
func (*CompositeProvider) Register ¶
func (c *CompositeProvider) Register(p Provider)
Register appends a provider to the routing chain.
func (*CompositeProvider) RequestApproval ¶
func (c *CompositeProvider) RequestApproval(ctx context.Context, req ApprovalRequest) (ApprovalResponse, error)
RequestApproval routes the request to the first provider whose CanHandle returns true. P2P sessions ("p2p:..." keys) use a dedicated fallback instead of the TTY fallback to ensure HeadlessProvider never auto-approves remote peer requests. If no provider matches, denies (fail-closed).
func (*CompositeProvider) SetP2PFallback ¶
func (c *CompositeProvider) SetP2PFallback(p Provider)
SetP2PFallback sets a dedicated approval provider for P2P sessions. P2P sessions are never routed to the TTY fallback when it is a HeadlessProvider, preventing remote peers from auto-approving.
func (*CompositeProvider) SetTTYFallback ¶
func (c *CompositeProvider) SetTTYFallback(p Provider)
SetTTYFallback sets the TTY provider used when no other provider matches.
type DisplayTier ¶ added in v0.7.0
type DisplayTier int
DisplayTier determines how an approval request is rendered in the TUI.
const ( // TierInline renders a compact single-line strip. TierInline DisplayTier = 1 // TierFullscreen renders a fullscreen overlay with diff preview. TierFullscreen DisplayTier = 2 )
func ClassifyTier ¶ added in v0.7.0
func ClassifyTier(safetyLevel, category, activity string) DisplayTier
ClassifyTier determines the display tier for an approval request based on safety level, category, and activity. Fullscreen is used when the tool is dangerous AND targets filesystem/automation or performs execute/write.
type Error ¶ added in v0.7.0
Error carries structured approval failure metadata while preserving a sentinel cause.
type GatewayApprover ¶
type GatewayApprover interface {
HasCompanions() bool
RequestApproval(ctx context.Context, message string) (ApprovalResponse, error)
}
GatewayApprover abstracts the gateway.Server methods needed for approval.
type GatewayProvider ¶
type GatewayProvider struct {
// contains filtered or unexported fields
}
GatewayProvider routes approval requests to companion apps via WebSocket.
func NewGatewayProvider ¶
func NewGatewayProvider(gw GatewayApprover) *GatewayProvider
NewGatewayProvider creates a GatewayProvider backed by the given gateway.
func (*GatewayProvider) CanHandle ¶
func (g *GatewayProvider) CanHandle(_ string) bool
CanHandle returns true when at least one companion is connected.
func (*GatewayProvider) Name ¶ added in v0.7.0
func (g *GatewayProvider) Name() string
func (*GatewayProvider) RequestApproval ¶
func (g *GatewayProvider) RequestApproval(ctx context.Context, req ApprovalRequest) (ApprovalResponse, error)
RequestApproval sends the approval request to connected companions.
type GrantStore ¶
type GrantStore struct {
// contains filtered or unexported fields
}
GrantStore tracks per-session, per-tool "always allow" grants in memory. Grants are cleared on application restart (no persistence). An optional TTL causes grants to expire automatically.
func NewGrantStore ¶
func NewGrantStore() *GrantStore
NewGrantStore creates an empty GrantStore with no TTL.
func (*GrantStore) CleanExpired ¶
func (s *GrantStore) CleanExpired() int
CleanExpired removes all grants that have exceeded the TTL. Returns the number of entries removed. No-op when TTL is zero.
func (*GrantStore) Grant ¶
func (s *GrantStore) Grant(sessionKey, toolName string)
Grant records an approval for the given session and tool.
func (*GrantStore) IsGranted ¶
func (s *GrantStore) IsGranted(sessionKey, toolName string) bool
IsGranted reports whether the tool has a valid (non-expired) grant.
func (*GrantStore) List ¶ added in v0.7.0
func (s *GrantStore) List() []GrantInfo
List returns all active (non-expired) grants sorted by session key then tool name. Expired grants are lazily cleaned before listing.
func (*GrantStore) Revoke ¶
func (s *GrantStore) Revoke(sessionKey, toolName string)
Revoke removes a single tool grant for the given session.
func (*GrantStore) RevokeSession ¶
func (s *GrantStore) RevokeSession(sessionKey string)
RevokeSession removes all grants for the given session.
func (*GrantStore) SetTTL ¶
func (s *GrantStore) SetTTL(ttl time.Duration)
SetTTL sets the time-to-live for grants. Zero disables expiry.
type HeadlessProvider ¶
type HeadlessProvider struct{}
HeadlessProvider auto-approves all tool execution requests. Intended for headless (Docker) environments where no TTY or companion is available. Every approval is logged at WARN level for audit.
func (*HeadlessProvider) CanHandle ¶
func (h *HeadlessProvider) CanHandle(_ string) bool
CanHandle always returns false. HeadlessProvider is used as a TTY fallback slot, not prefix-matched by session key.
func (*HeadlessProvider) Name ¶ added in v0.7.0
func (h *HeadlessProvider) Name() string
func (*HeadlessProvider) RequestApproval ¶
func (h *HeadlessProvider) RequestApproval(_ context.Context, req ApprovalRequest) (ApprovalResponse, error)
RequestApproval always approves and logs a warning for audit trail.
type HistoryEntry ¶ added in v0.7.0
type HistoryEntry struct {
Timestamp time.Time
RequestID string
ToolName string
SessionKey string
Summary string
SafetyLevel string
Outcome string // open set: "bypass", "granted", "denied", "timeout", "replay_blocked", "unavailable", etc.
Provider string
}
HistoryEntry records a single approval decision.
type HistoryStore ¶ added in v0.7.0
type HistoryStore struct {
// contains filtered or unexported fields
}
HistoryStore is an append-only in-memory store for approval decisions. It uses a ring buffer capped at maxSize entries. Safe for concurrent use.
func NewHistoryStore ¶ added in v0.7.0
func NewHistoryStore(maxSize int) *HistoryStore
NewHistoryStore creates a history store with the given capacity. When the capacity is reached, the oldest entry is evicted.
func (*HistoryStore) Append ¶ added in v0.7.0
func (s *HistoryStore) Append(entry HistoryEntry)
Append adds an entry. If at capacity, the oldest entry is evicted.
func (*HistoryStore) Count ¶ added in v0.7.0
func (s *HistoryStore) Count() int
Count returns the number of stored entries.
func (*HistoryStore) CountByOutcome ¶ added in v0.7.0
func (s *HistoryStore) CountByOutcome() map[string]int
CountByOutcome returns a map of outcome to count.
func (*HistoryStore) List ¶ added in v0.7.0
func (s *HistoryStore) List() []HistoryEntry
List returns all entries in newest-first order. The returned slice is a copy safe for concurrent use.
type Provider ¶
type Provider interface {
// RequestApproval sends an approval request and blocks until approved/denied or context is cancelled.
RequestApproval(ctx context.Context, req ApprovalRequest) (ApprovalResponse, error)
// CanHandle reports whether this provider can handle the given session key.
CanHandle(sessionKey string) bool
}
Provider defines the interface for approval request handling.
type RiskIndicator ¶ added in v0.7.0
type RiskIndicator struct {
Level string // "low", "moderate", "high", "critical"
Label string // human-readable description
}
RiskIndicator provides a human-readable risk assessment for an approval request.
func ComputeRisk ¶ added in v0.7.0
func ComputeRisk(safetyLevel, category string) RiskIndicator
ComputeRisk returns a risk indicator based on safety level and category.
type TTYProvider ¶
type TTYProvider struct{}
TTYProvider prompts the user via the terminal (stdin) for approval. CanHandle always returns false because TTY is a special fallback, not prefix-matched by session key.
func (*TTYProvider) CanHandle ¶
func (t *TTYProvider) CanHandle(_ string) bool
CanHandle always returns false. TTY is used as a fallback only.
func (*TTYProvider) Name ¶ added in v0.7.0
func (t *TTYProvider) Name() string
func (*TTYProvider) RequestApproval ¶
func (t *TTYProvider) RequestApproval(_ context.Context, req ApprovalRequest) (ApprovalResponse, error)
RequestApproval prompts the user on stderr and reads y/a/N from stdin. "y" or "yes" approves once; "a" or "always" approves and grants persistent access for the tool in this session.
type TurnApprovalEntry ¶ added in v0.7.0
type TurnApprovalState ¶ added in v0.7.0
type TurnApprovalState struct {
// contains filtered or unexported fields
}
func NewTurnApprovalState ¶ added in v0.7.0
func NewTurnApprovalState() *TurnApprovalState
func TurnApprovalStateFromContext ¶ added in v0.7.0
func TurnApprovalStateFromContext(ctx context.Context) *TurnApprovalState
func (*TurnApprovalState) Get ¶ added in v0.7.0
func (s *TurnApprovalState) Get(toolName string, params map[string]interface{}) (TurnApprovalEntry, bool, error)
func (*TurnApprovalState) Put ¶ added in v0.7.0
func (s *TurnApprovalState) Put(toolName string, params map[string]interface{}, entry TurnApprovalEntry) error
type TurnOutcome ¶ added in v0.7.0
type TurnOutcome string
const ( TurnOutcomeApproved TurnOutcome = "approved" TurnOutcomeDenied TurnOutcome = "denied" TurnOutcomeTimeout TurnOutcome = "timeout" )
func OutcomeFromError ¶ added in v0.7.0
func OutcomeFromError(err error) TurnOutcome