Skip to content
Merged
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
6 changes: 5 additions & 1 deletion .github/workflows/ci.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,8 @@ jobs:
--push
run: |
echo "Cache key: ${{ needs.setup.outputs.cache-key }}"
make build-controller build-golang-adk build-claude-harness
jobs=$(nproc)
make -j"$(( jobs < 4 ? jobs : 4 ))" build-controller build-golang-adk build-claude-harness build-byo-a2a
make helm-install-provider
kubectl rollout status deployment/kagent-controller -n kagent --timeout=120s
kubectl wait --for=condition=Ready pod -l app.kubernetes.io/component=controller -n kagent --timeout=120s
Expand All @@ -132,6 +133,9 @@ jobs:
RUNTIME_DIGEST=$(docker buildx imagetools inspect "localhost:5001/kagent-dev/kagent/golang-adk:${VERSION}" | awk '$1 == "Digest:" { print $2; exit }')
test -n "$RUNTIME_DIGEST"
export KAGENT_E2E_RUNTIME_IMAGE="localhost:5001/kagent-dev/kagent/golang-adk@${RUNTIME_DIGEST}"
BYO_DIGEST=$(docker buildx imagetools inspect "localhost:5001/kagent-dev/kagent/byo-a2a:${VERSION}" | awk '$1 == "Digest:" { print $2; exit }')
test -n "$BYO_DIGEST"
export KAGENT_E2E_BYO_IMAGE="localhost:5001/kagent-dev/kagent/byo-a2a@${BYO_DIGEST}"
CLAUDE_DIGEST=$(docker buildx imagetools inspect "localhost:5001/kagent-dev/kagent/claude-harness:${VERSION}" | awk '$1 == "Digest:" { print $2; exit }')
test -n "$CLAUDE_DIGEST"
export KAGENT_E2E_CLAUDE_IMAGE="localhost:5001/kagent-dev/kagent/claude-harness@${CLAUDE_DIGEST}"
Expand Down
6 changes: 6 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -295,6 +295,12 @@ build-golang-adk: proto-generate buildx-create
$(DOCKER_BUILDER) $(DOCKER_BUILD_ARGS) $(TOOLS_IMAGE_BUILD_ARGS) --build-arg BUILD_PACKAGE=adk/cmd/main.go -t $(GOLANG_ADK_IMG) -f go/Dockerfile ./go
$(DOCKER_PUSH) $(GOLANG_ADK_IMG)

.PHONY: build-byo-a2a
build-byo-a2a: ## Build and push the opaque BYO A2A e2e image
build-byo-a2a: buildx-create
$(DOCKER_BUILDER) $(DOCKER_BUILD_ARGS) $(TOOLS_IMAGE_BUILD_ARGS) --build-arg BUILD_PACKAGE=core/test/byoa2a/main.go -t $(DOCKER_REGISTRY)/$(DOCKER_REPO)/byo-a2a:$(VERSION) -f go/Dockerfile ./go
$(DOCKER_PUSH) $(DOCKER_REGISTRY)/$(DOCKER_REPO)/byo-a2a:$(VERSION)

.PHONY: build-claude-harness
build-claude-harness: ## Build and push the native Claude Harness image
build-claude-harness: buildx-create
Expand Down
32 changes: 23 additions & 9 deletions docs/plans/api-v2-execution-plan.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ Land API v2 through four milestones:

1. Merge #2362 and freeze final CRD/gRPC contracts.
2. Deliver a usable single-agent vertical slice with the existing kagent runtime.
3. Add composition, Codex, Claude, UI/CLI/MCP cutover, and remove legacy APIs.
3. Add composition, Codex, Claude, BYO A2A images, UI/CLI/MCP cutover, and remove legacy APIs.
4. Add single-member checkpoint/fork using released Substrate snapshot support.

API v2 is not complete until checkpoint/fork and their Substrate dependencies pass E2E coverage.
Expand All @@ -22,8 +22,8 @@ Public invariants:
final state is published; physical Actor suspension does not change a ready
AgentInstance's logical state.
- Substrate is the only compute backend.
- No public scheduling, service-account, Deployment, channel, profile, or BYO fields.
- V1 release-blocking adapters are kagent, Codex, and Claude.
- No public scheduling, service-account, Deployment, channel, or profile fields. Arbitrary images remain behind BYO Harness admission.
- V1 release-blocking adapters are kagent, Codex, Claude, and BYO A2A images.

## PR dependency graph

Expand All @@ -42,11 +42,12 @@ K0 #2362

K3 + K4 + K8 ─┬─ K14 Codex adapter
└─ K15 Claude adapter
K3 + K10 ───────── K15A BYO A2A adapter

Substrate v0.0.20 snapshots + snapshot-sourced actors ─ K16 dependency adoption
K6 + K10 + K16 ─ K17 checkpoints ─ K18 fork

K12 + K13 + K14 + K15 + K18 ─ K19 legacy removal ─ K20 release conformance
K12 + K13 + K14 + K15 + K15A + K18 ─ K19 legacy removal ─ K20 release conformance

S0 ate-api ActorTemplate resources ───────────────────────┐
K3 + K5 ─────────────────────────────────────┴─ K5A backing-resource cutover
Expand Down Expand Up @@ -324,7 +325,7 @@ CLI:
- Apply Harness and AgentTemplate manifests.
- Create/list/get/suspend/resume/delete AgentInstances through gRPC.
- Invoke and follow Tasks through upstream A2A.
- Remove SandboxAgent, AgentHarness, Deployment, BYO, session, and ACP branches.
- Remove SandboxAgent, AgentHarness, Deployment, legacy Agent BYO, session, and ACP branches.

MCP:

Expand Down Expand Up @@ -370,6 +371,19 @@ Implement the third release-blocking adapter:
- Map Claude output, tool calls, approvals, cancellation, and failures to the private upstream A2A service.
- Publish only capabilities proven by the conformance suite.

### K15A — BYO A2A Harness adapter

Allow users with Harness write access to supply a digest-pinned image that implements the private A2A runtime contract:

- Add a typed `byo` Harness variant. Keep the image, command, args, environment, credentials, WorkerPool, snapshot policy, and admission selector on the Harness; do not put arbitrary images on AgentTemplate. Command and args are generic workload fields; BYO requires an explicit command because Substrate does not use the image entrypoint.
- Require A2A v1 gRPC through the standard Actor ingress, streaming, `/readyz` on port 8081, and durable private state under `/data`. Keep ports, routing, Actor identity, and Substrate mechanics fixed and private.
- Make AgentTemplate model, prompt, tools, skills, and plugins optional for BYO attachments. Compile every provided field into the existing ADK `AgentConfig` shape and inject it through `KAGENT_CONFIG_JSON` with the generated card in `KAGENT_AGENT_CARD_JSON`; a BYO image may consume that configuration or ignore it.
- Extract the shared ADK-config construction into a semantic helper used by the kagent and BYO compilers. Do not create a second configuration format or make either compiler depend on the other.
- Keep the public Agent Card derived from the pinned AgentTemplate revision and gateway capabilities. Do not wake the Actor or trust runtime-provided interfaces, security, or routing metadata to construct it.
- Infer egress destinations from configured models and MCP servers. Do not expose image-owned egress configuration until its policy model is designed.
- Preserve the existing AgentInstance lifecycle, automatic suspension, checkpoint, fork, authorization, task persistence, and public A2A gateway without BYO-specific branches outside compilation.
- Cover an opaque A2A image that ignores ADK configuration and an ADK-config-aware image that consumes optional model, prompt, MCP, skill, and plugin inputs. Exercise send/stream, cancellation, suspension, checkpoint, fork, credential redaction, and egress denial in Kind.

### S1 — Upstream Substrate immutable ActorSnapshot API ✅

Released in Substrate v0.0.20:
Expand Down Expand Up @@ -452,7 +466,7 @@ No automatic migration of legacy Sessions or live SandboxAgents is provided. Alp

Enable blocking clean-install coverage:

- kagent, Codex, and Claude Harnesses.
- kagent, Codex, Claude, and BYO A2A Harnesses.
- Prompt/model/MCP/skills/plugins.
- Shared tools.
- Create idempotency and controller restart at each provisioning step.
Expand Down Expand Up @@ -492,15 +506,15 @@ K7 and K8 should branch from K3 and avoid editing each other’s source-specific
## Milestone gates

