From 2867bafd1e2a593bf08377e9b373071e6fff5eb6 Mon Sep 17 00:00:00 2001 From: Vibhav Bobade Date: Fri, 20 Mar 2026 21:06:33 +0530 Subject: [PATCH 1/3] TEP-0164: Agent-Native Workflows This TEP proposes making Tekton an agent-native workflow engine by integrating with kagent for agent runtime capabilities and adding orchestration, security, and provenance layers. Introduces AgentRun and AgentConfig CRDs under agent.tekton.dev that: - Delegate agent execution to kagent (LLM, MCP tools, agent loop) - Use Tekton Pipelines for pre/post hooks - Add per-run RBAC, NetworkPolicy, OPA policy enforcement - Record provenance for Tekton Chains consumption Co-Authored-By: Anitha Priya Natarajan Co-Authored-By: Claude Opus 4.6 (1M context) --- teps/0164-agent-native-workflows.md | 1832 +++++++++++++++++++++++++++ teps/README.md | 1 + 2 files changed, 1833 insertions(+) create mode 100644 teps/0164-agent-native-workflows.md diff --git a/teps/0164-agent-native-workflows.md b/teps/0164-agent-native-workflows.md new file mode 100644 index 000000000..2db237476 --- /dev/null +++ b/teps/0164-agent-native-workflows.md @@ -0,0 +1,1832 @@ +--- +status: proposed +title: Agent-Native Workflows +creation-date: '2026-03-20' +last-updated: '2026-03-20' +authors: +- '@waveywaves' +- '@anithapriyanatarajan' +--- + +# TEP-0164: Agent-Native Workflows + +--- + + +- [Summary](#summary) +- [Motivation](#motivation) + - [Goals](#goals) + - [Non-Goals](#non-goals) + - [Use Cases](#use-cases) + - [AI Code Review Gate](#ai-code-review-gate) + - [Multi-Step Test Lifecycle](#multi-step-test-lifecycle) + - [Deployment Decision Agent](#deployment-decision-agent) + - [Cluster Diagnostics Agent](#cluster-diagnostics-agent) + - [Requirements](#requirements) +- [Proposal](#proposal) + - [Overview](#overview) + - [AgentRun CRD](#agentrun-crd) + - [AgentConfig CRD](#agentconfig-crd) + - [Agent Lifecycle](#agent-lifecycle) + - [Standalone AgentRun](#standalone-agentrun) + - [AgentRun in a PipelineRun](#agentrun-in-a-pipelinerun) + - [Multiple Agents in a PipelineRun](#multiple-agents-in-a-pipelinerun) + - [Integration with kagent](#integration-with-kagent) + - [Integration with Tekton Pipelines](#integration-with-tekton-pipelines) + - [Codebase Access via MCP Tools](#codebase-access-via-mcp-tools) + - [Security Layer](#security-layer) + - [Provenance](#provenance) + - [Notes and Caveats](#notes-and-caveats) +- [Design Details](#design-details) + - [Execution Flow: Standalone AgentRun](#execution-flow-standalone-agentrun) + - [Execution Flow: PipelineRun with Agent Steps](#execution-flow-pipelinerun-with-agent-steps) + - [Agent CR Scoping and Reuse](#agent-cr-scoping-and-reuse) + - [kagent Resource Resolution](#kagent-resource-resolution) + - [Security Implementation Details](#security-implementation-details) + - [CustomTask Adapter](#customtask-adapter) + - [Provenance Recording](#provenance-recording) + - [AgentConfig Snapshot](#agentconfig-snapshot) +- [Design Evaluation](#design-evaluation) + - [Reusability](#reusability) + - [Simplicity](#simplicity) + - [Flexibility](#flexibility) + - [Conformance](#conformance) + - [User Experience](#user-experience) + - [Performance](#performance) + - [Risks and Mitigations](#risks-and-mitigations) + - [Drawbacks](#drawbacks) +- [Alternatives](#alternatives) + - [Build Agent Stack Inside Tekton](#build-agent-stack-inside-tekton) + - [Pod-Per-Run Without Agent CR](#pod-per-run-without-agent-cr) + - [Pipeline spec.agents Field](#pipeline-specagents-field) + - [Volume Mounts for Codebase Access](#volume-mounts-for-codebase-access) + - [Convention-Based Container Wrapping](#convention-based-container-wrapping) +- [Implementation Plan](#implementation-plan) + - [Milestones](#milestones) + - [Test Plan](#test-plan) + - [Infrastructure Needed](#infrastructure-needed) + - [Upgrade and Migration Strategy](#upgrade-and-migration-strategy) + - [Implementation Pull Requests](#implementation-pull-requests) +- [References](#references) + + +## Summary + +AI agents are appearing in CI/CD pipelines, doing code review, security +analysis, test generation, and deployment decisions. Today they run as +opaque Python scripts inside container steps. Tekton cannot see what +model they called, which tools they used, how many tokens they consumed, +or whether they followed security policy. + +This TEP proposes making Tekton an **agent-native workflow engine** by +introducing `AgentRun` and `AgentConfig` CRDs that use +[kagent][kagent]'s Agent CR as the agent runtime and add per-run +security controls, pipeline integration via CustomTask, and provenance +recording. + +```mermaid +flowchart TB + subgraph User["Pipeline Author"] + AR["AgentRun CR
(goal + configRef)"] + AC["AgentConfig CR
(model + tools + RBAC + OPA)"] + end + + subgraph TEP["AgentRun Controller (this TEP)"] + Lifecycle["Agent Lifecycle
create / reuse / cleanup"] + Security["Security Layer
RBAC + NetworkPolicy + OPA"] + Provenance["Provenance
model + tools + tokens"] + end + + subgraph Kagent["kagent"] + AgentCR["Agent CR"] + MC["ModelConfig"] + RMS["RemoteMCPServer"] + ADK["ADK Runtime"] + end + + subgraph Tekton["Tekton Pipelines"] + CT["CustomTask Protocol"] + PR["PipelineRun"] + Chains["Tekton Chains"] + end + + AR --> TEP + AC --> TEP + TEP --> AgentCR + TEP --> Security + TEP --> Provenance + MC --> AgentCR + RMS --> AgentCR + AgentCR --> ADK + CT --> TEP + PR --> CT + Provenance --> Chains +``` + +The execution model uses **kagent's existing Agent CR**, which creates a +Deployment and Service for the agent. For standalone AgentRuns, the Agent +CR is created for a single goal and cleaned up after completion. For +PipelineRuns with multiple agent steps, the Agent CR is created at the +first agent step and shared across subsequent steps that reference the +same AgentConfig, preserving conversation context. The Agent CR is +cleaned up when the PipelineRun completes. + +The controller adds security controls that neither kagent nor Tekton +Pipelines provides alone: per-run RBAC scoping with configurable rules, +per-run NetworkPolicy generation, layered policy enforcement (OPA at +goal submission + kagent tool allowlists at execution), prompt +auditability, and provenance recording for [Tekton Chains][chains]. + +A [CustomTask][customtask] adapter allows AgentRuns to participate in +Tekton Pipeline DAGs, making agent steps composable with traditional +container steps using standard result passing and `when` expressions. + +## Motivation + +AI agents are already appearing in CI/CD pipelines, but they are +invisible to the platform. A [comparison of two real-world pipelines][pipeline-comparison], +one implemented [without agents][pipeline-without-agents] and one +[with agents][pipeline-with-agents], illustrates the problem: + +**Without agents** (614 lines, 12 tasks): Template-based test plan +generation using static `case` statements. A manual approval gate where +humans write tests from scratch. Raw pass/fail counts posted as results. + +**With agents** (1247 lines, 14 tasks): An agent reads actual source +code, generates real test implementations, self-reviews its own work, +triages test failures (infrastructure vs test bugs vs real regressions), +and produces an intelligent summary report. + +The following diagram illustrates the difference between today's opaque +agent steps and the governed model proposed by this TEP: + +```mermaid +flowchart TB + subgraph TODAY["Today: Opaque Agent Steps"] + direction TB + T_Step["Container Step"] + T_Pip["pip install anthropic"] + T_Key["Read API key from file"] + T_Call["Call LLM directly"] + T_Parse["Parse response with regex"] + T_Out["Write stdout"] + + T_Step --> T_Pip --> T_Key --> T_Call --> T_Parse --> T_Out + + T_Invisible["Tekton sees:
container ran, exit 0"] + T_Out -.-> T_Invisible + end + + subgraph TEP["TEP-0164: Governed Agent Steps"] + direction TB + A_Run["AgentRun CR"] + A_OPA["Gate 1: OPA evaluates goal"] + A_RBAC["Gate 2: scoped RBAC + NetworkPolicy"] + A_Agent["kagent Agent CR
(model + tools configured)"] + A_Tools["Gate 3: allowedTools enforced"] + A_MCP["Gate 4: MCP-only data access"] + A_Result["Gate 5: results + provenance"] + + A_Run --> A_OPA --> A_RBAC --> A_Agent --> A_Tools --> A_MCP --> A_Result + end +``` + +The agents transform the pipeline from a structure-only scaffold into an +intelligence-augmented workflow. But every agent step is an opaque +container: + +```python +# Repeated in every agent step: ~100 lines of boilerplate +subprocess.check_call([sys.executable, "-m", "pip", "install", "anthropic", "-q"]) +client = anthropic.Anthropic(api_key=api_key) +# ... 80 more lines of prompt construction, response parsing +``` + +Tekton sees these as ordinary container steps. There is no way for a +platform operator to: + +- Scope agent permissions to exactly the Kubernetes resources they need +- Enforce which tools an agent may call, with real-time policy evaluation +- Audit which models and prompts were used across the organization +- Record agent behavior in SLSA-compatible attestations +- Set token budgets or network isolation per agent execution + +Meanwhile, [kagent][kagent] provides CRDs for [model configuration][kagent-models] +(8 providers), [MCP tool servers][kagent-tools] (with automatic tool +discovery), and agent runtimes (Python and Go ADKs). But kagent does not provide pipeline +orchestration, supply-chain provenance, or the per-execution security +controls that a CI/CD platform requires. + +### Goals + +1. Define `AgentRun` and `AgentConfig` CRDs that enable goal-driven + agent execution with per-run security controls +2. Use kagent's Agent CR as the agent runtime, creating Agent CRs + scoped to AgentRun or PipelineRun lifetimes +3. Use kagent's `ModelConfig` and `RemoteMCPServer` CRDs for model and + tool configuration without reimplementing them +4. Add per-run security controls: RBAC scoping with configurable rules, + NetworkPolicy, layered policy enforcement (OPA + kagent tool + restrictions) +5. Provide a CustomTask adapter so AgentRuns participate in Tekton + Pipeline DAGs with result passing and `when` expression support +6. Record agent execution provenance for consumption by Tekton Chains +7. Route all data access (codebase, cluster state) through MCP tool + calls for uniform policy enforcement and audit + +### Non-Goals + +1. Building a new agent runtime (kagent provides the ADK) +2. Building LLM provider integrations (kagent supports 8 providers) +3. Building MCP server infrastructure (kagent provides + `RemoteMCPServer` and `ToolServer`) +4. Adding new fields to Tekton's Pipeline or PipelineRun CRDs +5. Defining multi-agent communication protocols +6. Implementing or bundling any LLM model or inference engine + +### Use Cases + +#### AI Code Review Gate + +As a platform engineer, I want an agent step in my Pipeline that reviews +a pull request diff and returns a structured `approved`/`findings` +result, so that downstream steps can conditionally block or proceed, +with full provenance of what the agent saw and decided. + +This is a standalone agent task. One AgentRun, one goal, one result. + +#### Multi-Step Test Lifecycle + +As a QA engineer, I want a pipeline where an agent analyzes a codebase, +generates test implementations, self-reviews them, and triages any +failures. The agent should maintain context across these steps so it +does not re-read the codebase at each step. + +This is a multi-step agent workflow. Multiple AgentRuns in a PipelineRun +sharing the same Agent CR, with conversation context preserved across +steps. + +#### Deployment Decision Agent + +As a DevOps engineer, I want an agent step that queries my observability +stack via permitted MCP tools and returns a structured `proceed`/`hold` +recommendation, enforced by OPA policy so it cannot access resources +outside its declared scope. + +#### Cluster Diagnostics Agent + +As a cluster administrator, I want to create an AgentRun with a goal +like "diagnose why deployment api-server is failing in namespace +production" and have the agent investigate using only the Kubernetes +resources I have authorized via per-run RBAC. + +### Requirements + +| ID | Requirement | Priority | +|----|-------------|----------| +| R1 | AgentRun controller MUST create kagent Agent CRs for agent execution | Must | +| R2 | Agent CRs MUST be scoped to AgentRun (standalone) or PipelineRun (multi-step) lifetime | Must | +| R3 | Multiple AgentRuns with the same configRef in a PipelineRun MUST reuse the same Agent CR | Must | +| R4 | AgentRun controller MUST generate per-run RBAC using rules from AgentConfig | Must | +| R5 | AgentConfig MUST reference kagent ModelConfig for model selection | Must | +| R6 | AgentConfig MUST reference kagent RemoteMCPServer for tool providers | Must | +| R7 | Agent execution results MUST be recorded in AgentRun status | Must | +| R8 | Policy enforcement MUST use a five-gate model: OPA at goal submission (Gate 1) + RBAC and NetworkPolicy (Gate 2) + allowedTools/requireApproval at tool execution (Gate 3) + MCP-only data access (Gate 4) + provenance (Gate 5) | Must | +| R9 | OPA goal-level input MUST be constructed by the controller from AgentRun metadata, not from LLM-controlled data | Must | +| R10 | Per-tool-call OPA enforcement inside the kagent ADK SHOULD be contributed upstream as a future enhancement | Should | +| R11 | A CustomTask adapter MUST allow AgentRun to participate in Pipeline DAGs via the Tekton CustomRun protocol | Must | +| R12 | AgentRun results MUST be passable to downstream Pipeline steps via `when` expressions | Must | +| R13 | NetworkPolicy SHOULD be generated when networkPolicy is set to strict | Should | +| R14 | Provenance metadata MUST be recorded in AgentRun status (observable fields in Phase 1, full execution trace when kagent telemetry is available) | Must | +| R15 | All per-run resources MUST use owner references for garbage collection | Must | +| R16 | AgentRun SHOULD support Tekton PipelineRun-based pre/post hooks | Should | +| R17 | AgentRun MUST fail gracefully with a clear message when kagent CRDs are not installed | Must | +| R18 | OPA policy MUST default to fail-closed (deny-all) when no policy is configured, unconditionally | Must | +| R19 | All codebase and cluster access SHOULD go through MCP tool calls, not volume mounts | Should | + +## Proposal + +### Overview + +The following diagram shows how a PipelineRun with multiple agent steps +and a traditional container step works end-to-end: + +```mermaid +sequenceDiagram + participant User + participant Pipeline as PipelineRun + participant Ctrl as AgentRun Controller + participant K as kagent Controller + participant A1 as Agent: test-agent + participant A2 as Agent: security-agent + participant MCP as MCP Tool Server + + User->>Pipeline: Create PipelineRun + + Note over Pipeline,Ctrl: Step 1: security-review (configRef: security-agent) + Pipeline->>Ctrl: AgentRun created + Ctrl->>Ctrl: Gate 1: OPA evaluates goal + Ctrl->>Ctrl: Create RBAC (SA + Role) + Ctrl->>K: Create Agent CR (security-agent) + K->>A2: Deployment + Service ready + Ctrl->>A2: POST goal + A2->>MCP: read_file (allowedTools enforced) + MCP-->>A2: file content + A2-->>Ctrl: result: {vulnerabilities: [...]} + Pipeline->>Pipeline: results available + + Note over Pipeline,Ctrl: Step 2: analyze-code (configRef: test-agent) + Pipeline->>Ctrl: AgentRun created + Ctrl->>Ctrl: Gate 1: OPA evaluates goal + Ctrl->>Ctrl: Gate 2: Create RBAC (SA + Role) + Ctrl->>K: Create Agent CR (test-agent) + K->>A1: Deployment + Service ready + Ctrl->>A1: POST goal + A1->>MCP: search_code, list_functions (Gate 3: allowedTools) + A1-->>Ctrl: result: {analysis: ...} + + Note over Pipeline,Ctrl: Step 3: generate-tests (configRef: test-agent, REUSES agent) + Pipeline->>Ctrl: AgentRun created + Ctrl->>Ctrl: Gate 1: OPA evaluates goal + Ctrl->>Ctrl: Find existing Agent CR by labels + Ctrl->>A1: POST goal (agent has context from step 2) + A1->>MCP: read_file, write_file (Gate 3: allowedTools) + A1-->>Ctrl: result: {tests_generated: 12} + + Note over Pipeline,Ctrl: Step 4: run-tests (normal container step) + Pipeline->>Pipeline: go test ./... + + Note over Pipeline,Ctrl: PipelineRun completes + Pipeline->>Pipeline: Owner refs trigger cleanup + K->>A1: Delete Deployment + K->>A2: Delete Deployment +``` + +``` +PipelineRun +│ +├── AgentRun Controller sees agent steps (CustomTask references) +│ +├── Creates kagent Agent CR per unique configRef +│ └── kagent controller creates Deployment + Service +│ └── Agent HTTP server running ADK runtime +│ +├── Step 1 (agent, security-agent): Gate 1 OPA, Gate 2 RBAC, create Agent CR, POST goal +├── Step 2 (agent, test-agent): Gate 1 OPA, Gate 2 RBAC, create Agent CR, POST goal +├── Step 3 (agent, test-agent): Gate 1 OPA, reuse Agent CR, POST goal (context preserved) +├── Step 4 (container): normal Tekton step, uses agent results +│ +├── PipelineRun completes +└── Agent CRs garbage collected via owner references +``` + +| Layer | Responsibility | Owner | +|-------|---------------|-------| +| Agent Runtime | LLM calls, MCP tool execution, agent loop | kagent (Agent CR, ADK) | +| Model + Tool Config | Model endpoints, credentials, MCP servers | kagent (ModelConfig, RemoteMCPServer) | +| Agent Lifecycle | Create/reuse/cleanup Agent CRs per scope | AgentRun controller (this TEP) | +| Security | Per-run RBAC, NetworkPolicy, OPA | AgentRun controller (this TEP) | +| Pipeline Integration | DAG sequencing, hooks, result passing | Tekton Pipelines + CustomTask | +| Provenance | Attestation of agent behavior | AgentRun status + Tekton Chains | + +### AgentRun CRD + +```yaml +apiVersion: agent.tekton.dev/v1alpha1 +kind: AgentRun +metadata: + name: debug-api-server +spec: + configRef: + name: cluster-diagnostics + goal: | + Diagnose why deployment 'api-server' is failing in namespace 'production'. + context: + hints: + - "Check recent events" + - "Review pod logs for OOMKilled" +status: + phase: Succeeded + startTime: "2026-03-20T14:20:04Z" + completionTime: "2026-03-20T14:22:30Z" + iterations: 3 + agentRef: cluster-diagnostics-7xk2 # kagent Agent CR used + results: + - name: diagnosis + value: "OOMKilled: memory limit 256Mi too low for request pattern" + - name: recommendation + value: "Increase memory limit to 512Mi" + provenance: + buildType: "https://tekton.dev/agent-provenance/v1" + reproducible: false + internalParameters: + model: + provider: Anthropic + modelId: "claude-sonnet-4-6" + systemPromptHash: "sha256:abc123..." + tokenUsage: + totalTokens: 4200 + policyDecisions: + layer1_opa: + evaluated: 1 + allowed: 1 + denied: 0 +``` + +### AgentConfig CRD + +```yaml +apiVersion: agent.tekton.dev/v1alpha1 +kind: AgentConfig +metadata: + name: cluster-diagnostics +spec: + # -- kagent references -------------------------- + modelConfigRef: + name: claude-sonnet + namespace: kagent-system + + toolServers: + - ref: + name: k8s-read-tools + namespace: kagent-system + kind: RemoteMCPServer + allowedTools: + - k8s_get_resources + - k8s_get_logs + - k8s_describe + requireApproval: [] + + # -- Agent behavior ----------------------------- + maxIterations: 5 + timeout: 10m + tokenBudget: 16384 # enforced in Phase 2; informational in Phase 1 + systemPrompt: | + You are a Kubernetes cluster diagnostics agent. + You may ONLY use the tools provided. + + # -- Per-run RBAC (configurable rules) ---------- + rbac: + rules: + - apiGroups: [""] + resources: [pods, services, events] + verbs: [get, list, watch] + - apiGroups: [apps] + resources: [deployments, replicasets] + verbs: [get, list, watch] + + # -- OPA policy --------------------------------- + policy: + opa: + configMapRef: + name: agent-policies + key: tool-policy.rego + defaultDeny: true + + # -- Network isolation -------------------------- + networkPolicy: strict + + # -- Tekton hooks (optional) -------------------- + preHooks: + pipelineRef: + name: prompt-security-scan + postHooks: + pipelineRef: + name: audit-bundle-collection +``` + +### Agent Lifecycle + +#### Standalone AgentRun + +When an AgentRun is created outside a PipelineRun: + +1. Controller creates a kagent Agent CR, owner-referenced to the + AgentRun +2. kagent creates the Deployment + Service +3. Controller waits for Agent Ready condition +4. Controller POSTs the goal to the Agent Service via HTTP +5. Controller collects results from the response +6. AgentRun marked Succeeded/Failed +7. Agent CR garbage collected via owner reference + +The Agent Deployment is short-lived. It exists only for this one goal. + +#### AgentRun in a PipelineRun + +When multiple AgentRuns in a PipelineRun reference the same +AgentConfig: + +1. First AgentRun: controller creates a kagent Agent CR, labeled + with the PipelineRun UID and AgentConfig name +2. kagent creates the Deployment + Service +3. Controller POSTs the first goal, collects results +4. Second AgentRun (same configRef, same PipelineRun): controller + finds the existing Agent CR by label, reuses it +5. Controller POSTs the second goal. The agent has conversation + context from the first goal. +6. PipelineRun completes: Agent CR is cleaned up + +The Agent maintains conversation context across all steps that share +the same configRef within a PipelineRun. + +#### Multiple Agents in a PipelineRun + +Different configRef values create different Agent CRs: + +```yaml +tasks: + - name: security-review + taskRef: + apiVersion: agent.tekton.dev/v1alpha1 + kind: AgentRun + params: + - name: configRef + value: security-agent # Agent CR #1 + - name: goal + value: "Review for vulnerabilities" + + - name: analyze-code + taskRef: + apiVersion: agent.tekton.dev/v1alpha1 + kind: AgentRun + params: + - name: configRef + value: test-agent # Agent CR #2 + - name: goal + value: "Analyze the codebase" + + - name: generate-tests + runAfter: [analyze-code] + taskRef: + apiVersion: agent.tekton.dev/v1alpha1 + kind: AgentRun + params: + - name: configRef + value: test-agent # Reuses Agent CR #2 + - name: goal + value: "Generate tests based on your analysis" +``` + +This PipelineRun creates two Agent CRs. `security-agent` handles one +step. `test-agent` handles two steps with shared context. + +### Integration with kagent + +The AgentRun controller uses kagent in two ways: + +**1. Agent CR (agent runtime)** + +The controller creates kagent `Agent` CRs via the dynamic client. Each +Agent CR references a kagent `ModelConfig` for the LLM provider and +kagent `RemoteMCPServer` resources for tools. kagent's controller +handles creating the Deployment, Service, and configuring the ADK +runtime. The AgentRun controller does not build Pods directly. + +**2. Configuration CRDs (read-only, via dynamic client)** + +The controller reads kagent `ModelConfig` and `RemoteMCPServer` CRs to +validate that referenced models exist and tools are discovered. These +are long-lived cluster resources managed by platform administrators. + +The controller interacts with all kagent CRDs via +`k8s.io/client-go/dynamic` to avoid Go module version coupling +(kagent uses k8s.io v0.35, this controller uses v0.32). + +### Integration with Tekton Pipelines + +**CustomTask adapter** (Phase 1): AgentRun implements the Tekton +CustomTask protocol, allowing it to be referenced from Pipeline steps: + +```yaml +apiVersion: tekton.dev/v1 +kind: Pipeline +metadata: + name: review-and-deploy +spec: + tasks: + - name: code-review + taskRef: + apiVersion: agent.tekton.dev/v1alpha1 + kind: AgentRun + params: + - name: configRef + value: code-review-agent + - name: goal + value: "Review the PR diff for security issues" + - name: deploy + runAfter: [code-review] + when: + - input: "$(tasks.code-review.results.approved)" + operator: in + values: ["true"] + taskRef: + name: kubectl-deploy +``` + +**Pre/post hooks**: Optional Tekton PipelineRuns for security scanning +(pre) and audit collection (post) around agent execution. Created via +dynamic client, owner-referenced for cleanup. When Tekton Pipelines is +not installed, hooks configuration is rejected at validation time. + +### Codebase Access via MCP Tools + +Agents access codebases and cluster state through [MCP][mcp] tool +calls, not through volume mounts. This is a deliberate architectural decision. + +Volume mounts give the agent raw filesystem access. The agent can read +any file on the mounted volume. OPA policy cannot restrict which files +the agent reads because file reads happen inside the container, outside +the tool call protocol. + +MCP tools route every data access through the tool protocol. Every +`read_file`, `search_code`, `list_functions` call goes through the MCP +server, which means every call is restricted by kagent's +`allowedTools` enforcement and recorded in the agent's tool call +history for provenance. + +``` +Volume mount: Agent reads filesystem directly. No restriction. No audit. +MCP tools: Agent calls tool. kagent enforces allowedTools. MCP server reads file. Audited. +``` + +The following diagram shows how multiple agents access the same +codebase through a shared MCP server with different tool allowlists: + +```mermaid +flowchart LR + subgraph Pipeline["PipelineRun"] + Clone["git-clone step
(writes to PVC)"] + end + + subgraph MCP["MCP ToolServer Pod"] + PVC["Workspace PVC
(mounted read/write)"] + RF["read_file"] + SC["search_code"] + LF["list_functions"] + WF["write_file"] + end + + subgraph Agents["Agent Deployments"] + A1["Security Agent
allowedTools:
read_file, search_code"] + A2["Test Agent
allowedTools:
read_file, list_functions,
write_file"] + end + + Clone --> PVC + A1 -->|"read_file"| RF + A1 -->|"search_code"| SC + A1 -.->|"BLOCKED"| WF + A2 -->|"read_file"| RF + A2 -->|"list_functions"| LF + A2 -->|"write_file"| WF +``` + +For multi-agent pipelines working on the same codebase, all agents talk +to the same MCP server. Each agent has its own tool allowlist controlling +what it can access through that server: + +``` +PipelineRun +├── MCP Server (ToolServer CR, has workspace PVC mounted) +│ ├── read_file +│ ├── search_code +│ ├── list_functions +│ └── write_file +│ +├── Agent CR #1 (security-agent) +│ └── allowedTools: [read_file, search_code] +│ +├── Agent CR #2 (test-agent) +│ └── allowedTools: [read_file, list_functions, write_file] +``` + +Agents that need to write files (test generation) use a `write_file` +tool on the MCP server. The write is controlled by kagent's +`allowedTools` (Gate 3) and recorded in the provenance trace (Gate 5). + +### Security Layer + +#### Threat Model + +Agentic workflows introduce threats that do not exist in traditional +container-based CI/CD: + +```mermaid +flowchart TD + subgraph Threats["Threat Sources"] + LLM["LLM itself
(hallucination,
instruction failure)"] + Prompt["Prompt injection
(via pipeline params,
Jira tickets, PR descriptions)"] + Tools["Malicious tools
(compromised MCP servers,
poisoned skill packages)"] + Data["Poisoned data
(crafted pod logs,
error messages designed
to manipulate agent)"] + Human["Misconfiguration
(overly broad permissions,
missing policy)"] + end + + subgraph Impacts["What Can Go Wrong"] + Access["Agent accesses data
it should not"] + Exfil["Agent exfiltrates data
to LLM provider"] + Write["Agent calls write tools
it should not"] + Cost["Agent runs indefinitely
consuming tokens"] + Invisible["Nobody knows
what happened"] + end + + LLM --> Access + LLM --> Write + Prompt --> Access + Prompt --> Write + Tools --> Exfil + Data --> Write + Human --> Access + Human --> Cost + LLM --> Invisible + Tools --> Invisible +``` + +The [CoSAI Principles for Secure Agentic Systems][cosai] state: "The +non-deterministic nature of AI means we cannot always predict the exact +path an agent will take, making strong foundational cybersecurity +controls that strictly limit potential actions to expected and intended +purposes critical." + +The [OWASP Top 10 for Agentic Applications][owasp-agentic] identifies +agent behavior hijacking (ASI01), prompt injection (ASI02), and tool +misuse (ASI03) as the top risks. These are the threats this security +layer addresses. + +#### Five-Gate Security Architecture + +Each threat is stopped at a specific point in the execution path. +No single gate is sufficient. The five gates compose Kubernetes RBAC, +NetworkPolicy, OPA, kagent tool restrictions, and MCP protocol into a +coherent security boundary around agent execution. + +```mermaid +flowchart TD + Goal["Goal submitted"] + + subgraph G1["Gate 1: Goal Admission"] + OPA["OPA evaluates goal
+ namespace + tools"] + PreHook["Pre-hook pipeline
(prompt security scan)"] + Validate["AgentConfig validation
(RBAC rules, token budget)"] + end + + subgraph G2["Gate 2: Cluster Access"] + SA["Per-run ServiceAccount"] + Role["Per-run Role
(from AgentConfig.rbac.rules)"] + NetPol["Per-run NetworkPolicy
(only declared endpoints)"] + end + + subgraph G3["Gate 3: Tool Restriction"] + Allowed["kagent allowedTools
(static allowlist)"] + Approval["kagent requireApproval
(human gate)"] + FutureOPA["Future: per-tool-call OPA
(conditional policy)"] + end + + subgraph G4["Gate 4: Data Flow"] + MCPOnly["All access via MCP tools
(no volume mounts)"] + Audit["Every tool call
recorded in trace"] + end + + subgraph G5["Gate 5: Attestation"] + Prov["Provenance recorded
(buildType, model, tools)"] + NonDet["reproducible: false
(explicit non-determinism)"] + Chains["Tekton Chains
(cryptographic signature)"] + PostHook["Post-hook pipeline
(audit bundle)"] + end + + Goal --> G1 + G1 -->|pass| G2 + G2 --> G3 + G3 --> G4 + G4 --> G5 + + G1 -->|fail| Reject1["REJECT: goal denied"] + G3 -->|fail| Reject2["REJECT: tool blocked"] +``` + +| Gate | Threat Addressed | Provided By | Exists Today? | +|------|-----------------|-------------|---------------| +| 1. Goal admission | Prompt injection, misconfiguration | OPA (library) + Tekton pre-hook PipelineRun | Yes | +| 2. Cluster access | Unauthorized data access, lateral movement | Kubernetes RBAC + NetworkPolicy (native K8s) | Yes | +| 3. Tool restriction | Tool misuse, unauthorized write operations | kagent allowedTools + requireApproval | Yes | +| 4. Data flow | Data exfiltration, unaudited access | MCP protocol (all access via tool calls) | Yes | +| 5. Attestation | Invisible agent behavior, no accountability | Provenance struct + Tekton Chains | Partially (provenance new, Chains exists) | + +Every gate except provenance uses existing infrastructure. AgentRun +does not invent new security primitives. It composes existing +Kubernetes, kagent, and Tekton mechanisms into a per-execution +security boundary with cleanup and audit trail. + +#### Gate 1: Goal Admission (OPA) + +The controller evaluates OPA policy at goal submission time. The OPA +input includes the goal text, requested tool servers, target +namespaces, and the AgentConfig reference. OPA can reject the entire +execution before any agent is created. + +```rego +package agent.goals + +default allow = false + +allow { + input.namespace in data.allowed_namespaces +} + +deny[msg] { + some tool in input.requested_tools + tool in data.write_tools + not tool in input.require_approval + msg := sprintf("write tool %s must have requireApproval set", [tool]) +} +``` + +OPA input is constructed by the controller, not the LLM: + +```go +input := map[string]interface{}{ + "goal": agentRun.Spec.Goal, + "namespace": agentRun.Namespace, + "requested_tools": allowedToolsList, + "require_approval": requireApprovalList, + "config": agentConfigName, +} +``` + +Default policy is **fail-closed**: when no OPA policy ConfigMap is +configured, goal submission is denied unconditionally. The +`defaultDeny` field in AgentConfig is reserved for future use to +allow explicit opt-in to permissive mode; the default behavior is +always deny. + +The controller also sets `allowedTools` and `requireApproval` on +the Agent CR, which kagent enforces at Gate 3. + +#### Gate 2: Cluster Access (RBAC + NetworkPolicy) + +For each AgentRun (or per PipelineRun for shared agents), the +controller creates: +- A `ServiceAccount` named `-sa` +- A `Role` with rules from `AgentConfig.spec.rbac.rules` +- A `RoleBinding` binding the Role to the ServiceAccount + +The kagent Agent CR is configured with +`spec.declarative.deployment.serviceAccountName` so the agent +Deployment uses this scoped ServiceAccount. All resources are +owner-referenced for cleanup. + +When `networkPolicy: strict`, the controller generates a +NetworkPolicy that: +- Allows ingress from the AgentRun controller (HTTP communication) +- Allows egress to: Kubernetes API server (resolved cluster IP, + 443/tcp), DNS (53/udp), declared MCP tool server endpoints +- Denies all other traffic + +#### Gate 3: Tool Restriction (kagent) + +kagent's ADK runtime enforces `allowedTools` (the agent cannot call +tools not in the list) and `requireApproval` (the agent pauses and +waits for human approval before calling specified tools). These are +set by the AgentRun controller when constructing the Agent CR from +the AgentConfig. + +Future: per-tool-call OPA evaluation inside the kagent ADK, where +the full tool call input (namespace, resource type, label selectors) +is available. This requires an upstream contribution to kagent. + +#### Gate 4: Data Flow (MCP) + +All codebase and cluster access goes through MCP tool calls, not +volume mounts. This means every data access is visible in the +execution trace, restricted by the tool allowlist, and auditable +in provenance. See [Codebase Access via MCP Tools](#codebase-access-via-mcp-tools). + +#### Gate 5: Attestation (Provenance + Chains) + +See [Provenance](#provenance) for the full provenance schema, +buildType URI, telemetry data flow, and Chains integration. + +### Provenance + +#### Agent Provenance vs Build Provenance + +Traditional CI/CD provenance (SLSA, in-toto) assumes a deterministic +build: the same source, builder, and parameters produce the same +artifact. Agent execution is fundamentally different. The same goal, +model, and tools can produce different tool call sequences, different +reasoning paths, and different results on every run. This is not a +bug; it is the nature of LLM-based reasoning. + +This means agent provenance must be **descriptive** (what happened) +rather than **prescriptive** (what should happen). A verifier cannot +reproduce an agent execution from its provenance. Instead, provenance +answers: what model was used, what prompt was given, what tools were +called in what order, what policy decisions were made, and what +results were produced. + +The TEP proposes an agentic provenance extension that records this +metadata in a format compatible with [SLSA provenance][slsa] and +informed by the [PROV-AGENT][prov-agent] schema for tracking AI +agent interactions. The [LLM Agents for Interactive Workflow +Provenance][workflow-provenance] reference architecture provides +additional context for provenance capture in non-deterministic +workflows. + +#### buildType + +The TEP defines a new buildType URI for agentic executions: + +``` +https://tekton.dev/agent-provenance/v1 +``` + +This buildType signals to verifiers that the execution is +non-deterministic, the artifact cannot be reproduced from the same +inputs, and the provenance contains agent-specific fields +(model identity, tool call sequence, policy decisions). + +#### Provenance Fields + +The `AgentRun.status.provenance` captures the following, mapped to +SLSA predicate fields: + +```yaml +provenance: + # Build definition + buildType: "https://tekton.dev/agent-provenance/v1" + reproducible: false + reproducibilityNote: "LLM-based agent execution is non-deterministic" + + # External parameters (user-provided inputs) + externalParameters: + goal: "Diagnose why deployment api-server is failing" + goalHash: "sha256:def456..." + hints: ["Check recent events", "Review pod logs"] + agentConfigRef: cluster-diagnostics + agentConfigHash: "sha256:789abc..." # hash of snapshotted config + + # Internal parameters (system-determined) + internalParameters: + model: + provider: Anthropic + modelId: "claude-sonnet-4-6" + apiVersion: "2023-06-01" + temperature: 0.2 + maxTokens: 4096 + tokenBudget: 16384 + systemPromptHash: "sha256:abc123..." + maxIterations: 5 + timeout: "10m" + opaPolicy: + configMapRef: agent-policies + policyHash: "sha256:fed321..." + + # Resolved dependencies (runtime-discovered) + resolvedDependencies: + - name: kagent-agent-cr + uri: "kagent.dev/v1alpha2/Agent/default/cluster-diagnostics-7xk2" + - name: adk-runtime-image + uri: "ghcr.io/kagent-dev/kagent/app" + digest: "sha256:a1b2c3..." + - name: mcp-server-k8s-read-tools + uri: "kagent.dev/v1alpha2/RemoteMCPServer/kagent-system/k8s-read-tools" + toolsDiscovered: ["k8s_get_resources", "k8s_get_logs", "k8s_describe"] + - name: model-config + uri: "kagent.dev/v1alpha2/ModelConfig/kagent-system/claude-sonnet" + + # Execution trace (ordered tool call sequence) + executionTrace: + iterations: 3 + toolCalls: + - sequence: 1 + iteration: 1 + tool: k8s_get_resources + inputHash: "sha256:111..." + outputHash: "sha256:222..." + timestamp: "2026-03-20T14:20:05Z" + durationMs: 340 + policyVerdict: allowed + - sequence: 2 + iteration: 1 + tool: k8s_get_logs + inputHash: "sha256:333..." + outputHash: "sha256:444..." + timestamp: "2026-03-20T14:20:06Z" + durationMs: 520 + policyVerdict: allowed + - sequence: 3 + iteration: 2 + tool: k8s_describe + inputHash: "sha256:555..." + outputHash: "sha256:666..." + timestamp: "2026-03-20T14:20:08Z" + durationMs: 280 + policyVerdict: allowed + llmInvocations: + - sequence: 1 + iteration: 1 + requestHash: "sha256:aaa..." + responseHash: "sha256:bbb..." + promptTokens: 1200 + completionTokens: 800 + - sequence: 2 + iteration: 2 + requestHash: "sha256:ccc..." + responseHash: "sha256:ddd..." + promptTokens: 2400 + completionTokens: 600 + + # Token usage (total and per-invocation breakdown) + tokenUsage: + totalPromptTokens: 3600 + totalCompletionTokens: 1400 + totalTokens: 5000 + + # Policy decisions + policyDecisions: + layer1_opa: + engine: OPA + evaluated: 1 + allowed: 1 + denied: 0 + layer2_kagent: + allowedToolsEnforced: true + toolsBlocked: 0 + approvalsPaused: 0 + + # Builder identity + builder: + controllerVersion: "v0.1.0" + kagentVersion: "v0.7.13" + adkImageDigest: "sha256:a1b2c3..." + + # Result + resultHash: "sha256:eee..." +``` + +#### Telemetry Data Flow + +The controller cannot observe individual tool calls and LLM +invocations because they happen inside the kagent ADK runtime. The +execution trace is collected from the kagent Agent's HTTP response. + +The controller POSTs a goal to the Agent Service and expects a +structured JSON response that includes both the result and execution +telemetry: + +```json +{ + "result": { + "diagnosis": "OOMKilled: memory limit too low", + "recommendation": "Increase to 512Mi" + }, + "telemetry": { + "iterations": 3, + "toolCalls": [...], + "llmInvocations": [...], + "tokenUsage": {...} + } +} +``` + +kagent's ADK already tracks tool calls and LLM invocations internally +for its session management. Exposing this data in the HTTP response +is an upstream contribution to kagent. Until this is available, the +controller records what it can observe directly: model identity, +prompt hash, policy decisions (Gate 1), and timing metadata. + +#### Tekton Chains Integration + +Tekton Chains discovers agent provenance through the CustomTask +adapter. When an AgentRun completes as a CustomRun within a +PipelineRun, Chains processes it like any other step: + +1. Chains watches CustomRun completion events +2. The CustomRun status contains the `provenance` struct +3. Chains maps the struct to an in-toto attestation using the + `https://tekton.dev/agent-provenance/v1` buildType +4. The attestation is signed and stored alongside the PipelineRun + attestation + +For standalone AgentRuns (not in a Pipeline), a Chains extension +watches AgentRun completion events directly and produces standalone +attestations. + +The `reproducible: false` flag signals to any SLSA verifier that +this execution cannot be reproduced from the same inputs. This is +a necessary extension for non-deterministic build steps. + +#### Prompt Auditability + +The `systemPrompt` field in AgentConfig is mutable. The controller +records `systemPromptHash` (SHA-256) in provenance. This is +auditability, not immutability: you can verify what was used and +detect changes between runs. True immutability would require an +admission webhook and is out of scope. + +#### Token Budget + +The `tokenBudget` field is passed to the kagent Agent as +configuration. If the ADK runtime does not enforce it natively, the +controller enforces a timeout-based fallback. Token budgets via Pod +timeout are best-effort because a model can consume many tokens in a +short time. This limitation is acknowledged. + +#### Prior Art + +- [PROV-AGENT][prov-agent] extends W3C PROV with agent-specific + entities (AIAgent, AgentTool, AIModelInvocation) and relationships + for tracking non-deterministic agent interactions. The provenance + schema in this TEP is informed by PROV-AGENT's entity model. +- [CoSAI Principles][cosai] recommend adapting SLSA for agent and + model artifact provenance, with continuous runtime validation. +- [OWASP Top 10 for Agentic Applications][owasp-agentic] identifies + agent behavior hijacking (ASI01), prompt injection (ASI02), and + tool misuse (ASI03) as top risks. The provenance trace enables + post-hoc detection of all three. + +### Notes and Caveats + +- **kagent ADK image compatibility**: The controller creates kagent + Agent CRs that reference a specific ADK image tag. Breaking changes + in kagent's Agent CR spec would require controller updates. This is + mitigated by pinning to tested kagent versions in CI. +- **Cross-namespace secret access**: ModelConfig in `kagent-system` + references API key secrets in `kagent-system`. The Agent Deployment + runs in the user's namespace. kagent's controller handles secret + mounting in the Agent Deployment. The AgentRun controller does not + need to manage cross-namespace secrets directly. +- **AgentConfig mutability during execution**: If AgentConfig is + updated while an AgentRun is in the Acting phase, the running agent + is not affected because the Agent CR was created with a snapshot of + the configuration at reconcile time. Subsequent AgentRuns will use + the updated AgentConfig. + +## Design Details + +### Execution Flow: Standalone AgentRun + +``` +AgentRun.Phase: Pending + ├── Validate AgentConfig exists + ├── Snapshot AgentConfig spec (immutable for this run) + ├── Resolve kagent ModelConfig via dynamic client GET + ├── Resolve kagent RemoteMCPServer(s) via dynamic client GET + ├── Validate: model ready, tools discovered, OPA policy exists + ├── Create ServiceAccount, Role, RoleBinding (owner-referenced) + └── Create NetworkPolicy if strict (owner-referenced) + +AgentRun.Phase: PreHooks (if configured) + ├── Create Tekton PipelineRun (owner-referenced) + └── Watch PipelineRun completion + +AgentRun.Phase: Acting + ├── Create kagent Agent CR (owner-referenced to AgentRun): + │ spec.type: Declarative + │ spec.declarative.modelConfig: + │ spec.declarative.systemMessage: + │ spec.declarative.tools: + │ spec.declarative.deployment.serviceAccountName: + ├── Wait for Agent Ready condition + ├── POST goal to Agent Service HTTP endpoint + └── Collect results from response + +AgentRun.Phase: PostHooks (if configured) + ├── Create Tekton PipelineRun with results as params + └── Watch PipelineRun completion + +AgentRun.Phase: Succeeded / Failed + ├── Update status with results and provenance + ├── Emit Kubernetes events + └── Per-run resources cleaned up via owner references +``` + +### Execution Flow: PipelineRun with Agent Steps + +``` +PipelineRun starts with agent steps (CustomTask references) + +First AgentRun with configRef "test-agent": + ├── Create Agent CR "test-agent-" + │ labels: + │ agent.tekton.dev/pipelinerun: + │ agent.tekton.dev/config: test-agent + │ ownerReferences: [{kind: PipelineRun}] + ├── Create RBAC resources (owner-referenced to PipelineRun) + ├── Wait for Agent Ready + ├── POST goal, collect results + └── AgentRun marked Succeeded + +Second AgentRun with configRef "test-agent" (same PipelineRun): + ├── Find existing Agent CR by labels: + │ agent.tekton.dev/pipelinerun: + │ agent.tekton.dev/config: test-agent + ├── Agent already Ready + ├── POST goal (agent has context from first call) + ├── Collect results + └── AgentRun marked Succeeded + +PipelineRun completes: + └── Agent CR garbage collected via owner reference to PipelineRun +``` + +### Agent CR Scoping and Reuse + +The following diagram shows how the controller decides whether to +create a new Agent CR or reuse an existing one: + +```mermaid +flowchart TD + Start["AgentRun reconciled"] + InPipeline{"Part of a
PipelineRun?"} + + Standalone["Standalone mode"] + CreateNew["Create new Agent CR
owner-ref: AgentRun"] + + Pipeline["PipelineRun mode"] + Search["Search for Agent CR with labels:
pipelinerun=UID, config=name"] + Found{"Agent CR
exists?"} + Reuse["Reuse existing Agent CR
POST goal to running agent"] + CreatePR["Create new Agent CR
owner-ref: PipelineRun"] + + Start --> InPipeline + InPipeline -->|no| Standalone --> CreateNew + InPipeline -->|yes| Pipeline --> Search --> Found + Found -->|yes| Reuse + Found -->|no| CreatePR +``` + +The controller uses labels to track Agent CR ownership: + +| Label | Value | Purpose | +|-------|-------|---------| +| `agent.tekton.dev/config` | AgentConfig name | Identifies which config this agent uses | +| `agent.tekton.dev/agentrun` | AgentRun name | Set for standalone AgentRuns | +| `agent.tekton.dev/pipelinerun` | PipelineRun UID | Set for PipelineRun-scoped agents | + +Reuse logic: +- Standalone: always create a new Agent CR +- In PipelineRun: list Agent CRs with matching `pipelinerun` and + `config` labels. If found, reuse. If not, create. + +Owner references: +- Standalone: Agent CR owner-referenced to AgentRun +- In PipelineRun: Agent CR owner-referenced to PipelineRun (so it outlives + individual AgentRuns but is cleaned up when the PipelineRun ends) + +### kagent Resource Resolution + +The controller reads kagent CRDs via `k8s.io/client-go/dynamic`. + +At reconcile time, the controller: + +1. GETs the referenced `kagent.dev/v1alpha2 ModelConfig` to validate + the model exists and is ready +2. GETs each referenced `kagent.dev/v1alpha2 RemoteMCPServer` to + validate tools are discovered +3. Constructs the kagent Agent CR spec with the resolved references + +The controller does not build `config.json` itself. kagent's own +controller handles the translation from Agent CR to ADK configuration. + +If kagent CRDs are not installed in the cluster, the controller sets a +`KagentNotInstalled` condition on the AgentRun with a clear message. + +### Security Implementation Details + +The five-gate security architecture is described in the +[Security Layer](#security-layer) section of the Proposal. This +section provides implementation-level details for Gates 1 and 2, +which are implemented by the AgentRun controller. Gate 3 +(allowedTools/requireApproval) and Gate 4 (MCP-only access) are +enforced by [kagent][kagent] inside the agent runtime. Gate 5 +(provenance) is detailed in +[Provenance Recording](#provenance-recording). + +#### Gate 1: OPA Goal Admission + +``` +// Pseudocode: OPA evaluation at goal submission +allowResult := opaEngine.Evaluate("data.agent.goals.allow", input) +denyResults := opaEngine.Evaluate("data.agent.goals.deny", input) + +if !allowResult || len(denyResults) > 0 { + reject AgentRun with PolicyDenied condition +} +``` + +Default policy when no ConfigMap is configured: + +```rego +package agent.goals +default allow = false +``` + +#### Gate 2: RBAC Resources + +```yaml +apiVersion: v1 +kind: ServiceAccount +metadata: + name: debug-api-server-sa + ownerReferences: + - apiVersion: agent.tekton.dev/v1alpha1 + kind: AgentRun + name: debug-api-server + uid: +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + name: debug-api-server-role + ownerReferences: # abbreviated, same as above + - apiVersion: agent.tekton.dev/v1alpha1 + kind: AgentRun + name: debug-api-server + uid: +rules: # from AgentConfig.spec.rbac.rules + - apiGroups: [""] + resources: [pods, services, events] + verbs: [get, list, watch] + - apiGroups: [apps] + resources: [deployments, replicasets] + verbs: [get, list, watch] +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + name: debug-api-server-binding + ownerReferences: # abbreviated + - apiVersion: agent.tekton.dev/v1alpha1 + kind: AgentRun + name: debug-api-server + uid: +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: Role + name: debug-api-server-role +subjects: + - kind: ServiceAccount + name: debug-api-server-sa +``` + +The kagent Agent CR is configured with +`spec.declarative.deployment.serviceAccountName: debug-api-server-sa`. + +For PipelineRun-scoped agents, RBAC resources are owner-referenced to the +PipelineRun. Since all AgentRuns sharing this agent use the same +AgentConfig, there is no rule conflict. + +#### Gate 2: NetworkPolicy Resources + +```yaml +apiVersion: networking.k8s.io/v1 +kind: NetworkPolicy +metadata: + name: debug-api-server-netpol + ownerReferences: # abbreviated + - apiVersion: agent.tekton.dev/v1alpha1 + kind: AgentRun + name: debug-api-server + uid: +spec: + podSelector: + matchLabels: + agent.tekton.dev/config: cluster-diagnostics + policyTypes: [Ingress, Egress] + ingress: + - from: + - podSelector: + matchLabels: + app.kubernetes.io/component: agentrun-controller + egress: + - to: + - ipBlock: + cidr: /32 + ports: [{protocol: TCP, port: 443}] + - to: + - namespaceSelector: + matchLabels: + kubernetes.io/metadata.name: kube-system + ports: [{protocol: UDP, port: 53}] + # MCP tool server endpoints added dynamically +``` + +API server egress uses `ipBlock` with the resolved cluster IP, not +`namespaceSelector`, to avoid allowing the agent to reach arbitrary +HTTPS endpoints. + +### CustomTask Adapter + +When a Pipeline step references an AgentRun via `taskRef`, Tekton's +PipelineRun controller creates a `CustomRun` object (not an AgentRun +directly). The AgentRun controller watches `CustomRun` objects where +`spec.customRef.apiVersion` is `agent.tekton.dev/v1alpha1` and +`spec.customRef.kind` is `AgentRun`. + +```yaml +# Pipeline author writes: +taskRef: + apiVersion: agent.tekton.dev/v1alpha1 + kind: AgentRun + +# Tekton creates a CustomRun. AgentRun controller reconciles it. +``` + +The controller reconciles the `CustomRun` directly (it does not +create a separate `AgentRun` CR). The `CustomRun.spec.params` are +mapped to AgentRun spec fields: + +| CustomRun param | Maps to | +|-----------------|---------| +| `configRef` | AgentConfig name | +| `goal` | Goal text | +| `hints` | Context hints | + +The controller discovers the owning PipelineRun by inspecting +`CustomRun.metadata.ownerReferences` for a reference with +`kind: PipelineRun`. This PipelineRun UID is used for Agent CR +scoping and reuse. + +Results are written to `CustomRun.status.results` so downstream +Pipeline steps can reference them via `$(tasks..results.)`. +The `when` expression support follows from standard Tekton result +passing. + +Timeout and cancellation are handled by observing the `CustomRun` +spec: if Tekton sets a timeout or cancellation condition, the +controller stops the agent execution and cleans up. + +### Provenance Recording + +The provenance struct captures the full execution trace of an +agent run, mapped to SLSA predicate fields: + +```go +type AgentRunProvenance struct { + BuildType string `json:"buildType"` + Reproducible bool `json:"reproducible"` + ReproducibilityNote string `json:"reproducibilityNote,omitempty"` + ExternalParameters ExternalParams `json:"externalParameters"` + InternalParameters InternalParams `json:"internalParameters"` + ResolvedDependencies []ResolvedDependency `json:"resolvedDependencies"` + ExecutionTrace ExecutionTrace `json:"executionTrace"` + TokenUsage TokenUsage `json:"tokenUsage"` + PolicyDecisions PolicyDecisions `json:"policyDecisions"` + Builder BuilderIdentity `json:"builder"` + ResultHash string `json:"resultHash"` +} + +type ExternalParams struct { + Goal string `json:"goal"` + GoalHash string `json:"goalHash"` + Hints []string `json:"hints,omitempty"` + AgentConfigRef string `json:"agentConfigRef"` + AgentConfigHash string `json:"agentConfigHash"` +} + +type InternalParams struct { + Model ModelIdentity `json:"model"` + SystemPromptHash string `json:"systemPromptHash"` + MaxIterations int `json:"maxIterations"` + Timeout string `json:"timeout"` + TokenBudget int `json:"tokenBudget,omitempty"` + OPAPolicyHash string `json:"opaPolicyHash,omitempty"` +} + +type ModelIdentity struct { + Provider string `json:"provider"` + ModelID string `json:"modelId"` + APIVersion string `json:"apiVersion,omitempty"` + Temperature float64 `json:"temperature,omitempty"` + MaxTokens int `json:"maxTokens,omitempty"` +} + +type ResolvedDependency struct { + Name string `json:"name"` + URI string `json:"uri"` + Digest string `json:"digest,omitempty"` + ToolsDiscovered []string `json:"toolsDiscovered,omitempty"` +} + +type ExecutionTrace struct { + Iterations int `json:"iterations"` + ToolCalls []ToolCallRecord `json:"toolCalls"` + LLMInvocations []LLMInvocation `json:"llmInvocations"` +} + +type ToolCallRecord struct { + Sequence int `json:"sequence"` + Iteration int `json:"iteration"` + Tool string `json:"tool"` + InputHash string `json:"inputHash"` + OutputHash string `json:"outputHash"` + Timestamp string `json:"timestamp"` + DurationMs int `json:"durationMs"` + PolicyVerdict string `json:"policyVerdict"` +} + +type LLMInvocation struct { + Sequence int `json:"sequence"` + Iteration int `json:"iteration"` + RequestHash string `json:"requestHash"` + ResponseHash string `json:"responseHash"` + PromptTokens int `json:"promptTokens"` + CompletionTokens int `json:"completionTokens"` +} + +type TokenUsage struct { + TotalPromptTokens int `json:"totalPromptTokens"` + TotalCompletionTokens int `json:"totalCompletionTokens"` + TotalTokens int `json:"totalTokens"` +} + +type PolicyDecisions struct { + Layer1OPA OPADecisions `json:"layer1_opa"` + Layer2Kagent KagentDecisions `json:"layer2_kagent"` +} + +type OPADecisions struct { + Engine string `json:"engine"` + Evaluated int `json:"evaluated"` + Allowed int `json:"allowed"` + Denied int `json:"denied"` +} + +type KagentDecisions struct { + AllowedToolsEnforced bool `json:"allowedToolsEnforced"` + ToolsBlocked int `json:"toolsBlocked"` + ApprovalsPaused int `json:"approvalsPaused"` +} + +type BuilderIdentity struct { + ControllerVersion string `json:"controllerVersion"` + KagentVersion string `json:"kagentVersion"` + ADKImageDigest string `json:"adkImageDigest"` +} +``` + +The `ExecutionTrace` and `TokenUsage` fields depend on telemetry +from the kagent ADK runtime (see [Telemetry Data Flow](#telemetry-data-flow) +in the Proposal section). Until kagent exposes this telemetry in its +HTTP response, the controller populates what it can observe directly: +`ExternalParameters`, `InternalParameters`, `ResolvedDependencies`, +`PolicyDecisions` (Gate 1), and `Builder`. + +### AgentConfig Snapshot + +When an AgentRun is reconciled, the controller snapshots the +AgentConfig spec into the AgentRun status. This ensures that if the +AgentConfig is modified during execution, the running agent is not +affected and the provenance record reflects the actual configuration +used. + +## Design Evaluation + +### Reusability + +This proposal follows Tekton's [design principles][design-principles] +by reusing two existing projects: +- **[kagent][kagent]** provides the [Agent CR][kagent-agents], ADK + runtime, [ModelConfig][kagent-models], and + [RemoteMCPServer][kagent-tools] CRDs +- **Tekton Pipelines** provides Pipeline orchestration and the + [CustomTask][customtask] protocol + +The [AgentRun PoC][agentrun-poc] demonstrates the concept. The +controller adds agent lifecycle management, security, and provenance. + +### Simplicity + +Users interact with two CRDs (`AgentRun` and `AgentConfig`). In a +Pipeline, agent steps look like any other CustomTask reference. The +agent lifecycle (create, reuse, cleanup) is managed by the controller. + +### Flexibility + +- kagent ModelConfig supports 8 LLM providers +- MCP tool servers are pluggable +- RBAC rules are configurable per AgentConfig +- [OPA][opa] policies are user-defined via ConfigMaps +- Tekton Pipelines integration is optional +- Multiple agents with different configs can coexist in a PipelineRun + +### Conformance + +This proposal does not modify any existing Tekton APIs. New CRDs are +under the `agent.tekton.dev` API group. The CustomTask adapter follows +the existing CustomTask protocol. No changes to `Task`, `Pipeline`, +`TaskRun`, or `PipelineRun` resources. + +This proposal introduces kagent and OPA as additional concepts users +must understand. kagent CRDs are managed by cluster administrators. +OPA policies are managed by security teams. Pipeline authors only +interact with AgentRun and AgentConfig. + +### User Experience + +- **Cluster administrators** install kagent and configure ModelConfigs + and RemoteMCPServers +- **Platform engineers** create AgentConfigs with RBAC rules and OPA + policies +- **Pipeline authors** reference AgentRuns in Pipeline specs via + CustomTask +- **Security teams** define OPA policies and review agent provenance + +### Performance + +- **Agent startup**: kagent Agent Deployments require pod scheduling. + Typical startup is 5-15 seconds with pre-pulled images. For + PipelineRun-scoped agents, this cost is paid once and amortized + across all agent steps. +- **Controller footprint**: The controller creates Agent CRs and sends + HTTP requests. No LLM processing occurs in the controller. +- **Cleanup**: Owner references ensure no resource leaks. + +### Risks and Mitigations + +| Risk | Mitigation | +|------|------------| +| kagent Agent CR spec changes | Dynamic client is resilient to field additions. Pin to kagent v1alpha2. Test against kagent releases in CI. | +| kagent CRDs not installed | Controller checks at startup. Clear condition on AgentRun. | +| Tekton Pipelines not installed | Hooks are optional. CustomTask adapter degrades gracefully. Hooks configuration rejected at validation when Tekton is absent. | +| Agent Deployment startup latency | Pre-pulled images. PipelineRun-scoped agents amortize startup across steps. | +| OPA policy misconfiguration | Default fail-closed. Deny all when no policy is configured. | +| Per-tool-call OPA not available in Phase 1 | Five-gate model: OPA at goal level (Gate 1) + kagent allowedTools/requireApproval (Gate 3) provide meaningful security. Per-tool-call OPA is a future kagent contribution. | +| LLM-controlled tool inputs spoofing OPA | Gate 1 OPA input is constructed by the controller, not the LLM. Gate 3 tool restrictions are set on the Agent CR, not controllable by the LLM. | +| Token budget not enforced by ADK | Fallback to timeout. Acknowledge as best-effort. | +| PipelineRun cancelled while agent is running | Owner reference on Agent CR triggers garbage collection. Agent Deployment receives SIGTERM. | +| AgentConfig modified during execution | Config snapshotted at reconcile time. Running agent not affected. | + +### Drawbacks + +- **Dependency on kagent**: The proposal uses kagent's Agent CR and + CRDs. If kagent changes direction, the controller would need + adaptation. The dynamic client approach minimizes coupling. +- **Two systems to install**: Users must install kagent and the + AgentRun controller. Mitigated by Helm charts that bundle both. +- **Deployment overhead for standalone AgentRuns**: A single-goal + AgentRun creates a full Deployment + Service for one HTTP call. This + is the cost of using kagent's existing model. If kagent adds a + Job/batch mode ([kagent#1089][kagent-1089]), standalone AgentRuns + could switch to the lighter-weight model. + +## Alternatives + +### Build Agent Stack Inside Tekton + +Add an `agent` step type to `Task.spec.steps`, build MCPServerRef CRD, +build agent runtime sidecar, model configuration, tool discovery. + +Rejected: Massive scope that duplicates kagent. Would require the +Tekton community to build and maintain an agent runtime. + +### Pod-Per-Run Without Agent CR + +Create a Pod directly using kagent's ADK image for each AgentRun, +bypassing the Agent CR entirely. + +Rejected: Requires building a config.json translator that replicates +kagent's controller logic (model credential injection, MCP server +resolution, TLS configuration). Also requires kagent to support a +batch/one-shot mode ([kagent#1089][kagent-1089]) that does not exist +today. Using the Agent CR avoids both issues. + +### Pipeline spec.agents Field + +Add a new `spec.agents` field to Tekton's Pipeline CRD, analogous to +`spec.workspaces`, for declaring agent environments. + +Rejected: Requires changes to Tekton's core Pipeline CRD, which is a +much larger scope and would need its own TEP. The CustomTask approach +achieves the same result without modifying existing APIs. + +### Volume Mounts for Codebase Access + +Mount workspace PVCs directly into agent Deployments so agents can +read codebases via the filesystem. + +Rejected: Volume mounts give the agent raw filesystem access outside +the tool call protocol. OPA cannot restrict which files the agent +reads. No audit trail for file access. MCP tools route all data access +through the tool protocol, enabling uniform policy enforcement and +provenance recording. + +### Convention-Based Container Wrapping + +Continue wrapping agents in container steps with ad-hoc Python scripts. + +Rejected: This is the status quo. Opaque, insecure, unauditable. + +## Implementation Plan + +### Milestones + +**Phase 1: Core** +- AgentRun and AgentConfig CRDs with `rbac.rules`, `tokenBudget`, + `policy.opa.defaultDeny` fields +- kagent Agent CR creation with dynamic client (standalone lifecycle) +- Per-run RBAC generation (ServiceAccount + Role + RoleBinding) +- Real-time OPA enforcement (both `allow` and `deny`, namespaced + inputs, fail-closed default) +- kagent ModelConfig and RemoteMCPServer validation via dynamic client +- CustomTask adapter for Tekton Pipeline integration +- PipelineRun-scoped Agent CR reuse (same configRef = same agent) +- Provenance recording in AgentRun status +- AgentConfig snapshot at reconcile time + +**Phase 2: Hardening** +- Per-run NetworkPolicy generation (API server ipBlock, controller + ingress, MCP server egress) +- Tekton PipelineRun-based pre/post hooks +- Token budget enforcement (ADK config + timeout fallback) +- Tekton Chains extension for agent provenance attestation + +**Phase 3: Advanced** +- Per-tool-call OPA enforcement via kagent ADK hook (upstream + contribution to kagent) +- Pipeline-level agent cost aggregation +- Agent memory integration (kagent Memory CRD) +- Prompt auditability alerting (hash comparison between runs) +- Standalone AgentRun optimization via kagent batch mode + ([kagent#1089][kagent-1089]) when available + +### Test Plan + +- **Unit tests**: AgentConfig validation (rbac.rules required, OPA + configMapRef format), Agent CR construction (correct labels, owner + references, serviceAccountName), RBAC generation (rules from config, + not hardcoded), OPA input namespacing (verify key injection is + impossible), NetworkPolicy construction (ipBlock for API server, MCP + server egress), AgentConfig snapshot immutability +- **Integration tests**: End-to-end AgentRun lifecycle with mock kagent + CRDs (fake dynamic client), Agent CR reuse with same configRef in + mock PipelineRun, CustomTask adapter with mock Pipeline controller +- **E2E tests**: Full execution in Kind cluster with kagent installed. + Create ModelConfig, RemoteMCPServer, AgentConfig, AgentRun. Validate: + Agent CR created with correct SA, RBAC matches config rules, OPA + denies disallowed tools, results collected, provenance recorded. + Multi-step PipelineRun with shared agent context. +- **Security tests**: OPA input injection (verify `input.params.tool` + cannot overwrite `input.tool`), RBAC isolation (agent cannot access + resources outside declared rules), fail-closed default (no policy = + all denied) +- **Negative tests**: Non-existent AgentConfig reference, empty API + key secret, kagent CRDs not installed, PipelineRun cancellation + during agent execution, malformed Rego in OPA ConfigMap + +### Infrastructure Needed + +- Repository: `tektoncd/agentrun` (or initially + `waveywaves/tekton-agentrun`) +- CI pipeline: Kind cluster with kagent + Tekton Pipelines installed +- Helm chart for bundled installation + +### Upgrade and Migration Strategy + +This is a new feature with no existing behavior to migrate from. CRDs +are introduced at `v1alpha1` stability. Breaking changes are expected +during alpha. + +### Implementation Pull Requests + +To be populated when implementation begins. + +## References + +- [kagent][kagent] +- [kagent Agent CRD documentation][kagent-agents] +- [kagent ModelConfig documentation][kagent-models] +- [kagent RemoteMCPServer documentation][kagent-tools] +- [kagent batch/Job mode request][kagent-1089] +- [Model Context Protocol specification][mcp] +- [AgentRun PoC][agentrun-poc] +- [Pipeline comparison (with/without agents)][pipeline-comparison] +- [Tekton Chains][chains] +- [Tekton CustomTask specification][customtask] +- [SLSA Provenance Framework][slsa] +- [Open Policy Agent][opa] +- [Tekton Design Principles][design-principles] +- [PROV-AGENT: Unified Provenance for AI Agent Interactions][prov-agent] +- [LLM Agents for Interactive Workflow Provenance][workflow-provenance] +- [CoSAI Principles for Secure Agentic Systems][cosai] +- [OWASP Top 10 for Agentic Applications][owasp-agentic] + +[kagent]: https://kagent.dev +[kagent-agents]: https://kagent.dev/docs/kagent/concepts/agents +[kagent-models]: https://kagent.dev/docs/kagent/concepts/model-providers +[kagent-tools]: https://kagent.dev/docs/kagent/concepts/tool-servers +[kagent-1089]: https://github.com/kagent-dev/kagent/issues/1089 +[mcp]: https://modelcontextprotocol.io/ +[agentrun-poc]: https://github.com/waveywaves/tekton-agentrun +[pipeline-without-agents]: https://github.com/waveywaves/tektoncd-pipeline-mgmt/blob/main/demos/jira-test-lifecycle/pipeline-without-agents.yaml +[pipeline-with-agents]: https://github.com/waveywaves/tektoncd-pipeline-mgmt/blob/main/demos/jira-test-lifecycle/pipeline-with-agents.yaml +[pipeline-comparison]: https://github.com/waveywaves/tektoncd-pipeline-mgmt/tree/main/demos/jira-test-lifecycle +[chains]: https://github.com/tektoncd/chains +[customtask]: https://tekton.dev/docs/pipelines/runs/ +[slsa]: https://slsa.dev/ +[opa]: https://www.openpolicyagent.org/ +[design-principles]: https://github.com/tektoncd/community/blob/main/design-principles.md +[prov-agent]: https://arxiv.org/abs/2508.02866 +[workflow-provenance]: https://arxiv.org/abs/2509.13978 +[cosai]: https://www.coalitionforsecureai.org/announcing-the-cosai-principles-for-secure-by-design-agentic-systems/ +[owasp-agentic]: https://www.practical-devsecops.com/owasp-top-10-agentic-applications/ diff --git a/teps/README.md b/teps/README.md index 03e237a50..877646eb6 100644 --- a/teps/README.md +++ b/teps/README.md @@ -150,3 +150,4 @@ This is the complete list of Tekton TEPs: |[TEP-0161](0161-resolver-caching.md) | Resolver Caching for Task and Pipeline Resolution | proposed | 2024-06-15 | |[TEP-0162](0162-event-based-pruning-of-tekton-resources.md) | event based pruning of tekton resources | proposed | 2025-06-18 | |[TEP-0163](0163-profilebased-dynamic-compute-resources-for-steps.md) | Profile-Based Dynamic Compute Resources for Steps | proposed | 2025-09-01 | +|[TEP-0164](0164-agent-native-workflows.md) | Agent-Native Workflows | proposed | 2026-03-20 | From 5c7a4fa5d09742451580e388daa44e83869aad0b Mon Sep 17 00:00:00 2001 From: Vibhav Bobade Date: Mon, 31 Aug 2026 11:13:48 +0530 Subject: [PATCH 2/3] TEP-0170: define pluggable AgentTask execution --- teps/0164-agent-native-workflows.md | 1832 --------------------------- teps/0170-agent-native-workflows.md | 1715 +++++++++++++++++++++++++ teps/README.md | 2 +- 3 files changed, 1716 insertions(+), 1833 deletions(-) delete mode 100644 teps/0164-agent-native-workflows.md create mode 100644 teps/0170-agent-native-workflows.md diff --git a/teps/0164-agent-native-workflows.md b/teps/0164-agent-native-workflows.md deleted file mode 100644 index 2db237476..000000000 --- a/teps/0164-agent-native-workflows.md +++ /dev/null @@ -1,1832 +0,0 @@ ---- -status: proposed -title: Agent-Native Workflows -creation-date: '2026-03-20' -last-updated: '2026-03-20' -authors: -- '@waveywaves' -- '@anithapriyanatarajan' ---- - -# TEP-0164: Agent-Native Workflows - ---- - - -- [Summary](#summary) -- [Motivation](#motivation) - - [Goals](#goals) - - [Non-Goals](#non-goals) - - [Use Cases](#use-cases) - - [AI Code Review Gate](#ai-code-review-gate) - - [Multi-Step Test Lifecycle](#multi-step-test-lifecycle) - - [Deployment Decision Agent](#deployment-decision-agent) - - [Cluster Diagnostics Agent](#cluster-diagnostics-agent) - - [Requirements](#requirements) -- [Proposal](#proposal) - - [Overview](#overview) - - [AgentRun CRD](#agentrun-crd) - - [AgentConfig CRD](#agentconfig-crd) - - [Agent Lifecycle](#agent-lifecycle) - - [Standalone AgentRun](#standalone-agentrun) - - [AgentRun in a PipelineRun](#agentrun-in-a-pipelinerun) - - [Multiple Agents in a PipelineRun](#multiple-agents-in-a-pipelinerun) - - [Integration with kagent](#integration-with-kagent) - - [Integration with Tekton Pipelines](#integration-with-tekton-pipelines) - - [Codebase Access via MCP Tools](#codebase-access-via-mcp-tools) - - [Security Layer](#security-layer) - - [Provenance](#provenance) - - [Notes and Caveats](#notes-and-caveats) -- [Design Details](#design-details) - - [Execution Flow: Standalone AgentRun](#execution-flow-standalone-agentrun) - - [Execution Flow: PipelineRun with Agent Steps](#execution-flow-pipelinerun-with-agent-steps) - - [Agent CR Scoping and Reuse](#agent-cr-scoping-and-reuse) - - [kagent Resource Resolution](#kagent-resource-resolution) - - [Security Implementation Details](#security-implementation-details) - - [CustomTask Adapter](#customtask-adapter) - - [Provenance Recording](#provenance-recording) - - [AgentConfig Snapshot](#agentconfig-snapshot) -- [Design Evaluation](#design-evaluation) - - [Reusability](#reusability) - - [Simplicity](#simplicity) - - [Flexibility](#flexibility) - - [Conformance](#conformance) - - [User Experience](#user-experience) - - [Performance](#performance) - - [Risks and Mitigations](#risks-and-mitigations) - - [Drawbacks](#drawbacks) -- [Alternatives](#alternatives) - - [Build Agent Stack Inside Tekton](#build-agent-stack-inside-tekton) - - [Pod-Per-Run Without Agent CR](#pod-per-run-without-agent-cr) - - [Pipeline spec.agents Field](#pipeline-specagents-field) - - [Volume Mounts for Codebase Access](#volume-mounts-for-codebase-access) - - [Convention-Based Container Wrapping](#convention-based-container-wrapping) -- [Implementation Plan](#implementation-plan) - - [Milestones](#milestones) - - [Test Plan](#test-plan) - - [Infrastructure Needed](#infrastructure-needed) - - [Upgrade and Migration Strategy](#upgrade-and-migration-strategy) - - [Implementation Pull Requests](#implementation-pull-requests) -- [References](#references) - - -## Summary - -AI agents are appearing in CI/CD pipelines, doing code review, security -analysis, test generation, and deployment decisions. Today they run as -opaque Python scripts inside container steps. Tekton cannot see what -model they called, which tools they used, how many tokens they consumed, -or whether they followed security policy. - -This TEP proposes making Tekton an **agent-native workflow engine** by -introducing `AgentRun` and `AgentConfig` CRDs that use -[kagent][kagent]'s Agent CR as the agent runtime and add per-run -security controls, pipeline integration via CustomTask, and provenance -recording. - -```mermaid -flowchart TB - subgraph User["Pipeline Author"] - AR["AgentRun CR
(goal + configRef)"] - AC["AgentConfig CR
(model + tools + RBAC + OPA)"] - end - - subgraph TEP["AgentRun Controller (this TEP)"] - Lifecycle["Agent Lifecycle
create / reuse / cleanup"] - Security["Security Layer
RBAC + NetworkPolicy + OPA"] - Provenance["Provenance
model + tools + tokens"] - end - - subgraph Kagent["kagent"] - AgentCR["Agent CR"] - MC["ModelConfig"] - RMS["RemoteMCPServer"] - ADK["ADK Runtime"] - end - - subgraph Tekton["Tekton Pipelines"] - CT["CustomTask Protocol"] - PR["PipelineRun"] - Chains["Tekton Chains"] - end - - AR --> TEP - AC --> TEP - TEP --> AgentCR - TEP --> Security - TEP --> Provenance - MC --> AgentCR - RMS --> AgentCR - AgentCR --> ADK - CT --> TEP - PR --> CT - Provenance --> Chains -``` - -The execution model uses **kagent's existing Agent CR**, which creates a -Deployment and Service for the agent. For standalone AgentRuns, the Agent -CR is created for a single goal and cleaned up after completion. For -PipelineRuns with multiple agent steps, the Agent CR is created at the -first agent step and shared across subsequent steps that reference the -same AgentConfig, preserving conversation context. The Agent CR is -cleaned up when the PipelineRun completes. - -The controller adds security controls that neither kagent nor Tekton -Pipelines provides alone: per-run RBAC scoping with configurable rules, -per-run NetworkPolicy generation, layered policy enforcement (OPA at -goal submission + kagent tool allowlists at execution), prompt -auditability, and provenance recording for [Tekton Chains][chains]. - -A [CustomTask][customtask] adapter allows AgentRuns to participate in -Tekton Pipeline DAGs, making agent steps composable with traditional -container steps using standard result passing and `when` expressions. - -## Motivation - -AI agents are already appearing in CI/CD pipelines, but they are -invisible to the platform. A [comparison of two real-world pipelines][pipeline-comparison], -one implemented [without agents][pipeline-without-agents] and one -[with agents][pipeline-with-agents], illustrates the problem: - -**Without agents** (614 lines, 12 tasks): Template-based test plan -generation using static `case` statements. A manual approval gate where -humans write tests from scratch. Raw pass/fail counts posted as results. - -**With agents** (1247 lines, 14 tasks): An agent reads actual source -code, generates real test implementations, self-reviews its own work, -triages test failures (infrastructure vs test bugs vs real regressions), -and produces an intelligent summary report. - -The following diagram illustrates the difference between today's opaque -agent steps and the governed model proposed by this TEP: - -```mermaid -flowchart TB - subgraph TODAY["Today: Opaque Agent Steps"] - direction TB - T_Step["Container Step"] - T_Pip["pip install anthropic"] - T_Key["Read API key from file"] - T_Call["Call LLM directly"] - T_Parse["Parse response with regex"] - T_Out["Write stdout"] - - T_Step --> T_Pip --> T_Key --> T_Call --> T_Parse --> T_Out - - T_Invisible["Tekton sees:
container ran, exit 0"] - T_Out -.-> T_Invisible - end - - subgraph TEP["TEP-0164: Governed Agent Steps"] - direction TB - A_Run["AgentRun CR"] - A_OPA["Gate 1: OPA evaluates goal"] - A_RBAC["Gate 2: scoped RBAC + NetworkPolicy"] - A_Agent["kagent Agent CR
(model + tools configured)"] - A_Tools["Gate 3: allowedTools enforced"] - A_MCP["Gate 4: MCP-only data access"] - A_Result["Gate 5: results + provenance"] - - A_Run --> A_OPA --> A_RBAC --> A_Agent --> A_Tools --> A_MCP --> A_Result - end -``` - -The agents transform the pipeline from a structure-only scaffold into an -intelligence-augmented workflow. But every agent step is an opaque -container: - -```python -# Repeated in every agent step: ~100 lines of boilerplate -subprocess.check_call([sys.executable, "-m", "pip", "install", "anthropic", "-q"]) -client = anthropic.Anthropic(api_key=api_key) -# ... 80 more lines of prompt construction, response parsing -``` - -Tekton sees these as ordinary container steps. There is no way for a -platform operator to: - -- Scope agent permissions to exactly the Kubernetes resources they need -- Enforce which tools an agent may call, with real-time policy evaluation -- Audit which models and prompts were used across the organization -- Record agent behavior in SLSA-compatible attestations -- Set token budgets or network isolation per agent execution - -Meanwhile, [kagent][kagent] provides CRDs for [model configuration][kagent-models] -(8 providers), [MCP tool servers][kagent-tools] (with automatic tool -discovery), and agent runtimes (Python and Go ADKs). But kagent does not provide pipeline -orchestration, supply-chain provenance, or the per-execution security -controls that a CI/CD platform requires. - -### Goals - -1. Define `AgentRun` and `AgentConfig` CRDs that enable goal-driven - agent execution with per-run security controls -2. Use kagent's Agent CR as the agent runtime, creating Agent CRs - scoped to AgentRun or PipelineRun lifetimes -3. Use kagent's `ModelConfig` and `RemoteMCPServer` CRDs for model and - tool configuration without reimplementing them -4. Add per-run security controls: RBAC scoping with configurable rules, - NetworkPolicy, layered policy enforcement (OPA + kagent tool - restrictions) -5. Provide a CustomTask adapter so AgentRuns participate in Tekton - Pipeline DAGs with result passing and `when` expression support -6. Record agent execution provenance for consumption by Tekton Chains -7. Route all data access (codebase, cluster state) through MCP tool - calls for uniform policy enforcement and audit - -### Non-Goals - -1. Building a new agent runtime (kagent provides the ADK) -2. Building LLM provider integrations (kagent supports 8 providers) -3. Building MCP server infrastructure (kagent provides - `RemoteMCPServer` and `ToolServer`) -4. Adding new fields to Tekton's Pipeline or PipelineRun CRDs -5. Defining multi-agent communication protocols -6. Implementing or bundling any LLM model or inference engine - -### Use Cases - -#### AI Code Review Gate - -As a platform engineer, I want an agent step in my Pipeline that reviews -a pull request diff and returns a structured `approved`/`findings` -result, so that downstream steps can conditionally block or proceed, -with full provenance of what the agent saw and decided. - -This is a standalone agent task. One AgentRun, one goal, one result. - -#### Multi-Step Test Lifecycle - -As a QA engineer, I want a pipeline where an agent analyzes a codebase, -generates test implementations, self-reviews them, and triages any -failures. The agent should maintain context across these steps so it -does not re-read the codebase at each step. - -This is a multi-step agent workflow. Multiple AgentRuns in a PipelineRun -sharing the same Agent CR, with conversation context preserved across -steps. - -#### Deployment Decision Agent - -As a DevOps engineer, I want an agent step that queries my observability -stack via permitted MCP tools and returns a structured `proceed`/`hold` -recommendation, enforced by OPA policy so it cannot access resources -outside its declared scope. - -#### Cluster Diagnostics Agent - -As a cluster administrator, I want to create an AgentRun with a goal -like "diagnose why deployment api-server is failing in namespace -production" and have the agent investigate using only the Kubernetes -resources I have authorized via per-run RBAC. - -### Requirements - -| ID | Requirement | Priority | -|----|-------------|----------| -| R1 | AgentRun controller MUST create kagent Agent CRs for agent execution | Must | -| R2 | Agent CRs MUST be scoped to AgentRun (standalone) or PipelineRun (multi-step) lifetime | Must | -| R3 | Multiple AgentRuns with the same configRef in a PipelineRun MUST reuse the same Agent CR | Must | -| R4 | AgentRun controller MUST generate per-run RBAC using rules from AgentConfig | Must | -| R5 | AgentConfig MUST reference kagent ModelConfig for model selection | Must | -| R6 | AgentConfig MUST reference kagent RemoteMCPServer for tool providers | Must | -| R7 | Agent execution results MUST be recorded in AgentRun status | Must | -| R8 | Policy enforcement MUST use a five-gate model: OPA at goal submission (Gate 1) + RBAC and NetworkPolicy (Gate 2) + allowedTools/requireApproval at tool execution (Gate 3) + MCP-only data access (Gate 4) + provenance (Gate 5) | Must | -| R9 | OPA goal-level input MUST be constructed by the controller from AgentRun metadata, not from LLM-controlled data | Must | -| R10 | Per-tool-call OPA enforcement inside the kagent ADK SHOULD be contributed upstream as a future enhancement | Should | -| R11 | A CustomTask adapter MUST allow AgentRun to participate in Pipeline DAGs via the Tekton CustomRun protocol | Must | -| R12 | AgentRun results MUST be passable to downstream Pipeline steps via `when` expressions | Must | -| R13 | NetworkPolicy SHOULD be generated when networkPolicy is set to strict | Should | -| R14 | Provenance metadata MUST be recorded in AgentRun status (observable fields in Phase 1, full execution trace when kagent telemetry is available) | Must | -| R15 | All per-run resources MUST use owner references for garbage collection | Must | -| R16 | AgentRun SHOULD support Tekton PipelineRun-based pre/post hooks | Should | -| R17 | AgentRun MUST fail gracefully with a clear message when kagent CRDs are not installed | Must | -| R18 | OPA policy MUST default to fail-closed (deny-all) when no policy is configured, unconditionally | Must | -| R19 | All codebase and cluster access SHOULD go through MCP tool calls, not volume mounts | Should | - -## Proposal - -### Overview - -The following diagram shows how a PipelineRun with multiple agent steps -and a traditional container step works end-to-end: - -```mermaid -sequenceDiagram - participant User - participant Pipeline as PipelineRun - participant Ctrl as AgentRun Controller - participant K as kagent Controller - participant A1 as Agent: test-agent - participant A2 as Agent: security-agent - participant MCP as MCP Tool Server - - User->>Pipeline: Create PipelineRun - - Note over Pipeline,Ctrl: Step 1: security-review (configRef: security-agent) - Pipeline->>Ctrl: AgentRun created - Ctrl->>Ctrl: Gate 1: OPA evaluates goal - Ctrl->>Ctrl: Create RBAC (SA + Role) - Ctrl->>K: Create Agent CR (security-agent) - K->>A2: Deployment + Service ready - Ctrl->>A2: POST goal - A2->>MCP: read_file (allowedTools enforced) - MCP-->>A2: file content - A2-->>Ctrl: result: {vulnerabilities: [...]} - Pipeline->>Pipeline: results available - - Note over Pipeline,Ctrl: Step 2: analyze-code (configRef: test-agent) - Pipeline->>Ctrl: AgentRun created - Ctrl->>Ctrl: Gate 1: OPA evaluates goal - Ctrl->>Ctrl: Gate 2: Create RBAC (SA + Role) - Ctrl->>K: Create Agent CR (test-agent) - K->>A1: Deployment + Service ready - Ctrl->>A1: POST goal - A1->>MCP: search_code, list_functions (Gate 3: allowedTools) - A1-->>Ctrl: result: {analysis: ...} - - Note over Pipeline,Ctrl: Step 3: generate-tests (configRef: test-agent, REUSES agent) - Pipeline->>Ctrl: AgentRun created - Ctrl->>Ctrl: Gate 1: OPA evaluates goal - Ctrl->>Ctrl: Find existing Agent CR by labels - Ctrl->>A1: POST goal (agent has context from step 2) - A1->>MCP: read_file, write_file (Gate 3: allowedTools) - A1-->>Ctrl: result: {tests_generated: 12} - - Note over Pipeline,Ctrl: Step 4: run-tests (normal container step) - Pipeline->>Pipeline: go test ./... - - Note over Pipeline,Ctrl: PipelineRun completes - Pipeline->>Pipeline: Owner refs trigger cleanup - K->>A1: Delete Deployment - K->>A2: Delete Deployment -``` - -``` -PipelineRun -│ -├── AgentRun Controller sees agent steps (CustomTask references) -│ -├── Creates kagent Agent CR per unique configRef -│ └── kagent controller creates Deployment + Service -│ └── Agent HTTP server running ADK runtime -│ -├── Step 1 (agent, security-agent): Gate 1 OPA, Gate 2 RBAC, create Agent CR, POST goal -├── Step 2 (agent, test-agent): Gate 1 OPA, Gate 2 RBAC, create Agent CR, POST goal -├── Step 3 (agent, test-agent): Gate 1 OPA, reuse Agent CR, POST goal (context preserved) -├── Step 4 (container): normal Tekton step, uses agent results -│ -├── PipelineRun completes -└── Agent CRs garbage collected via owner references -``` - -| Layer | Responsibility | Owner | -|-------|---------------|-------| -| Agent Runtime | LLM calls, MCP tool execution, agent loop | kagent (Agent CR, ADK) | -| Model + Tool Config | Model endpoints, credentials, MCP servers | kagent (ModelConfig, RemoteMCPServer) | -| Agent Lifecycle | Create/reuse/cleanup Agent CRs per scope | AgentRun controller (this TEP) | -| Security | Per-run RBAC, NetworkPolicy, OPA | AgentRun controller (this TEP) | -| Pipeline Integration | DAG sequencing, hooks, result passing | Tekton Pipelines + CustomTask | -| Provenance | Attestation of agent behavior | AgentRun status + Tekton Chains | - -### AgentRun CRD - -```yaml -apiVersion: agent.tekton.dev/v1alpha1 -kind: AgentRun -metadata: - name: debug-api-server -spec: - configRef: - name: cluster-diagnostics - goal: | - Diagnose why deployment 'api-server' is failing in namespace 'production'. - context: - hints: - - "Check recent events" - - "Review pod logs for OOMKilled" -status: - phase: Succeeded - startTime: "2026-03-20T14:20:04Z" - completionTime: "2026-03-20T14:22:30Z" - iterations: 3 - agentRef: cluster-diagnostics-7xk2 # kagent Agent CR used - results: - - name: diagnosis - value: "OOMKilled: memory limit 256Mi too low for request pattern" - - name: recommendation - value: "Increase memory limit to 512Mi" - provenance: - buildType: "https://tekton.dev/agent-provenance/v1" - reproducible: false - internalParameters: - model: - provider: Anthropic - modelId: "claude-sonnet-4-6" - systemPromptHash: "sha256:abc123..." - tokenUsage: - totalTokens: 4200 - policyDecisions: - layer1_opa: - evaluated: 1 - allowed: 1 - denied: 0 -``` - -### AgentConfig CRD - -```yaml -apiVersion: agent.tekton.dev/v1alpha1 -kind: AgentConfig -metadata: - name: cluster-diagnostics -spec: - # -- kagent references -------------------------- - modelConfigRef: - name: claude-sonnet - namespace: kagent-system - - toolServers: - - ref: - name: k8s-read-tools - namespace: kagent-system - kind: RemoteMCPServer - allowedTools: - - k8s_get_resources - - k8s_get_logs - - k8s_describe - requireApproval: [] - - # -- Agent behavior ----------------------------- - maxIterations: 5 - timeout: 10m - tokenBudget: 16384 # enforced in Phase 2; informational in Phase 1 - systemPrompt: | - You are a Kubernetes cluster diagnostics agent. - You may ONLY use the tools provided. - - # -- Per-run RBAC (configurable rules) ---------- - rbac: - rules: - - apiGroups: [""] - resources: [pods, services, events] - verbs: [get, list, watch] - - apiGroups: [apps] - resources: [deployments, replicasets] - verbs: [get, list, watch] - - # -- OPA policy --------------------------------- - policy: - opa: - configMapRef: - name: agent-policies - key: tool-policy.rego - defaultDeny: true - - # -- Network isolation -------------------------- - networkPolicy: strict - - # -- Tekton hooks (optional) -------------------- - preHooks: - pipelineRef: - name: prompt-security-scan - postHooks: - pipelineRef: - name: audit-bundle-collection -``` - -### Agent Lifecycle - -#### Standalone AgentRun - -When an AgentRun is created outside a PipelineRun: - -1. Controller creates a kagent Agent CR, owner-referenced to the - AgentRun -2. kagent creates the Deployment + Service -3. Controller waits for Agent Ready condition -4. Controller POSTs the goal to the Agent Service via HTTP -5. Controller collects results from the response -6. AgentRun marked Succeeded/Failed -7. Agent CR garbage collected via owner reference - -The Agent Deployment is short-lived. It exists only for this one goal. - -#### AgentRun in a PipelineRun - -When multiple AgentRuns in a PipelineRun reference the same -AgentConfig: - -1. First AgentRun: controller creates a kagent Agent CR, labeled - with the PipelineRun UID and AgentConfig name -2. kagent creates the Deployment + Service -3. Controller POSTs the first goal, collects results -4. Second AgentRun (same configRef, same PipelineRun): controller - finds the existing Agent CR by label, reuses it -5. Controller POSTs the second goal. The agent has conversation - context from the first goal. -6. PipelineRun completes: Agent CR is cleaned up - -The Agent maintains conversation context across all steps that share -the same configRef within a PipelineRun. - -#### Multiple Agents in a PipelineRun - -Different configRef values create different Agent CRs: - -```yaml -tasks: - - name: security-review - taskRef: - apiVersion: agent.tekton.dev/v1alpha1 - kind: AgentRun - params: - - name: configRef - value: security-agent # Agent CR #1 - - name: goal - value: "Review for vulnerabilities" - - - name: analyze-code - taskRef: - apiVersion: agent.tekton.dev/v1alpha1 - kind: AgentRun - params: - - name: configRef - value: test-agent # Agent CR #2 - - name: goal - value: "Analyze the codebase" - - - name: generate-tests - runAfter: [analyze-code] - taskRef: - apiVersion: agent.tekton.dev/v1alpha1 - kind: AgentRun - params: - - name: configRef - value: test-agent # Reuses Agent CR #2 - - name: goal - value: "Generate tests based on your analysis" -``` - -This PipelineRun creates two Agent CRs. `security-agent` handles one -step. `test-agent` handles two steps with shared context. - -### Integration with kagent - -The AgentRun controller uses kagent in two ways: - -**1. Agent CR (agent runtime)** - -The controller creates kagent `Agent` CRs via the dynamic client. Each -Agent CR references a kagent `ModelConfig` for the LLM provider and -kagent `RemoteMCPServer` resources for tools. kagent's controller -handles creating the Deployment, Service, and configuring the ADK -runtime. The AgentRun controller does not build Pods directly. - -**2. Configuration CRDs (read-only, via dynamic client)** - -The controller reads kagent `ModelConfig` and `RemoteMCPServer` CRs to -validate that referenced models exist and tools are discovered. These -are long-lived cluster resources managed by platform administrators. - -The controller interacts with all kagent CRDs via -`k8s.io/client-go/dynamic` to avoid Go module version coupling -(kagent uses k8s.io v0.35, this controller uses v0.32). - -### Integration with Tekton Pipelines - -**CustomTask adapter** (Phase 1): AgentRun implements the Tekton -CustomTask protocol, allowing it to be referenced from Pipeline steps: - -```yaml -apiVersion: tekton.dev/v1 -kind: Pipeline -metadata: - name: review-and-deploy -spec: - tasks: - - name: code-review - taskRef: - apiVersion: agent.tekton.dev/v1alpha1 - kind: AgentRun - params: - - name: configRef - value: code-review-agent - - name: goal - value: "Review the PR diff for security issues" - - name: deploy - runAfter: [code-review] - when: - - input: "$(tasks.code-review.results.approved)" - operator: in - values: ["true"] - taskRef: - name: kubectl-deploy -``` - -**Pre/post hooks**: Optional Tekton PipelineRuns for security scanning -(pre) and audit collection (post) around agent execution. Created via -dynamic client, owner-referenced for cleanup. When Tekton Pipelines is -not installed, hooks configuration is rejected at validation time. - -### Codebase Access via MCP Tools - -Agents access codebases and cluster state through [MCP][mcp] tool -calls, not through volume mounts. This is a deliberate architectural decision. - -Volume mounts give the agent raw filesystem access. The agent can read -any file on the mounted volume. OPA policy cannot restrict which files -the agent reads because file reads happen inside the container, outside -the tool call protocol. - -MCP tools route every data access through the tool protocol. Every -`read_file`, `search_code`, `list_functions` call goes through the MCP -server, which means every call is restricted by kagent's -`allowedTools` enforcement and recorded in the agent's tool call -history for provenance. - -``` -Volume mount: Agent reads filesystem directly. No restriction. No audit. -MCP tools: Agent calls tool. kagent enforces allowedTools. MCP server reads file. Audited. -``` - -The following diagram shows how multiple agents access the same -codebase through a shared MCP server with different tool allowlists: - -```mermaid -flowchart LR - subgraph Pipeline["PipelineRun"] - Clone["git-clone step
(writes to PVC)"] - end - - subgraph MCP["MCP ToolServer Pod"] - PVC["Workspace PVC
(mounted read/write)"] - RF["read_file"] - SC["search_code"] - LF["list_functions"] - WF["write_file"] - end - - subgraph Agents["Agent Deployments"] - A1["Security Agent
allowedTools:
read_file, search_code"] - A2["Test Agent
allowedTools:
read_file, list_functions,
write_file"] - end - - Clone --> PVC - A1 -->|"read_file"| RF - A1 -->|"search_code"| SC - A1 -.->|"BLOCKED"| WF - A2 -->|"read_file"| RF - A2 -->|"list_functions"| LF - A2 -->|"write_file"| WF -``` - -For multi-agent pipelines working on the same codebase, all agents talk -to the same MCP server. Each agent has its own tool allowlist controlling -what it can access through that server: - -``` -PipelineRun -├── MCP Server (ToolServer CR, has workspace PVC mounted) -│ ├── read_file -│ ├── search_code -│ ├── list_functions -│ └── write_file -│ -├── Agent CR #1 (security-agent) -│ └── allowedTools: [read_file, search_code] -│ -├── Agent CR #2 (test-agent) -│ └── allowedTools: [read_file, list_functions, write_file] -``` - -Agents that need to write files (test generation) use a `write_file` -tool on the MCP server. The write is controlled by kagent's -`allowedTools` (Gate 3) and recorded in the provenance trace (Gate 5). - -### Security Layer - -#### Threat Model - -Agentic workflows introduce threats that do not exist in traditional -container-based CI/CD: - -```mermaid -flowchart TD - subgraph Threats["Threat Sources"] - LLM["LLM itself
(hallucination,
instruction failure)"] - Prompt["Prompt injection
(via pipeline params,
Jira tickets, PR descriptions)"] - Tools["Malicious tools
(compromised MCP servers,
poisoned skill packages)"] - Data["Poisoned data
(crafted pod logs,
error messages designed
to manipulate agent)"] - Human["Misconfiguration
(overly broad permissions,
missing policy)"] - end - - subgraph Impacts["What Can Go Wrong"] - Access["Agent accesses data
it should not"] - Exfil["Agent exfiltrates data
to LLM provider"] - Write["Agent calls write tools
it should not"] - Cost["Agent runs indefinitely
consuming tokens"] - Invisible["Nobody knows
what happened"] - end - - LLM --> Access - LLM --> Write - Prompt --> Access - Prompt --> Write - Tools --> Exfil - Data --> Write - Human --> Access - Human --> Cost - LLM --> Invisible - Tools --> Invisible -``` - -The [CoSAI Principles for Secure Agentic Systems][cosai] state: "The -non-deterministic nature of AI means we cannot always predict the exact -path an agent will take, making strong foundational cybersecurity -controls that strictly limit potential actions to expected and intended -purposes critical." - -The [OWASP Top 10 for Agentic Applications][owasp-agentic] identifies -agent behavior hijacking (ASI01), prompt injection (ASI02), and tool -misuse (ASI03) as the top risks. These are the threats this security -layer addresses. - -#### Five-Gate Security Architecture - -Each threat is stopped at a specific point in the execution path. -No single gate is sufficient. The five gates compose Kubernetes RBAC, -NetworkPolicy, OPA, kagent tool restrictions, and MCP protocol into a -coherent security boundary around agent execution. - -```mermaid -flowchart TD - Goal["Goal submitted"] - - subgraph G1["Gate 1: Goal Admission"] - OPA["OPA evaluates goal
+ namespace + tools"] - PreHook["Pre-hook pipeline
(prompt security scan)"] - Validate["AgentConfig validation
(RBAC rules, token budget)"] - end - - subgraph G2["Gate 2: Cluster Access"] - SA["Per-run ServiceAccount"] - Role["Per-run Role
(from AgentConfig.rbac.rules)"] - NetPol["Per-run NetworkPolicy
(only declared endpoints)"] - end - - subgraph G3["Gate 3: Tool Restriction"] - Allowed["kagent allowedTools
(static allowlist)"] - Approval["kagent requireApproval
(human gate)"] - FutureOPA["Future: per-tool-call OPA
(conditional policy)"] - end - - subgraph G4["Gate 4: Data Flow"] - MCPOnly["All access via MCP tools
(no volume mounts)"] - Audit["Every tool call
recorded in trace"] - end - - subgraph G5["Gate 5: Attestation"] - Prov["Provenance recorded
(buildType, model, tools)"] - NonDet["reproducible: false
(explicit non-determinism)"] - Chains["Tekton Chains
(cryptographic signature)"] - PostHook["Post-hook pipeline
(audit bundle)"] - end - - Goal --> G1 - G1 -->|pass| G2 - G2 --> G3 - G3 --> G4 - G4 --> G5 - - G1 -->|fail| Reject1["REJECT: goal denied"] - G3 -->|fail| Reject2["REJECT: tool blocked"] -``` - -| Gate | Threat Addressed | Provided By | Exists Today? | -|------|-----------------|-------------|---------------| -| 1. Goal admission | Prompt injection, misconfiguration | OPA (library) + Tekton pre-hook PipelineRun | Yes | -| 2. Cluster access | Unauthorized data access, lateral movement | Kubernetes RBAC + NetworkPolicy (native K8s) | Yes | -| 3. Tool restriction | Tool misuse, unauthorized write operations | kagent allowedTools + requireApproval | Yes | -| 4. Data flow | Data exfiltration, unaudited access | MCP protocol (all access via tool calls) | Yes | -| 5. Attestation | Invisible agent behavior, no accountability | Provenance struct + Tekton Chains | Partially (provenance new, Chains exists) | - -Every gate except provenance uses existing infrastructure. AgentRun -does not invent new security primitives. It composes existing -Kubernetes, kagent, and Tekton mechanisms into a per-execution -security boundary with cleanup and audit trail. - -#### Gate 1: Goal Admission (OPA) - -The controller evaluates OPA policy at goal submission time. The OPA -input includes the goal text, requested tool servers, target -namespaces, and the AgentConfig reference. OPA can reject the entire -execution before any agent is created. - -```rego -package agent.goals - -default allow = false - -allow { - input.namespace in data.allowed_namespaces -} - -deny[msg] { - some tool in input.requested_tools - tool in data.write_tools - not tool in input.require_approval - msg := sprintf("write tool %s must have requireApproval set", [tool]) -} -``` - -OPA input is constructed by the controller, not the LLM: - -```go -input := map[string]interface{}{ - "goal": agentRun.Spec.Goal, - "namespace": agentRun.Namespace, - "requested_tools": allowedToolsList, - "require_approval": requireApprovalList, - "config": agentConfigName, -} -``` - -Default policy is **fail-closed**: when no OPA policy ConfigMap is -configured, goal submission is denied unconditionally. The -`defaultDeny` field in AgentConfig is reserved for future use to -allow explicit opt-in to permissive mode; the default behavior is -always deny. - -The controller also sets `allowedTools` and `requireApproval` on -the Agent CR, which kagent enforces at Gate 3. - -#### Gate 2: Cluster Access (RBAC + NetworkPolicy) - -For each AgentRun (or per PipelineRun for shared agents), the -controller creates: -- A `ServiceAccount` named `-sa` -- A `Role` with rules from `AgentConfig.spec.rbac.rules` -- A `RoleBinding` binding the Role to the ServiceAccount - -The kagent Agent CR is configured with -`spec.declarative.deployment.serviceAccountName` so the agent -Deployment uses this scoped ServiceAccount. All resources are -owner-referenced for cleanup. - -When `networkPolicy: strict`, the controller generates a -NetworkPolicy that: -- Allows ingress from the AgentRun controller (HTTP communication) -- Allows egress to: Kubernetes API server (resolved cluster IP, - 443/tcp), DNS (53/udp), declared MCP tool server endpoints -- Denies all other traffic - -#### Gate 3: Tool Restriction (kagent) - -kagent's ADK runtime enforces `allowedTools` (the agent cannot call -tools not in the list) and `requireApproval` (the agent pauses and -waits for human approval before calling specified tools). These are -set by the AgentRun controller when constructing the Agent CR from -the AgentConfig. - -Future: per-tool-call OPA evaluation inside the kagent ADK, where -the full tool call input (namespace, resource type, label selectors) -is available. This requires an upstream contribution to kagent. - -#### Gate 4: Data Flow (MCP) - -All codebase and cluster access goes through MCP tool calls, not -volume mounts. This means every data access is visible in the -execution trace, restricted by the tool allowlist, and auditable -in provenance. See [Codebase Access via MCP Tools](#codebase-access-via-mcp-tools). - -#### Gate 5: Attestation (Provenance + Chains) - -See [Provenance](#provenance) for the full provenance schema, -buildType URI, telemetry data flow, and Chains integration. - -### Provenance - -#### Agent Provenance vs Build Provenance - -Traditional CI/CD provenance (SLSA, in-toto) assumes a deterministic -build: the same source, builder, and parameters produce the same -artifact. Agent execution is fundamentally different. The same goal, -model, and tools can produce different tool call sequences, different -reasoning paths, and different results on every run. This is not a -bug; it is the nature of LLM-based reasoning. - -This means agent provenance must be **descriptive** (what happened) -rather than **prescriptive** (what should happen). A verifier cannot -reproduce an agent execution from its provenance. Instead, provenance -answers: what model was used, what prompt was given, what tools were -called in what order, what policy decisions were made, and what -results were produced. - -The TEP proposes an agentic provenance extension that records this -metadata in a format compatible with [SLSA provenance][slsa] and -informed by the [PROV-AGENT][prov-agent] schema for tracking AI -agent interactions. The [LLM Agents for Interactive Workflow -Provenance][workflow-provenance] reference architecture provides -additional context for provenance capture in non-deterministic -workflows. - -#### buildType - -The TEP defines a new buildType URI for agentic executions: - -``` -https://tekton.dev/agent-provenance/v1 -``` - -This buildType signals to verifiers that the execution is -non-deterministic, the artifact cannot be reproduced from the same -inputs, and the provenance contains agent-specific fields -(model identity, tool call sequence, policy decisions). - -#### Provenance Fields - -The `AgentRun.status.provenance` captures the following, mapped to -SLSA predicate fields: - -```yaml -provenance: - # Build definition - buildType: "https://tekton.dev/agent-provenance/v1" - reproducible: false - reproducibilityNote: "LLM-based agent execution is non-deterministic" - - # External parameters (user-provided inputs) - externalParameters: - goal: "Diagnose why deployment api-server is failing" - goalHash: "sha256:def456..." - hints: ["Check recent events", "Review pod logs"] - agentConfigRef: cluster-diagnostics - agentConfigHash: "sha256:789abc..." # hash of snapshotted config - - # Internal parameters (system-determined) - internalParameters: - model: - provider: Anthropic - modelId: "claude-sonnet-4-6" - apiVersion: "2023-06-01" - temperature: 0.2 - maxTokens: 4096 - tokenBudget: 16384 - systemPromptHash: "sha256:abc123..." - maxIterations: 5 - timeout: "10m" - opaPolicy: - configMapRef: agent-policies - policyHash: "sha256:fed321..." - - # Resolved dependencies (runtime-discovered) - resolvedDependencies: - - name: kagent-agent-cr - uri: "kagent.dev/v1alpha2/Agent/default/cluster-diagnostics-7xk2" - - name: adk-runtime-image - uri: "ghcr.io/kagent-dev/kagent/app" - digest: "sha256:a1b2c3..." - - name: mcp-server-k8s-read-tools - uri: "kagent.dev/v1alpha2/RemoteMCPServer/kagent-system/k8s-read-tools" - toolsDiscovered: ["k8s_get_resources", "k8s_get_logs", "k8s_describe"] - - name: model-config - uri: "kagent.dev/v1alpha2/ModelConfig/kagent-system/claude-sonnet" - - # Execution trace (ordered tool call sequence) - executionTrace: - iterations: 3 - toolCalls: - - sequence: 1 - iteration: 1 - tool: k8s_get_resources - inputHash: "sha256:111..." - outputHash: "sha256:222..." - timestamp: "2026-03-20T14:20:05Z" - durationMs: 340 - policyVerdict: allowed - - sequence: 2 - iteration: 1 - tool: k8s_get_logs - inputHash: "sha256:333..." - outputHash: "sha256:444..." - timestamp: "2026-03-20T14:20:06Z" - durationMs: 520 - policyVerdict: allowed - - sequence: 3 - iteration: 2 - tool: k8s_describe - inputHash: "sha256:555..." - outputHash: "sha256:666..." - timestamp: "2026-03-20T14:20:08Z" - durationMs: 280 - policyVerdict: allowed - llmInvocations: - - sequence: 1 - iteration: 1 - requestHash: "sha256:aaa..." - responseHash: "sha256:bbb..." - promptTokens: 1200 - completionTokens: 800 - - sequence: 2 - iteration: 2 - requestHash: "sha256:ccc..." - responseHash: "sha256:ddd..." - promptTokens: 2400 - completionTokens: 600 - - # Token usage (total and per-invocation breakdown) - tokenUsage: - totalPromptTokens: 3600 - totalCompletionTokens: 1400 - totalTokens: 5000 - - # Policy decisions - policyDecisions: - layer1_opa: - engine: OPA - evaluated: 1 - allowed: 1 - denied: 0 - layer2_kagent: - allowedToolsEnforced: true - toolsBlocked: 0 - approvalsPaused: 0 - - # Builder identity - builder: - controllerVersion: "v0.1.0" - kagentVersion: "v0.7.13" - adkImageDigest: "sha256:a1b2c3..." - - # Result - resultHash: "sha256:eee..." -``` - -#### Telemetry Data Flow - -The controller cannot observe individual tool calls and LLM -invocations because they happen inside the kagent ADK runtime. The -execution trace is collected from the kagent Agent's HTTP response. - -The controller POSTs a goal to the Agent Service and expects a -structured JSON response that includes both the result and execution -telemetry: - -```json -{ - "result": { - "diagnosis": "OOMKilled: memory limit too low", - "recommendation": "Increase to 512Mi" - }, - "telemetry": { - "iterations": 3, - "toolCalls": [...], - "llmInvocations": [...], - "tokenUsage": {...} - } -} -``` - -kagent's ADK already tracks tool calls and LLM invocations internally -for its session management. Exposing this data in the HTTP response -is an upstream contribution to kagent. Until this is available, the -controller records what it can observe directly: model identity, -prompt hash, policy decisions (Gate 1), and timing metadata. - -#### Tekton Chains Integration - -Tekton Chains discovers agent provenance through the CustomTask -adapter. When an AgentRun completes as a CustomRun within a -PipelineRun, Chains processes it like any other step: - -1. Chains watches CustomRun completion events -2. The CustomRun status contains the `provenance` struct -3. Chains maps the struct to an in-toto attestation using the - `https://tekton.dev/agent-provenance/v1` buildType -4. The attestation is signed and stored alongside the PipelineRun - attestation - -For standalone AgentRuns (not in a Pipeline), a Chains extension -watches AgentRun completion events directly and produces standalone -attestations. - -The `reproducible: false` flag signals to any SLSA verifier that -this execution cannot be reproduced from the same inputs. This is -a necessary extension for non-deterministic build steps. - -#### Prompt Auditability - -The `systemPrompt` field in AgentConfig is mutable. The controller -records `systemPromptHash` (SHA-256) in provenance. This is -auditability, not immutability: you can verify what was used and -detect changes between runs. True immutability would require an -admission webhook and is out of scope. - -#### Token Budget - -The `tokenBudget` field is passed to the kagent Agent as -configuration. If the ADK runtime does not enforce it natively, the -controller enforces a timeout-based fallback. Token budgets via Pod -timeout are best-effort because a model can consume many tokens in a -short time. This limitation is acknowledged. - -#### Prior Art - -- [PROV-AGENT][prov-agent] extends W3C PROV with agent-specific - entities (AIAgent, AgentTool, AIModelInvocation) and relationships - for tracking non-deterministic agent interactions. The provenance - schema in this TEP is informed by PROV-AGENT's entity model. -- [CoSAI Principles][cosai] recommend adapting SLSA for agent and - model artifact provenance, with continuous runtime validation. -- [OWASP Top 10 for Agentic Applications][owasp-agentic] identifies - agent behavior hijacking (ASI01), prompt injection (ASI02), and - tool misuse (ASI03) as top risks. The provenance trace enables - post-hoc detection of all three. - -### Notes and Caveats - -- **kagent ADK image compatibility**: The controller creates kagent - Agent CRs that reference a specific ADK image tag. Breaking changes - in kagent's Agent CR spec would require controller updates. This is - mitigated by pinning to tested kagent versions in CI. -- **Cross-namespace secret access**: ModelConfig in `kagent-system` - references API key secrets in `kagent-system`. The Agent Deployment - runs in the user's namespace. kagent's controller handles secret - mounting in the Agent Deployment. The AgentRun controller does not - need to manage cross-namespace secrets directly. -- **AgentConfig mutability during execution**: If AgentConfig is - updated while an AgentRun is in the Acting phase, the running agent - is not affected because the Agent CR was created with a snapshot of - the configuration at reconcile time. Subsequent AgentRuns will use - the updated AgentConfig. - -## Design Details - -### Execution Flow: Standalone AgentRun - -``` -AgentRun.Phase: Pending - ├── Validate AgentConfig exists - ├── Snapshot AgentConfig spec (immutable for this run) - ├── Resolve kagent ModelConfig via dynamic client GET - ├── Resolve kagent RemoteMCPServer(s) via dynamic client GET - ├── Validate: model ready, tools discovered, OPA policy exists - ├── Create ServiceAccount, Role, RoleBinding (owner-referenced) - └── Create NetworkPolicy if strict (owner-referenced) - -AgentRun.Phase: PreHooks (if configured) - ├── Create Tekton PipelineRun (owner-referenced) - └── Watch PipelineRun completion - -AgentRun.Phase: Acting - ├── Create kagent Agent CR (owner-referenced to AgentRun): - │ spec.type: Declarative - │ spec.declarative.modelConfig: - │ spec.declarative.systemMessage: - │ spec.declarative.tools: - │ spec.declarative.deployment.serviceAccountName: - ├── Wait for Agent Ready condition - ├── POST goal to Agent Service HTTP endpoint - └── Collect results from response - -AgentRun.Phase: PostHooks (if configured) - ├── Create Tekton PipelineRun with results as params - └── Watch PipelineRun completion - -AgentRun.Phase: Succeeded / Failed - ├── Update status with results and provenance - ├── Emit Kubernetes events - └── Per-run resources cleaned up via owner references -``` - -### Execution Flow: PipelineRun with Agent Steps - -``` -PipelineRun starts with agent steps (CustomTask references) - -First AgentRun with configRef "test-agent": - ├── Create Agent CR "test-agent-" - │ labels: - │ agent.tekton.dev/pipelinerun: - │ agent.tekton.dev/config: test-agent - │ ownerReferences: [{kind: PipelineRun}] - ├── Create RBAC resources (owner-referenced to PipelineRun) - ├── Wait for Agent Ready - ├── POST goal, collect results - └── AgentRun marked Succeeded - -Second AgentRun with configRef "test-agent" (same PipelineRun): - ├── Find existing Agent CR by labels: - │ agent.tekton.dev/pipelinerun: - │ agent.tekton.dev/config: test-agent - ├── Agent already Ready - ├── POST goal (agent has context from first call) - ├── Collect results - └── AgentRun marked Succeeded - -PipelineRun completes: - └── Agent CR garbage collected via owner reference to PipelineRun -``` - -### Agent CR Scoping and Reuse - -The following diagram shows how the controller decides whether to -create a new Agent CR or reuse an existing one: - -```mermaid -flowchart TD - Start["AgentRun reconciled"] - InPipeline{"Part of a
PipelineRun?"} - - Standalone["Standalone mode"] - CreateNew["Create new Agent CR
owner-ref: AgentRun"] - - Pipeline["PipelineRun mode"] - Search["Search for Agent CR with labels:
pipelinerun=UID, config=name"] - Found{"Agent CR
exists?"} - Reuse["Reuse existing Agent CR
POST goal to running agent"] - CreatePR["Create new Agent CR
owner-ref: PipelineRun"] - - Start --> InPipeline - InPipeline -->|no| Standalone --> CreateNew - InPipeline -->|yes| Pipeline --> Search --> Found - Found -->|yes| Reuse - Found -->|no| CreatePR -``` - -The controller uses labels to track Agent CR ownership: - -| Label | Value | Purpose | -|-------|-------|---------| -| `agent.tekton.dev/config` | AgentConfig name | Identifies which config this agent uses | -| `agent.tekton.dev/agentrun` | AgentRun name | Set for standalone AgentRuns | -| `agent.tekton.dev/pipelinerun` | PipelineRun UID | Set for PipelineRun-scoped agents | - -Reuse logic: -- Standalone: always create a new Agent CR -- In PipelineRun: list Agent CRs with matching `pipelinerun` and - `config` labels. If found, reuse. If not, create. - -Owner references: -- Standalone: Agent CR owner-referenced to AgentRun -- In PipelineRun: Agent CR owner-referenced to PipelineRun (so it outlives - individual AgentRuns but is cleaned up when the PipelineRun ends) - -### kagent Resource Resolution - -The controller reads kagent CRDs via `k8s.io/client-go/dynamic`. - -At reconcile time, the controller: - -1. GETs the referenced `kagent.dev/v1alpha2 ModelConfig` to validate - the model exists and is ready -2. GETs each referenced `kagent.dev/v1alpha2 RemoteMCPServer` to - validate tools are discovered -3. Constructs the kagent Agent CR spec with the resolved references - -The controller does not build `config.json` itself. kagent's own -controller handles the translation from Agent CR to ADK configuration. - -If kagent CRDs are not installed in the cluster, the controller sets a -`KagentNotInstalled` condition on the AgentRun with a clear message. - -### Security Implementation Details - -The five-gate security architecture is described in the -[Security Layer](#security-layer) section of the Proposal. This -section provides implementation-level details for Gates 1 and 2, -which are implemented by the AgentRun controller. Gate 3 -(allowedTools/requireApproval) and Gate 4 (MCP-only access) are -enforced by [kagent][kagent] inside the agent runtime. Gate 5 -(provenance) is detailed in -[Provenance Recording](#provenance-recording). - -#### Gate 1: OPA Goal Admission - -``` -// Pseudocode: OPA evaluation at goal submission -allowResult := opaEngine.Evaluate("data.agent.goals.allow", input) -denyResults := opaEngine.Evaluate("data.agent.goals.deny", input) - -if !allowResult || len(denyResults) > 0 { - reject AgentRun with PolicyDenied condition -} -``` - -Default policy when no ConfigMap is configured: - -```rego -package agent.goals -default allow = false -``` - -#### Gate 2: RBAC Resources - -```yaml -apiVersion: v1 -kind: ServiceAccount -metadata: - name: debug-api-server-sa - ownerReferences: - - apiVersion: agent.tekton.dev/v1alpha1 - kind: AgentRun - name: debug-api-server - uid: ---- -apiVersion: rbac.authorization.k8s.io/v1 -kind: Role -metadata: - name: debug-api-server-role - ownerReferences: # abbreviated, same as above - - apiVersion: agent.tekton.dev/v1alpha1 - kind: AgentRun - name: debug-api-server - uid: -rules: # from AgentConfig.spec.rbac.rules - - apiGroups: [""] - resources: [pods, services, events] - verbs: [get, list, watch] - - apiGroups: [apps] - resources: [deployments, replicasets] - verbs: [get, list, watch] ---- -apiVersion: rbac.authorization.k8s.io/v1 -kind: RoleBinding -metadata: - name: debug-api-server-binding - ownerReferences: # abbreviated - - apiVersion: agent.tekton.dev/v1alpha1 - kind: AgentRun - name: debug-api-server - uid: -roleRef: - apiGroup: rbac.authorization.k8s.io - kind: Role - name: debug-api-server-role -subjects: - - kind: ServiceAccount - name: debug-api-server-sa -``` - -The kagent Agent CR is configured with -`spec.declarative.deployment.serviceAccountName: debug-api-server-sa`. - -For PipelineRun-scoped agents, RBAC resources are owner-referenced to the -PipelineRun. Since all AgentRuns sharing this agent use the same -AgentConfig, there is no rule conflict. - -#### Gate 2: NetworkPolicy Resources - -```yaml -apiVersion: networking.k8s.io/v1 -kind: NetworkPolicy -metadata: - name: debug-api-server-netpol - ownerReferences: # abbreviated - - apiVersion: agent.tekton.dev/v1alpha1 - kind: AgentRun - name: debug-api-server - uid: -spec: - podSelector: - matchLabels: - agent.tekton.dev/config: cluster-diagnostics - policyTypes: [Ingress, Egress] - ingress: - - from: - - podSelector: - matchLabels: - app.kubernetes.io/component: agentrun-controller - egress: - - to: - - ipBlock: - cidr: /32 - ports: [{protocol: TCP, port: 443}] - - to: - - namespaceSelector: - matchLabels: - kubernetes.io/metadata.name: kube-system - ports: [{protocol: UDP, port: 53}] - # MCP tool server endpoints added dynamically -``` - -API server egress uses `ipBlock` with the resolved cluster IP, not -`namespaceSelector`, to avoid allowing the agent to reach arbitrary -HTTPS endpoints. - -### CustomTask Adapter - -When a Pipeline step references an AgentRun via `taskRef`, Tekton's -PipelineRun controller creates a `CustomRun` object (not an AgentRun -directly). The AgentRun controller watches `CustomRun` objects where -`spec.customRef.apiVersion` is `agent.tekton.dev/v1alpha1` and -`spec.customRef.kind` is `AgentRun`. - -```yaml -# Pipeline author writes: -taskRef: - apiVersion: agent.tekton.dev/v1alpha1 - kind: AgentRun - -# Tekton creates a CustomRun. AgentRun controller reconciles it. -``` - -The controller reconciles the `CustomRun` directly (it does not -create a separate `AgentRun` CR). The `CustomRun.spec.params` are -mapped to AgentRun spec fields: - -| CustomRun param | Maps to | -|-----------------|---------| -| `configRef` | AgentConfig name | -| `goal` | Goal text | -| `hints` | Context hints | - -The controller discovers the owning PipelineRun by inspecting -`CustomRun.metadata.ownerReferences` for a reference with -`kind: PipelineRun`. This PipelineRun UID is used for Agent CR -scoping and reuse. - -Results are written to `CustomRun.status.results` so downstream -Pipeline steps can reference them via `$(tasks..results.)`. -The `when` expression support follows from standard Tekton result -passing. - -Timeout and cancellation are handled by observing the `CustomRun` -spec: if Tekton sets a timeout or cancellation condition, the -controller stops the agent execution and cleans up. - -### Provenance Recording - -The provenance struct captures the full execution trace of an -agent run, mapped to SLSA predicate fields: - -```go -type AgentRunProvenance struct { - BuildType string `json:"buildType"` - Reproducible bool `json:"reproducible"` - ReproducibilityNote string `json:"reproducibilityNote,omitempty"` - ExternalParameters ExternalParams `json:"externalParameters"` - InternalParameters InternalParams `json:"internalParameters"` - ResolvedDependencies []ResolvedDependency `json:"resolvedDependencies"` - ExecutionTrace ExecutionTrace `json:"executionTrace"` - TokenUsage TokenUsage `json:"tokenUsage"` - PolicyDecisions PolicyDecisions `json:"policyDecisions"` - Builder BuilderIdentity `json:"builder"` - ResultHash string `json:"resultHash"` -} - -type ExternalParams struct { - Goal string `json:"goal"` - GoalHash string `json:"goalHash"` - Hints []string `json:"hints,omitempty"` - AgentConfigRef string `json:"agentConfigRef"` - AgentConfigHash string `json:"agentConfigHash"` -} - -type InternalParams struct { - Model ModelIdentity `json:"model"` - SystemPromptHash string `json:"systemPromptHash"` - MaxIterations int `json:"maxIterations"` - Timeout string `json:"timeout"` - TokenBudget int `json:"tokenBudget,omitempty"` - OPAPolicyHash string `json:"opaPolicyHash,omitempty"` -} - -type ModelIdentity struct { - Provider string `json:"provider"` - ModelID string `json:"modelId"` - APIVersion string `json:"apiVersion,omitempty"` - Temperature float64 `json:"temperature,omitempty"` - MaxTokens int `json:"maxTokens,omitempty"` -} - -type ResolvedDependency struct { - Name string `json:"name"` - URI string `json:"uri"` - Digest string `json:"digest,omitempty"` - ToolsDiscovered []string `json:"toolsDiscovered,omitempty"` -} - -type ExecutionTrace struct { - Iterations int `json:"iterations"` - ToolCalls []ToolCallRecord `json:"toolCalls"` - LLMInvocations []LLMInvocation `json:"llmInvocations"` -} - -type ToolCallRecord struct { - Sequence int `json:"sequence"` - Iteration int `json:"iteration"` - Tool string `json:"tool"` - InputHash string `json:"inputHash"` - OutputHash string `json:"outputHash"` - Timestamp string `json:"timestamp"` - DurationMs int `json:"durationMs"` - PolicyVerdict string `json:"policyVerdict"` -} - -type LLMInvocation struct { - Sequence int `json:"sequence"` - Iteration int `json:"iteration"` - RequestHash string `json:"requestHash"` - ResponseHash string `json:"responseHash"` - PromptTokens int `json:"promptTokens"` - CompletionTokens int `json:"completionTokens"` -} - -type TokenUsage struct { - TotalPromptTokens int `json:"totalPromptTokens"` - TotalCompletionTokens int `json:"totalCompletionTokens"` - TotalTokens int `json:"totalTokens"` -} - -type PolicyDecisions struct { - Layer1OPA OPADecisions `json:"layer1_opa"` - Layer2Kagent KagentDecisions `json:"layer2_kagent"` -} - -type OPADecisions struct { - Engine string `json:"engine"` - Evaluated int `json:"evaluated"` - Allowed int `json:"allowed"` - Denied int `json:"denied"` -} - -type KagentDecisions struct { - AllowedToolsEnforced bool `json:"allowedToolsEnforced"` - ToolsBlocked int `json:"toolsBlocked"` - ApprovalsPaused int `json:"approvalsPaused"` -} - -type BuilderIdentity struct { - ControllerVersion string `json:"controllerVersion"` - KagentVersion string `json:"kagentVersion"` - ADKImageDigest string `json:"adkImageDigest"` -} -``` - -The `ExecutionTrace` and `TokenUsage` fields depend on telemetry -from the kagent ADK runtime (see [Telemetry Data Flow](#telemetry-data-flow) -in the Proposal section). Until kagent exposes this telemetry in its -HTTP response, the controller populates what it can observe directly: -`ExternalParameters`, `InternalParameters`, `ResolvedDependencies`, -`PolicyDecisions` (Gate 1), and `Builder`. - -### AgentConfig Snapshot - -When an AgentRun is reconciled, the controller snapshots the -AgentConfig spec into the AgentRun status. This ensures that if the -AgentConfig is modified during execution, the running agent is not -affected and the provenance record reflects the actual configuration -used. - -## Design Evaluation - -### Reusability - -This proposal follows Tekton's [design principles][design-principles] -by reusing two existing projects: -- **[kagent][kagent]** provides the [Agent CR][kagent-agents], ADK - runtime, [ModelConfig][kagent-models], and - [RemoteMCPServer][kagent-tools] CRDs -- **Tekton Pipelines** provides Pipeline orchestration and the - [CustomTask][customtask] protocol - -The [AgentRun PoC][agentrun-poc] demonstrates the concept. The -controller adds agent lifecycle management, security, and provenance. - -### Simplicity - -Users interact with two CRDs (`AgentRun` and `AgentConfig`). In a -Pipeline, agent steps look like any other CustomTask reference. The -agent lifecycle (create, reuse, cleanup) is managed by the controller. - -### Flexibility - -- kagent ModelConfig supports 8 LLM providers -- MCP tool servers are pluggable -- RBAC rules are configurable per AgentConfig -- [OPA][opa] policies are user-defined via ConfigMaps -- Tekton Pipelines integration is optional -- Multiple agents with different configs can coexist in a PipelineRun - -### Conformance - -This proposal does not modify any existing Tekton APIs. New CRDs are -under the `agent.tekton.dev` API group. The CustomTask adapter follows -the existing CustomTask protocol. No changes to `Task`, `Pipeline`, -`TaskRun`, or `PipelineRun` resources. - -This proposal introduces kagent and OPA as additional concepts users -must understand. kagent CRDs are managed by cluster administrators. -OPA policies are managed by security teams. Pipeline authors only -interact with AgentRun and AgentConfig. - -### User Experience - -- **Cluster administrators** install kagent and configure ModelConfigs - and RemoteMCPServers -- **Platform engineers** create AgentConfigs with RBAC rules and OPA - policies -- **Pipeline authors** reference AgentRuns in Pipeline specs via - CustomTask -- **Security teams** define OPA policies and review agent provenance - -### Performance - -- **Agent startup**: kagent Agent Deployments require pod scheduling. - Typical startup is 5-15 seconds with pre-pulled images. For - PipelineRun-scoped agents, this cost is paid once and amortized - across all agent steps. -- **Controller footprint**: The controller creates Agent CRs and sends - HTTP requests. No LLM processing occurs in the controller. -- **Cleanup**: Owner references ensure no resource leaks. - -### Risks and Mitigations - -| Risk | Mitigation | -|------|------------| -| kagent Agent CR spec changes | Dynamic client is resilient to field additions. Pin to kagent v1alpha2. Test against kagent releases in CI. | -| kagent CRDs not installed | Controller checks at startup. Clear condition on AgentRun. | -| Tekton Pipelines not installed | Hooks are optional. CustomTask adapter degrades gracefully. Hooks configuration rejected at validation when Tekton is absent. | -| Agent Deployment startup latency | Pre-pulled images. PipelineRun-scoped agents amortize startup across steps. | -| OPA policy misconfiguration | Default fail-closed. Deny all when no policy is configured. | -| Per-tool-call OPA not available in Phase 1 | Five-gate model: OPA at goal level (Gate 1) + kagent allowedTools/requireApproval (Gate 3) provide meaningful security. Per-tool-call OPA is a future kagent contribution. | -| LLM-controlled tool inputs spoofing OPA | Gate 1 OPA input is constructed by the controller, not the LLM. Gate 3 tool restrictions are set on the Agent CR, not controllable by the LLM. | -| Token budget not enforced by ADK | Fallback to timeout. Acknowledge as best-effort. | -| PipelineRun cancelled while agent is running | Owner reference on Agent CR triggers garbage collection. Agent Deployment receives SIGTERM. | -| AgentConfig modified during execution | Config snapshotted at reconcile time. Running agent not affected. | - -### Drawbacks - -- **Dependency on kagent**: The proposal uses kagent's Agent CR and - CRDs. If kagent changes direction, the controller would need - adaptation. The dynamic client approach minimizes coupling. -- **Two systems to install**: Users must install kagent and the - AgentRun controller. Mitigated by Helm charts that bundle both. -- **Deployment overhead for standalone AgentRuns**: A single-goal - AgentRun creates a full Deployment + Service for one HTTP call. This - is the cost of using kagent's existing model. If kagent adds a - Job/batch mode ([kagent#1089][kagent-1089]), standalone AgentRuns - could switch to the lighter-weight model. - -## Alternatives - -### Build Agent Stack Inside Tekton - -Add an `agent` step type to `Task.spec.steps`, build MCPServerRef CRD, -build agent runtime sidecar, model configuration, tool discovery. - -Rejected: Massive scope that duplicates kagent. Would require the -Tekton community to build and maintain an agent runtime. - -### Pod-Per-Run Without Agent CR - -Create a Pod directly using kagent's ADK image for each AgentRun, -bypassing the Agent CR entirely. - -Rejected: Requires building a config.json translator that replicates -kagent's controller logic (model credential injection, MCP server -resolution, TLS configuration). Also requires kagent to support a -batch/one-shot mode ([kagent#1089][kagent-1089]) that does not exist -today. Using the Agent CR avoids both issues. - -### Pipeline spec.agents Field - -Add a new `spec.agents` field to Tekton's Pipeline CRD, analogous to -`spec.workspaces`, for declaring agent environments. - -Rejected: Requires changes to Tekton's core Pipeline CRD, which is a -much larger scope and would need its own TEP. The CustomTask approach -achieves the same result without modifying existing APIs. - -### Volume Mounts for Codebase Access - -Mount workspace PVCs directly into agent Deployments so agents can -read codebases via the filesystem. - -Rejected: Volume mounts give the agent raw filesystem access outside -the tool call protocol. OPA cannot restrict which files the agent -reads. No audit trail for file access. MCP tools route all data access -through the tool protocol, enabling uniform policy enforcement and -provenance recording. - -### Convention-Based Container Wrapping - -Continue wrapping agents in container steps with ad-hoc Python scripts. - -Rejected: This is the status quo. Opaque, insecure, unauditable. - -## Implementation Plan - -### Milestones - -**Phase 1: Core** -- AgentRun and AgentConfig CRDs with `rbac.rules`, `tokenBudget`, - `policy.opa.defaultDeny` fields -- kagent Agent CR creation with dynamic client (standalone lifecycle) -- Per-run RBAC generation (ServiceAccount + Role + RoleBinding) -- Real-time OPA enforcement (both `allow` and `deny`, namespaced - inputs, fail-closed default) -- kagent ModelConfig and RemoteMCPServer validation via dynamic client -- CustomTask adapter for Tekton Pipeline integration -- PipelineRun-scoped Agent CR reuse (same configRef = same agent) -- Provenance recording in AgentRun status -- AgentConfig snapshot at reconcile time - -**Phase 2: Hardening** -- Per-run NetworkPolicy generation (API server ipBlock, controller - ingress, MCP server egress) -- Tekton PipelineRun-based pre/post hooks -- Token budget enforcement (ADK config + timeout fallback) -- Tekton Chains extension for agent provenance attestation - -**Phase 3: Advanced** -- Per-tool-call OPA enforcement via kagent ADK hook (upstream - contribution to kagent) -- Pipeline-level agent cost aggregation -- Agent memory integration (kagent Memory CRD) -- Prompt auditability alerting (hash comparison between runs) -- Standalone AgentRun optimization via kagent batch mode - ([kagent#1089][kagent-1089]) when available - -### Test Plan - -- **Unit tests**: AgentConfig validation (rbac.rules required, OPA - configMapRef format), Agent CR construction (correct labels, owner - references, serviceAccountName), RBAC generation (rules from config, - not hardcoded), OPA input namespacing (verify key injection is - impossible), NetworkPolicy construction (ipBlock for API server, MCP - server egress), AgentConfig snapshot immutability -- **Integration tests**: End-to-end AgentRun lifecycle with mock kagent - CRDs (fake dynamic client), Agent CR reuse with same configRef in - mock PipelineRun, CustomTask adapter with mock Pipeline controller -- **E2E tests**: Full execution in Kind cluster with kagent installed. - Create ModelConfig, RemoteMCPServer, AgentConfig, AgentRun. Validate: - Agent CR created with correct SA, RBAC matches config rules, OPA - denies disallowed tools, results collected, provenance recorded. - Multi-step PipelineRun with shared agent context. -- **Security tests**: OPA input injection (verify `input.params.tool` - cannot overwrite `input.tool`), RBAC isolation (agent cannot access - resources outside declared rules), fail-closed default (no policy = - all denied) -- **Negative tests**: Non-existent AgentConfig reference, empty API - key secret, kagent CRDs not installed, PipelineRun cancellation - during agent execution, malformed Rego in OPA ConfigMap - -### Infrastructure Needed - -- Repository: `tektoncd/agentrun` (or initially - `waveywaves/tekton-agentrun`) -- CI pipeline: Kind cluster with kagent + Tekton Pipelines installed -- Helm chart for bundled installation - -### Upgrade and Migration Strategy - -This is a new feature with no existing behavior to migrate from. CRDs -are introduced at `v1alpha1` stability. Breaking changes are expected -during alpha. - -### Implementation Pull Requests - -To be populated when implementation begins. - -## References - -- [kagent][kagent] -- [kagent Agent CRD documentation][kagent-agents] -- [kagent ModelConfig documentation][kagent-models] -- [kagent RemoteMCPServer documentation][kagent-tools] -- [kagent batch/Job mode request][kagent-1089] -- [Model Context Protocol specification][mcp] -- [AgentRun PoC][agentrun-poc] -- [Pipeline comparison (with/without agents)][pipeline-comparison] -- [Tekton Chains][chains] -- [Tekton CustomTask specification][customtask] -- [SLSA Provenance Framework][slsa] -- [Open Policy Agent][opa] -- [Tekton Design Principles][design-principles] -- [PROV-AGENT: Unified Provenance for AI Agent Interactions][prov-agent] -- [LLM Agents for Interactive Workflow Provenance][workflow-provenance] -- [CoSAI Principles for Secure Agentic Systems][cosai] -- [OWASP Top 10 for Agentic Applications][owasp-agentic] - -[kagent]: https://kagent.dev -[kagent-agents]: https://kagent.dev/docs/kagent/concepts/agents -[kagent-models]: https://kagent.dev/docs/kagent/concepts/model-providers -[kagent-tools]: https://kagent.dev/docs/kagent/concepts/tool-servers -[kagent-1089]: https://github.com/kagent-dev/kagent/issues/1089 -[mcp]: https://modelcontextprotocol.io/ -[agentrun-poc]: https://github.com/waveywaves/tekton-agentrun -[pipeline-without-agents]: https://github.com/waveywaves/tektoncd-pipeline-mgmt/blob/main/demos/jira-test-lifecycle/pipeline-without-agents.yaml -[pipeline-with-agents]: https://github.com/waveywaves/tektoncd-pipeline-mgmt/blob/main/demos/jira-test-lifecycle/pipeline-with-agents.yaml -[pipeline-comparison]: https://github.com/waveywaves/tektoncd-pipeline-mgmt/tree/main/demos/jira-test-lifecycle -[chains]: https://github.com/tektoncd/chains -[customtask]: https://tekton.dev/docs/pipelines/runs/ -[slsa]: https://slsa.dev/ -[opa]: https://www.openpolicyagent.org/ -[design-principles]: https://github.com/tektoncd/community/blob/main/design-principles.md -[prov-agent]: https://arxiv.org/abs/2508.02866 -[workflow-provenance]: https://arxiv.org/abs/2509.13978 -[cosai]: https://www.coalitionforsecureai.org/announcing-the-cosai-principles-for-secure-by-design-agentic-systems/ -[owasp-agentic]: https://www.practical-devsecops.com/owasp-top-10-agentic-applications/ diff --git a/teps/0170-agent-native-workflows.md b/teps/0170-agent-native-workflows.md new file mode 100644 index 000000000..b6bb54286 --- /dev/null +++ b/teps/0170-agent-native-workflows.md @@ -0,0 +1,1715 @@ +--- +status: proposed +title: AgentTask and Pluggable Agent Execution +creation-date: '2026-03-20' +last-updated: '2026-08-30' +authors: +- '@waveywaves' +- '@anithapriyanatarajan' +- '@theakshaypant' +- '@maruiz93' +--- + +# TEP-0170: AgentTask and Pluggable Agent Execution + +--- + + +- [Summary](#summary) +- [Motivation](#motivation) + - [Goals](#goals) + - [Non-Goals](#non-goals) + - [Use Cases](#use-cases) + - [Migrate an Existing Agent Harness](#migrate-an-existing-agent-harness) + - [Orchestrate a Kubernetes-Native Agent Platform](#orchestrate-a-kubernetes-native-agent-platform) + - [Orchestrate a Remote Agent Service](#orchestrate-a-remote-agent-service) + - [Run a Containerized Agent](#run-a-containerized-agent) + - [Use a Protocol-Based Agent](#use-a-protocol-based-agent) + - [Requirements](#requirements) +- [Proposal](#proposal) + - [Concepts](#concepts) + - [Architecture](#architecture) + - [AgentTask](#agenttask) + - [Using AgentTask in a Pipeline](#using-agenttask-in-a-pipeline) + - [Agent Executor Framework](#agent-executor-framework) + - [Framework Responsibilities](#framework-responsibilities) + - [Executor Responsibilities](#executor-responsibilities) + - [Bring Your Own Executor](#bring-your-own-executor) + - [Worked Adapter Examples](#worked-adapter-examples) + - [Fullsend](#fullsend) + - [OpenShift Lightspeed Agentic Operator](#openshift-lightspeed-agentic-operator) + - [OpenHands](#openhands) + - [Reference TaskRun Executor](#reference-taskrun-executor) + - [Relationship to Remote Resolution](#relationship-to-remote-resolution) + - [Integration with Tekton Projects](#integration-with-tekton-projects) + - [Pipelines](#pipelines) + - [Triggers](#triggers) + - [Results](#results) + - [Chains](#chains) + - [Security and Responsibility Boundaries](#security-and-responsibility-boundaries) + - [Notes and Caveats](#notes-and-caveats) +- [Design Details](#design-details) + - [Preliminary AgentTask API](#preliminary-agenttask-api) + - [Executor Selection and Claiming](#executor-selection-and-claiming) + - [Executor Interface](#executor-interface) + - [Execution Lifecycle](#execution-lifecycle) + - [Acknowledgement](#acknowledgement) + - [Idempotency and Recovery](#idempotency-and-recovery) + - [Progress and Heartbeats](#progress-and-heartbeats) + - [Cancellation and Timeout](#cancellation-and-timeout) + - [Cleanup](#cleanup) + - [Retries](#retries) + - [Status and Outcome Semantics](#status-and-outcome-semantics) + - [Results, Logs, Artifacts, and Traces](#results-logs-artifacts-and-traces) + - [Parameters and Workspaces](#parameters-and-workspaces) + - [Identity and Credentials](#identity-and-credentials) + - [Definition Resolution and Provenance](#definition-resolution-and-provenance) + - [Conformance](#conformance) +- [Design Evaluation](#design-evaluation) + - [Reusability](#reusability) + - [Simplicity](#simplicity) + - [Flexibility](#flexibility) + - [Conformance](#conformance-1) + - [User Experience](#user-experience) + - [Performance](#performance) + - [Risks and Mitigations](#risks-and-mitigations) + - [Drawbacks](#drawbacks) +- [Alternatives](#alternatives) + - [Use Ordinary Tasks Only](#use-ordinary-tasks-only) + - [Use TaskRun spec.managedBy](#use-taskrun-specmanagedby) + - [Use CustomRun Controllers Without a Framework](#use-customrun-controllers-without-a-framework) + - [Introduce AgentRun](#introduce-agentrun) + - [Introduce AgentExecutorClass](#introduce-agentexecutorclass) + - [Standardize a Generic Agent Container Protocol](#standardize-a-generic-agent-container-protocol) + - [Standardize an Opaque Implementation Reference](#standardize-an-opaque-implementation-reference) + - [Use the Resolver Interface for Execution](#use-the-resolver-interface-for-execution) + - [Depend on kagent or Another Single Runtime](#depend-on-kagent-or-another-single-runtime) + - [Add Agent Fields to Pipeline](#add-agent-fields-to-pipeline) +- [Implementation Plan](#implementation-plan) + - [Milestones](#milestones) + - [Test Plan](#test-plan) + - [Infrastructure Needed](#infrastructure-needed) + - [Upgrade and Migration Strategy](#upgrade-and-migration-strategy) + - [Implementation Pull Requests](#implementation-pull-requests) +- [References](#references) + + +## Summary + +AI agents are increasingly used for code review, remediation, test generation, +incident diagnosis, and other work that must participate in a CI/CD graph. +Some agents are ordinary batch containers. Others are controlled by a +Kubernetes operator, a remote service, or an existing agent harness with its +own prompts, tools, approval flow, identity, sandbox, and runtime. + +Tekton can run the first category as an ordinary `Task`, but it does not offer +a portable Task-like contract for the other categories. Integrations can use a +[Custom Task][custom-tasks], but each integration must independently implement +selection, validation, acknowledgement, recovery, cancellation, results, and +status normalization. + +This TEP proposes: + +1. A reusable, namespaced `AgentTask` definition that declares the parameters, + workspaces, and results visible to a Pipeline and explicitly selects an + agent executor. +2. The existing `CustomRun` as the durable record for every `AgentTask` + execution. This TEP does not introduce `AgentRun`. +3. An Agent Executor Framework, modeled on the organizational patterns of + Tekton's remote resolver framework, that provides the common controller + lifecycle and a conformance contract for independently installed + executors. +4. Executor implementations that preserve an agent platform's native runtime + rather than reproducing it inside Tekton. + +Tekton Pipelines remains the DAG orchestrator. An executor may create a +`TaskRun` or Kubernetes workload, create and observe a platform-native custom +resource, or call a remote API. Fullsend, the OpenShift Lightspeed Agentic +Operator, and OpenHands are worked examples of those three integration +shapes. + +The proposal does not standardize prompts, models, tools, memory, agent loops, +approvals, sandboxes, or model-provider credentials. It standardizes only the +lifecycle and data that Tekton needs to schedule an agent invocation in a +Pipeline and observe its outcome. + +## Motivation + +A Pipeline author should be able to place an agent invocation in a graph, +provide inputs, wait for completion or approval, consume results, cancel the +run, and identify the implementation that produced the result. They should not +need to understand whether the implementation uses a Pod, a native custom +resource, or an HTTP conversation. + +The existing choices each cover only part of that need: + +- An ordinary `TaskRun` is strongly coupled to Tekton's Pod execution model. + It is the preferred solution for an agent that is already a batch + container, but not for an existing platform that owns execution elsewhere. +- `TaskRun.spec.managedBy` delegates the complete `TaskRun` lifecycle. The + external controller must reproduce ordinary `TaskRun` behavior, and a + Pipeline cannot currently select it as a per-task runtime in a portable way. +- `CustomRun` was designed for non-Pod execution and already carries params, + workspaces, service account, retries, timeout, cancellation, conditions, and + results. It is therefore the appropriate execution record. +- Custom Task authors lack a maintained framework that implements the common + lifecycle. [TEP-0071][tep-0071] identified that gap but was deferred. +- Remote resolvers demonstrate a successful Tekton extension model, but their + `Validate` and `Resolve` contract is a short, side-effect-free fetch that + returns immutable bytes. An agent execution is long-running, effectful, + observable, cancellable, and cleanup-sensitive. + +Without a shared contract, every agent integration invents different +condition reasons, cancellation behavior, result mappings, log locations, and +recovery semantics. Pipeline authors then depend on each platform's API rather +than a Tekton-facing contract. + +### Goals + +1. Define a portable `AgentTask` contract that is meaningful to Tekton and is + reusable across invocations. +2. Use `CustomRun` as the single execution record for `AgentTask` in a + Pipeline or as a standalone Custom Task invocation. +3. Let users bring an existing agent implementation by installing an executor + controller or bridge. +4. Provide common acknowledgement, idempotency, recovery, status, + cancellation, timeout, cleanup, and result semantics. +5. Preserve the native prompts, models, tools, approval mechanisms, memory, + sandboxes, and internal phases of existing agent platforms. +6. Reuse Tekton params, workspaces, service accounts, Pipeline scheduling, + `when` expressions, retries, timeouts, Triggers, Results, and Chains where + their current contracts permit. +7. Make executor installation and authoring comparable to resolver + installation and authoring: explicit selection, independent deployment, + narrow RBAC, a small interface, a template, and conformance tests. +8. Validate the design against a batch executor, a Kubernetes-native + controller, and a remote service. + +### Non-Goals + +1. Building an agent runtime, model gateway, prompt service, tool protocol, + memory store, or sandbox implementation in Tekton. +2. Defining the internal phases of an agent loop or requiring all platforms to + expose the same internal events. +3. Standardizing MCP, A2A, ACP, or another agent communication protocol. +4. Replacing a platform's native approval or human-intervention API. +5. Adding agent-specific fields to `Pipeline`, `PipelineRun`, `Task`, or + `TaskRun`. +6. Introducing a second run resource such as `AgentRun` alongside + `CustomRun`. +7. Requiring kagent, Fullsend, OpenShift Lightspeed, OpenHands, or any other + agent platform. +8. Making arbitrary platform-native configuration portable. Such + configuration remains behind the executor boundary. +9. Guaranteeing that an agent's semantic answer is correct. Conformance covers + execution behavior, not model quality. + +### Use Cases + +#### Migrate an Existing Agent Harness + +A platform team uses Fullsend for GitHub event normalization, agent harnesses, +skills, output validation, credential minting, sandbox policy, and selectable +agent runtimes. It wants Kubernetes and Tekton to replace GitHub Actions as the +workflow and execution infrastructure without rewriting those Fullsend +components. + +A Tekton Trigger creates a `PipelineRun`. Ordinary Tasks prepare the source and +report back to GitHub. An `AgentTask` selects the Fullsend executor, which runs +the existing harness in its sandbox and reports bounded results to the +`CustomRun`. + +#### Orchestrate a Kubernetes-Native Agent Platform + +A cluster operator already runs the OpenShift Lightspeed Agentic Operator. +Lightspeed owns `AgenticRun`, `AgenticRunApproval`, sandbox claims, step +conditions, and typed result custom resources. + +An `AgentTask` selects a Lightspeed executor. The executor creates and observes +an `AgenticRun`, preserves Lightspeed approvals and internal phases, and maps +only the lifecycle and declared results needed by Tekton. + +#### Orchestrate a Remote Agent Service + +An organization runs an OpenHands Agent Server outside the Pipeline +controller. An `AgentTask` selects an OpenHands executor. The executor starts a +conversation, stores the conversation ID as the native execution reference, +observes events until completion, and exposes result, log, and artifact +references through the `CustomRun`. + +#### Run a Containerized Agent + +An agent is distributed as a Task or container and does not require a separate +platform. A reference executor creates a child `TaskRun` and reuses Tekton's +existing Pod, workspace, result, log, and cancellation behavior. Authors who +do not need the common `AgentTask` surface can continue to use that Task +directly. + +#### Use a Protocol-Based Agent + +An agent already exposes a standard execution protocol such as A2A. An +executor translates the common `AgentTask` lifecycle to that protocol. The +protocol is an implementation choice; `AgentTask` does not copy protocol +messages or platform-specific configuration into the Tekton API. + +### Requirements + +| ID | Requirement | Priority | +|----|-------------|----------| +| R1 | `AgentTask` MUST declare its Pipeline-visible parameters, workspaces, and results. | Must | +| R2 | `AgentTask` MUST explicitly select one executor by a DNS-qualified name. | Must | +| R3 | A run-specific goal, event, or context MUST be provided through declared params or bound inputs, not by creating a new `AgentTask` definition for each run. | Must | +| R4 | Pipeline MUST continue to use `CustomRun` as the execution record for `AgentTask`. | Must | +| R5 | The proposal MUST NOT require a new `AgentRun` resource. | Must | +| R6 | The framework MUST acknowledge or reject an execution within a bounded interval. | Must | +| R7 | Execution creation MUST be idempotent across reconciliation and controller restart. | Must | +| R8 | The native execution identity MUST be persisted or deterministically recoverable before the executor reports the run as accepted. | Must | +| R9 | The executor MUST observe `CustomRun` cancellation and timeout and MUST drive the native execution toward termination. | Must | +| R10 | Terminal `CustomRun` status MUST distinguish successful completion, agent failure, infrastructure failure, `RunCancelled`, and expiry of `CustomRun.spec.timeout` with stable reasons. | Must | +| R11 | The executor MUST confirm cleanup or leave a native execution reference and explicit cleanup failure. | Must | +| R12 | Declared scalar results MUST be consumable by downstream Pipeline tasks and `when` expressions. | Must | +| R13 | Logs, artifacts, and traces MUST be represented by bounded references rather than copied unbounded into status. | Must | +| R14 | The effective `AgentTask` identity, executor name, executor version, and native execution reference MUST be available for provenance. | Must | +| R15 | The framework MUST validate and present the `CustomRun` service account name and workspace bindings to the executor; the executor MUST document its mapping or reject an unsupported binding. | Must | +| R16 | Credentials and Secret values MUST NOT be placed in `AgentTask` params, `CustomRun` results, status messages, logs references, or provenance. | Must | +| R17 | An executor MUST document its mapping to native sandbox, approval, tool, model, and agent-loop controls and MUST NOT silently bypass those controls. | Must | +| R18 | A retry after native execution starts MUST create a distinct attempt identity and MUST NOT occur merely because reconciliation returned a transient error. | Must | +| R19 | Executors MUST be independently installable and MUST receive only the RBAC needed for their backend. | Must | +| R20 | The project MUST publish an executor conformance suite and a minimal implementation template. | Must | +| R21 | `AgentTask` definitions SHOULD be resolvable and pinned using Tekton remote resolution. | Should | +| R22 | Tekton Results SHOULD persist the complete `CustomRun` lifecycle and discover referenced agent logs and artifacts. | Should | +| R23 | Tekton Chains SHOULD attest completed `AgentTask` executions. | Should | + +## Proposal + +### Concepts + +**AgentTask** +: A reusable, namespaced Custom Task definition. It declares the + Pipeline-visible contract and selects an executor. It does not describe a + model, prompt format, tool protocol, or sandbox. + +**CustomRun** +: The existing Tekton execution request and durable Pipeline child. One + `CustomRun` represents one `AgentTask` attempt history. There is no separate + `AgentRun`. + +**Agent executor** +: An implementation that translates the common lifecycle to a native + execution. It may create a Kubernetes workload, create another custom + resource, call a remote service, or use a standard protocol. + +**Agent Executor Framework** +: Shared controller machinery that loads and validates `AgentTask`, routes a + `CustomRun`, manages the common lifecycle, and normalizes observations from + an executor. + +**Adapter** +: The implementation role played by an executor when it bridges Tekton to an + existing platform. It is not a new CRD, central plugin registry, sidecar, or + mandatory network service. + +The responsibilities are intentionally split: + +| Component | Contribution | +|-----------|--------------| +| `CustomRun` | Per-run params and workspaces, Pipeline ownership, retries, timeout and cancellation requests, conditions, and results. | +| `AgentTask` | Reusable declarations, explicit executor binding, definition identity, and validation independent of one invocation. | +| Agent Executor Framework | Selection, bounded claim, idempotency, status normalization, cancellation, cleanup, and conformance. | +| Executor | Creation and observation of the platform-native execution and mapping of native outputs. | + +`AgentTask` therefore contributes more than another reference around +`CustomRun`: it gives different implementations one reusable contract that can +be validated, resolved, identified in provenance, and invoked consistently by +Pipelines. + +### Architecture + +```mermaid +flowchart LR + Event[Event source] --> Trigger[Tekton Trigger] + Trigger --> PR[PipelineRun] + PR --> CR[CustomRun] + AT[AgentTask] --> CR + + subgraph Framework[Agent Executor Framework] + Lifecycle[pre-claim validation and routing] + Route[executor selection] + end + + CR --> Lifecycle --> Route + + Route --> FS[Fullsend controller
framework + executor] + Route --> LS[Lightspeed controller
framework + executor] + Route --> OH[OpenHands controller
framework + executor] + Route --> TR[TaskRun controller
framework + executor] + + FS --> Job[Kubernetes Job and native sandbox] + LS --> AR[AgenticRun] + OH --> Conversation[OpenHands conversation API] + TR --> ChildTR[Child TaskRun] + + Job --> Obs[normalized observation] + AR --> Obs + Conversation --> Obs + ChildTR --> Obs + Obs --> CR + CR --> PR +``` + +Pipeline scheduling remains unchanged. When a `PipelineTask.taskRef` has the +`AgentTask` API version and kind, Pipeline treats it as a Custom Task and +creates a `CustomRun`. The framework and selected executor reconcile that +`CustomRun`; Pipeline waits on its standard `Succeeded` condition and consumes +its standard results. + +The executor name is part of the definition rather than the `PipelineTask`. +Pipeline authors therefore do not repeat executor plumbing at every +invocation. Changing the implementation publishes a versioned AgentTask and +updates references through the same promotion process used for other Task +definitions. + +### AgentTask + +An `AgentTask` contains only fields consumed by the AgentTask API, framework, +or Pipeline contract: + +```yaml +apiVersion: agent.tekton.dev/v1alpha1 +kind: AgentTask +metadata: + name: repository-review +spec: + description: Review a repository change and return a bounded decision. + params: + - name: request + type: string + description: The requested review goal. + - name: revision + type: string + description: The immutable source revision to inspect. + workspaces: + - name: source + description: Checked-out source for executors that support a workspace. + results: + - name: outcome + description: The executor-defined review outcome. + - name: report-url + description: A reference to the complete report. + executorRef: + name: fullsend.ai/executor + params: + - name: agent + value: review +``` + +`spec.params`, `spec.workspaces`, and `spec.results` are the reusable contract. +`spec.executorRef` binds that contract to an installed implementation. +Executor params identify existing platform configuration, such as a Fullsend +agent, a Lightspeed executor profile, an OpenHands profile, or a Task. + +The following do not become portable `AgentTask` fields: + +- model name or provider credentials; +- system prompt or agent instructions; +- MCP servers or tool definitions; +- internal planning, execution, or verification phases; +- conversation memory; +- native approval policy; +- container, Pod, sandbox, or virtual-machine templates. + +Those belong to the selected implementation. Adding them to the common API +would couple Tekton to one runtime and make existing platforms surrender +working controls. + +### Using AgentTask in a Pipeline + +```yaml +apiVersion: tekton.dev/v1 +kind: Pipeline +metadata: + name: review-change +spec: + params: + - name: request + type: string + - name: revision + type: string + workspaces: + - name: source + tasks: + - name: fetch-source + taskRef: + name: git-clone + params: + - name: revision + value: $(params.revision) + workspaces: + - name: output + workspace: source + + - name: review + runAfter: [fetch-source] + taskRef: + apiVersion: agent.tekton.dev/v1alpha1 + kind: AgentTask + name: repository-review + params: + - name: request + value: $(params.request) + - name: revision + value: $(params.revision) + workspaces: + - name: source + workspace: source + + - name: report + runAfter: [review] + taskRef: + name: report-review + params: + - name: outcome + value: $(tasks.review.results.outcome) + - name: report-url + value: $(tasks.review.results.report-url) +``` + +Pipeline authors use normal task dependencies, params, workspaces, result +references, retries, timeouts, `when` expressions, and finally tasks. Creating +a `CustomRun` directly remains the standalone invocation mechanism. + +### Agent Executor Framework + +The framework follows the resolver framework's extension model but uses a +non-blocking reconciliation contract. It consists of a small pre-claim +lifecycle reconciler and a controller library embedded in each Go executor +controller. The library calls the executor implementation in-process; there is +no unspecified RPC or intermediate request resource. + +Each executor is compiled and deployed as a controller. A distribution may +bundle several executor controllers in one binary, as Tekton does for built-in +resolvers, but this is packaging rather than a dynamic plugin system. + +#### Framework Responsibilities + +The framework: + +1. watches `CustomRun`s that reference or embed `AgentTask`; +2. loads or resolves the effective `AgentTask`; +3. validates declared params, workspaces, results, and executor selection; +4. records the effective definition identity and executor selection; +5. gives an installed executor a bounded claim interval; +6. initializes standard conditions, attempt identity, and timestamps; +7. supplies a stable idempotency key derived from the `CustomRun` UID and + attempt number; +8. handles framework-owned cancellation, timeout, heartbeat, and finalizer + behavior; +9. serializes executor observations into standard conditions, results, and + bounded references; +10. emits Tekton events and metrics; and +11. prevents two executor controllers from owning the same run. + +#### Executor Responsibilities + +An executor: + +1. validates implementation-specific params without exposing credentials; +2. creates or adopts exactly one native execution for an attempt; +3. persists or deterministically reconstructs its native execution identity; +4. observes native progress without blocking a reconcile call; +5. maps native state to the common observation model; +6. requests native cancellation and confirms termination; +7. confirms cleanup or reports a durable cleanup reference; +8. maps declared scalar results; and +9. publishes references to logs, artifacts, traces, and native detail. + +In the Go path, the executor implementation does not patch `CustomRun`; it +returns an observation to the framework wrapper in the same process. After an +atomic claim, that wrapper is the sole status writer for the run. The +pre-claim reconciler no longer mutates its status. + +#### Bring Your Own Executor + +An agent platform author has two supported paths: + +- Implement the Go executor interface and use the supplied controller + framework and project template. +- Implement a controller directly against the `AgentTask` and `CustomRun` + APIs. After claiming a run, that controller becomes its sole status writer + and implements the same compare-and-swap ownership, lifecycle, and + conformance requirements. This permits implementations in other languages. + +An implementation advertises a DNS-qualified selector such as +`fullsend.ai/executor`. Installing that implementation does not require adding +a platform-specific CRD to Tekton or registering code in a central service. +The implementation may, of course, use its own CRDs behind the boundary. + +A platform that already implements a suitable execution protocol can provide a +thin protocol executor. A platform without such a protocol provides a native +controller or API bridge. Merely placing an opaque object reference in +`AgentTask` is insufficient: the executor must implement the common lifecycle. + +### Worked Adapter Examples + +The following examples are non-normative. They validate that the common +boundary accommodates materially different platforms. Fullsend workflow +files, OpenShift Lightspeed CRDs, OpenHands payloads, commands, prompts, and +configuration do not become part of the `AgentTask` API. + +Each adapter must define the same boundary explicitly: + +| Adapter | Native identity | Workspace and identity | Results and observability | Cancellation and cleanup | +|---------|-----------------|------------------------|---------------------------|--------------------------| +| Fullsend | Deterministic Job name or service run ID keyed by the CustomRun attempt. | Mount the bound workspace into the Job, or upload an immutable snapshot; use the CustomRun service account only for a Kubernetes workload. | Validated Fullsend output becomes declared scalar results plus report and transcript references. | Stop the Job or service run, observe termination, and remove run-scoped sandbox resources. | +| Lightspeed | Deterministic `AgenticRun` name and UID. | Map only bindings supported by the selected executor profile; otherwise reject them. Lightspeed retains sandbox identity. | Map terminal conditions and scalar summaries; reference typed result CRs and sandbox logs. | Request the supported native stop operation or deletion, observe a terminal condition, and confirm child cleanup. | +| OpenHands | Conversation ID persisted before acceptance. | Use an operator-managed workspace/profile mapping and workload identity; never send the Kubernetes service-account token. | Map bounded terminal values; reference the conversation, trajectory, workspace artifacts, and logs. | Request stop/delete, confirm authoritative conversation termination, then apply the configured workspace-retention policy. | + +#### Fullsend + +Fullsend separates event dispatch, agent infrastructure, sandbox, harness, and +runtime. Its harness owns agent instructions, skills, output-schema +validation, pre- and post-scripts, runtime selection, credential minting, and +sandbox policy. These should remain Fullsend concerns. + +A Fullsend-backed definition may look like: + +```yaml +apiVersion: agent.tekton.dev/v1alpha1 +kind: AgentTask +metadata: + name: fullsend-review +spec: + params: + - name: event + type: object + - name: revision + type: string + workspaces: + - name: source + results: + - name: outcome + - name: report-url + executorRef: + name: fullsend.ai/executor + params: + - name: agent + value: review +``` + +The adapter would: + +1. convert declared Tekton params and source bindings into Fullsend's existing + normalized dispatch input; +2. create a deterministically named Kubernetes Job or invoke a managed + Fullsend service using the `CustomRun` attempt identity, and persist the Job + or service run ID before acceptance; +3. mount the workspace and use the CustomRun service account for a Job, or use + a documented immutable upload and workload-identity mapping for a managed + service; +4. let Fullsend select and bootstrap its runtime, harness, skills, sandbox, + credentials, and policy; +5. observe the Fullsend process and validated structured output; +6. return bounded scalar results and references to the complete report and + transcript; and +7. request termination on cancellation or timeout, observe it, and confirm + cleanup of run-scoped sandbox resources. + +A GitHub migration becomes: + +```text +GitHub webhook + -> Tekton Trigger + -> PipelineRun + -> source preparation Task + -> Fullsend-backed AgentTask + -> GitHub reporting Task/finally Task +``` + +GitHub remains the source of events, repository intent, and status reporting. +Tekton replaces GitHub Actions as the workflow graph and Kubernetes execution +infrastructure. Fullsend retains its own agent definitions, security hooks, +sandbox, credential exchange, runtime adapters, behavior tests, and output +validation. No Fullsend-specific CRD or command is required by this TEP. + +#### OpenShift Lightspeed Agentic Operator + +The OpenShift Lightspeed Agentic Operator is Kubernetes-native. It already +owns the `AgenticRun` lifecycle, per-step conditions, `AgenticRunApproval`, +sandbox resources, and typed Analysis, Execution, Verification, and Escalation +result custom resources. + +This follows the integration model in the Lightspeed +[Component Developer Guide][lightspeed-component-guide]. In that model, a +component-owned adapter receives an event and creates a namespaced +`AgenticRun`; the operator owns the subsequent agent and sandbox lifecycle. A +Lightspeed executor plays that adapter role for a Tekton `CustomRun`. The +guide's current step 3 is **Create an AgenticRun**. `Proposed` is a phase +derived later from `AgenticRun` conditions, not a separate Proposal resource. + +A Lightspeed-backed `AgentTask` selects the executor and an executor-managed +profile. The profile is adapter configuration that materializes Lightspeed's +inline workflow fields; it is not a new Lightspeed CRD: + +```yaml +executorRef: + name: lightspeed.openshift.io/agenticrun + params: + - name: profile + value: remediation +``` + +The adapter would: + +1. create an `AgenticRun` with a deterministic name and correlation labels + derived from the `CustomRun` attempt because Lightspeed does not deduplicate + runs for adapters; +2. use its own controller service account with the namespace-scoped access + described by Lightspeed, such as the `lightspeed-component-owner` role. The + `CustomRun` service account name does not authorize the controller's API + request; +3. map the declared request and target namespaces, then let the selected + executor profile materialize native workflow shape, agent names, + `analysisOutput`, skills images, tools, and same-namespace + `requiredSecrets` references; +4. keep Secret values out of `AgentTask` and `CustomRun` and reject any + service-account or workspace binding the profile cannot honor; +5. watch conditions and derive native phase. `Proposed` and `Escalating` are + non-terminal and must not be reported as completed; +6. report `WaitingForApproval` to Tekton while the native + `AgenticRunApproval` remains the approval authority; +7. map terminal native conditions to the common outcome taxonomy and expose + scalar summaries plus references to typed result CRs and sandbox logs; and +8. use a platform-supported per-run cancellation operation when available, + otherwise delete the `AgenticRun` and observe child cleanup. An adapter + must not toggle a cluster-wide emergency stop for one `CustomRun`. + +The adapter does not flatten Lightspeed's remediation, assisted, or advisory +workflow shapes into separate Tekton Tasks. Tekton orchestrates around one +logical agent invocation; Lightspeed continues to orchestrate inside it. + +#### OpenHands + +OpenHands Agent Server exposes conversations and events through an HTTP and +WebSocket API. It owns the agent implementation, workspace, tools, sandbox, +conversation history, and provider configuration. + +An OpenHands-backed definition selects an operator-managed profile: + +```yaml +executorRef: + name: openhands.dev/agent-server + params: + - name: profile + value: repository-change +``` + +The executor would: + +1. create a conversation using the declared goal and profile; +2. map a bound workspace through the profile's documented repository, + snapshot, or persistent-workspace mechanism and reject unsupported + bindings; +3. use the `CustomRun` attempt identity as an idempotency or correlation key; +4. persist the returned conversation ID before reporting the run accepted; +5. use the event stream only as a reconciliation trigger and derive terminal + state from the authoritative conversation-state endpoint, so unknown event + variants do not block observation; +6. expose the conversation URL, trajectory, workspace outputs, and logs as + references; +7. map declared bounded results from the terminal conversation; and +8. on cancellation or timeout, request stop/delete, confirm authoritative + termination, and apply the configured workspace-retention policy. + +The `AgentTask` does not embed an OpenHands conversation request. Executor +params refer to an operator-managed OpenHands profile, while run-specific +values remain declared Tekton params. + +#### Reference TaskRun Executor + +A reference executor may create a child `TaskRun` for users whose agent is +already packaged as a Tekton Task. It would: + +- resolve the referenced Task using existing resolution support; +- map matching params and workspaces; +- set an owner reference to the `CustomRun`; +- observe standard TaskRun conditions and results; +- reuse TaskRun logs, Pod cancellation, compute configuration, and Chains + support; and +- propagate only declared AgentTask results. + +This executor is an onboarding and conformance implementation, not a reason to +wrap every Task. If the Pipeline does not need a portable `AgentTask` +contract, the Task should be referenced directly. + +### Relationship to Remote Resolution + +Resource resolution and execution are separate stages: + +```text +AgentTask reference + -> resolve and pin definition + -> create/claim CustomRun + -> execute native workload or service +``` + +The existing resolver architecture provides useful patterns: + +| Resolver pattern | Agent execution use | +|------------------|---------------------| +| Explicit selector | `executorRef.name` | +| `ResolutionRequest` envelope | Existing `CustomRun` envelope | +| Deterministic request identity | Attempt idempotency key and native name | +| Owner references | Native Kubernetes child ownership | +| Shared framework and template | Agent Executor Framework and template | +| ConfigMap watcher | Optional executor administrator configuration | +| Narrow per-resolver RBAC | Narrow per-executor RBAC | +| Source and digest metadata | Resolved AgentTask identity and digest | +| Conformance tests | Executor lifecycle conformance | + +The resolver method set itself is not reused. `Resolve` performs a bounded +fetch and returns immutable bytes. Agent execution must persist a native +handle, repeatedly observe state, surface progress, handle external effects, +cancellation and cleanup, and survive controller restarts. + +An `AgentTask` referenced through a resolver should eventually produce the same +pinned definition metadata as a resolved Task. This requires extending remote +resolution and Custom Task handling to accept and snapshot `AgentTask` +definitions. Execution never occurs inside a resolver. + +### Integration with Tekton Projects + +#### Pipelines + +Pipeline already creates a `CustomRun` for a task reference with a non-Task +API version and kind. `CustomRun` supplies params, workspace bindings, service +account name, retries, timeout, cancellation request, conditions, string +results, retry history, and schemaless extra fields. + +The initial implementation can therefore add the `AgentTask` CRD and +controllers without adding agent-specific Pipeline fields. The AgentTask +framework validates the definition-level contract and writes standard +`CustomRun` status. + +#### Triggers + +Triggers remains the event-to-`PipelineRun` entry point. Event normalization +may be performed by a Trigger binding, an ordinary Task, or the selected +platform. `AgentTask` does not define a GitHub-, GitLab-, alert-, or +message-specific event schema. + +#### Results + +Tekton Results already persists the `CustomRun` lifecycle. It does not collect +CustomRun logs because a Custom Task is not necessarily Pod-backed. This TEP +requires an executor to publish log and artifact references. A Results +integration should discover those references and associate external log +providers or records with the owning `CustomRun` and `PipelineRun`. + +Large transcripts, prompts, model responses, source archives, and structured +reports must not be stored directly in Kubernetes status or ordinary Tekton +results. They belong in an access-controlled artifact or logging backend. + +#### Chains + +Tekton Chains currently observes `TaskRun` and `PipelineRun`, not `CustomRun`. +Chains support must be extended, or an equivalent PipelineRun-level +attestation must include completed AgentTask evidence. + +The minimum attested evidence is: + +- effective AgentTask name, UID, resource version, and content digest; +- executor selector and implementation version; +- `CustomRun` UID and attempt identity; +- native execution reference or a privacy-preserving digest; +- declared input source references and digests when available; +- terminal reason and declared result/artifact references; and +- timestamps and owning PipelineRun identity. + +Raw prompts, Secret values, credentials, unrestricted transcripts, and +sensitive model responses are excluded by default. + +### Security and Responsibility Boundaries + +The framework is responsible for secure lifecycle plumbing, not for replacing +an executor's sandbox or tool policy. + +| Concern | Owner | +|---------|-------| +| Pipeline ordering, timeout request, workspace binding, service account selection | Tekton Pipeline and CustomRun | +| AgentTask validation, executor claim, common status, idempotency, cancellation coordination | Agent Executor Framework | +| Model, prompt, tools, memory, internal approvals, sandbox, native policy | Selected agent platform | +| Mapping Tekton identity and inputs into the platform without credential leakage | Executor | +| Cluster admission, namespace quotas, network policy, and workload policy | Cluster operator | +| External-service identity and short-lived credential exchange | Executor/platform identity provider | +| Result, log, artifact, and provenance access control | Tekton installation and backend operators | + +Executor controllers receive the `CustomRun` service account name, but their +own Kubernetes API calls still use the controller's identity and RBAC. The +field does not grant impersonation. A Kubernetes executor may create a child +workload using the selected service account if its controller is authorized to +do so. A remote executor must not copy a service-account bearer token into a +remote service; it should use workload identity or an explicit, scoped +exchange supported by its platform. + +A workspace binding is authority to use the bound data only through the +executor's documented mapping. An executor that cannot safely map a workspace +must reject it with `CustomRunWorkspaceNotSupported` rather than silently +ignoring it. + +### Notes and Caveats + +- `CustomRun` currently supports only string results. The alpha AgentTask API + therefore exposes scalar string results; structured or large outputs use + artifact references until CustomRun gains typed results. +- `CustomRun.status.extraFields` is schemaless. The alpha framework can define + and version a reserved AgentTask status profile there, but a future + `CustomRun` API should provide typed execution and artifact references. +- Existing custom controllers are not automatically conformant executors. + They must implement the acknowledgement, idempotency, cancellation, + cleanup, and status contract. +- Native approval remains platform-specific. Tekton can display a waiting + state and link to the approval object or service, but this TEP does not + create a universal approval API. +- An agent may complete successfully while returning a negative business + decision such as `approved=false`. That is a successful execution with a + result, not an infrastructure failure. +- DNS-qualified executor names prevent accidental naming collisions but do + not provide installation discovery. The alpha design uses bounded claiming; + an `ExecutorClass` resource may be considered later only if operational + discovery and capability advertisement prove necessary. + +## Design Details + +### Preliminary AgentTask API + +The following structure is preliminary and subject to API review: + +```go +type AgentTaskSpec struct { + Description string `json:"description,omitempty"` + Params []ParamSpec `json:"params,omitempty"` + Workspaces []WorkspaceDeclaration `json:"workspaces,omitempty"` + Results []AgentTaskResult `json:"results,omitempty"` + ExecutorRef ExecutorRef `json:"executorRef"` +} + +type ExecutorRef struct { + Name string `json:"name"` + Params []Param `json:"params,omitempty"` +} + +type AgentTaskResult struct { + Name string `json:"name"` + Description string `json:"description,omitempty"` +} +``` + +The API should reuse Tekton parameter and workspace declaration types when +versioning and dependency boundaries allow it. Alpha result values are +strings because `CustomRunResult.Value` is currently a string. + +Normative validation includes: + +- `executorRef.name` is required and DNS-qualified; +- param, workspace, and result names are unique; +- executor params have unique names; +- runtime params not declared by the AgentTask are rejected; +- required params and workspaces are present; +- result names produced by an executor were declared; and +- `executorRef` and the Pipeline-visible contract are immutable. + +Immutability prevents an in-flight reference from silently changing meaning. +A changed implementation or contract uses a new AgentTask name or resolved +revision. Metadata that does not affect execution may remain mutable. + +The API does not require a cluster-scoped `ClusterAgentTask`. Namespaced +resources, remote resolution, and normal promotion tooling cover the initial +use cases without a second definition kind. + +### Executor Selection and Claiming + +Current Custom Task filters distinguish only API version and kind. Every +AgentTask executor would therefore observe the same `CustomRun` kind unless a +second selector is introduced. + +The framework uses this sequence: + +1. The AgentTask lifecycle reconciler loads the referenced or embedded + definition and validates it. +2. It writes the immutable label + `agent.tekton.dev/executor=` and the effective AgentTask + digest to the `CustomRun`. +3. Executor controllers filter on that label and independently reconcile only + their selector. +4. A matching executor atomically writes its stable installation identity, + `claimedAt`, and initial heartbeat to the reserved AgentTask status. All + replicas of one controller deployment share that installation identity. +5. After the claim succeeds, the framework wrapper embedded in that executor + controller is the sole status writer. The pre-claim reconciler stops + mutating status, and a controller with a different installation identity + stops when it observes the claim. +6. If no executor claims the run before the configured acknowledgement + deadline, the pre-claim reconciler marks it failed with + `ExecutorNotFound`. + +The label value must use a reversible or collision-resistant encoding because +Kubernetes label values cannot contain every character allowed in a +DNS-qualified selector. The unmodified selector remains in status and +provenance. + +This uses the existing `CustomRun` as the request envelope. It does not add an +executor registration CRD or an internal `AgentExecutionRequest` that would +become a second run record. + +### Executor Interface + +A Go interface may resemble the following, but observable behavior rather +than this exact method set is normative: + +```go +type Executor interface { + Initialize(context.Context) error + Name(context.Context) string + Validate(context.Context, *AgentTask, *CustomRun) error + Reconcile(context.Context, Request) (Observation, error) + Cancel(context.Context, Request) (Observation, error) +} +``` + +`Request` contains the effective immutable AgentTask, CustomRun, attempt +identity, selected service account, workspace bindings, and executor +administrator configuration. + +`Observation` contains bounded state: + +```go +type Observation struct { + State State + Reason string + Message string + ExecutionRef *ExecutionReference + Results []CustomRunResult + Logs []Reference + Artifacts []Reference + Traces []Reference + RequeueAfter time.Duration + CleanupComplete bool +} +``` + +`Reconcile` and `Cancel` must return quickly. A long operation happens in the +native platform; the controller watches, polls, or requeues. Implementations +must not retain the only copy of execution state in process memory. For this +interface, the framework wrapper and executor implementation run in the same +controller process; `Observation` is not a network protocol. + +An executor error means the controller could not complete reconciliation. It +is not automatically an agent failure. Typed errors distinguish transient +controller/backend errors, invalid requests, missing dependencies, and +terminal native failures. + +### Execution Lifecycle + +```mermaid +stateDiagram-v2 + [*] --> Pending + Pending --> Accepted: executor claims and persists native identity + Pending --> Failed: invalid or acknowledgement deadline + Accepted --> Running + Running --> Waiting: native approval or external input + Waiting --> Running: native platform resumes + Running --> Succeeded: declared work completed + Running --> Failed: agent or infrastructure failure + Pending --> Cancelling: cancellation request + Accepted --> Cancelling: cancellation request + Running --> Cancelling: cancellation request + Waiting --> Cancelling: cancellation request + Accepted --> TimingOut: spec.timeout expires + Running --> TimingOut: spec.timeout expires + Waiting --> TimingOut: spec.timeout expires + Cancelling --> Cancelled: native termination confirmed + Cancelling --> Failed: cleanup cannot be confirmed + TimingOut --> TimedOut: native termination confirmed + TimingOut --> Failed: cleanup cannot be confirmed + Succeeded --> [*] + Failed --> [*] + Cancelled --> [*] + TimedOut --> [*] +``` + +These phases are represented through the standard `Succeeded` condition and a +versioned AgentTask status profile; they do not add another Pipeline state +machine. Native internal phases may be shown in a backend-specific detail +field or link but do not affect Pipeline scheduling unless mapped to the +common lifecycle. + +#### Acknowledgement + +A run is accepted only after the executor has either: + +- created or adopted a native execution and persisted its reference; or +- reserved an idempotent remote execution key that can be recovered after a + restart. + +Merely receiving an informer event is not acknowledgement. The framework +records claim and acceptance latency. An unclaimed or repeatedly unavailable +executor produces a terminal condition rather than leaving a Pipeline waiting +indefinitely. + +#### Idempotency and Recovery + +The framework supplies an idempotency key derived from: + +```text +: +``` + +Kubernetes executors use a deterministic child name plus owner or correlation +metadata. Remote executors pass an idempotency key when supported and persist +the returned native ID. When the backend lacks idempotent creation, the +adapter must implement lookup by correlation key before creating another run. + +On restart, the executor first adopts the recorded or deterministic native +execution. It must not create another execution because an in-memory cache was +lost. + +#### Progress and Heartbeats + +The executor framework records bounded progress messages and a heartbeat while +an execution is active. A heartbeat proves that the controller can still +observe the backend; it does not require the agent itself to emit synthetic +progress. + +A stale heartbeat is an observability and alerting signal; it does not by +itself rewrite the run's terminal state. Another replica with the same stable +installation identity may reconcile and adopt the native execution. Automatic +takeover by a differently configured executor installation is not permitted. +If the selected executor later proves that the native execution failed, it +reports `InfrastructureFailed` and preserves the native reference. + +#### Cancellation and Timeout + +Pipeline cancellation is expressed through the existing +`CustomRun.spec.status=RunCancelled`. Before an executor claims the run, the +pre-claim reconciler can terminate it immediately because no native execution +exists. After claim, the selected framework wrapper calls the executor's +cancellation path until the backend confirms terminal state or the cleanup +deadline expires, then uses the existing `CustomRunCancelled` reason. + +Pipeline currently uses the same `RunCancelled` value when the owning +PipelineRun times out; only `statusMessage` distinguishes that source. This +TEP preserves that behavior rather than claiming a typed timeout cause that +CustomRun does not carry. + +`CustomRun.spec.timeout` is separately authoritative for the invocation. If it +expires before claim, the pre-claim reconciler marks the run timed out without +calling an executor. After claim, the selected wrapper requests native +termination and, after confirmation, uses the existing +`CustomRunTimedOut` reason. An executor should also configure a native timeout +when the backend supports one, but a missing native timeout does not remove +the framework's obligation to act. + +The framework does not mark a run cancelled or timed out merely because it +sent a request. Failure to confirm native termination becomes `CleanupFailed` +with the native reference retained. + +#### Cleanup + +Kubernetes child resources use controller references when valid. Cross- +namespace resources and remote executions use correlation labels or IDs and a +finalizer on the `CustomRun`. + +The finalizer remains until the executor confirms that run-scoped resources +are deleted or intentionally retained by a declared backend policy. The +framework uses an operator-configured maximum cleanup interval so a broken +remote service cannot block Kubernetes deletion forever. Expiry removes the +finalizer only after recording `CleanupFailed` and the recovery reference. + +#### Retries + +Controller reconciliation retries and agent execution retries are different: + +- A transient API error requeues reconciliation of the same attempt. +- `CustomRun.spec.retries` is the maximum number of additional execution + attempts. The current zero-based attempt number is + `len(status.retriesStatus)`. +- When an executor reports a terminal retryable failure, the framework first + confirms native termination and cleanup. If retries remain, it appends a + deep copy of the completed current status, including its condition, + execution reference, and AgentTask extra fields, to + `status.retriesStatus`. +- In the same conflict-checked status transition, the framework clears + attempt-scoped current fields and marks the run `Running` with reason + `Retrying`. The new attempt ID is then derived from the CustomRun UID and the + new `len(status.retriesStatus)` value. +- If no retries remain, the terminal failure stays on the current status. +- An agent or tool action inside the native platform may retry according to + native policy without becoming a new Tekton attempt. + +Because the archived retry status is the authoritative attempt counter, a +controller restart cannot increment it from memory. Because agents may make +external changes, the framework never starts a new attempt solely because +status observation temporarily failed. The executor explicitly marks whether +a terminal infrastructure failure is safe to retry. + +### Status and Outcome Semantics + +Pipeline continues to read the standard `Succeeded` condition: + +| Condition | Common reason | Meaning | +|-----------|---------------|---------| +| Unknown | `Pending` | Definition is being validated or waiting for a claim. | +| Unknown | `Accepted` | Native identity is durable; work has not yet started. | +| Unknown | `Running` | Native execution is active. | +| Unknown | `WaitingForApproval` | Native platform is waiting for human or external input. | +| True | `Succeeded` | Invocation completed and declared results are valid. | +| False | `InvalidAgentTask` | Definition or runtime bindings are invalid. | +| False | `ExecutorNotFound` | No matching executor claimed the run. | +| False | `AgentFailed` | Native agent completed unsuccessfully. | +| False | `InfrastructureFailed` | Executor, platform, sandbox, or workload failed. | +| False | `CustomRunWorkspaceNotSupported` | The selected executor cannot honor a bound workspace. | +| False | `CustomRunCancelled` | A `RunCancelled` request was observed and native termination was confirmed. | +| False | `CustomRunTimedOut` | `CustomRun.spec.timeout` elapsed and native termination was confirmed. | +| False | `CleanupFailed` | Native termination or cleanup could not be confirmed. | + +An agent's domain decision is a result. For example, a security reviewer that +returns `outcome=reject` has successfully performed its work. It should be +`Succeeded=True`; a downstream `when` expression decides whether deployment +continues. + +The alpha status profile stored in `CustomRun.status.extraFields` includes: + +```yaml +schemaVersion: agent.tekton.dev/v1alpha1 +agentTask: + apiVersion: agent.tekton.dev/v1alpha1 + name: repository-review + uid: 6d3c... + resourceVersion: "1042" + digest: sha256:... +executor: + name: fullsend.ai/executor + version: v0.1.0 + installationID: fullsend-executor.production + claimedAt: "..." + lastHeartbeatTime: "..." +attempt: + number: 0 + id: 6d3c...:0 +executionRef: + apiVersion: batch/v1 + kind: Job + namespace: ci + name: repository-review-6d3c + uid: 9b4a... +logs: + - name: agent + uri: https://logs.example/runs/6d3c +artifacts: + - name: report + uri: oci://registry.example/reports@sha256:... +``` + +The status profile has these normative ownership and compatibility rules: + +| Field | Required | Writer | Rule | +|-------|----------|--------|------| +| `schemaVersion` | Always | Pre-claim reconciler | Readers reject an unsupported major schema and ignore unknown additive fields. | +| `agentTask` identity and digest | Always | Pre-claim reconciler | Immutable after routing; identifies the exact local or resolved definition. | +| `executor.name` | Always | Pre-claim reconciler | Equals the unmodified `executorRef.name`. | +| `executor.installationID`, version, and claim time | After claim | Selected framework wrapper | Written by compare-and-swap; immutable for the attempt. | +| `executor.lastHeartbeatTime` | While active | Selected framework wrapper | Rate-limited and monotonically nondecreasing. | +| `attempt.number` and `attempt.id` | Always | Framework | Derived from retry history and CustomRun UID; immutable within an attempt. | +| `executionRef` | From acceptance | Selected framework wrapper | Required before `Accepted`; immutable except to add server-assigned identity fields. | +| `logs`, `artifacts`, and `traces` | Optional | Selected framework wrapper | At most 32 references per category; names are unique and URIs are at most 2048 bytes. | + +Condition messages are at most 4096 bytes. Each status writer uses a +resource-version-checked patch. The pre-claim reconciler writes only while no +executor claim exists; after claim, the selected framework wrapper owns the +profile and standard condition. A conformant direct controller assumes that +same post-claim writer role. + +URI schemes are not limited to HTTP. References may identify Kubernetes +objects, OCI artifacts, Tekton Results records, or platform-native resources. +References must be usable without an embedded credential. The profile schema +is versioned independently from native executor detail. + +### Results, Logs, Artifacts, and Traces + +Declared scalar results are written to `CustomRun.status.results`; undeclared +results are rejected. Result names follow ordinary Tekton substitution rules. + +The complete agent transcript is not a result. Executors publish logs through +one of these paths: + +- Pod/TaskRun logs for Kubernetes workloads; +- a configured external logging provider; +- a platform-native log or conversation URL; or +- a Tekton Results-compatible record when that API supports it. + +Artifacts and traces are similarly referenced. Every reference includes a +name and URI and may include media type, digest, and size. A reference must not +contain a bearer token or embedded credential. Access is controlled by the +referenced backend. + +Executors must truncate condition messages and reject status payloads that +would approach Kubernetes object-size limits. + +### Parameters and Workspaces + +Runtime params are validated against the AgentTask declaration before an +executor receives them. Params are data, not a place for credentials or +unbounded source archives. + +A workspace declaration describes a Pipeline-visible input or output binding. +Mapping is executor-specific: + +- a TaskRun or Job executor may mount the bound volume; +- a native controller may pass an existing PVC reference if its API supports + one; +- a remote executor may upload a content-addressed snapshot or use an existing + repository reference; and +- an executor unable to honor the binding rejects it. + +An executor must document whether writes are visible through the original +workspace, returned as an artifact, or committed through the platform's native +SCM integration. The common API does not silently equate those behaviors. + +### Identity and Credentials + +`CustomRun.spec.serviceAccountName` identifies the Kubernetes identity selected +for a child execution. The framework validates and presents the name to the +executor; it does not cause framework or executor-controller API calls to run +as that service account. Those calls use the controller's own RBAC. + +A Kubernetes executor may set the selected service account on a child workload +when its controller is authorized to do so. Impersonation, if an executor +chooses to support it, requires explicit impersonation RBAC and authorization +checks and is not implied by this TEP. A remote executor must use an explicit +workload-identity exchange or backend credential binding. Copying a projected +Kubernetes bearer token into a remote request is not conformant. + +Executor administrator configuration may reference Secrets through normal +Kubernetes references. Secret values are read only by the executor that needs +them and never copied into AgentTask, CustomRun status, results, events, +provenance, or command-line arguments. + +### Definition Resolution and Provenance + +The effective AgentTask must be stable for an attempt. For a local reference, +the framework records UID, resource version, and a canonical spec digest. The +alpha API makes execution-relevant fields immutable. + +For a remote reference, the resolver returns content and source metadata. The +framework validates the returned kind, stores the digest and source URI, and +executes that exact content. A retry uses the pinned definition unless the +user creates a new CustomRun. + +Provenance records what Tekton can verify, not unverifiable claims about the +agent's reasoning. An executor may add signed platform evidence, model or tool +metadata, and policy decisions, but the common attestation distinguishes: + +- Tekton-observed definition and lifecycle data; +- executor-reported metadata; and +- externally verifiable artifact digests or attestations. + +### Conformance + +The executor conformance suite creates `AgentTask` and `CustomRun` fixtures +against a deterministic fake native backend. It verifies at least: + +1. valid run acceptance and successful scalar results; +2. rejection of missing, extra, or wrong-type params; +3. rejection or correct mapping of workspaces; +4. deterministic create/adopt behavior after controller restart; +5. no duplicate native execution after a transient create response failure; +6. bounded acknowledgement and heartbeat behavior; +7. cancellation before acceptance, while running, and while waiting; +8. timeout with native termination confirmation; +9. cleanup success and cleanup deadline failure; +10. distinction between domain outcome, agent failure, and infrastructure + failure; +11. safe retry with a new attempt identity; +12. log and artifact references without embedded credentials; +13. Secret redaction from status, events, and provenance; and +14. unknown native progress/event variants not breaking observation. + +Platform-specific adapters add end-to-end tests against supported platform +versions. Fullsend, Lightspeed, and OpenHands tests verify mappings at their +public boundary rather than asserting internal helper calls. + +## Design Evaluation + +### Reusability + +The proposal reuses Pipeline DAG scheduling, Custom Tasks, `CustomRun`, params, +workspaces, service accounts, retries, timeouts, conditions, result +substitution, Triggers, and remote resolution patterns. + +`AgentTask` is a definition rather than a per-run object. One definition can +be invoked with different goals, repositories, revisions, and event context. +The same Pipeline authoring surface works with a TaskRun, Fullsend, +Lightspeed, OpenHands, or a protocol executor. + +The framework is deliberately scoped to agent executions rather than reviving +a generic Custom Task SDK without concrete lifecycle requirements. + +### Simplicity + +The minimum Pipeline change is no Pipeline API change. Pipeline creates a +`CustomRun` exactly as it does for other Custom Tasks. + +The proposal adds one reusable definition kind and one framework. It avoids a +second run CRD, per-platform Custom Task kinds in Pipeline YAML, a dynamic +plugin service, and a universal agent protocol. + +Simple container agents should continue to use Tasks. AgentTask is justified +when a portable agent contract or non-Pod execution boundary is needed. + +### Flexibility + +Executor implementations are independently deployed and may use Kubernetes +resources, remote APIs, protocol clients, or child TaskRuns. Tekton does not +import their SDKs into the Pipeline controller. + +Platform-specific behavior remains behind `executorRef`. This preserves +Fullsend harnesses and sandboxes, Lightspeed native approvals and typed +results, and OpenHands conversations and workspaces. + +The cost of this flexibility is that some details remain native and are +visible through references rather than one universal schema. + +### Conformance + +Pipeline authors do not need to understand how TaskRun Pods or native agent +resources are implemented. They see a Task-like definition, normal Pipeline +bindings, standard conditions, and declared results. + +The proposal introduces `AgentTask` and a versioned status profile but no new +Pipeline concepts. API documentation must specify the relationship to Custom +Tasks and the subset of Tekton result types supported by `CustomRun`. + +Executor authors must understand Kubernetes controller semantics. The +framework and conformance suite remove repeated informer, claiming, +idempotency, cancellation, and status code. + +### User Experience + +- **Pipeline authors** reference AgentTasks like other Custom Tasks and consume + declared results. +- **Agent platform authors** implement one executor lifecycle rather than a + complete Pipeline integration for every use case. +- **Cluster operators** install approved executors, configure their RBAC and + backends, and can identify the native execution from the CustomRun. +- **Approvers** continue using the platform-native approval surface, linked + from the CustomRun and Tekton UI. +- **Security and supply-chain teams** receive stable executor, definition, + result, and artifact evidence without storing sensitive transcripts in + Kubernetes. + +`tkn` and Dashboard should show the common reason, executor, native reference, +last heartbeat, declared results, and log/artifact links. Native detail may be +shown by following the reference. + +### Performance + +The additional lifecycle reconcile and AgentTask lookup are small compared +with agent execution. Informers and indexes avoid listing definitions for each +run. The projected executor label lets controllers filter before expensive +backend calls. + +Polling executors use bounded exponential backoff and backend-provided retry +hints. Kubernetes-native executors should watch child resources. Remote event +streams may trigger reconciliation but must not require one persistent stream +per run in the controller process. + +Heartbeats and progress updates are rate-limited to avoid excessive Kubernetes +and Results writes. Large logs and artifacts stay out of the API server. + +### Risks and Mitigations + +| Risk | Mitigation | +|------|------------| +| Common API grows into a lowest-common-denominator agent runtime | Limit AgentTask to Pipeline-consumed declarations and executor selection; keep native configuration behind the boundary. | +| Two executors claim a run | Atomic claim with stable installation identity; only the selected DNS-qualified executor may claim; a different claimant stops. | +| Controller restart duplicates external effects | Stable attempt identity, deterministic child names, idempotency keys, and adopt-before-create conformance tests. | +| Backend is unreachable during cancellation | Reconcile cancellation until deadline; preserve native reference and report `CleanupFailed`. | +| Automatic retry repeats external changes | Separate reconciliation retries from execution attempts; require terminal cleanup and explicit retryability. | +| Status or logs expose credentials or prompts | Bounded references, Secret-redaction tests, no embedded tokens, and access-controlled backends. | +| Workspace semantics differ between executors | Each executor documents the mapping and rejects unsupported bindings. | +| Platform-native approvals confuse Pipeline users | Standard `WaitingForApproval` reason plus a native approval reference; native system remains authoritative. | +| Missing executor leaves a Pipeline waiting | Bounded claim deadline and terminal `ExecutorNotFound` status. | +| Resolver and executor terminology become conflated | Keep resolution and execution as separate stages and interfaces. | +| Results cannot collect CustomRun logs today | Standardize log references first; extend Results providers without assuming a Pod. | +| Chains cannot attest CustomRuns today | Add explicit CustomRun/AgentTask support or include equivalent child evidence in PipelineRun attestation. | +| AgentTask API couples Pipeline to new dependencies | Implement as a Custom Task extension; do not import platform SDKs into Pipeline. | + +### Drawbacks + +- Users install another CRD and controller even though simple agents already + work as Tasks. +- A common lifecycle cannot make every native feature portable. Users may need + the platform's UI or CRDs for approvals and detailed diagnosis. +- `CustomRun` is still v1beta1 and has string-only results and schemaless + extension status. +- A bridging executor adds another reconciliation layer and may delay native + status by one reconcile interval. +- Full provenance and logs require changes outside Tekton Pipelines, notably + Results and Chains. +- Independently installed executors create a compatibility matrix that must be + managed through conformance and supported-version documentation. + +## Alternatives + +### Use Ordinary Tasks Only + +An ordinary Task is the correct choice for an agent that is a container and +needs no external lifecycle. It is insufficient for platforms that own a +Kubernetes custom resource, remote conversation, approvals, or non-Pod +sandbox. Forcing those systems into a Pod wrapper either loses their native +controls or creates an opaque polling script. + +### Use TaskRun spec.managedBy + +`TaskRun.spec.managedBy` delegates the complete TaskRun to another controller. +It is useful when an external system intentionally implements TaskRun +semantics. It does not provide a per-AgentTask implementation binding, is not +currently propagated through the relevant Pipeline per-task templates, and +requires the manager to reproduce substantial TaskRun behavior including +results, status, cancellation, logging, and provenance. + +This remains an alternative for externally managed ordinary Tasks, not the +primary AgentTask record. + +### Use CustomRun Controllers Without a Framework + +Every platform can define a Custom Task kind and controller today. This is the +baseline and proves that Pipeline does not need an agent-specific execution +engine. + +It is rejected as the complete solution because each controller would +reimplement claiming, timeout, cancellation, retries, status reasons, logs, +artifacts, and provenance. Pipeline definitions would also be tied to +platform-specific kinds rather than a reusable AgentTask contract. + +### Introduce AgentRun + +A dedicated `AgentRun` could expose agent-specific status. It would duplicate +`CustomRun`, which already represents one execution and is the object Pipeline +creates, waits for, retries, cancels, and reads results from. Maintaining both +would require ownership and status synchronization. + +The proposal instead versions agent-specific status within CustomRun and can +promote generally useful fields into a future CustomRun API. + +### Introduce AgentExecutorClass + +An `AgentExecutorClass` CRD could advertise installed implementations, +capabilities, defaults, and readiness. It adds registration, lifecycle, RBAC, +and failure modes before the need is demonstrated. + +The alpha design uses explicit DNS-qualified names, labels, and bounded claims, +following resolver selection. A class resource can be proposed later if users +need discovery, admission-time capability negotiation, or centrally managed +multi-tenant defaults. + +### Standardize a Generic Agent Container Protocol + +A JSON stdin/stdout or OCI image contract would simplify a batch executor but +would force existing platforms to abandon or wrap their native lifecycle. It +also duplicates ordinary Tasks for simple containers. + +A TaskRun reference executor provides this onboarding path with existing +Tekton contracts. Other executors remain free to use a protocol internally. + +### Standardize an Opaque Implementation Reference + +An AgentTask containing only `implementationRef` would give Pipeline no +portable declarations, validation, lifecycle, policy, result, or provenance +semantics. It would rename a Custom Task reference without improving +interoperability. + +AgentTask therefore requires a Tekton-consumed contract and a conformant +executor. Native configuration references remain executor params, not the +whole API. + +### Use the Resolver Interface for Execution + +The resolver framework offers useful controller organization, selector, +configuration, ownership, and error patterns. Its execution contract is the +wrong shape: it performs one bounded fetch and returns immutable bytes. Agents +need persisted handles, repeated observation, cancellation, cleanup, +heartbeats, logs, artifacts, and external-effect safety. + +The proposal copies the extension pattern and keeps the interfaces separate. + +### Depend on kagent or Another Single Runtime + +A kagent-specific design could directly expose its Agent, ModelConfig, and MCP +resources. The same issue applies to choosing Fullsend, Lightspeed, OpenHands, +or another platform: Tekton would inherit that platform's API and release +cycle, and users of other systems would need a second abstraction. + +Each may instead provide an executor. The common API does not select a winner +among agent runtimes. + +### Add Agent Fields to Pipeline + +A `Pipeline.spec.agents` field or agent step type would make agent concepts +part of Pipeline's core API and Pod-oriented reconciler. Custom Tasks already +provide a composition point and avoid changing existing Pipeline resources. + +## Implementation Plan + +### Milestones + +**Phase 1: Alpha contract and reference executor** + +- Define the namespaced `AgentTask` v1alpha1 CRD and validation. +- Define and version the AgentTask profile in `CustomRun.status.extraFields`. +- Implement lifecycle validation, executor routing label, bounded claim, + idempotency, timeout, cancellation, cleanup, and common status mapping. +- Publish the executor Go framework, direct-controller documentation, and + project template. +- Implement a deterministic fake executor and the conformance suite. +- Implement the reference TaskRun executor. +- Add `tkn` and Dashboard-readable labels, events, and status fields where + feasible. + +**Phase 2: Existing-platform validation** + +- Implement and test a Fullsend reference executor using a Kubernetes workload + or managed service boundary while preserving its harness and sandbox. +- Build contract-tested prototypes for an OpenShift Lightspeed Agentic + Operator executor using `AgenticRun`, native approvals, sandbox logs, and + typed result references, and an OpenHands executor using its conversation + and event APIs. +- Decide production ownership with each upstream community before promising a + supported adapter release. +- Publish the three adapter mappings and an executor compatibility and + supported-version matrix. + +Additional protocol executors, such as an A2A bridge, use the same public +contract but are not required for alpha. + +**Phase 3: Tekton ecosystem integration** + +- Extend remote resolution to validate, pin, and report provenance for + AgentTask definitions. +- Extend Tekton Results to associate CustomRun log, artifact, and trace + references with stored lifecycle records. +- Extend Tekton Chains to attest AgentTask CustomRuns or define equivalent + PipelineRun child evidence. +- Evaluate typed CustomRun results and typed execution references through the + appropriate Pipeline API process. + +Promotion beyond alpha requires at least two independent executor +implementations, conformance coverage, cancellation and restart fault tests, +and one end-to-end Pipeline using an external platform. + +### Test Plan + +- **API tests:** defaulting, DNS-qualified executor validation, unique + declarations, immutability, unsupported result types, and Secret-safe + serialization. +- **Framework unit tests:** selector projection, claim races, status ownership, + reason mapping, truncation, timeout calculation, finalizer behavior, and + typed error handling. +- **Controller integration tests:** restart and adoption, lost create response, + stale heartbeat, cancellation races, cleanup deadline, safe retry, and + owner/correlation metadata. +- **Pipeline end-to-end tests:** params, workspaces, result substitution, + `when`, retry, timeout, cancellation, finally tasks, and PipelineRun pruning. +- **Conformance tests:** all scenarios listed in the Conformance section, + runnable against in-tree and external executors. +- **Adapter tests:** fake-server contract tests plus supported-platform + end-to-end tests for Fullsend, Lightspeed, and OpenHands. +- **Security tests:** duplicate claim, cross-namespace reference denial, + service-account misuse, status/log URL credential leakage, malicious + backend messages, oversized results, and Secret redaction. +- **Provenance tests:** resolved definition digest, executor version, native + identity, artifact digests, and omission of sensitive content. +- **Scalability tests:** informer filtering, heartbeat write rate, many waiting + approvals, remote polling backoff, and controller restart with active runs. + +Tests assert observable resources and lifecycle behavior, not exact reconcile +counts or executor helper structure. + +### Infrastructure Needed + +The initial implementation may live as a Tekton extension while the API and +executor framework mature. Project governance will determine whether it +belongs in `tektoncd/pipeline` or a separate Tekton repository. + +CI requires: + +- a Kind cluster with Tekton Pipelines; +- deterministic fake native backends; +- optional jobs for supported Fullsend, Lightspeed, and OpenHands versions; +- a Results/logging backend for reference tests; and +- Chains integration tests when that phase begins. + +No agent platform is a mandatory dependency of Tekton Pipelines installation. + +### Upgrade and Migration Strategy + +This is a new alpha API. Existing Tasks, custom controllers, managed TaskRuns, +and agent platforms continue to work. + +A platform can migrate incrementally: + +1. keep its native runtime and controller unchanged; +2. add an executor that creates or calls the native execution; +3. define AgentTasks for reusable Pipeline contracts; +4. move Pipeline orchestration to CustomRuns; and +5. adopt common logs, artifacts, resolution, and provenance as those + integrations become available. + +Alpha AgentTask definitions and status profiles may require conversion before +beta. The effective definition digest and executor selector make version skew +visible. No migration may silently reinterpret an in-flight CustomRun. + +### Implementation Pull Requests + +To be populated with merged implementation pull requests. + +## References + +- [TEP-0002: Custom Tasks][tep-0002] +- [TEP-0060: Remote Resource Resolution][tep-0060] +- [TEP-0071: Custom Task SDK][tep-0071] +- [TEP-0083: Polling Runs in Tekton][tep-0083] +- [TEP-0114: Custom Tasks Beta][tep-0114] +- [Tekton CustomRun documentation][custom-runs] +- [Tekton remote resolution documentation][resolution] +- [Tekton Results watcher][results-watcher] +- [Tekton Results logging support][results-logging] +- [Tekton Chains][chains] +- [Fullsend][fullsend] +- [Fullsend architecture][fullsend-architecture] +- [OpenShift Lightspeed Agentic Operator][lightspeed] +- [OpenShift Lightspeed Component Developer Guide][lightspeed-component-guide] +- [OpenHands software-agent-sdk and Agent Server][openhands] +- [OpenHands Agent Server API][openhands-server] +- [Agent2Agent Protocol][a2a] +- [Tekton Design Principles][design-principles] + +[tep-0002]: https://github.com/tektoncd/community/blob/main/teps/0002-custom-tasks.md +[tep-0060]: https://github.com/tektoncd/community/blob/main/teps/0060-remote-resource-resolution.md +[tep-0071]: https://github.com/tektoncd/community/blob/main/teps/0071-custom-task-sdk.md +[tep-0083]: https://github.com/tektoncd/community/blob/main/teps/0083-polling-runs-in-tekton.md +[tep-0114]: https://github.com/tektoncd/community/blob/main/teps/0114-custom-tasks-beta.md +[custom-tasks]: https://tekton.dev/docs/pipelines/runs/ +[custom-runs]: https://tekton.dev/docs/pipelines/customruns/ +[resolution]: https://tekton.dev/docs/pipelines/resolution-getting-started/ +[results-watcher]: https://github.com/tektoncd/results/blob/main/docs/watcher/README.md +[results-logging]: https://github.com/tektoncd/results/blob/main/docs/logging-support.md +[chains]: https://github.com/tektoncd/chains +[fullsend]: https://github.com/fullsend-ai/fullsend +[fullsend-architecture]: https://github.com/fullsend-ai/fullsend/blob/main/docs/architecture.md +[lightspeed]: https://github.com/openshift/lightspeed-agentic-operator +[lightspeed-component-guide]: https://github.com/openshift/lightspeed-agentic-operator/blob/main/docs/component-developer-guide.md#3-create-an-agenticrun +[openhands]: https://github.com/OpenHands/software-agent-sdk +[openhands-server]: https://github.com/OpenHands/software-agent-sdk/tree/main/openhands-agent-server +[a2a]: https://a2a-protocol.org/latest/ +[design-principles]: https://github.com/tektoncd/community/blob/main/design-principles.md diff --git a/teps/README.md b/teps/README.md index 877646eb6..acaec93cc 100644 --- a/teps/README.md +++ b/teps/README.md @@ -150,4 +150,4 @@ This is the complete list of Tekton TEPs: |[TEP-0161](0161-resolver-caching.md) | Resolver Caching for Task and Pipeline Resolution | proposed | 2024-06-15 | |[TEP-0162](0162-event-based-pruning-of-tekton-resources.md) | event based pruning of tekton resources | proposed | 2025-06-18 | |[TEP-0163](0163-profilebased-dynamic-compute-resources-for-steps.md) | Profile-Based Dynamic Compute Resources for Steps | proposed | 2025-09-01 | -|[TEP-0164](0164-agent-native-workflows.md) | Agent-Native Workflows | proposed | 2026-03-20 | +|[TEP-0170](0170-agent-native-workflows.md) | AgentTask and Pluggable Agent Execution | proposed | 2026-08-30 | From d8ceea366f263f53c309b23e55ef1f53e6b6e91e Mon Sep 17 00:00:00 2001 From: waveywaves <11972233+waveywaves@users.noreply.github.com> Date: Mon, 31 Aug 2026 21:15:25 +0530 Subject: [PATCH 3/3] TEP-0170: rename executors to AgentTask adapters Signed-off-by: waveywaves <11972233+waveywaves@users.noreply.github.com> --- teps/0170-agent-native-workflows.md | 452 ++++++++++++++-------------- teps/README.md | 2 +- 2 files changed, 226 insertions(+), 228 deletions(-) diff --git a/teps/0170-agent-native-workflows.md b/teps/0170-agent-native-workflows.md index b6bb54286..d7991acee 100644 --- a/teps/0170-agent-native-workflows.md +++ b/teps/0170-agent-native-workflows.md @@ -2,7 +2,7 @@ status: proposed title: AgentTask and Pluggable Agent Execution creation-date: '2026-03-20' -last-updated: '2026-08-30' +last-updated: '2026-08-31' authors: - '@waveywaves' - '@anithapriyanatarajan' @@ -31,15 +31,15 @@ authors: - [Architecture](#architecture) - [AgentTask](#agenttask) - [Using AgentTask in a Pipeline](#using-agenttask-in-a-pipeline) - - [Agent Executor Framework](#agent-executor-framework) + - [AgentTask Adapter Framework](#agenttask-adapter-framework) - [Framework Responsibilities](#framework-responsibilities) - - [Executor Responsibilities](#executor-responsibilities) - - [Bring Your Own Executor](#bring-your-own-executor) - - [Worked Adapter Examples](#worked-adapter-examples) + - [AgentTask Adapter Responsibilities](#agenttask-adapter-responsibilities) + - [Bring Your Own AgentTask Adapter](#bring-your-own-agenttask-adapter) + - [Worked AgentTask Adapter Examples](#worked-agenttask-adapter-examples) - [Fullsend](#fullsend) - [OpenShift Lightspeed Agentic Operator](#openshift-lightspeed-agentic-operator) - [OpenHands](#openhands) - - [Reference TaskRun Executor](#reference-taskrun-executor) + - [Reference TaskRun AgentTask Adapter](#reference-taskrun-agenttask-adapter) - [Relationship to Remote Resolution](#relationship-to-remote-resolution) - [Integration with Tekton Projects](#integration-with-tekton-projects) - [Pipelines](#pipelines) @@ -50,8 +50,8 @@ authors: - [Notes and Caveats](#notes-and-caveats) - [Design Details](#design-details) - [Preliminary AgentTask API](#preliminary-agenttask-api) - - [Executor Selection and Claiming](#executor-selection-and-claiming) - - [Executor Interface](#executor-interface) + - [AgentTask Adapter Selection and Claiming](#agenttask-adapter-selection-and-claiming) + - [AgentTask Adapter Interface](#agenttask-adapter-interface) - [Execution Lifecycle](#execution-lifecycle) - [Acknowledgement](#acknowledgement) - [Idempotency and Recovery](#idempotency-and-recovery) @@ -79,7 +79,7 @@ authors: - [Use TaskRun spec.managedBy](#use-taskrun-specmanagedby) - [Use CustomRun Controllers Without a Framework](#use-customrun-controllers-without-a-framework) - [Introduce AgentRun](#introduce-agentrun) - - [Introduce AgentExecutorClass](#introduce-agentexecutorclass) + - [Introduce AgentTaskAdapterClass](#introduce-agenttaskadapterclass) - [Standardize a Generic Agent Container Protocol](#standardize-a-generic-agent-container-protocol) - [Standardize an Opaque Implementation Reference](#standardize-an-opaque-implementation-reference) - [Use the Resolver Interface for Execution](#use-the-resolver-interface-for-execution) @@ -112,21 +112,21 @@ This TEP proposes: 1. A reusable, namespaced `AgentTask` definition that declares the parameters, workspaces, and results visible to a Pipeline and explicitly selects an - agent executor. + AgentTask Adapter. 2. The existing `CustomRun` as the durable record for every `AgentTask` execution. This TEP does not introduce `AgentRun`. -3. An Agent Executor Framework, modeled on the organizational patterns of +3. An AgentTask Adapter Framework, modeled on the organizational patterns of Tekton's remote resolver framework, that provides the common controller - lifecycle and a conformance contract for independently installed - executors. -4. Executor implementations that preserve an agent platform's native runtime - rather than reproducing it inside Tekton. + lifecycle and a conformance contract for independently installed adapters. +4. AgentTask Adapters that preserve an agent platform's native runtime rather + than reproducing it inside Tekton. -Tekton Pipelines remains the DAG orchestrator. An executor may create a -`TaskRun` or Kubernetes workload, create and observe a platform-native custom -resource, or call a remote API. Fullsend, the OpenShift Lightspeed Agentic -Operator, and OpenHands are worked examples of those three integration -shapes. +Tekton Pipelines remains the DAG orchestrator. An AgentTask Adapter is an +active `CustomRun` controller: it creates or calls a platform-native execution, +observes it, and maps its lifecycle back to Tekton. It may create a `TaskRun` +or Kubernetes workload, create and observe a platform-native custom resource, +or call a remote API. Fullsend, the OpenShift Lightspeed Agentic Operator, and +OpenHands are worked examples of those three integration shapes. The proposal does not standardize prompts, models, tools, memory, agent loops, approvals, sandboxes, or model-provider credentials. It standardizes only the @@ -170,7 +170,7 @@ than a Tekton-facing contract. reusable across invocations. 2. Use `CustomRun` as the single execution record for `AgentTask` in a Pipeline or as a standalone Custom Task invocation. -3. Let users bring an existing agent implementation by installing an executor +3. Let users bring an existing agent implementation by installing an adapter controller or bridge. 4. Provide common acknowledgement, idempotency, recovery, status, cancellation, timeout, cleanup, and result semantics. @@ -179,10 +179,10 @@ than a Tekton-facing contract. 6. Reuse Tekton params, workspaces, service accounts, Pipeline scheduling, `when` expressions, retries, timeouts, Triggers, Results, and Chains where their current contracts permit. -7. Make executor installation and authoring comparable to resolver +7. Make adapter installation and authoring comparable to resolver installation and authoring: explicit selection, independent deployment, narrow RBAC, a small interface, a template, and conformance tests. -8. Validate the design against a batch executor, a Kubernetes-native +8. Validate the design against a batch container, a Kubernetes-native controller, and a remote service. ### Non-Goals @@ -200,7 +200,7 @@ than a Tekton-facing contract. 7. Requiring kagent, Fullsend, OpenShift Lightspeed, OpenHands, or any other agent platform. 8. Making arbitrary platform-native configuration portable. Such - configuration remains behind the executor boundary. + configuration remains behind the adapter boundary. 9. Guaranteeing that an agent's semantic answer is correct. Conformance covers execution behavior, not model quality. @@ -215,7 +215,7 @@ workflow and execution infrastructure without rewriting those Fullsend components. A Tekton Trigger creates a `PipelineRun`. Ordinary Tasks prepare the source and -report back to GitHub. An `AgentTask` selects the Fullsend executor, which runs +report back to GitHub. An `AgentTask` selects the Fullsend adapter, which runs the existing harness in its sandbox and reports bounded results to the `CustomRun`. @@ -225,14 +225,14 @@ A cluster operator already runs the OpenShift Lightspeed Agentic Operator. Lightspeed owns `AgenticRun`, `AgenticRunApproval`, sandbox claims, step conditions, and typed result custom resources. -An `AgentTask` selects a Lightspeed executor. The executor creates and observes +An `AgentTask` selects a Lightspeed adapter. The adapter creates and observes an `AgenticRun`, preserves Lightspeed approvals and internal phases, and maps only the lifecycle and declared results needed by Tekton. #### Orchestrate a Remote Agent Service An organization runs an OpenHands Agent Server outside the Pipeline -controller. An `AgentTask` selects an OpenHands executor. The executor starts a +controller. An `AgentTask` selects an OpenHands adapter. The adapter starts a conversation, stores the conversation ID as the native execution reference, observes events until completion, and exposes result, log, and artifact references through the `CustomRun`. @@ -240,7 +240,7 @@ references through the `CustomRun`. #### Run a Containerized Agent An agent is distributed as a Task or container and does not require a separate -platform. A reference executor creates a child `TaskRun` and reuses Tekton's +platform. A reference adapter creates a child `TaskRun` and reuses Tekton's existing Pod, workspace, result, log, and cancellation behavior. Authors who do not need the common `AgentTask` surface can continue to use that Task directly. @@ -248,7 +248,7 @@ directly. #### Use a Protocol-Based Agent An agent already exposes a standard execution protocol such as A2A. An -executor translates the common `AgentTask` lifecycle to that protocol. The +adapter translates the common `AgentTask` lifecycle to that protocol. The protocol is an implementation choice; `AgentTask` does not copy protocol messages or platform-specific configuration into the Tekton API. @@ -257,25 +257,25 @@ messages or platform-specific configuration into the Tekton API. | ID | Requirement | Priority | |----|-------------|----------| | R1 | `AgentTask` MUST declare its Pipeline-visible parameters, workspaces, and results. | Must | -| R2 | `AgentTask` MUST explicitly select one executor by a DNS-qualified name. | Must | +| R2 | `AgentTask` MUST explicitly select one adapter by a DNS-qualified name. | Must | | R3 | A run-specific goal, event, or context MUST be provided through declared params or bound inputs, not by creating a new `AgentTask` definition for each run. | Must | | R4 | Pipeline MUST continue to use `CustomRun` as the execution record for `AgentTask`. | Must | | R5 | The proposal MUST NOT require a new `AgentRun` resource. | Must | | R6 | The framework MUST acknowledge or reject an execution within a bounded interval. | Must | | R7 | Execution creation MUST be idempotent across reconciliation and controller restart. | Must | -| R8 | The native execution identity MUST be persisted or deterministically recoverable before the executor reports the run as accepted. | Must | -| R9 | The executor MUST observe `CustomRun` cancellation and timeout and MUST drive the native execution toward termination. | Must | +| R8 | The native execution identity MUST be persisted or deterministically recoverable before the adapter reports the run as accepted. | Must | +| R9 | The adapter MUST observe `CustomRun` cancellation and timeout and MUST drive the native execution toward termination. | Must | | R10 | Terminal `CustomRun` status MUST distinguish successful completion, agent failure, infrastructure failure, `RunCancelled`, and expiry of `CustomRun.spec.timeout` with stable reasons. | Must | -| R11 | The executor MUST confirm cleanup or leave a native execution reference and explicit cleanup failure. | Must | +| R11 | The adapter MUST confirm cleanup or leave a native execution reference and explicit cleanup failure. | Must | | R12 | Declared scalar results MUST be consumable by downstream Pipeline tasks and `when` expressions. | Must | | R13 | Logs, artifacts, and traces MUST be represented by bounded references rather than copied unbounded into status. | Must | -| R14 | The effective `AgentTask` identity, executor name, executor version, and native execution reference MUST be available for provenance. | Must | -| R15 | The framework MUST validate and present the `CustomRun` service account name and workspace bindings to the executor; the executor MUST document its mapping or reject an unsupported binding. | Must | +| R14 | The effective `AgentTask` identity, adapter name, adapter version, and native execution reference MUST be available for provenance. | Must | +| R15 | The framework MUST validate and present the `CustomRun` service account name and workspace bindings to the adapter; the adapter MUST document its mapping or reject an unsupported binding. | Must | | R16 | Credentials and Secret values MUST NOT be placed in `AgentTask` params, `CustomRun` results, status messages, logs references, or provenance. | Must | -| R17 | An executor MUST document its mapping to native sandbox, approval, tool, model, and agent-loop controls and MUST NOT silently bypass those controls. | Must | +| R17 | An adapter MUST document its mapping to native sandbox, approval, tool, model, and agent-loop controls and MUST NOT silently bypass those controls. | Must | | R18 | A retry after native execution starts MUST create a distinct attempt identity and MUST NOT occur merely because reconciliation returned a transient error. | Must | -| R19 | Executors MUST be independently installable and MUST receive only the RBAC needed for their backend. | Must | -| R20 | The project MUST publish an executor conformance suite and a minimal implementation template. | Must | +| R19 | Adapters MUST be independently installable and MUST receive only the RBAC needed for their backend. | Must | +| R20 | The project MUST publish an adapter conformance suite and a minimal implementation template. | Must | | R21 | `AgentTask` definitions SHOULD be resolvable and pinned using Tekton remote resolution. | Should | | R22 | Tekton Results SHOULD persist the complete `CustomRun` lifecycle and discover referenced agent logs and artifacts. | Should | | R23 | Tekton Chains SHOULD attest completed `AgentTask` executions. | Should | @@ -286,7 +286,7 @@ messages or platform-specific configuration into the Tekton API. **AgentTask** : A reusable, namespaced Custom Task definition. It declares the - Pipeline-visible contract and selects an executor. It does not describe a + Pipeline-visible contract and selects an adapter. It does not describe a model, prompt format, tool protocol, or sandbox. **CustomRun** @@ -294,29 +294,27 @@ messages or platform-specific configuration into the Tekton API. `CustomRun` represents one `AgentTask` attempt history. There is no separate `AgentRun`. -**Agent executor** -: An implementation that translates the common lifecycle to a native - execution. It may create a Kubernetes workload, create another custom - resource, call a remote service, or use a standard protocol. +**AgentTask Adapter** +: An active `CustomRun` controller that maps an `AgentTask` invocation to one + native execution backend. It owns creation or adoption, observation, + cancellation, cleanup, and result mapping. It may create a Kubernetes + workload or custom resource, call a remote service, or use a standard + protocol. It is an implementation role, not a new CRD, central plugin + registry, sidecar, or mandatory network service. -**Agent Executor Framework** +**AgentTask Adapter Framework** : Shared controller machinery that loads and validates `AgentTask`, routes a `CustomRun`, manages the common lifecycle, and normalizes observations from - an executor. - -**Adapter** -: The implementation role played by an executor when it bridges Tekton to an - existing platform. It is not a new CRD, central plugin registry, sidecar, or - mandatory network service. + an adapter. The responsibilities are intentionally split: | Component | Contribution | |-----------|--------------| | `CustomRun` | Per-run params and workspaces, Pipeline ownership, retries, timeout and cancellation requests, conditions, and results. | -| `AgentTask` | Reusable declarations, explicit executor binding, definition identity, and validation independent of one invocation. | -| Agent Executor Framework | Selection, bounded claim, idempotency, status normalization, cancellation, cleanup, and conformance. | -| Executor | Creation and observation of the platform-native execution and mapping of native outputs. | +| `AgentTask` | Reusable declarations, explicit adapter binding, definition identity, and validation independent of one invocation. | +| AgentTask Adapter Framework | Selection, bounded claim, idempotency, status normalization, cancellation, cleanup, and conformance. | +| Adapter | Creation and observation of the platform-native execution and mapping of native outputs. | `AgentTask` therefore contributes more than another reference around `CustomRun`: it gives different implementations one reusable contract that can @@ -332,17 +330,17 @@ flowchart LR PR --> CR[CustomRun] AT[AgentTask] --> CR - subgraph Framework[Agent Executor Framework] + subgraph Framework[AgentTask Adapter Framework] Lifecycle[pre-claim validation and routing] - Route[executor selection] + Route[adapter selection] end CR --> Lifecycle --> Route - Route --> FS[Fullsend controller
framework + executor] - Route --> LS[Lightspeed controller
framework + executor] - Route --> OH[OpenHands controller
framework + executor] - Route --> TR[TaskRun controller
framework + executor] + Route --> FS[Fullsend controller
framework + adapter] + Route --> LS[Lightspeed controller
framework + adapter] + Route --> OH[OpenHands controller
framework + adapter] + Route --> TR[TaskRun controller
framework + adapter] FS --> Job[Kubernetes Job and native sandbox] LS --> AR[AgenticRun] @@ -359,12 +357,12 @@ flowchart LR Pipeline scheduling remains unchanged. When a `PipelineTask.taskRef` has the `AgentTask` API version and kind, Pipeline treats it as a Custom Task and -creates a `CustomRun`. The framework and selected executor reconcile that +creates a `CustomRun`. The framework and selected adapter reconcile that `CustomRun`; Pipeline waits on its standard `Succeeded` condition and consumes its standard results. -The executor name is part of the definition rather than the `PipelineTask`. -Pipeline authors therefore do not repeat executor plumbing at every +The adapter name is part of the definition rather than the `PipelineTask`. +Pipeline authors therefore do not repeat adapter plumbing at every invocation. Changing the implementation publishes a versioned AgentTask and updates references through the same promotion process used for other Task definitions. @@ -390,23 +388,23 @@ spec: description: The immutable source revision to inspect. workspaces: - name: source - description: Checked-out source for executors that support a workspace. + description: Checked-out source for adapters that support a workspace. results: - name: outcome - description: The executor-defined review outcome. + description: The adapter-defined review outcome. - name: report-url description: A reference to the complete report. - executorRef: - name: fullsend.ai/executor + adapterRef: + name: fullsend.ai/agenttask-adapter params: - name: agent value: review ``` `spec.params`, `spec.workspaces`, and `spec.results` are the reusable contract. -`spec.executorRef` binds that contract to an installed implementation. -Executor params identify existing platform configuration, such as a Fullsend -agent, a Lightspeed executor profile, an OpenHands profile, or a Task. +`spec.adapterRef` binds that contract to an installed implementation. +Adapter params identify existing platform configuration, such as a Fullsend +agent, a Lightspeed adapter profile, an OpenHands profile, or a Task. The following do not become portable `AgentTask` fields: @@ -478,16 +476,16 @@ Pipeline authors use normal task dependencies, params, workspaces, result references, retries, timeouts, `when` expressions, and finally tasks. Creating a `CustomRun` directly remains the standalone invocation mechanism. -### Agent Executor Framework +### AgentTask Adapter Framework The framework follows the resolver framework's extension model but uses a non-blocking reconciliation contract. It consists of a small pre-claim -lifecycle reconciler and a controller library embedded in each Go executor -controller. The library calls the executor implementation in-process; there is +lifecycle reconciler and a controller library embedded in each Go adapter +controller. The library calls the adapter implementation in-process; there is no unspecified RPC or intermediate request resource. -Each executor is compiled and deployed as a controller. A distribution may -bundle several executor controllers in one binary, as Tekton does for built-in +Each adapter is compiled and deployed as a controller. A distribution may +bundle several adapter controllers in one binary, as Tekton does for built-in resolvers, but this is packaging rather than a dynamic plugin system. #### Framework Responsibilities @@ -496,22 +494,22 @@ The framework: 1. watches `CustomRun`s that reference or embed `AgentTask`; 2. loads or resolves the effective `AgentTask`; -3. validates declared params, workspaces, results, and executor selection; -4. records the effective definition identity and executor selection; -5. gives an installed executor a bounded claim interval; +3. validates declared params, workspaces, results, and adapter selection; +4. records the effective definition identity and adapter selection; +5. gives an installed adapter a bounded claim interval; 6. initializes standard conditions, attempt identity, and timestamps; 7. supplies a stable idempotency key derived from the `CustomRun` UID and attempt number; 8. handles framework-owned cancellation, timeout, heartbeat, and finalizer behavior; -9. serializes executor observations into standard conditions, results, and +9. serializes adapter observations into standard conditions, results, and bounded references; 10. emits Tekton events and metrics; and -11. prevents two executor controllers from owning the same run. +11. prevents two adapter controllers from owning the same run. -#### Executor Responsibilities +#### AgentTask Adapter Responsibilities -An executor: +An adapter: 1. validates implementation-specific params without exposing credentials; 2. creates or adopts exactly one native execution for an attempt; @@ -523,16 +521,16 @@ An executor: 8. maps declared scalar results; and 9. publishes references to logs, artifacts, traces, and native detail. -In the Go path, the executor implementation does not patch `CustomRun`; it +In the Go path, the adapter implementation does not patch `CustomRun`; it returns an observation to the framework wrapper in the same process. After an atomic claim, that wrapper is the sole status writer for the run. The pre-claim reconciler no longer mutates its status. -#### Bring Your Own Executor +#### Bring Your Own AgentTask Adapter An agent platform author has two supported paths: -- Implement the Go executor interface and use the supplied controller +- Implement the Go adapter interface and use the supplied controller framework and project template. - Implement a controller directly against the `AgentTask` and `CustomRun` APIs. After claiming a run, that controller becomes its sole status writer @@ -540,16 +538,16 @@ An agent platform author has two supported paths: conformance requirements. This permits implementations in other languages. An implementation advertises a DNS-qualified selector such as -`fullsend.ai/executor`. Installing that implementation does not require adding +`fullsend.ai/agenttask-adapter`. Installing that implementation does not require adding a platform-specific CRD to Tekton or registering code in a central service. The implementation may, of course, use its own CRDs behind the boundary. A platform that already implements a suitable execution protocol can provide a -thin protocol executor. A platform without such a protocol provides a native +thin protocol adapter. A platform without such a protocol provides a native controller or API bridge. Merely placing an opaque object reference in -`AgentTask` is insufficient: the executor must implement the common lifecycle. +`AgentTask` is insufficient: the adapter must implement the common lifecycle. -### Worked Adapter Examples +### Worked AgentTask Adapter Examples The following examples are non-normative. They validate that the common boundary accommodates materially different platforms. Fullsend workflow @@ -561,7 +559,7 @@ Each adapter must define the same boundary explicitly: | Adapter | Native identity | Workspace and identity | Results and observability | Cancellation and cleanup | |---------|-----------------|------------------------|---------------------------|--------------------------| | Fullsend | Deterministic Job name or service run ID keyed by the CustomRun attempt. | Mount the bound workspace into the Job, or upload an immutable snapshot; use the CustomRun service account only for a Kubernetes workload. | Validated Fullsend output becomes declared scalar results plus report and transcript references. | Stop the Job or service run, observe termination, and remove run-scoped sandbox resources. | -| Lightspeed | Deterministic `AgenticRun` name and UID. | Map only bindings supported by the selected executor profile; otherwise reject them. Lightspeed retains sandbox identity. | Map terminal conditions and scalar summaries; reference typed result CRs and sandbox logs. | Request the supported native stop operation or deletion, observe a terminal condition, and confirm child cleanup. | +| Lightspeed | Deterministic `AgenticRun` name and UID. | Map only bindings supported by the selected adapter profile; otherwise reject them. Lightspeed retains sandbox identity. | Map terminal conditions and scalar summaries; reference typed result CRs and sandbox logs. | Request the supported native stop operation or deletion, observe a terminal condition, and confirm child cleanup. | | OpenHands | Conversation ID persisted before acceptance. | Use an operator-managed workspace/profile mapping and workload identity; never send the Kubernetes service-account token. | Map bounded terminal values; reference the conversation, trajectory, workspace artifacts, and logs. | Request stop/delete, confirm authoritative conversation termination, then apply the configured workspace-retention policy. | #### Fullsend @@ -589,8 +587,8 @@ spec: results: - name: outcome - name: report-url - executorRef: - name: fullsend.ai/executor + adapterRef: + name: fullsend.ai/agenttask-adapter params: - name: agent value: review @@ -642,16 +640,16 @@ This follows the integration model in the Lightspeed [Component Developer Guide][lightspeed-component-guide]. In that model, a component-owned adapter receives an event and creates a namespaced `AgenticRun`; the operator owns the subsequent agent and sandbox lifecycle. A -Lightspeed executor plays that adapter role for a Tekton `CustomRun`. The +Lightspeed adapter plays that adapter role for a Tekton `CustomRun`. The guide's current step 3 is **Create an AgenticRun**. `Proposed` is a phase derived later from `AgenticRun` conditions, not a separate Proposal resource. -A Lightspeed-backed `AgentTask` selects the executor and an executor-managed +A Lightspeed-backed `AgentTask` selects the adapter and an adapter-managed profile. The profile is adapter configuration that materializes Lightspeed's inline workflow fields; it is not a new Lightspeed CRD: ```yaml -executorRef: +adapterRef: name: lightspeed.openshift.io/agenticrun params: - name: profile @@ -668,7 +666,7 @@ The adapter would: `CustomRun` service account name does not authorize the controller's API request; 3. map the declared request and target namespaces, then let the selected - executor profile materialize native workflow shape, agent names, + adapter profile materialize native workflow shape, agent names, `analysisOutput`, skills images, tools, and same-namespace `requiredSecrets` references; 4. keep Secret values out of `AgentTask` and `CustomRun` and reject any @@ -696,14 +694,14 @@ conversation history, and provider configuration. An OpenHands-backed definition selects an operator-managed profile: ```yaml -executorRef: +adapterRef: name: openhands.dev/agent-server params: - name: profile value: repository-change ``` -The executor would: +The adapter would: 1. create a conversation using the declared goal and profile; 2. map a bound workspace through the profile's documented repository, @@ -720,13 +718,13 @@ The executor would: 8. on cancellation or timeout, request stop/delete, confirm authoritative termination, and apply the configured workspace-retention policy. -The `AgentTask` does not embed an OpenHands conversation request. Executor +The `AgentTask` does not embed an OpenHands conversation request. Adapter params refer to an operator-managed OpenHands profile, while run-specific values remain declared Tekton params. -#### Reference TaskRun Executor +#### Reference TaskRun AgentTask Adapter -A reference executor may create a child `TaskRun` for users whose agent is +A reference adapter may create a child `TaskRun` for users whose agent is already packaged as a Tekton Task. It would: - resolve the referenced Task using existing resolution support; @@ -737,7 +735,7 @@ already packaged as a Tekton Task. It would: support; and - propagate only declared AgentTask results. -This executor is an onboarding and conformance implementation, not a reason to +This adapter is an onboarding and conformance implementation, not a reason to wrap every Task. If the Pipeline does not need a portable `AgentTask` contract, the Task should be referenced directly. @@ -756,15 +754,15 @@ The existing resolver architecture provides useful patterns: | Resolver pattern | Agent execution use | |------------------|---------------------| -| Explicit selector | `executorRef.name` | +| Explicit selector | `adapterRef.name` | | `ResolutionRequest` envelope | Existing `CustomRun` envelope | | Deterministic request identity | Attempt idempotency key and native name | | Owner references | Native Kubernetes child ownership | -| Shared framework and template | Agent Executor Framework and template | -| ConfigMap watcher | Optional executor administrator configuration | -| Narrow per-resolver RBAC | Narrow per-executor RBAC | +| Shared framework and template | AgentTask Adapter Framework and template | +| ConfigMap watcher | Optional adapter administrator configuration | +| Narrow per-resolver RBAC | Narrow per-adapter RBAC | | Source and digest metadata | Resolved AgentTask identity and digest | -| Conformance tests | Executor lifecycle conformance | +| Conformance tests | Adapter lifecycle conformance | The resolver method set itself is not reused. `Resolve` performs a bounded fetch and returns immutable bytes. Agent execution must persist a native @@ -801,7 +799,7 @@ message-specific event schema. Tekton Results already persists the `CustomRun` lifecycle. It does not collect CustomRun logs because a Custom Task is not necessarily Pod-backed. This TEP -requires an executor to publish log and artifact references. A Results +requires an adapter to publish log and artifact references. A Results integration should discover those references and associate external log providers or records with the owning `CustomRun` and `PipelineRun`. @@ -818,7 +816,7 @@ attestation must include completed AgentTask evidence. The minimum attested evidence is: - effective AgentTask name, UID, resource version, and content digest; -- executor selector and implementation version; +- adapter selector and implementation version; - `CustomRun` UID and attempt identity; - native execution reference or a privacy-preserving digest; - declared input source references and digests when available; @@ -831,28 +829,28 @@ sensitive model responses are excluded by default. ### Security and Responsibility Boundaries The framework is responsible for secure lifecycle plumbing, not for replacing -an executor's sandbox or tool policy. +an adapter's sandbox or tool policy. | Concern | Owner | |---------|-------| | Pipeline ordering, timeout request, workspace binding, service account selection | Tekton Pipeline and CustomRun | -| AgentTask validation, executor claim, common status, idempotency, cancellation coordination | Agent Executor Framework | +| AgentTask validation, adapter claim, common status, idempotency, cancellation coordination | AgentTask Adapter Framework | | Model, prompt, tools, memory, internal approvals, sandbox, native policy | Selected agent platform | -| Mapping Tekton identity and inputs into the platform without credential leakage | Executor | +| Mapping Tekton identity and inputs into the platform without credential leakage | Adapter | | Cluster admission, namespace quotas, network policy, and workload policy | Cluster operator | -| External-service identity and short-lived credential exchange | Executor/platform identity provider | +| External-service identity and short-lived credential exchange | Adapter/platform identity provider | | Result, log, artifact, and provenance access control | Tekton installation and backend operators | -Executor controllers receive the `CustomRun` service account name, but their +Adapter controllers receive the `CustomRun` service account name, but their own Kubernetes API calls still use the controller's identity and RBAC. The -field does not grant impersonation. A Kubernetes executor may create a child +field does not grant impersonation. A Kubernetes adapter may create a child workload using the selected service account if its controller is authorized to -do so. A remote executor must not copy a service-account bearer token into a +do so. A remote adapter must not copy a service-account bearer token into a remote service; it should use workload identity or an explicit, scoped exchange supported by its platform. A workspace binding is authority to use the bound data only through the -executor's documented mapping. An executor that cannot safely map a workspace +adapter's documented mapping. An adapter that cannot safely map a workspace must reject it with `CustomRunWorkspaceNotSupported` rather than silently ignoring it. @@ -864,7 +862,7 @@ ignoring it. - `CustomRun.status.extraFields` is schemaless. The alpha framework can define and version a reserved AgentTask status profile there, but a future `CustomRun` API should provide typed execution and artifact references. -- Existing custom controllers are not automatically conformant executors. +- Existing custom controllers are not automatically conformant adapters. They must implement the acknowledgement, idempotency, cancellation, cleanup, and status contract. - Native approval remains platform-specific. Tekton can display a waiting @@ -873,9 +871,9 @@ ignoring it. - An agent may complete successfully while returning a negative business decision such as `approved=false`. That is a successful execution with a result, not an infrastructure failure. -- DNS-qualified executor names prevent accidental naming collisions but do +- DNS-qualified adapter names prevent accidental naming collisions but do not provide installation discovery. The alpha design uses bounded claiming; - an `ExecutorClass` resource may be considered later only if operational + an `AgentTaskAdapterClass` resource may be considered later only if operational discovery and capability advertisement prove necessary. ## Design Details @@ -890,10 +888,10 @@ type AgentTaskSpec struct { Params []ParamSpec `json:"params,omitempty"` Workspaces []WorkspaceDeclaration `json:"workspaces,omitempty"` Results []AgentTaskResult `json:"results,omitempty"` - ExecutorRef ExecutorRef `json:"executorRef"` + AdapterRef AgentTaskAdapterRef `json:"adapterRef"` } -type ExecutorRef struct { +type AgentTaskAdapterRef struct { Name string `json:"name"` Params []Param `json:"params,omitempty"` } @@ -910,13 +908,13 @@ strings because `CustomRunResult.Value` is currently a string. Normative validation includes: -- `executorRef.name` is required and DNS-qualified; +- `adapterRef.name` is required and DNS-qualified; - param, workspace, and result names are unique; -- executor params have unique names; +- adapter params have unique names; - runtime params not declared by the AgentTask are rejected; - required params and workspaces are present; -- result names produced by an executor were declared; and -- `executorRef` and the Pipeline-visible contract are immutable. +- result names produced by an adapter were declared; and +- `adapterRef` and the Pipeline-visible contract are immutable. Immutability prevents an in-flight reference from silently changing meaning. A changed implementation or contract uses a new AgentTask name or resolved @@ -926,10 +924,10 @@ The API does not require a cluster-scoped `ClusterAgentTask`. Namespaced resources, remote resolution, and normal promotion tooling cover the initial use cases without a second definition kind. -### Executor Selection and Claiming +### AgentTask Adapter Selection and Claiming Current Custom Task filters distinguish only API version and kind. Every -AgentTask executor would therefore observe the same `CustomRun` kind unless a +AgentTask adapter would therefore observe the same `CustomRun` kind unless a second selector is introduced. The framework uses this sequence: @@ -937,20 +935,20 @@ The framework uses this sequence: 1. The AgentTask lifecycle reconciler loads the referenced or embedded definition and validates it. 2. It writes the immutable label - `agent.tekton.dev/executor=` and the effective AgentTask + `agent.tekton.dev/adapter=` and the effective AgentTask digest to the `CustomRun`. -3. Executor controllers filter on that label and independently reconcile only +3. Adapter controllers filter on that label and independently reconcile only their selector. -4. A matching executor atomically writes its stable installation identity, +4. A matching adapter atomically writes its stable installation identity, `claimedAt`, and initial heartbeat to the reserved AgentTask status. All replicas of one controller deployment share that installation identity. -5. After the claim succeeds, the framework wrapper embedded in that executor +5. After the claim succeeds, the framework wrapper embedded in that adapter controller is the sole status writer. The pre-claim reconciler stops mutating status, and a controller with a different installation identity stops when it observes the claim. -6. If no executor claims the run before the configured acknowledgement +6. If no adapter claims the run before the configured acknowledgement deadline, the pre-claim reconciler marks it failed with - `ExecutorNotFound`. + `AgentTaskAdapterNotFound`. The label value must use a reversible or collision-resistant encoding because Kubernetes label values cannot contain every character allowed in a @@ -958,16 +956,16 @@ DNS-qualified selector. The unmodified selector remains in status and provenance. This uses the existing `CustomRun` as the request envelope. It does not add an -executor registration CRD or an internal `AgentExecutionRequest` that would +adapter registration CRD or an internal `AgentExecutionRequest` that would become a second run record. -### Executor Interface +### AgentTask Adapter Interface A Go interface may resemble the following, but observable behavior rather than this exact method set is normative: ```go -type Executor interface { +type AgentTaskAdapter interface { Initialize(context.Context) error Name(context.Context) string Validate(context.Context, *AgentTask, *CustomRun) error @@ -977,7 +975,7 @@ type Executor interface { ``` `Request` contains the effective immutable AgentTask, CustomRun, attempt -identity, selected service account, workspace bindings, and executor +identity, selected service account, workspace bindings, and adapter administrator configuration. `Observation` contains bounded state: @@ -992,7 +990,7 @@ type Observation struct { Logs []Reference Artifacts []Reference Traces []Reference - RequeueAfter time.Duration + RequeueAfter time.Duration CleanupComplete bool } ``` @@ -1000,10 +998,10 @@ type Observation struct { `Reconcile` and `Cancel` must return quickly. A long operation happens in the native platform; the controller watches, polls, or requeues. Implementations must not retain the only copy of execution state in process memory. For this -interface, the framework wrapper and executor implementation run in the same +interface, the framework wrapper and adapter implementation run in the same controller process; `Observation` is not a network protocol. -An executor error means the controller could not complete reconciliation. It +An adapter error means the controller could not complete reconciliation. It is not automatically an agent failure. Typed errors distinguish transient controller/backend errors, invalid requests, missing dependencies, and terminal native failures. @@ -1013,7 +1011,7 @@ terminal native failures. ```mermaid stateDiagram-v2 [*] --> Pending - Pending --> Accepted: executor claims and persists native identity + Pending --> Accepted: adapter claims and persists native identity Pending --> Failed: invalid or acknowledgement deadline Accepted --> Running Running --> Waiting: native approval or external input @@ -1045,7 +1043,7 @@ common lifecycle. #### Acknowledgement -A run is accepted only after the executor has either: +A run is accepted only after the adapter has either: - created or adopted a native execution and persisted its reference; or - reserved an idempotent remote execution key that can be recovered after a @@ -1053,7 +1051,7 @@ A run is accepted only after the executor has either: Merely receiving an informer event is not acknowledgement. The framework records claim and acceptance latency. An unclaimed or repeatedly unavailable -executor produces a terminal condition rather than leaving a Pipeline waiting +adapter produces a terminal condition rather than leaving a Pipeline waiting indefinitely. #### Idempotency and Recovery @@ -1064,18 +1062,18 @@ The framework supplies an idempotency key derived from: : ``` -Kubernetes executors use a deterministic child name plus owner or correlation -metadata. Remote executors pass an idempotency key when supported and persist +Kubernetes adapters use a deterministic child name plus owner or correlation +metadata. Remote adapters pass an idempotency key when supported and persist the returned native ID. When the backend lacks idempotent creation, the adapter must implement lookup by correlation key before creating another run. -On restart, the executor first adopts the recorded or deterministic native +On restart, the adapter first adopts the recorded or deterministic native execution. It must not create another execution because an in-memory cache was lost. #### Progress and Heartbeats -The executor framework records bounded progress messages and a heartbeat while +The adapter framework records bounded progress messages and a heartbeat while an execution is active. A heartbeat proves that the controller can still observe the backend; it does not require the agent itself to emit synthetic progress. @@ -1083,16 +1081,16 @@ progress. A stale heartbeat is an observability and alerting signal; it does not by itself rewrite the run's terminal state. Another replica with the same stable installation identity may reconcile and adopt the native execution. Automatic -takeover by a differently configured executor installation is not permitted. -If the selected executor later proves that the native execution failed, it +takeover by a differently configured adapter installation is not permitted. +If the selected adapter later proves that the native execution failed, it reports `InfrastructureFailed` and preserves the native reference. #### Cancellation and Timeout Pipeline cancellation is expressed through the existing -`CustomRun.spec.status=RunCancelled`. Before an executor claims the run, the +`CustomRun.spec.status=RunCancelled`. Before an adapter claims the run, the pre-claim reconciler can terminate it immediately because no native execution -exists. After claim, the selected framework wrapper calls the executor's +exists. After claim, the selected framework wrapper calls the adapter's cancellation path until the backend confirms terminal state or the cleanup deadline expires, then uses the existing `CustomRunCancelled` reason. @@ -1103,9 +1101,9 @@ CustomRun does not carry. `CustomRun.spec.timeout` is separately authoritative for the invocation. If it expires before claim, the pre-claim reconciler marks the run timed out without -calling an executor. After claim, the selected wrapper requests native +calling an adapter. After claim, the selected wrapper requests native termination and, after confirmation, uses the existing -`CustomRunTimedOut` reason. An executor should also configure a native timeout +`CustomRunTimedOut` reason. An adapter should also configure a native timeout when the backend supports one, but a missing native timeout does not remove the framework's obligation to act. @@ -1119,7 +1117,7 @@ Kubernetes child resources use controller references when valid. Cross- namespace resources and remote executions use correlation labels or IDs and a finalizer on the `CustomRun`. -The finalizer remains until the executor confirms that run-scoped resources +The finalizer remains until the adapter confirms that run-scoped resources are deleted or intentionally retained by a declared backend policy. The framework uses an operator-configured maximum cleanup interval so a broken remote service cannot block Kubernetes deletion forever. Expiry removes the @@ -1133,7 +1131,7 @@ Controller reconciliation retries and agent execution retries are different: - `CustomRun.spec.retries` is the maximum number of additional execution attempts. The current zero-based attempt number is `len(status.retriesStatus)`. -- When an executor reports a terminal retryable failure, the framework first +- When an adapter reports a terminal retryable failure, the framework first confirms native termination and cleanup. If retries remain, it appends a deep copy of the completed current status, including its condition, execution reference, and AgentTask extra fields, to @@ -1149,7 +1147,7 @@ Controller reconciliation retries and agent execution retries are different: Because the archived retry status is the authoritative attempt counter, a controller restart cannot increment it from memory. Because agents may make external changes, the framework never starts a new attempt solely because -status observation temporarily failed. The executor explicitly marks whether +status observation temporarily failed. The adapter explicitly marks whether a terminal infrastructure failure is safe to retry. ### Status and Outcome Semantics @@ -1164,10 +1162,10 @@ Pipeline continues to read the standard `Succeeded` condition: | Unknown | `WaitingForApproval` | Native platform is waiting for human or external input. | | True | `Succeeded` | Invocation completed and declared results are valid. | | False | `InvalidAgentTask` | Definition or runtime bindings are invalid. | -| False | `ExecutorNotFound` | No matching executor claimed the run. | +| False | `AgentTaskAdapterNotFound` | No matching adapter claimed the run. | | False | `AgentFailed` | Native agent completed unsuccessfully. | -| False | `InfrastructureFailed` | Executor, platform, sandbox, or workload failed. | -| False | `CustomRunWorkspaceNotSupported` | The selected executor cannot honor a bound workspace. | +| False | `InfrastructureFailed` | Adapter, platform, sandbox, or workload failed. | +| False | `CustomRunWorkspaceNotSupported` | The selected adapter cannot honor a bound workspace. | | False | `CustomRunCancelled` | A `RunCancelled` request was observed and native termination was confirmed. | | False | `CustomRunTimedOut` | `CustomRun.spec.timeout` elapsed and native termination was confirmed. | | False | `CleanupFailed` | Native termination or cleanup could not be confirmed. | @@ -1187,10 +1185,10 @@ agentTask: uid: 6d3c... resourceVersion: "1042" digest: sha256:... -executor: - name: fullsend.ai/executor +adapter: + name: fullsend.ai/agenttask-adapter version: v0.1.0 - installationID: fullsend-executor.production + installationID: fullsend-agenttask-adapter.production claimedAt: "..." lastHeartbeatTime: "..." attempt: @@ -1216,30 +1214,30 @@ The status profile has these normative ownership and compatibility rules: |-------|----------|--------|------| | `schemaVersion` | Always | Pre-claim reconciler | Readers reject an unsupported major schema and ignore unknown additive fields. | | `agentTask` identity and digest | Always | Pre-claim reconciler | Immutable after routing; identifies the exact local or resolved definition. | -| `executor.name` | Always | Pre-claim reconciler | Equals the unmodified `executorRef.name`. | -| `executor.installationID`, version, and claim time | After claim | Selected framework wrapper | Written by compare-and-swap; immutable for the attempt. | -| `executor.lastHeartbeatTime` | While active | Selected framework wrapper | Rate-limited and monotonically nondecreasing. | +| `adapter.name` | Always | Pre-claim reconciler | Equals the unmodified `adapterRef.name`. | +| `adapter.installationID`, version, and claim time | After claim | Selected framework wrapper | Written by compare-and-swap; immutable for the attempt. | +| `adapter.lastHeartbeatTime` | While active | Selected framework wrapper | Rate-limited and monotonically nondecreasing. | | `attempt.number` and `attempt.id` | Always | Framework | Derived from retry history and CustomRun UID; immutable within an attempt. | | `executionRef` | From acceptance | Selected framework wrapper | Required before `Accepted`; immutable except to add server-assigned identity fields. | | `logs`, `artifacts`, and `traces` | Optional | Selected framework wrapper | At most 32 references per category; names are unique and URIs are at most 2048 bytes. | Condition messages are at most 4096 bytes. Each status writer uses a resource-version-checked patch. The pre-claim reconciler writes only while no -executor claim exists; after claim, the selected framework wrapper owns the +adapter claim exists; after claim, the selected framework wrapper owns the profile and standard condition. A conformant direct controller assumes that same post-claim writer role. URI schemes are not limited to HTTP. References may identify Kubernetes objects, OCI artifacts, Tekton Results records, or platform-native resources. References must be usable without an embedded credential. The profile schema -is versioned independently from native executor detail. +is versioned independently from native adapter detail. ### Results, Logs, Artifacts, and Traces Declared scalar results are written to `CustomRun.status.results`; undeclared results are rejected. Result names follow ordinary Tekton substitution rules. -The complete agent transcript is not a result. Executors publish logs through +The complete agent transcript is not a result. Adapters publish logs through one of these paths: - Pod/TaskRun logs for Kubernetes workloads; @@ -1252,26 +1250,26 @@ name and URI and may include media type, digest, and size. A reference must not contain a bearer token or embedded credential. Access is controlled by the referenced backend. -Executors must truncate condition messages and reject status payloads that +Adapters must truncate condition messages and reject status payloads that would approach Kubernetes object-size limits. ### Parameters and Workspaces Runtime params are validated against the AgentTask declaration before an -executor receives them. Params are data, not a place for credentials or +adapter receives them. Params are data, not a place for credentials or unbounded source archives. A workspace declaration describes a Pipeline-visible input or output binding. -Mapping is executor-specific: +Mapping is adapter-specific: -- a TaskRun or Job executor may mount the bound volume; +- a TaskRun or Job adapter may mount the bound volume; - a native controller may pass an existing PVC reference if its API supports one; -- a remote executor may upload a content-addressed snapshot or use an existing +- a remote adapter may upload a content-addressed snapshot or use an existing repository reference; and -- an executor unable to honor the binding rejects it. +- an adapter unable to honor the binding rejects it. -An executor must document whether writes are visible through the original +An adapter must document whether writes are visible through the original workspace, returned as an artifact, or committed through the platform's native SCM integration. The common API does not silently equate those behaviors. @@ -1279,18 +1277,18 @@ SCM integration. The common API does not silently equate those behaviors. `CustomRun.spec.serviceAccountName` identifies the Kubernetes identity selected for a child execution. The framework validates and presents the name to the -executor; it does not cause framework or executor-controller API calls to run +adapter; it does not cause framework or adapter-controller API calls to run as that service account. Those calls use the controller's own RBAC. -A Kubernetes executor may set the selected service account on a child workload -when its controller is authorized to do so. Impersonation, if an executor +A Kubernetes adapter may set the selected service account on a child workload +when its controller is authorized to do so. Impersonation, if an adapter chooses to support it, requires explicit impersonation RBAC and authorization -checks and is not implied by this TEP. A remote executor must use an explicit +checks and is not implied by this TEP. A remote adapter must use an explicit workload-identity exchange or backend credential binding. Copying a projected Kubernetes bearer token into a remote request is not conformant. -Executor administrator configuration may reference Secrets through normal -Kubernetes references. Secret values are read only by the executor that needs +Adapter administrator configuration may reference Secrets through normal +Kubernetes references. Secret values are read only by the adapter that needs them and never copied into AgentTask, CustomRun status, results, events, provenance, or command-line arguments. @@ -1306,16 +1304,16 @@ executes that exact content. A retry uses the pinned definition unless the user creates a new CustomRun. Provenance records what Tekton can verify, not unverifiable claims about the -agent's reasoning. An executor may add signed platform evidence, model or tool +agent's reasoning. An adapter may add signed platform evidence, model or tool metadata, and policy decisions, but the common attestation distinguishes: - Tekton-observed definition and lifecycle data; -- executor-reported metadata; and +- adapter-reported metadata; and - externally verifiable artifact digests or attestations. ### Conformance -The executor conformance suite creates `AgentTask` and `CustomRun` fixtures +The adapter conformance suite creates `AgentTask` and `CustomRun` fixtures against a deterministic fake native backend. It verifies at least: 1. valid run acceptance and successful scalar results; @@ -1349,7 +1347,7 @@ substitution, Triggers, and remote resolution patterns. `AgentTask` is a definition rather than a per-run object. One definition can be invoked with different goals, repositories, revisions, and event context. The same Pipeline authoring surface works with a TaskRun, Fullsend, -Lightspeed, OpenHands, or a protocol executor. +Lightspeed, OpenHands, or a protocol adapter. The framework is deliberately scoped to agent executions rather than reviving a generic Custom Task SDK without concrete lifecycle requirements. @@ -1368,11 +1366,11 @@ when a portable agent contract or non-Pod execution boundary is needed. ### Flexibility -Executor implementations are independently deployed and may use Kubernetes +Adapter implementations are independently deployed and may use Kubernetes resources, remote APIs, protocol clients, or child TaskRuns. Tekton does not import their SDKs into the Pipeline controller. -Platform-specific behavior remains behind `executorRef`. This preserves +Platform-specific behavior remains behind `adapterRef`. This preserves Fullsend harnesses and sandboxes, Lightspeed native approvals and typed results, and OpenHands conversations and workspaces. @@ -1389,7 +1387,7 @@ The proposal introduces `AgentTask` and a versioned status profile but no new Pipeline concepts. API documentation must specify the relationship to Custom Tasks and the subset of Tekton result types supported by `CustomRun`. -Executor authors must understand Kubernetes controller semantics. The +Adapter authors must understand Kubernetes controller semantics. The framework and conformance suite remove repeated informer, claiming, idempotency, cancellation, and status code. @@ -1397,17 +1395,17 @@ idempotency, cancellation, and status code. - **Pipeline authors** reference AgentTasks like other Custom Tasks and consume declared results. -- **Agent platform authors** implement one executor lifecycle rather than a +- **Agent platform authors** implement one adapter lifecycle rather than a complete Pipeline integration for every use case. -- **Cluster operators** install approved executors, configure their RBAC and +- **Cluster operators** install approved adapters, configure their RBAC and backends, and can identify the native execution from the CustomRun. - **Approvers** continue using the platform-native approval surface, linked from the CustomRun and Tekton UI. -- **Security and supply-chain teams** receive stable executor, definition, +- **Security and supply-chain teams** receive stable adapter, definition, result, and artifact evidence without storing sensitive transcripts in Kubernetes. -`tkn` and Dashboard should show the common reason, executor, native reference, +`tkn` and Dashboard should show the common reason, adapter, native reference, last heartbeat, declared results, and log/artifact links. Native detail may be shown by following the reference. @@ -1415,11 +1413,11 @@ shown by following the reference. The additional lifecycle reconcile and AgentTask lookup are small compared with agent execution. Informers and indexes avoid listing definitions for each -run. The projected executor label lets controllers filter before expensive +run. The projected adapter label lets controllers filter before expensive backend calls. -Polling executors use bounded exponential backoff and backend-provided retry -hints. Kubernetes-native executors should watch child resources. Remote event +Polling adapters use bounded exponential backoff and backend-provided retry +hints. Kubernetes-native adapters should watch child resources. Remote event streams may trigger reconciliation but must not require one persistent stream per run in the controller process. @@ -1430,16 +1428,16 @@ and Results writes. Large logs and artifacts stay out of the API server. | Risk | Mitigation | |------|------------| -| Common API grows into a lowest-common-denominator agent runtime | Limit AgentTask to Pipeline-consumed declarations and executor selection; keep native configuration behind the boundary. | -| Two executors claim a run | Atomic claim with stable installation identity; only the selected DNS-qualified executor may claim; a different claimant stops. | +| Common API grows into a lowest-common-denominator agent runtime | Limit AgentTask to Pipeline-consumed declarations and adapter selection; keep native configuration behind the boundary. | +| Two adapters claim a run | Atomic claim with stable installation identity; only the selected DNS-qualified adapter may claim; a different claimant stops. | | Controller restart duplicates external effects | Stable attempt identity, deterministic child names, idempotency keys, and adopt-before-create conformance tests. | | Backend is unreachable during cancellation | Reconcile cancellation until deadline; preserve native reference and report `CleanupFailed`. | | Automatic retry repeats external changes | Separate reconciliation retries from execution attempts; require terminal cleanup and explicit retryability. | | Status or logs expose credentials or prompts | Bounded references, Secret-redaction tests, no embedded tokens, and access-controlled backends. | -| Workspace semantics differ between executors | Each executor documents the mapping and rejects unsupported bindings. | +| Workspace semantics differ between adapters | Each adapter documents the mapping and rejects unsupported bindings. | | Platform-native approvals confuse Pipeline users | Standard `WaitingForApproval` reason plus a native approval reference; native system remains authoritative. | -| Missing executor leaves a Pipeline waiting | Bounded claim deadline and terminal `ExecutorNotFound` status. | -| Resolver and executor terminology become conflated | Keep resolution and execution as separate stages and interfaces. | +| Missing adapter leaves a Pipeline waiting | Bounded claim deadline and terminal `AgentTaskAdapterNotFound` status. | +| Resolver and adapter terminology become conflated | Keep resolution and execution as separate stages and interfaces. | | Results cannot collect CustomRun logs today | Standardize log references first; extend Results providers without assuming a Pod. | | Chains cannot attest CustomRuns today | Add explicit CustomRun/AgentTask support or include equivalent child evidence in PipelineRun attestation. | | AgentTask API couples Pipeline to new dependencies | Implement as a Custom Task extension; do not import platform SDKs into Pipeline. | @@ -1452,11 +1450,11 @@ and Results writes. Large logs and artifacts stay out of the API server. the platform's UI or CRDs for approvals and detailed diagnosis. - `CustomRun` is still v1beta1 and has string-only results and schemaless extension status. -- A bridging executor adds another reconciliation layer and may delay native +- A bridging adapter adds another reconciliation layer and may delay native status by one reconcile interval. - Full provenance and logs require changes outside Tekton Pipelines, notably Results and Chains. -- Independently installed executors create a compatibility matrix that must be +- Independently installed adapters create a compatibility matrix that must be managed through conformance and supported-version documentation. ## Alternatives @@ -1502,9 +1500,9 @@ would require ownership and status synchronization. The proposal instead versions agent-specific status within CustomRun and can promote generally useful fields into a future CustomRun API. -### Introduce AgentExecutorClass +### Introduce AgentTaskAdapterClass -An `AgentExecutorClass` CRD could advertise installed implementations, +An `AgentTaskAdapterClass` CRD could advertise installed implementations, capabilities, defaults, and readiness. It adds registration, lifecycle, RBAC, and failure modes before the need is demonstrated. @@ -1515,12 +1513,12 @@ multi-tenant defaults. ### Standardize a Generic Agent Container Protocol -A JSON stdin/stdout or OCI image contract would simplify a batch executor but +A JSON stdin/stdout or OCI image contract would simplify a batch adapter but would force existing platforms to abandon or wrap their native lifecycle. It also duplicates ordinary Tasks for simple containers. -A TaskRun reference executor provides this onboarding path with existing -Tekton contracts. Other executors remain free to use a protocol internally. +A TaskRun reference adapter provides this onboarding path with existing +Tekton contracts. Other adapters remain free to use a protocol internally. ### Standardize an Opaque Implementation Reference @@ -1530,7 +1528,7 @@ semantics. It would rename a Custom Task reference without improving interoperability. AgentTask therefore requires a Tekton-consumed contract and a conformant -executor. Native configuration references remain executor params, not the +adapter. Native configuration references remain adapter params, not the whole API. ### Use the Resolver Interface for Execution @@ -1550,7 +1548,7 @@ resources. The same issue applies to choosing Fullsend, Lightspeed, OpenHands, or another platform: Tekton would inherit that platform's API and release cycle, and users of other systems would need a second abstraction. -Each may instead provide an executor. The common API does not select a winner +Each may instead provide an adapter. The common API does not select a winner among agent runtimes. ### Add Agent Fields to Pipeline @@ -1563,33 +1561,33 @@ provide a composition point and avoid changing existing Pipeline resources. ### Milestones -**Phase 1: Alpha contract and reference executor** +**Phase 1: Alpha contract and reference adapter** - Define the namespaced `AgentTask` v1alpha1 CRD and validation. - Define and version the AgentTask profile in `CustomRun.status.extraFields`. -- Implement lifecycle validation, executor routing label, bounded claim, +- Implement lifecycle validation, adapter routing label, bounded claim, idempotency, timeout, cancellation, cleanup, and common status mapping. -- Publish the executor Go framework, direct-controller documentation, and +- Publish the adapter Go framework, direct-controller documentation, and project template. -- Implement a deterministic fake executor and the conformance suite. -- Implement the reference TaskRun executor. +- Implement a deterministic fake adapter and the conformance suite. +- Implement the reference TaskRun adapter. - Add `tkn` and Dashboard-readable labels, events, and status fields where feasible. **Phase 2: Existing-platform validation** -- Implement and test a Fullsend reference executor using a Kubernetes workload +- Implement and test a Fullsend reference adapter using a Kubernetes workload or managed service boundary while preserving its harness and sandbox. - Build contract-tested prototypes for an OpenShift Lightspeed Agentic - Operator executor using `AgenticRun`, native approvals, sandbox logs, and - typed result references, and an OpenHands executor using its conversation + Operator adapter using `AgenticRun`, native approvals, sandbox logs, and + typed result references, and an OpenHands adapter using its conversation and event APIs. - Decide production ownership with each upstream community before promising a supported adapter release. -- Publish the three adapter mappings and an executor compatibility and +- Publish the three adapter mappings and an adapter compatibility and supported-version matrix. -Additional protocol executors, such as an A2A bridge, use the same public +Additional protocol adapters, such as an A2A bridge, use the same public contract but are not required for alpha. **Phase 3: Tekton ecosystem integration** @@ -1603,13 +1601,13 @@ contract but are not required for alpha. - Evaluate typed CustomRun results and typed execution references through the appropriate Pipeline API process. -Promotion beyond alpha requires at least two independent executor +Promotion beyond alpha requires at least two independent adapter implementations, conformance coverage, cancellation and restart fault tests, and one end-to-end Pipeline using an external platform. ### Test Plan -- **API tests:** defaulting, DNS-qualified executor validation, unique +- **API tests:** defaulting, DNS-qualified adapter validation, unique declarations, immutability, unsupported result types, and Secret-safe serialization. - **Framework unit tests:** selector projection, claim races, status ownership, @@ -1621,24 +1619,24 @@ and one end-to-end Pipeline using an external platform. - **Pipeline end-to-end tests:** params, workspaces, result substitution, `when`, retry, timeout, cancellation, finally tasks, and PipelineRun pruning. - **Conformance tests:** all scenarios listed in the Conformance section, - runnable against in-tree and external executors. + runnable against in-tree and external adapters. - **Adapter tests:** fake-server contract tests plus supported-platform end-to-end tests for Fullsend, Lightspeed, and OpenHands. - **Security tests:** duplicate claim, cross-namespace reference denial, service-account misuse, status/log URL credential leakage, malicious backend messages, oversized results, and Secret redaction. -- **Provenance tests:** resolved definition digest, executor version, native +- **Provenance tests:** resolved definition digest, adapter version, native identity, artifact digests, and omission of sensitive content. - **Scalability tests:** informer filtering, heartbeat write rate, many waiting approvals, remote polling backoff, and controller restart with active runs. Tests assert observable resources and lifecycle behavior, not exact reconcile -counts or executor helper structure. +counts or adapter helper structure. ### Infrastructure Needed The initial implementation may live as a Tekton extension while the API and -executor framework mature. Project governance will determine whether it +adapter framework mature. Project governance will determine whether it belongs in `tektoncd/pipeline` or a separate Tekton repository. CI requires: @@ -1659,14 +1657,14 @@ and agent platforms continue to work. A platform can migrate incrementally: 1. keep its native runtime and controller unchanged; -2. add an executor that creates or calls the native execution; +2. add an adapter that creates or calls the native execution; 3. define AgentTasks for reusable Pipeline contracts; 4. move Pipeline orchestration to CustomRuns; and 5. adopt common logs, artifacts, resolution, and provenance as those integrations become available. Alpha AgentTask definitions and status profiles may require conversion before -beta. The effective definition digest and executor selector make version skew +beta. The effective definition digest and adapter selector make version skew visible. No migration may silently reinterpret an in-flight CustomRun. ### Implementation Pull Requests diff --git a/teps/README.md b/teps/README.md index 11d89d1c4..cb919f481 100644 --- a/teps/README.md +++ b/teps/README.md @@ -152,4 +152,4 @@ This is the complete list of Tekton TEPs: |[TEP-0162](0162-event-based-pruning-of-tekton-resources.md) | event based pruning of tekton resources | proposed | 2025-06-18 | |[TEP-0163](0163-profilebased-dynamic-compute-resources-for-steps.md) | Profile-Based Dynamic Compute Resources for Steps | proposed | 2025-09-01 | |[TEP-0164](0164-tekton-kueue-integration.md) | Tekton Kueue Integration | proposed | 2026-01-28 | -|[TEP-0170](0170-agent-native-workflows.md) | AgentTask and Pluggable Agent Execution | proposed | 2026-08-30 | +|[TEP-0170](0170-agent-native-workflows.md) | AgentTask and Pluggable Agent Execution | proposed | 2026-08-31 |