Skip to content

Proposal: split agent configuration into Harness and AgentTemplate CRDs #2366

Description

@EItanya

Proposal: split agent configuration into Harness and AgentTemplate CRDs

Summary

This proposal separates authored agent behavior from the runtime that executes
it:

  • Harness defines a reusable runtime implementation and infrastructure policy.
  • AgentTemplate defines one logical agent and its ordinary MCP or
    AgentTemplate-backed tools.

The root AgentTemplate is always the public entrypoint. AgentTemplate-backed
tools recursively form the owned composition. A template with no such tools is
the ordinary single-agent case; composition does not require another resource
kind or creation API.

Harness + rooted AgentTemplate tree
                  │
              preparation
                  │
        one prepared runtime plan
                  │
        CreateAgentInstance (gRPC)
                  │
one AgentInstance + one public A2A conversation

The public invariant is:

one AgentInstance = one rooted AgentTemplate tree = one public conversation

The controller may use one or more internal runtime boundaries to satisfy the
template's isolation requirements. Processes, pods, Actors, and Substrate
topology do not appear in the authored AgentTemplate API.

Why change the current API?

The current API combines reusable behavior, runtime configuration, compute
lifecycle, and conversation state. The split lets teams reuse behavior across
runtime offerings, validate complete AgentTemplate trees before use, and keep live
conversation state outside Kubernetes reconciliation.

The proposed split gives each concern one owner:

Concern Owner
Runtime implementation and infrastructure policy Harness CRD
Model, prompt, plugins, tools, and skills AgentTemplate CRD
Preparation and runtime compilation Kagent controller
Live identity and lifecycle Kagent gRPC AgentInstance API
Conversation and Tasks A2A v1 gRPC

Harness

A Harness defines a reusable runtime implementation and its infrastructure
policy.

Rules:

  • Exactly one typed runtime variant is present.
  • The controller derives runtime capabilities; authors do not claim them.
  • Image overrides must be digest-pinned.
  • Each environment entry contains either a literal or a credential reference.
  • Preparation retains credential references but never reads secret values.
  • Omitted admission accepts no AgentTemplates.
  • Every runtime variant consumes the same internal resolved-bundle contract.
  • A Harness creates no compute merely by existing.

AgentTemplate

An AgentTemplate defines portable agent behavior and its available tools. A
tool may come from an MCP server or another AgentTemplate.

Conceptual shape:

type AgentTemplateSpec struct {
    ModelConfig   LocalObjectReference `json:"modelConfig"`
    SystemPrompt  PromptValue          `json:"systemPrompt,omitempty"`
    PromptTemplate PromptTemplateSpec  `json:"promptTemplate,omitempty"`
    Tools         []ToolBinding        `json:"tools,omitempty"`
    Skills        []Skill              `json:"skills,omitempty"`
    Plugins       []PluginBundle       `json:"plugins,omitempty"`
    Harnesses     HarnessAttachments   `json:"harnesses,omitempty"`
}

type PluginBundle struct {
    Source ArtifactSource `json:"source"`
    Skills []string       `json:"skills"`
}

type ToolBinding struct {
    MCP   *MCPToolBinding   `json:"mcp,omitempty"`
    Agent *AgentToolBinding `json:"agent,omitempty"`
}

type AgentToolBinding struct {
    Name        string               `json:"name"`
    Description string               `json:"description"`
    TemplateRef LocalObjectReference `json:"templateRef"`
    Isolation   AgentToolIsolation   `json:"isolation,omitempty"`
}

Rules:

  • modelConfig is the only required behavioral field initially.
  • References are same-namespace.
  • Each tool binding contains exactly one of mcp or agent.
  • MCP bindings select tools explicitly.
  • AgentTemplate labels are copied to new AgentInstances for discovery only.
  • Labels do not authorize access or attach resources.
  • The root AgentTemplate is always the public entrypoint.
  • Each agent binding creates a distinct logical tool, even when two
    bindings reference the same AgentTemplate.
  • description is required and tells the parent when to use that binding.
  • Shared compiles the referenced template into the Harness's native sub-agent
    configuration in the parent's runtime boundary.
  • Dedicated creates a separate runtime boundary and presents it to the parent
    as a binding-scoped MCP invoke tool. Omitted isolation is Shared.
  • Portable V2 supports one native sub-agent level per runtime boundary: a
    template reached through Shared may contain Dedicated, but not Shared,
    agent bindings. A Dedicated edge begins a new boundary and resets the limit.
  • Agent references form a same-namespace rooted tree; cycles are rejected.
  • Referenced templates inherit the selected root Harness and must request and be admitted
    by it.
  • Existing Go-template and ConfigMap prompt composition is reused; preparation
    renders and pins its inputs.
  • Plugin packages conform to the vendor-neutral Agent Plugins 1.0.0 format:
    root plugin.json, Agent Skills under skills/, and optional mcp.json.
  • V1 is a conformant skills-only Agent Plugins client. It exposes explicitly
    selected Agent Skills and ignores mcp.json and client extensions; MCP and
    agent composition remain in the existing typed tool bindings.
  • Plugin sources are immutable OCI digests, full Git commits, or version-pinned
    S3 objects. S3 sources contain bucket, key, and versionId; ETag is not a
    substitute for an S3 version ID.
  • Template inheritance and a BaseContext CRD are deferred.

Example plugin selection:

plugins:
- source:
    bucket:
      s3:
        bucket: agent-content
        key: plugins/repository-practices.zip
        versionId: 3HL4kqtJlcpXroDTDmJ+rmSpXd3dIbrHY
  skills:
  - code-review

This is package ingestion, not a kagent extension SDK. There is no
AgentPlugin CRD, marketplace controller, or OpenClaw-style in-process plugin
mechanism in this proposal. Agent Plugins 1.0.0 is currently a Working Draft,
so support targets its exact canonical schema identifier rather than an
unspecified compatible version.

Harness input contract

Harnesses do not consume the authored AgentTemplate graph directly. Preparation
compiles one internal resolved bundle per runtime boundary. A bundle contains
its root configuration, direct native sub-agent definitions, and binding-scoped
MCP configuration for dedicated agent tools. For CLI Harnesses, preparation
generates ordinary Claude --agents JSON or Codex custom-agent TOML rather than
requiring SDK integration. Dedicated endpoints expose only the bound child and
retain its private conversation continuity; Actor addresses and credentials are
not exposed to the model. This bundle is compiler output, not another CRD.

An agent tool has a deliberately small portable contract: the parent supplies a
natural-language task and receives one terminal result or error. Native Shared
invocation remains model-driven. Dedicated MCP exposes the same contract as
invoke(task); streaming child Tasks and child INPUT_REQUIRED are deferred.

Harness capabilities and conformance

Harness compatibility is visible but not user-authored. Kagent maintains
versioned capability metadata for each supported adapter and pinned runtime,
verifies it with executable conformance tests, and publishes the resulting
capabilities in Harness status. Preparation and user interfaces consume that
same record rather than inferring behavior from the Harness name.

The initial vocabulary covers native Shared agent tools and maximum depth,
Dedicated MCP tools and MCP injection, streaming, interruption, input-required
and approval handling, modalities, resume behavior, and complete checkpoint
coverage. Capabilities describe supported behavior; conditions describe current
adapter and dependency health.

Preparation derives requirements from the actual runtime bundle and reports the
unsupported field and a stable reason. A Harness without native sub-agents may
still prepare an MCP-only template, while a Shared binding fails precisely.
The conformance suite exercises a real pinned runtime: basic turns, streaming,
MCP and agent tools, interruption, approvals, resume, and checkpoint restoration.
A capability is enabled for a release only after its behavior passes.

Attachment and preparation

A root pair is eligible for preparation only when:

  1. the root and every referenced AgentTemplate explicitly include the Harness;
  2. the Harness selector admits every template in the tree;
  3. every resource is in the same namespace;
  4. the references resolve to a rooted, acyclic tree; and
  5. every resolved runtime bundle is compatible with the Harness.

The explicit request plus admission selector lets both resource owners approve
the relationship without adding an attachment CRD. Wildcard attachment is not
proposed initially.

For each accepted root pair, the controller:

  1. resolves the complete AgentTemplate tree and rejects cycles;
  2. resolves every prompt, plugin, tool, skill, and non-secret dependency;
  3. derives compatibility requirements for each runtime boundary;
  4. compiles native sub-agents and dedicated MCP bindings into runtime bundles;
  5. creates an immutable Substrate ActorTemplate for each bundle; and
  6. records one prepared revision.

The revision pins:

  • the Harness UID and generation;
  • every AgentTemplate UID and generation;
  • rendered prompts and resolved non-secret inputs;
  • selected plugin contents and source digests or object versions;
  • credential references, never credential values;
  • agent-tool bindings and isolation requirements; and
  • every ActorTemplate name, UID, generation, spec digest, and readiness state.

Minimum conditions are Accepted, ResolvedRefs, Compatible, and Prepared.
Failures identify the source, field path, and stable reason without exposing
credential values.

Changing any pinned input prepares a new revision. Existing AgentInstances stay
pinned. New creation uses the latest successfully prepared revision; whether a
previous successful revision remains usable during preparation is an open
question.

Example

apiVersion: kagent.dev/v1alpha2
kind: Harness
metadata:
  namespace: agent-team
  name: python-runtime
spec:
  python: {}
  workload:
    image: registry.example.com/agent-runtime@sha256:0000000000000000000000000000000000000000000000000000000000000000
  allowedAgentTemplates:
    selector:
      matchLabels:
        example.com/runtime: python
---
apiVersion: kagent.dev/v1alpha2
kind: AgentTemplate
metadata:
  namespace: agent-team
  name: incident-coordinator
  labels:
    example.com/runtime: python
    example.com/context: operations
    example.com/purpose: incident
spec:
  modelConfig:
    name: default-model
  systemPrompt: Coordinate the incident investigation.
  tools:
  - agent:
      name: researcher
      description: Research the available evidence before conclusions are drawn.
      templateRef:
        name: incident-researcher
      isolation: Shared
  - agent:
      name: reviewer
      description: Review a proposed conclusion for unsupported claims.
      templateRef:
        name: incident-reviewer
      isolation: Dedicated
  plugins:
  - source:
      bucket:
        s3:
          bucket: agent-content
          key: plugins/repository-practices.zip
          versionId: 3HL4kqtJlcpXroDTDmJ+rmSpXd3dIbrHY
    skills:
    - code-review
  harnesses:
    include:
    - name: python-runtime

The agent-tool references point to ordinary reusable AgentTemplates:

apiVersion: kagent.dev/v1alpha2
kind: AgentTemplate
metadata:
  namespace: agent-team
  name: incident-researcher
  labels:
    example.com/runtime: python
spec:
  modelConfig:
    name: default-model
  systemPrompt: Investigate the available evidence.
  harnesses:
    include:
    - name: python-runtime
---
apiVersion: kagent.dev/v1alpha2
kind: AgentTemplate
metadata:
  namespace: agent-team
  name: incident-reviewer
  labels:
    example.com/runtime: python
spec:
  modelConfig:
    name: default-model
  systemPrompt: Review the investigation for unsupported conclusions.
  harnesses:
    include:
    - name: python-runtime

Live instances are not CRDs

CreateAgentInstance(namespace, harness, agent_template, request_id) selects the
latest prepared tree and creates all required runtime members. The returned
AgentInstance is one namespaced gRPC resource persisted by kagent and exposes one
public A2A conversation through its root template.

This keeps high-churn conversation and lifecycle state out of Kubernetes while
leaving shared authored configuration under normal Kubernetes RBAC and GitOps.

Creation is idempotent through request_id. Editing or deleting source CRDs does
not mutate or cascade-delete existing instances.

Lifecycle operations apply to the complete tree. Kagent fences every runtime
member before suspend or checkpoint and publishes no checkpoint until the
public history head and complete internal snapshot set are committed. These
coordination details remain in the lifecycle API rather than the CRDs.

Deliberate initial limits

This proposal does not add:

  • multiple conversations per AgentInstance;
  • cyclic or shared-DAG AgentTemplate graphs;
  • different Harnesses within one AgentTemplate tree;
  • more than one consecutive native Shared level per runtime boundary;
  • native multi-profile host management;
  • template inheritance or BaseContext;
  • shared-store or shared-filesystem CRDs;
  • channel routing;
  • resume-or-create;
  • native in-process or gateway plugin extensions;
  • bucket providers other than S3; or
  • a public Thread or Session resource.

Shared mutable data remains behind explicitly selected external MCP tools. A
Dedicated agent tool also uses MCP, but its child Actor and private conversation
are owned and checkpointed with the AgentInstance. A later hosted
agent design may add AgentHost and HostedAgent if native multi-profile sharing
becomes a demonstrated requirement.

Questions for review

  1. Is the Harness/AgentTemplate responsibility split understandable?
  2. Is bilateral attachment worth the extra declaration on both resources?
  3. Where should per-pair preparation status live so it remains useful through
    kubectl without duplicating unbounded status?
  4. Should creation continue using the last successful prepared revision while a
    newer revision is still preparing?
  5. Is one native Shared level per runtime boundary the right portable
    intersection for CLI Harnesses, with deeper composition using Dedicated?
  6. Is a rooted tree sufficient initially, or is shared-DAG composition required?
  7. Is a skills-only Agent Plugins 1.0.0 client the right initial portable
    subset, with standard mcp.json support deferred?

Context

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    Status
    Backlog

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions