Files
pulse/internal/unifiedresources/capabilities.go
T
rcourtman ea5c105ff2 Log refused actions and name the missed command-agent lookup
A refused action plan, decision, or execution returned its 409 to the
client and left no trace in the server journal, so every remote report
of "Docker / Podman command agent is not connected" stalled on greps
that could never match anything. Refusals now log one warn line with
the resource, capability, and reason code, and the Docker command-agent
resolver reports which lookup missed (stale enrollment token binding vs
agent-id/hostname session), carried as an optional diagnostic detail on
the readiness contract and in the refusal envelope.

Refs #1728
2026-08-23 06:33:30 +01:00

88 lines
3.9 KiB
Go

package unifiedresources
// ActionApprovalLevel describes what kind of confirmation is required to execute a capability.
type ActionApprovalLevel string
const (
ApprovalNone ActionApprovalLevel = "none" // Safe to auto-execute without human
ApprovalDryRun ActionApprovalLevel = "dry_run_only" // AI can only plan/dry-run this action
ApprovalAdmin ActionApprovalLevel = "admin" // Requires an administrator confirmation
ApprovalMultiFactor ActionApprovalLevel = "mfa" // Requires strict explicit 2FA
)
// ActionAutoAuthorizationClass declares whether a capability may ever be
// authorized by core-owned Patrol policy instead of a human decision. This is
// independent from MinimumApprovalLevel: the approval floor remains the human
// fallback and audit classification, while this class is an explicit
// capability-owner opt-in to policy authorization. Empty and unknown values
// always mean never.
type ActionAutoAuthorizationClass string
const (
AutoAuthorizeNever ActionAutoAuthorizationClass = "never"
AutoAuthorizeLowRisk ActionAutoAuthorizationClass = "low_risk"
AutoAuthorizeElevated ActionAutoAuthorizationClass = "elevated"
)
func IsValidActionAutoAuthorizationClass(value ActionAutoAuthorizationClass) bool {
switch value {
case "", AutoAuthorizeNever, AutoAuthorizeLowRisk, AutoAuthorizeElevated:
return true
default:
return false
}
}
func NormalizeActionAutoAuthorizationClass(value ActionAutoAuthorizationClass) ActionAutoAuthorizationClass {
if value == "" || !IsValidActionAutoAuthorizationClass(value) {
return AutoAuthorizeNever
}
return value
}
// CapabilityType represents a standard unified intent or a vendor-specific escape hatch.
type CapabilityType string
const (
CapabilityTypeCommon CapabilityType = "common"
CapabilityTypeNative CapabilityType = "native"
)
// CapabilityParam defines the input schema for a capability.
type CapabilityParam struct {
Name string `json:"name"`
Type string `json:"type"` // e.g., "string", "boolean", "int"
Required bool `json:"required"`
Enum []string `json:"enum,omitempty"`
Pattern string `json:"pattern,omitempty"` // Regex validation
DefaultValue any `json:"defaultValue,omitempty"`
IsSensitive bool `json:"isSensitive"` // True = masked from persistent audit logs
Description string `json:"description,omitempty"`
}
// ResourceCapability defines a bounded safe action that can be performed against a resource.
type ResourceCapability struct {
Name string `json:"name"`
Type CapabilityType `json:"type"`
Description string `json:"description"`
MinimumApprovalLevel ActionApprovalLevel `json:"minimumApprovalLevel"` // Policy may escalate this
AutoAuthorization ActionAutoAuthorizationClass `json:"autoAuthorization,omitempty"` // Missing/never stays human-authorized
SupportsRollback bool `json:"supportsRollback,omitempty"` // True only when the executor declares a real recovery strategy
Platform string `json:"platform,omitempty"`
InternalHandler string `json:"-"` // DO NOT expose execution plumbing to public surfaces
Params []CapabilityParam `json:"params,omitempty"`
}
// ResourceActionReadiness carries backend-owned current executability state
// for a capability that may be known but unavailable right now.
type ResourceActionReadiness struct {
Name string `json:"name"`
Available bool `json:"available"`
ReasonCode string `json:"reasonCode,omitempty"`
Reason string `json:"reason,omitempty"`
// Detail names the concrete lookup or identity the refusal was judged
// against (which token, agent id, or hostname missed). It feeds server
// logs and error envelopes; Reason stays the stable user-facing copy.
Detail string `json:"detail,omitempty"`
}