approval

package
v0.7.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Apr 13, 2026 License: MIT Imports: 14 Imported by: 0

Documentation

Overview

Package approval provides a unified interface for tool execution approval across multiple channels (Gateway WebSocket, Telegram, Discord, Slack, TTY).

Index

Constants

View Source
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

View Source
var (
	ErrDenied      = errors.New("approval denied")
	ErrTimeout     = errors.New("approval timeout")
	ErrUnavailable = errors.New("approval unavailable")
)

Functions

func ApprovalTargetFromContext

func ApprovalTargetFromContext(ctx context.Context) string

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 FormatToolExecutionError(toolName string, kind error) error

func ProviderFromError added in v0.7.0

func ProviderFromError(err error) string

func RequestIDFromError added in v0.7.0

func RequestIDFromError(err error) string

func TurnApprovalKey added in v0.7.0

func TurnApprovalKey(toolName string, params map[string]interface{}) (string, string, error)

func WithApprovalTarget

func WithApprovalTarget(ctx context.Context, target string) context.Context

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

func WrapError added in v0.7.0

func WrapError(kind error, provider, requestID, message string) error

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

type ApprovalResponse struct {
	Approved    bool
	AlwaysAllow bool
	Provider    string
}

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

type Error struct {
	Kind      error
	Provider  string
	RequestID string
	Message   string
}

Error carries structured approval failure metadata while preserving a sentinel cause.

func (*Error) Error added in v0.7.0

func (e *Error) Error() string

func (*Error) Unwrap added in v0.7.0

func (e *Error) Unwrap() error

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 GrantInfo added in v0.7.0

type GrantInfo struct {
	SessionKey string
	ToolName   string
	GrantedAt  time.Time
}

GrantInfo represents a single active grant for display purposes.

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 TurnApprovalEntry struct {
	Outcome    TurnOutcome
	Provider   string
	RequestID  string
	Summary    string
	ParamsHash string
	Timeouts   int
}

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"
	TurnOutcomeUnavailable TurnOutcome = "unavailable"
)

func OutcomeFromError added in v0.7.0

func OutcomeFromError(err error) TurnOutcome

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL