backend

package
v0.0.0-...-bea457f Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 24, 2026 License: MIT Imports: 27 Imported by: 0

Documentation

Overview

Package backend abstracts how a CompiledSpec is executed.

MVP ships LocalBackend (spawn agent-runtime as a subprocess). Future backends — AgentCoreBackend, K8sBackend — implement the same interface; the CLI does not change.

v0.3.3a split the run path into two explicit phases:

Resolve(spec, binding) → (ResolvedRunSpec, warnings, err)
Submit(resolved)       → SessionHandle

Pre-v0.3.3 there was only Submit(spec). Resolve gives backends a hook to inspect/validate the chosen RuntimeBinding before any subprocess starts — and (in slice 3.3b) to emit `warning` wire events when the spec's runtime.requirements aren't met by the binding's target. Today (3.3a) the LocalBackend's Resolve is a no-op; the matcher lands in 3.3b.

KubernetesBackend launches the agent-runtime-base image as a Pod and streams its NDJSON wire-event stdout back to the CLI. Submission + log-streaming + cleanup all run on the host process; the cluster only sees a Pod and a single-key ConfigMap.

Slice 4.3 (skeleton):

  • Resolve() delegates to the shared matcher (matcher.go)
  • Submit() materializes a ConfigMap with the spec YAML, creates a Pod that mounts it at /workspace/spec.yaml, and starts a goroutine that follows the Pod logs and forwards parsed wire events on Events(h).
  • Stop() deletes the Pod (foreground propagation) which terminates the running container; the ConfigMap is reaped in the same call.
  • Capabilities() advertises streaming and a configurable concurrency ceiling. The default ceiling (1) matches LocalBackend; the K8s backend can be configured higher per Binding once batch scenarios show up.

Out of scope for slice 4.3 (tracked as later 4.x slices):

  • SecurityContext / NetworkPolicy enforcement (slice 4.5)
  • Job vs Pod toggle, replicas, retries
  • serviceAccount / imagePullSecrets / kubeconfig context selection (slice 4.4)
  • Pre-existing Secret validation (we just reference it by name and let the Pod admission webhook / kubelet surface "Secret not found" when the Pod is scheduled)

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type Backend

type Backend interface {
	// Resolve picks (or validates) a binding for the given spec and
	// produces an executable run shape. Backends may emit wire `warning`
	// or `error` events here — callers should drain the returned slice
	// before reading Events(handle), so warnings appear in the canonical
	// stream order. v0.3.3a: LocalBackend.Resolve is a no-op when
	// `binding` is nil (wraps the spec verbatim). Slice 3.3b adds the
	// capability matcher that fires warnings.
	Resolve(ctx context.Context, spec adl.CompiledSpec, binding *adl.RuntimeBinding) (adl.ResolvedRunSpec, []wire.Event, error)
	// Submit starts the run for a resolved spec.
	Submit(ctx context.Context, run adl.ResolvedRunSpec) (SessionHandle, error)
	Events(h SessionHandle) <-chan wire.Event
	Stop(h SessionHandle) error
	Capabilities() Caps
}

type Caps

type Caps struct {
	SupportsStreaming bool
	SupportsMCP       bool
	MaxConcurrency    int
}

type KubernetesBackend

type KubernetesBackend struct {
	// contains filtered or unexported fields
}

KubernetesBackend implements Backend over the Kubernetes API.

func NewKubernetesBackend

func NewKubernetesBackend(cfg KubernetesConfig) (*KubernetesBackend, error)

NewKubernetesBackend loads kubeconfig, builds a typed clientset, and returns the backend. Returns an error when the kubeconfig can't be loaded or the cluster isn't reachable — failing here is cheaper than surfacing a vague "session.ended reason=error" after Submit.

func (*KubernetesBackend) Capabilities

func (b *KubernetesBackend) Capabilities() Caps

func (*KubernetesBackend) Events

func (b *KubernetesBackend) Events(h SessionHandle) <-chan wire.Event

func (*KubernetesBackend) Resolve

Resolve uses the shared matcher. The KubernetesBackend additionally requires the binding's target.type to be "kubernetes" — running a local-target Binding through this backend is a wiring bug, not a capability gap.

func (*KubernetesBackend) Stop

Stop tears down the running Pod and its spec Secret. Cancellation of the streaming goroutine is best-effort — the caller may already have observed a terminal wire event (session.ended) before calling Stop. Stop is idempotent: streamPodLogs also calls cleanup() on normal completion. We also mark the session as user-cancelled BEFORE cancelling the context, so the streaming goroutine emits the canonical reason=cancelled terminal event instead of synthesizing a reason=error (codex pass 2 of slice 4.3 caught the wrong-reason gap).

func (*KubernetesBackend) Submit

Submit creates the ConfigMap + Pod and starts a goroutine that streams Pod logs as wire events on the SessionHandle's channel. Returns once the resources are accepted by the API server; the caller drains Events(h) for the actual run progress.

Naming: every Submit picks a fresh `agentctl-<counter>-<unix-millis>` suffix so multiple concurrent runs (or rapid retries after a failed Pod) don't collide. The counter is per-Backend-instance, so multiple agentctl processes hitting the same cluster also need timestamp disambiguation — hence the millis suffix.

type KubernetesConfig

type KubernetesConfig struct {
	// Kubeconfig is the path to a kubeconfig file. Empty means use
	// the standard precedence: in-cluster ServiceAccount → KUBECONFIG
	// env → ~/.kube/config.
	Kubeconfig string
	// Context selects a kubeconfig context. Empty uses the kubeconfig's
	// `current-context`. Ignored when running in-cluster.
	Context string
}

KubernetesConfig bundles the Backend's runtime options. The struct is intentionally narrow — per-Binding fields (namespace, image, secret) live on the Binding, not here. KubernetesConfig only carries connection settings that aren't part of the ADL surface.

type LocalBackend

type LocalBackend struct {
	// contains filtered or unexported fields
}

LocalBackend runs the runtime as a child process and ferries the wire protocol over its stdin/stdout.

func NewLocalBackend

func NewLocalBackend(cfg LocalConfig) *LocalBackend

func (*LocalBackend) Capabilities

func (b *LocalBackend) Capabilities() Caps

func (*LocalBackend) Events

func (b *LocalBackend) Events(h SessionHandle) <-chan wire.Event

func (*LocalBackend) Resolve

Resolve delegates to the shared matcher (selector + capability check). Slice 4.3 extracted the policy logic into matcher.go so KubernetesBackend applies the same rules. Pre-4.3 the logic lived inline here.

func (*LocalBackend) Stop

func (b *LocalBackend) Stop(h SessionHandle) error

func (*LocalBackend) Submit

type LocalConfig

type LocalConfig struct {
	RuntimeCommand []string
}

LocalConfig configures the local subprocess backend. RuntimeCommand is the argv used to invoke the agent-runtime — typically ["node", "dist/index.js"] but configurable so tests can inject a shell-script stand-in.

type SessionHandle

type SessionHandle string

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL