Documentation
¶
Index ¶
- Constants
- Variables
- func ExtractToolName(summary string) string
- func ValidStatus(s Status) bool
- type ActionFunc
- type ActionKind
- type AutoApproveRule
- type AutoApproveScope
- type AutoApproveStore
- type CallbackAction
- type Handler
- type Manager
- func (m *Manager) AddPermanentRule(ctx context.Context, agentName, toolName, createdBy string) (*AutoApproveRule, error)
- func (m *Manager) AddSessionRule(agentName, toolName, conversationID, createdBy string)
- func (m *Manager) ClearSessionRules(conversationID string)
- func (m *Manager) ExpirePending(ctx context.Context) (int, error)
- func (m *Manager) Get(ctx context.Context, id string) (*Request, error)
- func (m *Manager) GetByCallbackData(ctx context.Context, callbackData string) (*Request, error)
- func (m *Manager) List(ctx context.Context, status Status) ([]Request, error)
- func (m *Manager) ListAutoApproveRules(ctx context.Context, agentName string) ([]AutoApproveRule, error)
- func (m *Manager) RemoveAutoApproveRule(ctx context.Context, id string) error
- func (m *Manager) Resolve(ctx context.Context, id string, approved bool, resolvedBy string) (*Request, error)
- func (m *Manager) ResolveByCallback(ctx context.Context, callbackData string, resolvedBy string) (*Request, error)
- func (m *Manager) ShouldAutoApprove(ctx context.Context, agentName, toolName, conversationID string) (bool, AutoApproveScope)
- func (m *Manager) StartExpiryWorker(ctx context.Context, interval time.Duration)
- func (m *Manager) Submit(ctx context.Context, agentName string, kind ActionKind, summary string, ...) (*Request, error)
- func (m *Manager) SubmitAndWait(ctx context.Context, agentName string, kind ActionKind, summary string, ...) (Status, *Request, error)
- func (m *Manager) WaitForResolution(ctx context.Context, id string) Status
- type Registry
- type Request
- type SQLiteStore
- func (s *SQLiteStore) Close() error
- func (s *SQLiteStore) Create(ctx context.Context, req Request) (string, error)
- func (s *SQLiteStore) CreateAutoApproveRule(ctx context.Context, rule AutoApproveRule) (string, error)
- func (s *SQLiteStore) DeleteAutoApproveRule(ctx context.Context, id string) error
- func (s *SQLiteStore) ExpireBefore(ctx context.Context, deadline time.Time) (int, error)
- func (s *SQLiteStore) ExpirePending(ctx context.Context) (int, error)
- func (s *SQLiteStore) Get(ctx context.Context, id string) (*Request, error)
- func (s *SQLiteStore) GetByCallbackData(ctx context.Context, callbackData string) (*Request, error)
- func (s *SQLiteStore) List(ctx context.Context, status Status) ([]Request, error)
- func (s *SQLiteStore) ListAutoApproveRules(ctx context.Context, agentName string) ([]AutoApproveRule, error)
- func (s *SQLiteStore) MatchAutoApproveRule(ctx context.Context, agentName, toolName string) (*AutoApproveRule, error)
- func (s *SQLiteStore) Resolve(ctx context.Context, id string, status Status, resolvedBy string) error
- func (s *SQLiteStore) ResolveByCallbackPrefix(ctx context.Context, prefix string, status Status, resolvedBy string) (*Request, error)
- type Status
- type Store
Constants ¶
const DefaultTTL = 24 * time.Hour
DefaultTTL is the time an approval request stays pending before it is automatically expired by the background worker.
Variables ¶
var ( ErrNotFound = errors.New("approval: not found") ErrAlreadyResolved = errors.New("approval: already resolved") )
Sentinel errors returned by Store implementations.
var ErrStaleCallback = fmt.Errorf("approval: callback refers to a non-pending request")
ErrStaleCallback is returned by ResolveByCallback when the callback refers to an approval that exists but is no longer pending (already resolved, expired, or approved). The caller should surface its Status to the user.
Functions ¶
func ExtractToolName ¶ added in v0.15.1
ExtractToolName parses the tool name from an approval summary. Expected format: `Execute tool "toolname" with args: ...`
func ValidStatus ¶
ValidStatus reports whether s is one of the four known status values. An empty string is also accepted (means "all" in list queries).
Types ¶
type ActionFunc ¶
ActionFunc is the callback invoked when an approval is resolved. It receives the stored payload and performs the approved action.
type ActionKind ¶
type ActionKind string
ActionKind categorises what kind of action is awaiting approval.
const ( // ActionKindUserUpdate is a request to update the agent's USER.md persona file. ActionKindUserUpdate ActionKind = "user_update" // ActionKindSoulUpdate is a request to update the agent's SOUL.md persona file. ActionKindSoulUpdate ActionKind = "soul_update" // ActionKindIdentityUpdate is a request to update the agent's IDENTITY.md persona file. ActionKindIdentityUpdate ActionKind = "identity_update" // ActionKindCreateSkill is a request to create a new skill file in the agent's skills directory. ActionKindCreateSkill ActionKind = "create_skill" // ActionKindModifySchedule is a request to register a new schedule entry at runtime. ActionKindModifySchedule ActionKind = "modify_schedule" // ActionKindInstallTool is a request to add or remove an MCP tool or plugin at runtime. ActionKindInstallTool ActionKind = "install_tool" // ActionKindModifyConfig is a request to change a runtime configuration setting (e.g. fallback rules). ActionKindModifyConfig ActionKind = "modify_config" // ActionKindBrowserProfile is a request to clear or delete a browser profile. ActionKindBrowserProfile ActionKind = "browser_profile" // ActionKindToolCall is a request to execute an MCP tool call (supervised tier). ActionKindToolCall ActionKind = "tool_call" )
type AutoApproveRule ¶ added in v0.15.1
type AutoApproveRule struct {
ID string `db:"id" json:"id"`
AgentName string `db:"agent_name" json:"agent_name"`
ToolName string `db:"tool_name" json:"tool_name"`
Scope AutoApproveScope `db:"scope" json:"scope"`
ConversationID string `db:"conversation_id" json:"conversation_id,omitempty"`
CreatedAt time.Time `db:"created_at" json:"created_at"`
CreatedBy string `db:"created_by" json:"created_by"`
}
AutoApproveRule is a rule that allows a specific tool to bypass the approval workflow for a given agent. Session-scoped rules are held in memory; permanent rules are persisted in SQLite.
type AutoApproveScope ¶ added in v0.15.1
type AutoApproveScope string
AutoApproveScope identifies where an auto-approve rule originated.
const ( // ScopeSession is an ephemeral in-memory rule scoped to a conversation. ScopeSession AutoApproveScope = "session" // ScopePermanent is a persisted rule scoped to an agent (survives restarts). ScopePermanent AutoApproveScope = "permanent" )
type AutoApproveStore ¶ added in v0.15.1
type AutoApproveStore interface {
CreateAutoApproveRule(ctx context.Context, rule AutoApproveRule) (string, error)
DeleteAutoApproveRule(ctx context.Context, id string) error
ListAutoApproveRules(ctx context.Context, agentName string) ([]AutoApproveRule, error)
MatchAutoApproveRule(ctx context.Context, agentName, toolName string) (*AutoApproveRule, error)
}
AutoApproveStore defines the persistence interface for permanent auto-approve rules.
type CallbackAction ¶ added in v0.15.1
type CallbackAction string
CallbackAction identifies what a callback button requested.
const ( CallbackApprove CallbackAction = "approve" CallbackDeny CallbackAction = "deny" CallbackApproveSession CallbackAction = "approve_session" CallbackApproveAlways CallbackAction = "approve_always" )
type Handler ¶
type Handler struct {
// contains filtered or unexported fields
}
Handler implements the adapter.CallbackResolver interface for approval callbacks. It maps Telegram inline keyboard button data (e.g. "appr:{id}:approve") to human-readable confirmation strings by delegating resolution to the Manager.
It satisfies adapter.CallbackResolver without importing that package:
var _ adapter.CallbackResolver = (*Handler)(nil)
func NewCallbackHandler ¶
NewCallbackHandler returns a Handler that resolves approval callbacks via m.
type Manager ¶
type Manager struct {
// contains filtered or unexported fields
}
Manager coordinates the persistent Store with the in-memory action Registry. It is the primary API used by the Engine and REST API server.
func NewManager ¶
NewManager creates a Manager backed by the given store. The store must also implement AutoApproveStore for auto-approve support.
func (*Manager) AddPermanentRule ¶ added in v0.15.1
func (m *Manager) AddPermanentRule(ctx context.Context, agentName, toolName, createdBy string) (*AutoApproveRule, error)
AddPermanentRule creates a persistent auto-approve rule for the given agent+tool.
func (*Manager) AddSessionRule ¶ added in v0.15.1
AddSessionRule creates an ephemeral auto-approve rule for the current conversation.
func (*Manager) ClearSessionRules ¶ added in v0.15.1
ClearSessionRules removes all session-scoped auto-approve rules for a conversation.
func (*Manager) ExpirePending ¶
ExpirePending expires all pending approvals. Call at startup.
func (*Manager) GetByCallbackData ¶
GetByCallbackData fetches an approval by its callback_data prefix regardless of status. Used to provide informative feedback when a user clicks an already-resolved or expired Telegram button.
func (*Manager) ListAutoApproveRules ¶ added in v0.15.1
func (m *Manager) ListAutoApproveRules(ctx context.Context, agentName string) ([]AutoApproveRule, error)
ListAutoApproveRules returns all auto-approve rules for the given agent. Pass "" for all agents. Combines permanent (DB) and session (in-memory) rules.
func (*Manager) RemoveAutoApproveRule ¶ added in v0.15.1
RemoveAutoApproveRule deletes a permanent auto-approve rule by ID.
func (*Manager) Resolve ¶
func (m *Manager) Resolve(ctx context.Context, id string, approved bool, resolvedBy string) (*Request, error)
Resolve marks an approval as approved or denied and, if approved, invokes the registered action closure. Returns the updated Request.
func (*Manager) ResolveByCallback ¶
func (m *Manager) ResolveByCallback(ctx context.Context, callbackData string, resolvedBy string) (*Request, error)
ResolveByCallback parses the full Telegram callback data string ("appr:{id}:approve", ":deny", ":approve_session", ":approve_always"), resolves the approval, and optionally creates an auto-approve rule. Returns ErrNotFound for unknown callbacks, ErrStaleCallback when the approval is no longer pending.
func (*Manager) ShouldAutoApprove ¶ added in v0.15.1
func (m *Manager) ShouldAutoApprove(ctx context.Context, agentName, toolName, conversationID string) (bool, AutoApproveScope)
ShouldAutoApprove checks whether the given tool call should be auto-approved. It checks session rules (in-memory) first, then permanent rules (DB). Returns (true, scope) on match, (false, "") on no match.
func (*Manager) StartExpiryWorker ¶
StartExpiryWorker starts a background goroutine that expires pending approvals whose TTL has elapsed. It ticks every interval until ctx is cancelled. Expired closures are removed from the in-memory registry. Safe to call once per process lifetime.
func (*Manager) Submit ¶
func (m *Manager) Submit( ctx context.Context, agentName string, kind ActionKind, summary string, payload string, externalID string, adapterName string, conversationID string, action ActionFunc, ) (*Request, error)
Submit creates a new pending approval, registers the action closure, and returns the persisted Request with its ID populated. The request expires after DefaultTTL if not resolved.
func (*Manager) SubmitAndWait ¶ added in v0.15.1
func (m *Manager) SubmitAndWait( ctx context.Context, agentName string, kind ActionKind, summary string, payload string, externalID string, adapterName string, conversationID string, ) (Status, *Request, error)
SubmitAndWait submits an approval request and blocks until it is resolved. The returned status is StatusApproved or StatusDenied. If the context is cancelled before resolution, StatusExpired is returned. Unlike Submit, the action closure is a no-op — the caller is expected to take action based on the returned status.
type Registry ¶
type Registry struct {
// contains filtered or unexported fields
}
Registry holds in-memory action closures keyed by approval ID. These are ephemeral: on restart the registry is empty, and ExpirePending ensures no stale DB entries are left in "pending" state.
func (*Registry) Pop ¶
func (r *Registry) Pop(id string) (ActionFunc, bool)
Pop retrieves and removes the action for the given ID atomically. Returns (nil, false) if no action is registered for that ID.
func (*Registry) Register ¶
func (r *Registry) Register(id string, fn ActionFunc)
Register stores an action closure under the given ID.
type Request ¶
type Request struct {
ID string `db:"id" json:"id"`
AgentName string `db:"agent_name" json:"agent_name"`
Kind ActionKind `db:"kind" json:"kind"`
Status Status `db:"status" json:"status"`
// Summary is a human-readable one-liner shown in the approval UI.
Summary string `db:"summary" json:"summary"`
// Payload is the content to apply when approved (e.g. full USER.md text).
Payload string `db:"payload" json:"payload"`
// CallbackData is the base prefix embedded in Telegram inline button data.
// Format: "appr:{id}" — buttons append ":approve" or ":deny".
CallbackData string `db:"callback_data" json:"callback_data,omitempty"`
// ExternalID is the adapter-level chat/channel ID to reply to after resolution.
ExternalID string `db:"external_id" json:"external_id"`
// AdapterName identifies which adapter to use for confirmation messages.
AdapterName string `db:"adapter_name" json:"adapter_name"`
// ConversationID links this approval to the engine conversation that created it.
ConversationID string `db:"conversation_id" json:"conversation_id"`
CreatedAt time.Time `db:"created_at" json:"created_at"`
ExpiresAt *time.Time `db:"expires_at" json:"expires_at,omitempty"`
ResolvedAt *time.Time `db:"resolved_at" json:"resolved_at,omitempty"`
// ResolvedBy records who resolved the approval: "telegram", "api", or "expired".
ResolvedBy string `db:"resolved_by" json:"resolved_by,omitempty"`
}
Request is the persisted record of a pending or resolved approval.
type SQLiteStore ¶
type SQLiteStore struct {
// contains filtered or unexported fields
}
SQLiteStore implements Store using SQLite.
func NewInMemoryStore ¶
func NewInMemoryStore() (*SQLiteStore, error)
NewInMemoryStore creates an in-memory SQLite approval store (for testing).
func NewSQLiteStore ¶
func NewSQLiteStore(dbPath string) (*SQLiteStore, error)
NewSQLiteStore opens or creates a SQLite database at the given path and applies the approval schema. The file is opened with WAL mode so it can coexist with the memory store's connection to the same file.
func (*SQLiteStore) Close ¶
func (s *SQLiteStore) Close() error
func (*SQLiteStore) CreateAutoApproveRule ¶ added in v0.15.1
func (s *SQLiteStore) CreateAutoApproveRule(ctx context.Context, rule AutoApproveRule) (string, error)
func (*SQLiteStore) DeleteAutoApproveRule ¶ added in v0.15.1
func (s *SQLiteStore) DeleteAutoApproveRule(ctx context.Context, id string) error
func (*SQLiteStore) ExpireBefore ¶
func (*SQLiteStore) ExpirePending ¶
func (s *SQLiteStore) ExpirePending(ctx context.Context) (int, error)
func (*SQLiteStore) GetByCallbackData ¶
func (*SQLiteStore) ListAutoApproveRules ¶ added in v0.15.1
func (s *SQLiteStore) ListAutoApproveRules(ctx context.Context, agentName string) ([]AutoApproveRule, error)
func (*SQLiteStore) MatchAutoApproveRule ¶ added in v0.15.1
func (s *SQLiteStore) MatchAutoApproveRule(ctx context.Context, agentName, toolName string) (*AutoApproveRule, error)
func (*SQLiteStore) ResolveByCallbackPrefix ¶
type Store ¶
type Store interface {
// Create persists a new approval request and returns the assigned ID.
Create(ctx context.Context, req Request) (string, error)
// Get fetches a single approval by ID. Returns ErrNotFound if absent.
Get(ctx context.Context, id string) (*Request, error)
// GetByCallbackData fetches a single approval by its callback_data prefix,
// regardless of status. Returns ErrNotFound if absent.
GetByCallbackData(ctx context.Context, callbackData string) (*Request, error)
// List returns approvals filtered by status. Pass an empty string for all.
List(ctx context.Context, status Status) ([]Request, error)
// Resolve transitions the status of a pending approval.
// Returns ErrNotFound if the ID does not exist.
// Returns ErrAlreadyResolved if the approval is not currently pending.
Resolve(ctx context.Context, id string, status Status, resolvedBy string) error
// ResolveByCallbackPrefix looks up by callback_data prefix, then resolves.
// Returns ErrNotFound if no pending approval matches.
ResolveByCallbackPrefix(ctx context.Context, prefix string, status Status, resolvedBy string) (*Request, error)
// ExpirePending marks all pending approvals as expired. Call at startup.
// Returns the number of rows affected.
ExpirePending(ctx context.Context) (int, error)
// ExpireBefore marks all pending approvals whose expires_at is before
// deadline as expired. Used by the background expiry worker.
// Returns the number of rows affected.
ExpireBefore(ctx context.Context, deadline time.Time) (int, error)
Close() error
}
Store defines the persistence interface for approval requests. In-memory action closures are managed separately by the Registry, since closures cannot be serialised. On restart, any pending rows are expired by ExpirePending so stale entries are never silently lost.