Skip to content

Commit 583d7a4

Browse files
peterjEItanya
andauthored
feat: agent sandbox support (#1640)
adds a new SandboxAgent CRD that creates a sandbox agent using agent-sandbox CRD. Install: ``` export VERSION="v0.3.10" kubectl apply -f https://github.com/kubernetes-sigs/agent-sandbox/releases/download/${VERSION}/manifest.yaml kubectl apply -f https://github.com/kubernetes-sigs/agent-sandbox/releases/download/${VERSION}/extensions.yaml ``` Then create SandboxAgent resource (same as Agent resource). --------- Signed-off-by: Peter Jausovec <peter.jausovec@solo.io> Signed-off-by: Eitan Yarmush <eitan.yarmush@solo.io> Co-authored-by: Eitan Yarmush <eitan.yarmush@solo.io>
1 parent e372846 commit 583d7a4

92 files changed

Lines changed: 20432 additions & 1446 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.agents/skills/kagent

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
../../.claude/skills/kagent

.agents/skills/kagent-dev

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
../../.claude/skills/kagent-dev

.github/workflows/ci.yaml

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ env:
1515
# Cache key components for better organization
1616
CACHE_KEY_PREFIX: kagent-v2
1717
BRANCH_CACHE_KEY: ${{ github.head_ref || github.ref_name }}
18+
AGENT_SANDBOX_VERSION: v0.3.10
1819
# Consistent builder configuration
1920
BUILDX_BUILDER_NAME: kagent-builder-v0.23.0
2021
BUILDX_VERSION: v0.23.0
@@ -66,6 +67,17 @@ jobs:
6667
with:
6768
install_only: true
6869

70+
- name: Create Kind cluster
71+
run: |
72+
make create-kind-cluster
73+
74+
- name: Install agent-sandbox
75+
run: |
76+
kubectl apply -f "https://github.com/kubernetes-sigs/agent-sandbox/releases/download/${AGENT_SANDBOX_VERSION}/manifest.yaml"
77+
kubectl wait --for=condition=Established crd/sandboxes.agents.x-k8s.io --timeout=90s
78+
kubectl rollout status deployment/agent-sandbox-controller -n agent-sandbox-system --timeout=120s
79+
kubectl wait --for=condition=Ready pod -l app=agent-sandbox-controller -n agent-sandbox-system --timeout=120s
80+
6981
- name: Install Kagent
7082
id: install-kagent
7183
env:
@@ -79,10 +91,11 @@ jobs:
7991
--platform=linux/amd64
8092
--push
8193
run: |
82-
make create-kind-cluster
8394
echo "Cache key: ${{ needs.setup.outputs.cache-key }}"
8495
make helm-install
8596
make push-test-agent push-test-skill
97+
kubectl rollout status deployment/kagent-controller -n kagent --timeout=120s
98+
kubectl wait --for=condition=Ready pod -l app.kubernetes.io/component=controller -n kagent --timeout=120s
8699
kubectl wait --for=condition=Ready agents.kagent.dev -n kagent --all --timeout=60s || kubectl get po -n kagent -o wide ||:
87100
kubectl wait --for=condition=Ready agents.kagent.dev -n kagent --all --timeout=60s
88101
@@ -113,15 +126,15 @@ jobs:
113126
run: |
114127
# Upgrade helm to use namespace-scoped RBAC
115128
make helm-install-provider
116-
129+
117130
# Wait for controller to be ready after upgrade
118131
kubectl rollout status deployment/kagent-controller -n kagent --timeout=90s
119-
132+
120133
# Setup environment variables (reusing logic from previous step)
121134
HOST_IP=$(docker network inspect kind -f '{{range .IPAM.Config}}{{if .Gateway}}{{.Gateway}}{{"\n"}}{{end}}{{end}}' | grep -E '^[0-9]+\.' | head -1)
122135
export KAGENT_LOCAL_HOST=$HOST_IP
123136
export KAGENT_URL="http://$(kubectl get svc -n kagent kagent-controller -o jsonpath='{.status.loadBalancer.ingress[0].ip}'):8083"
124-
137+
125138
# Run critical tests with namespace-scoped RBAC to verify the controller didn't lose needed permissions
126139
cd go
127140
go test -v github.com/kagent-dev/kagent/go/core/test/e2e -run '^TestE2EInvokeInlineAgent$|^TestE2EInvokeDeclarativeAgentWithMcpServerTool$' -failfast
@@ -131,6 +144,10 @@ jobs:
131144
echo "::error::Failed to run e2e tests"
132145
echo "::error::Kubectl get pods -n kagent"
133146
kubectl describe pods -n kagent
147+
echo "::error::Kubectl get pods -n agent-sandbox-system"
148+
kubectl get pods -n agent-sandbox-system -o wide || true
149+
echo "::error::Kubectl logs -n agent-sandbox-system deployment/agent-sandbox-controller"
150+
kubectl logs -n agent-sandbox-system deployment/agent-sandbox-controller || true
134151
echo "::error::Kubectl get events -n kagent"
135152
kubectl get events -n kagent
136153
echo "::error::Kubectl get agents -n kagent"

go/api/config/crd/bases/kagent.dev_sandboxagents.yaml

Lines changed: 8198 additions & 0 deletions
Large diffs are not rendered by default.

go/api/database/client.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ type Client interface {
5151
ListTasksForSession(ctx context.Context, sessionID string) ([]*protocol.Task, error)
5252
ListSessions(ctx context.Context, userID string) ([]Session, error)
5353
ListSessionsForAgent(ctx context.Context, agentID string, userID string) ([]Session, error)
54+
ListSessionsForAgentAllUsers(ctx context.Context, agentID string) ([]Session, error)
5455
ListAgents(ctx context.Context) ([]Agent, error)
5556
ListToolServers(ctx context.Context) ([]ToolServer, error)
5657
ListToolsForServer(ctx context.Context, serverName string, groupKind string) ([]Tool, error)

go/api/database/models.go

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import (
55
"time"
66

77
"github.com/kagent-dev/kagent/go/api/adk"
8+
"github.com/kagent-dev/kagent/go/api/v1alpha2"
89
"github.com/pgvector/pgvector-go"
910
"trpc.group/trpc-go/trpc-a2a-go/protocol"
1011
)
@@ -15,8 +16,9 @@ type Agent struct {
1516
UpdatedAt time.Time `json:"updated_at"`
1617
DeletedAt *time.Time `json:"deleted_at,omitempty"`
1718

18-
Type string `json:"type"`
19-
Config *adk.AgentConfig `json:"config"`
19+
Type string `json:"type"`
20+
WorkloadType v1alpha2.WorkloadMode `json:"workload_type"`
21+
Config *adk.AgentConfig `json:"config"`
2022
}
2123

2224
type Event struct {

go/api/httpapi/types.go

Lines changed: 63 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import (
44
"github.com/kagent-dev/kagent/go/api/database"
55
"github.com/kagent-dev/kagent/go/api/v1alpha1"
66
"github.com/kagent-dev/kagent/go/api/v1alpha2"
7+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
78
)
89

910
// Common types
@@ -83,9 +84,68 @@ type UpdateModelConfigRequest struct {
8384

8485
// Agent types
8586

87+
type AgentResource struct {
88+
APIVersion string `json:"apiVersion,omitempty"`
89+
Kind string `json:"kind,omitempty"`
90+
Metadata metav1.ObjectMeta `json:"metadata,omitempty"`
91+
Spec v1alpha2.AgentSpec `json:"spec,omitempty"`
92+
Status v1alpha2.AgentStatus `json:"status,omitempty"`
93+
}
94+
95+
func AgentResourceFrom(agent v1alpha2.AgentObject) *AgentResource {
96+
if agent == nil {
97+
return nil
98+
}
99+
100+
spec := agent.GetAgentSpec()
101+
status := agent.GetAgentStatus()
102+
gvk := agent.GetObjectKind().GroupVersionKind()
103+
apiVersion := gvk.GroupVersion().String()
104+
kind := gvk.Kind
105+
var metadata metav1.ObjectMeta
106+
if apiVersion == "" {
107+
apiVersion = v1alpha2.GroupVersion.String()
108+
}
109+
if kind == "" {
110+
if agent.GetWorkloadMode() == v1alpha2.WorkloadModeSandbox {
111+
kind = "SandboxAgent"
112+
} else {
113+
kind = "Agent"
114+
}
115+
}
116+
switch typed := agent.(type) {
117+
case *v1alpha2.Agent:
118+
metadata = *typed.ObjectMeta.DeepCopy()
119+
case *v1alpha2.SandboxAgent:
120+
metadata = *typed.ObjectMeta.DeepCopy()
121+
default:
122+
metadata = metav1.ObjectMeta{
123+
Name: agent.GetName(),
124+
Namespace: agent.GetNamespace(),
125+
Labels: agent.GetLabels(),
126+
Annotations: agent.GetAnnotations(),
127+
ResourceVersion: agent.GetResourceVersion(),
128+
Generation: agent.GetGeneration(),
129+
}
130+
}
131+
132+
res := &AgentResource{
133+
APIVersion: apiVersion,
134+
Kind: kind,
135+
Metadata: metadata,
136+
}
137+
if spec != nil {
138+
res.Spec = *spec.DeepCopy()
139+
}
140+
if status != nil {
141+
res.Status = *status.DeepCopy()
142+
}
143+
return res
144+
}
145+
86146
type AgentResponse struct {
87-
ID string `json:"id"`
88-
Agent *v1alpha2.Agent `json:"agent"`
147+
ID string `json:"id"`
148+
Agent *AgentResource `json:"agent"`
89149
// Config *adk.AgentConfig `json:"config"`
90150
ModelProvider v1alpha2.ModelProvider `json:"modelProvider"`
91151
Model string `json:"model"`
@@ -94,6 +154,7 @@ type AgentResponse struct {
94154
Tools []*v1alpha2.Tool `json:"tools"`
95155
DeploymentReady bool `json:"deploymentReady"`
96156
Accepted bool `json:"accepted"`
157+
WorkloadMode v1alpha2.WorkloadMode `json:"workloadMode,omitempty"`
97158
}
98159

99160
// Session types

go/api/v1alpha2/agentobject.go

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
package v1alpha2
2+
3+
import "sigs.k8s.io/controller-runtime/pkg/client"
4+
5+
type WorkloadMode string
6+
7+
const (
8+
WorkloadModeDeployment WorkloadMode = "deployment"
9+
WorkloadModeSandbox WorkloadMode = "sandbox"
10+
)
11+
12+
// AgentObject is the shared shape implemented by agent-style CRDs that expose the
13+
// same Spec/Status model but reconcile to different workload types.
14+
// +kubebuilder:object:generate=false
15+
type AgentObject interface {
16+
client.Object
17+
GetAgentSpec() *AgentSpec
18+
GetAgentStatus() *AgentStatus
19+
GetWorkloadMode() WorkloadMode
20+
}
21+
22+
func (a *Agent) GetAgentSpec() *AgentSpec {
23+
if a == nil {
24+
return nil
25+
}
26+
return &a.Spec
27+
}
28+
29+
func (a *Agent) GetAgentStatus() *AgentStatus {
30+
if a == nil {
31+
return nil
32+
}
33+
return &a.Status
34+
}
35+
36+
func (a *Agent) GetWorkloadMode() WorkloadMode {
37+
return WorkloadModeDeployment
38+
}
39+
40+
func (a *SandboxAgent) GetAgentSpec() *AgentSpec {
41+
if a == nil {
42+
return nil
43+
}
44+
return &a.Spec
45+
}
46+
47+
func (a *SandboxAgent) GetAgentStatus() *AgentStatus {
48+
if a == nil {
49+
return nil
50+
}
51+
return &a.Status
52+
}
53+
54+
func (a *SandboxAgent) GetWorkloadMode() WorkloadMode {
55+
return WorkloadModeSandbox
56+
}
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
/*
2+
Copyright 2025.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package v1alpha2
18+
19+
import (
20+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
21+
)
22+
23+
// +kubebuilder:object:root=true
24+
// +kubebuilder:subresource:status
25+
// +kubebuilder:printcolumn:name="Ready",type="string",JSONPath=".status.conditions[?(@.type=='Ready')].status",description="Whether the sandbox workload is ready."
26+
// +kubebuilder:printcolumn:name="Accepted",type="string",JSONPath=".status.conditions[?(@.type=='Accepted')].status",description="Whether configuration was accepted."
27+
// SandboxAgent declares an agent that runs in an isolated sandbox (agent-sandbox Sandbox CR).
28+
type SandboxAgent struct {
29+
metav1.TypeMeta `json:",inline"`
30+
metav1.ObjectMeta `json:"metadata,omitempty"`
31+
32+
Spec AgentSpec `json:"spec,omitempty"`
33+
Status AgentStatus `json:"status,omitempty"`
34+
}
35+
36+
// +kubebuilder:object:root=true
37+
38+
// SandboxAgentList contains a list of SandboxAgent.
39+
type SandboxAgentList struct {
40+
metav1.TypeMeta `json:",inline"`
41+
metav1.ListMeta `json:"metadata,omitempty"`
42+
Items []SandboxAgent `json:"items"`
43+
}
44+
45+
func init() {
46+
SchemeBuilder.Register(&SandboxAgent{}, &SandboxAgentList{})
47+
}

go/api/v1alpha2/zz_generated.deepcopy.go

Lines changed: 59 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)