CRDs: Provider, Channel, ToolBinding, AgentConfig + resource refs

- Provider CRD: cluster-scoped, API key from Secrets, model lists with pricing
- Channel CRD: Discord/Slack/Telegram bindings with bot token refs
- ToolBinding CRD: per-tool risk tiers with pattern escalation for exec-like tools
- AgentConfig CRD: reusable config templates mirroring agents.defaults.*
- Agent: now references AgentConfig, ToolBinding, Channel, Provider by name
- AgentGroup: same resource refs + shared config inheritance
This commit is contained in:
cos Agent 2026-05-11 21:13:26 -07:00
parent 1a831bdfe5
commit 4bc4e0e442
6 changed files with 497 additions and 10 deletions

View file

@ -12,27 +12,48 @@ import (
// AgentSpec defines the desired state of an Agent.
type AgentSpec struct {
// AgentID is the OpenClaw agent identifier (e.g., "main", "engineering").
AgentID string `json:"agentId"`
// Model is the LLM model identifier (e.g., "deepseek-v4-pro:cloud").
Model string `json:"model"`
// +optional
Model string `json:"model,omitempty"`
// Thinking is the thinking level override (e.g., "off", "low", "high").
// +optional
Thinking string `json:"thinking,omitempty"`
// Workspace configures the agent workspace volume and config.
Workspace AgentWorkspace `json:"workspace"`
// AgentConfigRef references an AgentConfig template.
// +optional
AgentConfigRef string `json:"agentConfigRef,omitempty"`
// Channels configures the communication channels for this agent.
// Workspace configures the agent workspace volume and config.
// +optional
Workspace *AgentWorkspace `json:"workspace,omitempty"`
// Providers lists the provider references for this agent.
// +optional
Providers []string `json:"providers,omitempty"`
// ChannelRefs lists the Channel resources this agent should bind to.
// +optional
ChannelRefs []string `json:"channelRefs,omitempty"`
// Channels configures inline channel bindings (alternative to ChannelRefs).
// +optional
Channels []AgentChannel `json:"channels,omitempty"`
// ToolBindingRefs lists ToolBinding resources for this agent.
// +optional
ToolBindingRefs []string `json:"toolBindingRefs,omitempty"`
// Judge configures the Agent Judge authorization layer.
// +optional
Judge AgentJudgeConfig `json:"judge,omitempty"`
Judge *AgentJudgeConfig `json:"judge,omitempty"`
// Resources defines compute resource requirements.
// +optional
Resources ResourceRequirements `json:"resources,omitempty"`
Resources *ResourceRequirements `json:"resources,omitempty"`
// Image is the container image to run. Defaults to the Kubeclaw agent runner.
// +optional
@ -41,9 +62,6 @@ type AgentSpec struct {
// Replicas is the number of agent instances. Defaults to 1.
// +optional
Replicas *int32 `json:"replicas,omitempty"`
// AgentID is the OpenClaw agent identifier (e.g., "main", "engineering").
AgentID string `json:"agentId"`
}
// AgentWorkspace configures the workspace for the agent.

View file

@ -0,0 +1,154 @@
/*
Copyright 2025 Scott Scoble.
Licensed under the MIT License.
*/
package v1alpha1
import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
// AgentConfigSpec mirrors OpenClaw's agents.defaults.* configuration block.
// It's a reusable template that Agent resources can reference.
type AgentConfigSpec struct {
// Model is the default LLM model.
// +optional
Model string `json:"model,omitempty"`
// Thinking is the thinking level (off/low/high).
// +optional
Thinking string `json:"thinking,omitempty"`
// Workspace configures the agent workspace.
// +optional
Workspace *WorkspaceConfig `json:"workspace,omitempty"`
// MemorySearch configures memory search behavior.
// +optional
MemorySearch *MemorySearchConfig `json:"memorySearch,omitempty"`
// Heartbeat configures the agent heartbeat.
// +optional
Heartbeat *HeartbeatConfig `json:"heartbeat,omitempty"`
// Skills configures skill loading.
// +optional
Skills *SkillsConfig `json:"skills,omitempty"`
// Sandbox configures the execution sandbox.
// +optional
Sandbox *SandboxConfig `json:"sandbox,omitempty"`
// Tools controls tool availability.
// +optional
Tools *ToolsConfig `json:"tools,omitempty"`
}
// WorkspaceConfig mirrors the workspace directory config.
type WorkspaceConfig struct {
// Dir is the workspace root directory.
Dir string `json:"dir"`
}
// MemorySearchConfig mirrors memory search defaults.
type MemorySearchConfig struct {
// Enabled controls memory search availability.
// +optional
Enabled bool `json:"enabled,omitempty"`
// MaxResults limits search results.
// +optional
MaxResults int `json:"maxResults,omitempty"`
// MinScore is the minimum relevance score.
// +optional
MinScore float64 `json:"minScore,omitempty"`
}
// HeartbeatConfig mirrors the heartbeat configuration.
type HeartbeatConfig struct {
// Enabled controls the heartbeat.
// +optional
Enabled bool `json:"enabled,omitempty"`
// Interval is the heartbeat check interval.
// +optional
Interval string `json:"interval,omitempty"`
// Prompt is the heartbeat trigger prompt.
// +optional
Prompt string `json:"prompt,omitempty"`
}
// SkillsConfig mirrors skill loading config.
type SkillsConfig struct {
// Allowed is a list of allowed skill IDs.
// +optional
Allowed []string `json:"allowed,omitempty"`
// Denied is a list of denied skill IDs.
// +optional
Denied []string `json:"denied,omitempty"`
}
// SandboxConfig mirrors the sandbox configuration.
type SandboxConfig struct {
// Enabled controls sandbox containment.
// +optional
Enabled bool `json:"enabled,omitempty"`
// AllowedExec is a list of allowed executable paths.
// +optional
AllowedExec []string `json:"allowedExec,omitempty"`
}
// ToolsConfig mirrors tool configuration.
type ToolsConfig struct {
// Allow controls tool allowlist.
// +optional
Allow []string `json:"allow,omitempty"`
// Deny controls tool denylist.
// +optional
Deny []string `json:"deny,omitempty"`
}
// AgentConfigStatus defines the observed state of an AgentConfig.
type AgentConfigStatus struct {
// Ready indicates the template is valid.
Ready bool `json:"ready"`
// Validated indicates the config was validated.
Validated bool `json:"validated"`
}
// +kubebuilder:object:root=true
// +kubebuilder:subresource:status
// +kubebuilder:resource:scope=Namespaced
// +kubebuilder:printcolumn:name="DefaultModel",type=string,JSONPath=`.spec.model`
// +kubebuilder:printcolumn:name="Ready",type=boolean,JSONPath=`.status.ready`
// +kubebuilder:printcolumn:name="Age",type=date,JSONPath=`.metadata.creationTimestamp`
// AgentConfig is a reusable configuration template for agents.
type AgentConfig struct {
metav1.TypeMeta `json:",inline"`
metav1.ObjectMeta `json:"metadata,omitempty"`
Spec AgentConfigSpec `json:"spec,omitempty"`
Status AgentConfigStatus `json:"status,omitempty"`
}
// +kubebuilder:object:root=true
// AgentConfigList contains a list of AgentConfig.
type AgentConfigList struct {
metav1.TypeMeta `json:",inline"`
metav1.ListMeta `json:"metadata,omitempty"`
Items []AgentConfig `json:"items"`
}
func init() {
SchemeBuilder.Register(&AgentConfig{}, &AgentConfigList{})
}

View file

@ -15,6 +15,18 @@ type AgentGroupSpec struct {
// Agents lists the agents in this group.
Agents []AgentGroupMember `json:"agents"`
// AgentConfigRef references an AgentConfig template for all members.
// +optional
AgentConfigRef string `json:"agentConfigRef,omitempty"`
// ProviderRefs lists Provider resources for the group.
// +optional
ProviderRefs []string `json:"providerRefs,omitempty"`
// ChannelRefs lists Channel resources for the group.
// +optional
ChannelRefs []string `json:"channelRefs,omitempty"`
// SharedWorkspace references a ConfigMap with shared workspace config.
// +optional
SharedWorkspaceConfigMap string `json:"sharedWorkspaceConfigMap,omitempty"`
@ -27,9 +39,13 @@ type AgentGroupSpec struct {
// +optional
JudgePolicyRef string `json:"judgePolicyRef,omitempty"`
// ToolBindingRefs lists ToolBinding resources for the group.
// +optional
ToolBindingRefs []string `json:"toolBindingRefs,omitempty"`
// NetworkPolicy controls inter-agent communication.
// +optional
NetworkPolicy NetworkPolicySpec `json:"networkPolicy,omitempty"`
NetworkPolicy *NetworkPolicySpec `json:"networkPolicy,omitempty"`
}
// AgentGroupMember defines a member of an AgentGroup.

View file

@ -0,0 +1,84 @@
/*
Copyright 2025 Scott Scoble.
Licensed under the MIT License.
*/
package v1alpha1
import (
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
// ChannelSpec defines a communication channel binding.
type ChannelSpec struct {
// Type is the channel platform (e.g., "discord", "slack", "telegram", "signal").
Type string `json:"type"`
// Enabled controls whether this channel is active.
// +optional
Enabled bool `json:"enabled,omitempty"`
// BotTokenSecretRef references a Secret containing the bot token.
BotTokenSecretRef *corev1.SecretKeySelector `json:"botTokenSecretRef"`
// WebhookURL is the incoming webhook URL (for outbound-only channels).
// +optional
WebhookURL string `json:"webhookUrl,omitempty"`
// GuildID is the Discord/Telegram group identifier.
// +optional
GuildID string `json:"guildId,omitempty"`
// AdditionalConfig is platform-specific configuration passed as-is.
// +optional
AdditionalConfig map[string]string `json:"additionalConfig,omitempty"`
}
// ChannelStatus defines the observed state of a Channel.
type ChannelStatus struct {
// Ready is true if the channel connection is established.
Ready bool `json:"ready"`
// ConnectionState is the current connection state.
// +optional
ConnectionState string `json:"connectionState,omitempty"`
// LastConnected is when the channel was last successfully connected.
// +optional
LastConnected *metav1.Time `json:"lastConnected,omitempty"`
// ConnectedAgents lists agents currently bound to this channel.
// +optional
ConnectedAgents []string `json:"connectedAgents,omitempty"`
}
// +kubebuilder:object:root=true
// +kubebuilder:subresource:status
// +kubebuilder:resource:scope=Namespaced
// +kubebuilder:printcolumn:name="Type",type=string,JSONPath=`.spec.type`
// +kubebuilder:printcolumn:name="Ready",type=boolean,JSONPath=`.status.ready`
// +kubebuilder:printcolumn:name="Age",type=date,JSONPath=`.metadata.creationTimestamp`
// Channel is the Schema for the channels API.
type Channel struct {
metav1.TypeMeta `json:",inline"`
metav1.ObjectMeta `json:"metadata,omitempty"`
Spec ChannelSpec `json:"spec,omitempty"`
Status ChannelStatus `json:"status,omitempty"`
}
// +kubebuilder:object:root=true
// ChannelList contains a list of Channel.
type ChannelList struct {
metav1.TypeMeta `json:",inline"`
metav1.ListMeta `json:"metadata,omitempty"`
Items []Channel `json:"items"`
}
func init() {
SchemeBuilder.Register(&Channel{}, &ChannelList{})
}

View file

@ -0,0 +1,100 @@
/*
Copyright 2025 Scott Scoble.
Licensed under the MIT License.
*/
package v1alpha1
import (
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
// ProviderSpec defines an LLM provider configuration.
type ProviderSpec struct {
// Type is the provider type (e.g., "ollama", "openai", "anthropic", "deepseek").
Type string `json:"type"`
// BaseURL is the provider API endpoint.
// +optional
BaseURL string `json:"baseUrl,omitempty"`
// APIKeySecretRef references a Secret key containing the API key.
// +optional
APIKeySecretRef *corev1.SecretKeySelector `json:"apiKeySecretRef,omitempty"`
// Models is the list of model identifiers available through this provider.
// +optional
Models []ProviderModel `json:"models,omitempty"`
// ExtraHeaders are additional HTTP headers for provider requests.
// +optional
ExtraHeaders map[string]string `json:"extraHeaders,omitempty"`
}
// ProviderModel defines a single model within a provider.
type ProviderModel struct {
// ID is the model identifier (e.g., "deepseek-v4-pro:cloud").
ID string `json:"id"`
// Default is true if this is the default model for the provider.
// +optional
Default bool `json:"default,omitempty"`
// Cost configures pricing for this model.
// +optional
Cost *ModelCost `json:"cost,omitempty"`
}
// ModelCost defines pricing for a model.
type ModelCost struct {
// Input is the cost per million input tokens.
Input float64 `json:"input"`
// Output is the cost per million output tokens.
Output float64 `json:"output"`
}
// ProviderStatus defines the observed state of a Provider.
type ProviderStatus struct {
// Ready is true if the provider is reachable and configured.
Ready bool `json:"ready"`
// Condition is the current condition of the provider.
// +optional
Condition string `json:"condition,omitempty"`
// LastChecked is when the provider was last validated.
// +optional
LastChecked *metav1.Time `json:"lastChecked,omitempty"`
}
// +kubebuilder:object:root=true
// +kubebuilder:subresource:status
// +kubebuilder:resource:scope=Cluster
// +kubebuilder:printcolumn:name="Type",type=string,JSONPath=`.spec.type`
// +kubebuilder:printcolumn:name="Ready",type=boolean,JSONPath=`.status.ready`
// +kubebuilder:printcolumn:name="Age",type=date,JSONPath=`.metadata.creationTimestamp`
// Provider is the Schema for the providers API.
type Provider struct {
metav1.TypeMeta `json:",inline"`
metav1.ObjectMeta `json:"metadata,omitempty"`
Spec ProviderSpec `json:"spec,omitempty"`
Status ProviderStatus `json:"status,omitempty"`
}
// +kubebuilder:object:root=true
// ProviderList contains a list of Provider.
type ProviderList struct {
metav1.TypeMeta `json:",inline"`
metav1.ListMeta `json:"metadata,omitempty"`
Items []Provider `json:"items"`
}
func init() {
SchemeBuilder.Register(&Provider{}, &ProviderList{})
}

View file

@ -0,0 +1,115 @@
/*
Copyright 2025 Scott Scoble.
Licensed under the MIT License.
*/
package v1alpha1
import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
// ToolBindingSpec defines which tools an agent can use and their risk tiers.
type ToolBindingSpec struct {
// Tools is a list of tool access rules.
Tools []ToolRule `json:"tools"`
// DefaultTier is the risk tier for tools not explicitly listed.
// +optional
// +kubebuilder:validation:Enum=no-judge;lightweight-approval;full-judge-review;human-only;denied
DefaultTier string `json:"defaultTier,omitempty"`
// TargetAgentRefs lists agents or agent groups this binding applies to.
// If empty, applies cluster-wide.
// +optional
TargetAgentRefs []TargetRef `json:"targetAgentRefs,omitempty"`
}
// ToolRule defines access rules for a single tool or tool pattern.
type ToolRule struct {
// Name is the tool name or glob pattern (e.g., "exec", "read", "web_*").
Name string `json:"name"`
// Tier is the risk tier for this tool.
// +kubebuilder:validation:Enum=no-judge;lightweight-approval;full-judge-review;human-only;denied
Tier string `json:"tier"`
// Patterns are inline script escalation patterns for exec-like tools.
// +optional
Patterns []RiskPattern `json:"patterns,omitempty"`
// Reason is a human-readable explanation for this rule.
// +optional
Reason string `json:"reason,omitempty"`
}
// RiskPattern defines a pattern to watch for within a tool's parameters.
type RiskPattern struct {
// Name is a descriptive name (e.g., "inline-script-execution").
Name string `json:"name"`
// Pattern is the match pattern (glob or regex).
Pattern string `json:"pattern"`
// EscalateTier is the tier to escalate to when matched.
// +kubebuilder:validation:Enum=lightweight-approval;full-judge-review;human-only
EscalateTier string `json:"escalateTier"`
}
// TargetRef identifies a target resource.
type TargetRef struct {
// Kind is the resource kind (Agent, AgentGroup, or empty for all).
// +optional
// +kubebuilder:validation:Enum=Agent;AgentGroup
Kind string `json:"kind,omitempty"`
// Name is the resource name.
Name string `json:"name"`
// Namespace scopes the target (empty = all namespaces).
// +optional
Namespace string `json:"namespace,omitempty"`
}
// ToolBindingStatus defines the observed state of a ToolBinding.
type ToolBindingStatus struct {
// Ready indicates the binding is active and enforced.
Ready bool `json:"ready"`
// BoundAgents is the count of agents currently using this binding.
BoundAgents int `json:"boundAgents,omitempty"`
// LastUpdated is when this binding was last reconciled.
// +optional
LastUpdated *metav1.Time `json:"lastUpdated,omitempty"`
}
// +kubebuilder:object:root=true
// +kubebuilder:subresource:status
// +kubebuilder:resource:scope=Cluster
// +kubebuilder:printcolumn:name="DefaultTier",type=string,JSONPath=`.spec.defaultTier`
// +kubebuilder:printcolumn:name="Ready",type=boolean,JSONPath=`.status.ready`
// +kubebuilder:printcolumn:name="Age",type=date,JSONPath=`.metadata.creationTimestamp`
// ToolBinding is the Schema for the toolbindings API.
type ToolBinding struct {
metav1.TypeMeta `json:",inline"`
metav1.ObjectMeta `json:"metadata,omitempty"`
Spec ToolBindingSpec `json:"spec,omitempty"`
Status ToolBindingStatus `json:"status,omitempty"`
}
// +kubebuilder:object:root=true
// ToolBindingList contains a list of ToolBinding.
type ToolBindingList struct {
metav1.TypeMeta `json:",inline"`
metav1.ListMeta `json:"metadata,omitempty"`
Items []ToolBinding `json:"items"`
}
func init() {
SchemeBuilder.Register(&ToolBinding{}, &ToolBindingList{})
}