- Preview 1: K0–K6 — single kagent AgentTemplate can prepare, instantiate, chat, suspend, resume, and delete through final APIs.
- Preview 2: K7–K8 and K10–K15 — full configuration, Shared composition, Codex, Claude, UI, CLI, and MCP behavior.
- Preview 2: K7–K8 and K10–K15A — full configuration, Shared composition, Codex, Claude, BYO A2A, UI, CLI, and MCP behavior.
- Release candidate: S1–S2 and K16–K19 — checkpoint/fork complete and legacy surface deleted.
- API v2 complete: K20 passes with all three release-blocking adapters and Substrate E2E.
- API v2 complete: K20 passes with all four release-blocking adapters and Substrate E2E.

## Deliberate exclusions

- No AgentHost, HostedAgent, shared Actors, managed native profiles, or channels.
- No OpenClaw or Hermes release requirement.
- No BYO/fallback runtime.
- No externally hosted BYO agent or non-Substrate fallback runtime.
- No cross-namespace references.
- No multiple conversations or parallel Tasks per AgentInstance.
- No template inheritance, BaseContext, shared-store, or filesystem CRDs.
Expand Down
62 changes: 44 additions & 18 deletions go/api/config/crd/bases/kagent.dev_agenttemplates.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -48,15 +48,23 @@ spec:
description:
type: string
modelConfig:
description: AgentTemplateLocalReference identifies a resource in
the AgentTemplate's namespace.
description: ModelConfig is required by managed harnesses and optional
for BYO harnesses.
properties:
name:
minLength: 1
default: ""
description: |-
Name of the referent.
This field is effectively required, but due to backwards compatibility is
allowed to be empty. Instances of this type with an empty value here are
almost certainly wrong.
More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
type: string
required:
- name
type: object
x-kubernetes-map-type: atomic
x-kubernetes-validations:
- message: name must not be empty
rule: has(self.name) && self.name != ''
plugins:
items:
description: PluginBundle selects Agent Skills from one immutable
Expand Down Expand Up @@ -302,15 +310,24 @@ spec:
minLength: 1
type: string
templateRef:
description: AgentTemplateLocalReference identifies a resource
in the AgentTemplate's namespace.
description: |-
LocalObjectReference contains enough information to let you locate the
referenced object inside the same namespace.
properties:
name:
minLength: 1
default: ""
description: |-
Name of the referent.
This field is effectively required, but due to backwards compatibility is
allowed to be empty. Instances of this type with an empty value here are
almost certainly wrong.
More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
type: string
required:
- name
type: object
x-kubernetes-map-type: atomic
x-kubernetes-validations:
- message: name must not be empty
rule: has(self.name) && self.name != ''
required:
- description
- name
Expand All @@ -321,21 +338,32 @@ spec:
MCP server.
properties:
server:
description: AgentTemplateTypedLocalReference identifies
a typed resource in the AgentTemplate's namespace.
description: |-
TypedLocalObjectReference contains enough information to let you locate the
typed referenced object inside the same namespace.
properties:
apiGroup:
description: |-
APIGroup is the group for the resource being referenced.
If APIGroup is not specified, the specified Kind must be in the core API group.
For any other third-party types, APIGroup is required.
type: string
kind:
enum:
- RemoteMCPServer
minLength: 1
description: Kind is the type of resource being referenced
type: string
name:
minLength: 1
description: Name is the name of resource being referenced
type: string
required:
- kind
- name
type: object
x-kubernetes-map-type: atomic
x-kubernetes-validations:
- message: kind must be RemoteMCPServer
rule: self.kind == 'RemoteMCPServer'
- message: apiGroup must be omitted
rule: '!has(self.apiGroup)'
tools:
description: |-
Tools optionally limits which server tools are exposed. An omitted or empty
Expand All @@ -356,8 +384,6 @@ spec:
rule: has(self.mcp) != has(self.agent)
maxItems: 50
type: array
required:
- modelConfig
type: object
x-kubernetes-validations:
- message: systemPrompt and systemPromptFrom are mutually exclusive
Expand Down
22 changes: 20 additions & 2 deletions go/api/config/crd/bases/kagent.dev_harnesses.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,10 @@ spec:
required:
- selector
type: object
byo:
description: BYOHarness selects an image that implements kagent's
private A2A contract.
type: object
claude:
description: ClaudeHarness selects the Claude runtime adapter.
type: object
Expand Down Expand Up @@ -234,6 +238,18 @@ spec:
description: HarnessWorkload identifies the immutable runtime image
used by a Harness.
properties:
args:
description: Args overrides the image command arguments when set.
items:
type: string
maxItems: 64
type: array
command:
description: Command overrides the image entrypoint when set.
items:
type: string
maxItems: 32
type: array
image:
description: Image is an OCI image reference pinned by sha256
digest.
Expand All @@ -247,9 +263,11 @@ spec:
- workload
type: object
x-kubernetes-validations:
- message: exactly one of kagent, codex, or claude must be specified
- message: exactly one of kagent, codex, claude, or byo must be specified
rule: '(has(self.kagent) ? 1 : 0) + (has(self.codex) ? 1 : 0) + (has(self.claude)
? 1 : 0) == 1'
? 1 : 0) + (has(self.byo) ? 1 : 0) == 1'
- message: BYO harnesses must specify workload.command
rule: '!has(self.byo) || size(self.workload.command) > 0'
status:
description: HarnessStatus reports controller-derived capabilities and
current health.
Expand Down
32 changes: 10 additions & 22 deletions go/api/v1alpha3/agenttemplate_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,28 +17,11 @@ limitations under the License.
package v1alpha3

