Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 26 additions & 5 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -63,15 +63,19 @@ ACP_SANDBOX_OPENCLAW_IMAGE_NAME ?= acp-sandbox-openclaw
CONTROLLER_IMAGE_TAG ?= $(VERSION)
UI_IMAGE_TAG ?= $(VERSION)
APP_IMAGE_TAG ?= $(VERSION)
APP_FULL_IMAGE_TAG ?= $(VERSION)-full
Comment thread
jmhbh marked this conversation as resolved.
KAGENT_ADK_IMAGE_TAG ?= $(VERSION)
KAGENT_ADK_FULL_IMAGE_TAG ?= $(VERSION)-full
GOLANG_ADK_IMAGE_TAG ?= $(VERSION)
GOLANG_ADK_FULL_IMAGE_TAG ?= $(VERSION)-full
SKILLS_INIT_IMAGE_TAG ?= $(VERSION)
ACP_SANDBOX_IMAGE_TAG ?= $(VERSION)
CONTROLLER_IMG ?= $(DOCKER_REGISTRY)/$(DOCKER_REPO)/$(CONTROLLER_IMAGE_NAME):$(CONTROLLER_IMAGE_TAG)
UI_IMG ?= $(DOCKER_REGISTRY)/$(DOCKER_REPO)/$(UI_IMAGE_NAME):$(UI_IMAGE_TAG)
APP_IMG ?= $(DOCKER_REGISTRY)/$(DOCKER_REPO)/$(APP_IMAGE_NAME):$(APP_IMAGE_TAG)
APP_FULL_IMG ?= $(DOCKER_REGISTRY)/$(DOCKER_REPO)/$(APP_IMAGE_NAME):$(APP_FULL_IMAGE_TAG)
KAGENT_ADK_IMG ?= $(DOCKER_REGISTRY)/$(DOCKER_REPO)/$(KAGENT_ADK_IMAGE_NAME):$(KAGENT_ADK_IMAGE_TAG)
KAGENT_ADK_FULL_IMG ?= $(DOCKER_REGISTRY)/$(DOCKER_REPO)/$(KAGENT_ADK_IMAGE_NAME):$(KAGENT_ADK_FULL_IMAGE_TAG)
GOLANG_ADK_IMG ?= $(DOCKER_REGISTRY)/$(DOCKER_REPO)/$(GOLANG_ADK_IMAGE_NAME):$(GOLANG_ADK_IMAGE_TAG)
GOLANG_ADK_FULL_IMG ?= $(DOCKER_REGISTRY)/$(DOCKER_REPO)/$(GOLANG_ADK_IMAGE_NAME):$(GOLANG_ADK_FULL_IMAGE_TAG)
SKILLS_INIT_IMG ?= $(DOCKER_REGISTRY)/$(DOCKER_REPO)/$(SKILLS_INIT_IMAGE_NAME):$(SKILLS_INIT_IMAGE_TAG)
Expand Down Expand Up @@ -208,12 +212,14 @@ build-all: buildx-create

.PHONY: build
build: ## Build and push all component images
build: buildx-create build-ui build-skills-init build-golang-adk build-golang-adk-full build-app build-controller
build: buildx-create build-ui build-skills-init build-golang-adk build-golang-adk-full build-app build-app-full build-controller
@echo "Build completed successfully."
@echo "Controller Image: $(CONTROLLER_IMG)"
@echo "UI Image: $(UI_IMG)"
@echo "App Image: $(APP_IMG)"
@echo "App Full Image: $(APP_FULL_IMG)"
@echo "Kagent ADK Image: $(KAGENT_ADK_IMG)"
@echo "Kagent ADK Full Image: $(KAGENT_ADK_FULL_IMG)"
@echo "Golang ADK Image: $(GOLANG_ADK_IMG)"
@echo "Golang ADK Full Image: $(GOLANG_ADK_FULL_IMG)"
@echo "Skills Init Image: $(SKILLS_INIT_IMG)"
Expand Down Expand Up @@ -241,7 +247,9 @@ build-img-versions: ## Print the fully-qualified image tags for all components
@echo controller=$(CONTROLLER_IMG)
@echo ui=$(UI_IMG)
@echo app=$(APP_IMG)
@echo app-full=$(APP_FULL_IMG)
@echo kagent-adk=$(KAGENT_ADK_IMG)
@echo kagent-adk-full=$(KAGENT_ADK_FULL_IMG)
@echo golang-adk=$(GOLANG_ADK_IMG)
@echo golang-adk-full=$(GOLANG_ADK_FULL_IMG)
@echo skills-init=$(SKILLS_INIT_IMG)
Expand All @@ -256,10 +264,11 @@ controller-manifests: ## Regenerate CRD manifests and copy them into the Helm ch

.PHONY: build-controller
build-controller: ## Build and push the controller image (embeds agent runtime + acp-sandbox digests via scripts/controller-digest-ldflags.sh)
build-controller: buildx-create controller-manifests build-app build-golang-adk build-golang-adk-full build-acp-sandbox-openclaw build-acp-sandbox-hermes
build-controller: buildx-create controller-manifests build-app build-app-full build-golang-adk build-golang-adk-full build-acp-sandbox-openclaw build-acp-sandbox-hermes
@set -e; \
DIGEST_LDFLAGS=$$(CONTAINER_RUNTIME=$(CONTAINER_RUNTIME) \
APP_IMG=$(APP_IMG) \
APP_FULL_IMG=$(APP_FULL_IMG) \
GOLANG_ADK_IMG=$(GOLANG_ADK_IMG) \
GOLANG_ADK_FULL_IMG=$(GOLANG_ADK_FULL_IMG) \
ACP_SANDBOX_OPENCLAW_IMG=$(ACP_SANDBOX_OPENCLAW_IMG) \
Expand All @@ -284,11 +293,23 @@ build-kagent-adk: buildx-create
$(DOCKER_PUSH) $(KAGENT_ADK_IMG)

.PHONY: build-app
build-app: ## Build and push the app image (depends on kagent-adk)
build-app: ## Build and push the app image (distroless slim; depends on kagent-adk)
build-app: buildx-create build-kagent-adk
$(DOCKER_BUILDER) $(DOCKER_BUILD_ARGS) $(TOOLS_IMAGE_BUILD_ARGS) --build-arg KAGENT_ADK_VERSION=$(KAGENT_ADK_IMAGE_TAG) --build-arg DOCKER_REGISTRY=$(DOCKER_REGISTRY) -t $(APP_IMG) -f python/Dockerfile.app ./python
$(DOCKER_PUSH) $(APP_IMG)

.PHONY: build-kagent-adk-full
build-kagent-adk-full: ## Build and push the full Python kagent ADK image (includes sandbox runtime)
build-kagent-adk-full: buildx-create
$(DOCKER_BUILDER) $(DOCKER_BUILD_ARGS) $(TOOLS_IMAGE_BUILD_ARGS) -t $(KAGENT_ADK_FULL_IMG) -f python/Dockerfile.full ./python
$(DOCKER_PUSH) $(KAGENT_ADK_FULL_IMG)

.PHONY: build-app-full
build-app-full: ## Build and push the full app image (sandbox runtime; depends on kagent-adk-full)
build-app-full: buildx-create build-kagent-adk-full
$(DOCKER_BUILDER) $(DOCKER_BUILD_ARGS) $(TOOLS_IMAGE_BUILD_ARGS) --build-arg KAGENT_ADK_VERSION=$(KAGENT_ADK_FULL_IMAGE_TAG) --build-arg DOCKER_REGISTRY=$(DOCKER_REGISTRY) -t $(APP_FULL_IMG) -f python/Dockerfile.app ./python
$(DOCKER_PUSH) $(APP_FULL_IMG)

.PHONY: build-golang-adk
build-golang-adk: ## Build and push the Go ADK image
build-golang-adk: buildx-create
Expand Down Expand Up @@ -342,8 +363,8 @@ lint: ## Run linters for Go and Python
make -C python lint

.PHONY: push-test-agent
push-test-agent: buildx-create build-kagent-adk ## Build and push E2E test agent images to the local registry
echo "Building FROM DOCKER_REGISTRY=$(DOCKER_REGISTRY)/$(DOCKER_REPO)/kagent-adk:$(VERSION)"
push-test-agent: buildx-create build-kagent-adk build-kagent-adk-full ## Build and push E2E test agent images to the local registry
echo "Building FROM DOCKER_REGISTRY=$(DOCKER_REGISTRY)/$(DOCKER_REPO)/kagent-adk:$(VERSION)-full"
$(DOCKER_BUILDER) $(DOCKER_BUILD_ARGS) $(TOOLS_IMAGE_BUILD_ARGS) -t $(DOCKER_REGISTRY)/kebab:latest -f go/core/test/e2e/agents/kebab/Dockerfile ./go/core/test/e2e/agents/kebab
$(DOCKER_PUSH) $(DOCKER_REGISTRY)/kebab:latest
kubectl apply --namespace kagent --context kind-$(KIND_CLUSTER_NAME) -f go/core/test/e2e/agents/kebab/agent.yaml
Expand Down
2 changes: 0 additions & 2 deletions go/api/config/crd/bases/kagent.dev_sandboxagents.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -11328,8 +11328,6 @@ spec:
x-kubernetes-validations:
- message: spec.skills is not supported for sandbox agents
rule: '!has(self.skills)'
- message: BYO agents are not supported for sandbox agents
rule: '!has(self.type) || self.type != ''BYO'''
- message: type must be specified
rule: has(self.type)
- message: type must be either Declarative or BYO
Expand Down
55 changes: 30 additions & 25 deletions go/api/v1alpha2/agent_runtime_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,31 +6,36 @@ import (
"github.com/stretchr/testify/require"
)

func TestEffectiveDeclarativeRuntimeForAgent(t *testing.T) {
substrateSpec := AgentSpec{
Type: AgentType_Declarative,
Declarative: &DeclarativeAgentSpec{
Runtime: DeclarativeRuntime_Python,
func TestEffectiveDeclarativeRuntime(t *testing.T) {
tests := []struct {
name string
spec *AgentSpec
want DeclarativeRuntime
}{
{
name: "nil spec defaults to Python",
spec: nil,
want: DeclarativeRuntime_Python,
},
{
name: "unset runtime defaults to Python",
spec: &AgentSpec{Type: AgentType_Declarative, Declarative: &DeclarativeAgentSpec{}},
want: DeclarativeRuntime_Python,
},
{
name: "explicit Python runtime",
spec: &AgentSpec{Type: AgentType_Declarative, Declarative: &DeclarativeAgentSpec{Runtime: DeclarativeRuntime_Python}},
want: DeclarativeRuntime_Python,
},
{
name: "explicit Go runtime is honored",
spec: &AgentSpec{Type: AgentType_Declarative, Declarative: &DeclarativeAgentSpec{Runtime: DeclarativeRuntime_Go}},
want: DeclarativeRuntime_Go,
},
}

t.Run("regular Agent keeps configured runtime", func(t *testing.T) {
agent := &Agent{Spec: substrateSpec}
require.Equal(t, DeclarativeRuntime_Python, EffectiveDeclarativeRuntimeForAgent(agent))
})

t.Run("SandboxAgent uses Go", func(t *testing.T) {
sa := &SandboxAgent{Spec: SandboxAgentSpec{AgentSpec: substrateSpec}}
require.Equal(t, DeclarativeRuntime_Go, EffectiveDeclarativeRuntimeForAgent(sa))
})

t.Run("regular Agent honors Go runtime", func(t *testing.T) {
agent := &Agent{Spec: AgentSpec{
Type: AgentType_Declarative,
Declarative: &DeclarativeAgentSpec{
Runtime: DeclarativeRuntime_Go,
},
}}
require.Equal(t, DeclarativeRuntime_Go, EffectiveDeclarativeRuntimeForAgent(agent))
})
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
require.Equal(t, tt.want, EffectiveDeclarativeRuntime(tt.spec))
})
}
}
30 changes: 17 additions & 13 deletions go/api/v1alpha2/agent_spec_validation.go
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
package v1alpha2

import "fmt"
import (
"fmt"
"strings"
)

const (
substrateSandboxSkillsUnsupportedMsg = "spec.skills is not supported for sandbox agents"
substrateSandboxPythonRuntimeUnsupportedMsg = "spec.declarative.runtime must be \"go\" for sandbox agents"
substrateSandboxBYOUnsupportedMsg = "BYO agents are not supported for sandbox agents"
substrateSandboxSkillsUnsupportedMsg = "spec.skills is not supported for sandbox agents"
substrateSandboxBYOMissingCommandMsg = "BYO agents on substrate must set spec.byo.deployment.cmd (substrate does not fall back to the image entrypoint)"
)

// AgentSpecHasSkills reports whether the spec configures any skill sources.
Expand All @@ -18,23 +20,25 @@ func AgentSpecHasSkills(spec *AgentSpec) bool {
}

// ValidateSubstrateSandboxAgentSpec rejects sandbox agent configurations that kagent
// does not support on Agent Substrate (for example declarative skills or BYO agents).
// does not support on Agent Substrate (for example declarative skills). Declarative
// Python/Go and BYO (Go/Python) agents are supported; BYO agents must provide an explicit
// command because substrate copies the container Command verbatim with no image-entrypoint
// fallback.
func ValidateSubstrateSandboxAgentSpec(agent *SandboxAgent) error {
if agent == nil {
return nil
}
spec := agent.GetAgentSpec()
if spec.Type == AgentType_BYO {
return fmt.Errorf("%s", substrateSandboxBYOUnsupportedMsg)
}
if AgentSpecHasSkills(spec) {
return fmt.Errorf("%s", substrateSandboxSkillsUnsupportedMsg)
}
if spec.Type == AgentType_Declarative &&
spec.Declarative != nil &&
spec.Declarative.Runtime != "" &&
spec.Declarative.Runtime != DeclarativeRuntime_Go {
return fmt.Errorf("%s", substrateSandboxPythonRuntimeUnsupportedMsg)
if spec.Type == AgentType_BYO {
dep := spec.BYO
// Trim so a whitespace-only cmd is rejected like an empty one (substrate would treat it
// as no command, and the UI trims before validating — keep backend/UI aligned).
if dep == nil || dep.Deployment == nil || dep.Deployment.Cmd == nil || strings.TrimSpace(*dep.Deployment.Cmd) == "" {
return fmt.Errorf("%s", substrateSandboxBYOMissingCommandMsg)
}
Comment thread
jmhbh marked this conversation as resolved.
}
return nil
}
36 changes: 31 additions & 5 deletions go/api/v1alpha2/agent_spec_validation_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ func TestValidateSubstrateSandboxAgentSpec(t *testing.T) {
require.Contains(t, err.Error(), substrateSandboxSkillsUnsupportedMsg)
})

t.Run("rejects python runtime", func(t *testing.T) {
t.Run("allows python runtime", func(t *testing.T) {
agent := &SandboxAgent{
Spec: SandboxAgentSpec{
AgentSpec: AgentSpec{
Expand All @@ -36,23 +36,49 @@ func TestValidateSubstrateSandboxAgentSpec(t *testing.T) {
},
},
}
require.NoError(t, ValidateSubstrateSandboxAgentSpec(agent))
})

t.Run("rejects BYO agents without an explicit command", func(t *testing.T) {
agent := &SandboxAgent{
Spec: SandboxAgentSpec{
AgentSpec: AgentSpec{
Type: AgentType_BYO,
BYO: &BYOAgentSpec{Deployment: &ByoDeploymentSpec{Image: "example/agent:latest"}},
},
},
}
err := ValidateSubstrateSandboxAgentSpec(agent)
require.Error(t, err)
require.Contains(t, err.Error(), substrateSandboxPythonRuntimeUnsupportedMsg)
require.Contains(t, err.Error(), substrateSandboxBYOMissingCommandMsg)
})

t.Run("rejects BYO agents", func(t *testing.T) {
t.Run("rejects BYO agents with a whitespace-only command", func(t *testing.T) {
cmd := " "
agent := &SandboxAgent{
Spec: SandboxAgentSpec{
AgentSpec: AgentSpec{
Type: AgentType_BYO,
BYO: &BYOAgentSpec{},
BYO: &BYOAgentSpec{Deployment: &ByoDeploymentSpec{Image: "example/agent:latest", Cmd: &cmd}},
},
},
}
err := ValidateSubstrateSandboxAgentSpec(agent)
require.Error(t, err)
require.Contains(t, err.Error(), substrateSandboxBYOUnsupportedMsg)
require.Contains(t, err.Error(), substrateSandboxBYOMissingCommandMsg)
})

t.Run("allows BYO agents with an explicit command", func(t *testing.T) {
cmd := "/app"
agent := &SandboxAgent{
Spec: SandboxAgentSpec{
AgentSpec: AgentSpec{
Type: AgentType_BYO,
BYO: &BYOAgentSpec{Deployment: &ByoDeploymentSpec{Image: "example/agent:latest", Cmd: &cmd}},
},
},
}
require.NoError(t, ValidateSubstrateSandboxAgentSpec(agent))
})

t.Run("allows go runtime", func(t *testing.T) {
Expand Down
13 changes: 1 addition & 12 deletions go/api/v1alpha2/agent_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -252,6 +252,7 @@ type SandboxConfig struct {
}

// EffectiveDeclarativeRuntime returns the ADK runtime from spec fields (defaults to Python when not set).
// All agents (including substrate SandboxAgents) honor spec.declarative.runtime.
func EffectiveDeclarativeRuntime(spec *AgentSpec) DeclarativeRuntime {
if spec == nil {
return DeclarativeRuntime_Python
Expand All @@ -263,18 +264,6 @@ func EffectiveDeclarativeRuntime(spec *AgentSpec) DeclarativeRuntime {
return runtime
}

// EffectiveDeclarativeRuntimeForAgent returns the runtime for a reconciled agent object.
// SandboxAgents always use Go; regular Agents honor spec.declarative.runtime.
func EffectiveDeclarativeRuntimeForAgent(agent AgentObject) DeclarativeRuntime {
spec := agent.GetAgentSpec()
if agent.GetWorkloadMode() == WorkloadModeSandbox &&
spec != nil &&
spec.Type == AgentType_Declarative {
return DeclarativeRuntime_Go
}
return EffectiveDeclarativeRuntime(spec)
}

// NetworkConfig configures outbound network access for sandboxed execution paths.
type NetworkConfig struct {
// AllowedDomains lists the domains that sandboxed execution may contact.
Expand Down
1 change: 0 additions & 1 deletion go/api/v1alpha2/sandboxagent_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,6 @@ type SandboxAgent struct {
}

// +kubebuilder:validation:XValidation:rule="!has(self.skills)",message="spec.skills is not supported for sandbox agents"
// +kubebuilder:validation:XValidation:rule="!has(self.type) || self.type != 'BYO'",message="BYO agents are not supported for sandbox agents"
type SandboxAgentSpec struct {
AgentSpec `json:",inline"`

Expand Down
4 changes: 2 additions & 2 deletions go/core/internal/controller/sandboxagent_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ func (r *SandboxAgentController) Reconcile(ctx context.Context, req ctrl.Request
return ctrl.Result{}, fmt.Errorf("get SandboxAgent: %w", err)
}

if r.SubstrateLifecycle != nil {
if r.substrateConfigured() {
if res, err := r.reconcileSubstrateSandboxAgent(ctx, &sa); err != nil || !res.IsZero() {
return res, err
}
Expand Down Expand Up @@ -108,7 +108,7 @@ func (r *SandboxAgentController) SetupWithManager(mgr ctrl.Manager) error {
if err != nil {
return err
}
if r.SubstrateLifecycle != nil {
if r.substrateConfigured() {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we get rid of this r.substrateConfigured() in a follow-up. If substrate is not configured we shouldn't even run this reconciler at all, nothing will work. Right now it's scattered everywhere

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it makes sense to separate this into a separate substrate controller, I can work on that as a follow up.

build = build.Watches(
&atev1alpha1.ActorTemplate{},
handler.EnqueueRequestsFromMapFunc(r.enqueueSandboxAgentForSubstrateResource),
Expand Down
Loading
Loading