diff --git a/api/v1alpha1/agent_types.go b/api/v1alpha1/agent_types.go index 9260c7c..c1f2933 100644 --- a/api/v1alpha1/agent_types.go +++ b/api/v1alpha1/agent_types.go @@ -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. diff --git a/api/v1alpha1/agentconfig_types.go b/api/v1alpha1/agentconfig_types.go new file mode 100644 index 0000000..fd5fdfb --- /dev/null +++ b/api/v1alpha1/agentconfig_types.go @@ -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{}) +} diff --git a/api/v1alpha1/agentgroup_types.go b/api/v1alpha1/agentgroup_types.go index 2a4d029..964fcf4 100644 --- a/api/v1alpha1/agentgroup_types.go +++ b/api/v1alpha1/agentgroup_types.go @@ -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. diff --git a/api/v1alpha1/channel_types.go b/api/v1alpha1/channel_types.go new file mode 100644 index 0000000..b61f1f2 --- /dev/null +++ b/api/v1alpha1/channel_types.go @@ -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{}) +} diff --git a/api/v1alpha1/provider_types.go b/api/v1alpha1/provider_types.go new file mode 100644 index 0000000..2c9d146 --- /dev/null +++ b/api/v1alpha1/provider_types.go @@ -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{}) +} diff --git a/api/v1alpha1/toolbinding_types.go b/api/v1alpha1/toolbinding_types.go new file mode 100644 index 0000000..02bf7d7 --- /dev/null +++ b/api/v1alpha1/toolbinding_types.go @@ -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{}) +}