import (
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
)

// AgentTemplateLocalReference identifies a resource in the AgentTemplate's namespace.
type AgentTemplateLocalReference struct {
// +kubebuilder:validation:MinLength=1
// +required
Name string `json:"name"`
}

// AgentTemplateTypedLocalReference identifies a typed resource in the AgentTemplate's namespace.
type AgentTemplateTypedLocalReference struct {
// +kubebuilder:validation:Enum=RemoteMCPServer
// +kubebuilder:validation:MinLength=1
// +required
Kind string `json:"kind"`
// +kubebuilder:validation:MinLength=1
// +required
Name string `json:"name"`
}

// AgentTemplateConfigMapKeyReference identifies a key in a same-namespace ConfigMap.
type AgentTemplateConfigMapKeyReference struct {
// +kubebuilder:validation:MinLength=1
Expand Down Expand Up @@ -72,8 +55,10 @@ type AgentTemplatePromptSource struct {

// MCPToolBinding binds tools from a same-namespace MCP server.
type MCPToolBinding struct {
// +kubebuilder:validation:XValidation:rule="self.kind == 'RemoteMCPServer'",message="kind must be RemoteMCPServer"
// +kubebuilder:validation:XValidation:rule="!has(self.apiGroup)",message="apiGroup must be omitted"
// +required
Server AgentTemplateTypedLocalReference `json:"server"`
Server corev1.TypedLocalObjectReference `json:"server"`
// Tools optionally limits which server tools are exposed. An omitted or empty
// list exposes every tool. Harnesses that cannot enforce a partial selection
// may expose the whole server and report a warning.
Expand Down Expand Up @@ -102,8 +87,9 @@ type AgentToolBinding struct {
// +kubebuilder:validation:MinLength=1
// +required
Description string `json:"description"`
// +kubebuilder:validation:XValidation:rule="has(self.name) && self.name != ''",message="name must not be empty"
// +required
TemplateRef AgentTemplateLocalReference `json:"templateRef"`
TemplateRef corev1.LocalObjectReference `json:"templateRef"`
// +kubebuilder:default=Shared
// +optional
Isolation AgentToolIsolation `json:"isolation,omitempty"`
Expand Down Expand Up @@ -197,8 +183,10 @@ type PluginBundle struct {
// AgentTemplateSpec defines portable agent behavior.
// +kubebuilder:validation:XValidation:rule="!(has(self.systemPrompt) && has(self.systemPromptFrom))",message="systemPrompt and systemPromptFrom are mutually exclusive"
type AgentTemplateSpec struct {
// +required
ModelConfig AgentTemplateLocalReference `json:"modelConfig"`
// ModelConfig is required by managed harnesses and optional for BYO harnesses.
// +kubebuilder:validation:XValidation:rule="has(self.name) && self.name != ''",message="name must not be empty"
// +optional
ModelConfig *corev1.LocalObjectReference `json:"modelConfig,omitempty"`
// +optional
Description string `json:"description,omitempty"`
// +optional
Expand Down
Loading
Loading