AI Agents

  • Fixed

    Full Changelog: https://github.com/giantswarm/klaus-operator/compare/v0.0.154...v0.0.155

  • Fixed

    Full Changelog: https://github.com/giantswarm/klaus/compare/v0.1.1...v0.1.2

  • Fixed

    Full Changelog: https://github.com/giantswarm/klausctl/compare/v0.4.1...v0.4.2

  • Fixed

    • gateway.parameters.dataPlaneResources is settable through the meta chart (#303). The connectivity chart reads the key (the agentgateway data-plane container’s ephemeral-storage requests and limits, merged onto the container through AgentgatewayParameters) and its schema declares it, but the meta chart’s values.yaml did not, so the meta chart’s generated values.schema.json (gateway.parameters is additionalProperties: false) refused it at render time — additional properties 'dataPlaneResources' not allowed — although forwardAllValues would have carried it to the connectivity release unchanged; an installation migrating from the standalone chart, whose umbrella accepted the key, hit it. The meta chart now declares the key with the connectivity chart’s defaults (requests.ephemeral-storage: 50Mi, limits.ephemeral-storage: 512Mi), so the rendered platform objects are unchanged until an installation overrides them. make verify-meta’s schema-symmetry check compared the two charts’ top-level keys only and missed the nested key; it is now tests/verify-schema-symmetry.py, which walks the connectivity schema’s properties (and array items) under every block the meta chart forwards and fails on any nested key the meta schema rejects — and the other way round, on any forwarded meta key the connectivity schema does not declare (the roster travels as components.<name>.enabled only) — with negative cases proving both directions fail, naming the path. The walk found no other gap.
  • Added

    • The ATS proves the product on every PR (roadmap#4348, PRD Q8; tests/ats/, README “Development”). The kind smoke is the quick start on a bare cluster: the Gateway API CRDs, a lab Dex (tests/ats/lab-dex.yaml, ported from the standalone chart — static users, the platform identity Secret, a self-signed CA, a CoreDNS rewrite; Dex terminates TLS itself, there is no edge Gateway on kind) and an in-cluster registry the candidate archive is pushed to (tests/ats/registry.yaml, helm push --plain-http); helm install --wait with the bundled engine, muster and its OAuth server, dicebear, connectivity, kagent and agent-manager (tests/test-values.yaml + tests/ats/values-kagent.yaml + tests/ats/values-round-trips.yaml) and self-management on against that registry at the candidate’s exact version (a branch build is a prerelease, which the derived range never matches — Masterminds semver). It asserts the install’s promises (deployed, FluxInstance Ready, every component HelmRelease Ready as agent-platform-flux, the operator on the Flux CRDs, the kagent namespace), the adoption (helm history = the install + one adoption revision), the auth round trip (401 with the RFC 9728 chain; a Dex user through the OAuth password grant with the cross-client audience, and through muster’s login flow — RFC 7591 registration, code + PKCE, the Dex form), the agent round trips (a declarative Agent Ready against the default ModelConfig with a placeholder key; agent-manager’s create_agent through muster as the Dex user with the forwarded token → OCIRepository + HelmRelease of the agent chart in kagent, executed as kagent-flux, Ready → the Agent Ready — the Flux write path the standalone smoke never had), the fixpoint two intervals after the adoption (history unchanged, the values Secret and helm get values equal to the values used), the refused helm upgrade (the admission policy’s message in Helm’s output, no revision) and the ordered teardown (helm uninstall --wait within budget, no Flux CRD left, the operator CRDs remaining, no controller, no Job of the release, no release, the policy gone; the agents’ HelmRelease objects gone with the CRDs, their Deployments and Agent objects orphaned in the kept kagent namespace). The own-Flux scenario (tests/ats/test_own_flux.py, the functional step) runs on the same cluster afterwards: Flux’s source-controller + helm-controller from the pinned upstream manifest, the chart through a HelmRelease from the in-cluster registry with components.flux.enabled: false — Ready, the platform HelmReleases Ready under that Flux without a serviceAccountName, no operator, no FluxInstance, one helm-controller, the Flux CRDs’ field managers exactly what flux install left — an agent through that Flux (OCIRepository + HelmRelease of the agent chart as kagent-flux, the Agent Ready), the render guard when the value is flipped on (the HelmRelease fails with this cluster runs Flux; set components.flux.enabled=false or install the chart through it; no operator, no FluxInstance, the platform and the agent still Ready, the Flux CRDs’ content unchanged — helm-controller applies a chart’s crds/ before it renders, so it joins the CRDs’ field managers with identical content, asserted and logged; flipped back it recovers) and the way back. .ats/main.yaml runs smoke and functional; upgrade stays skipped. The standalone chart’s tests/ats/test_smoke.py has no assertion this suite lacks (tests/ats/README.md, the parity table).
    • components.<name>.insecure (default absent): OCIRepository.spec.insecure: true for that component — a plain-HTTP registry, a lab’s in-cluster one; never a public registry. The ATS uses it to install the connectivity chart of the checkout from the in-cluster registry (components.agent-platform-connectivity.{repository,versionRange,insecure}), so a PR’s connectivity changes are tested on kind before they are released. make verify-insecure.

    Fixed

    • The connectivity release’s Namespace for kagent (kagent.namespaceOverride) carries helm.sh/resource-policy: keep. Helm deleted it with the release — on helm uninstall of the platform, taking the agents’ workloads with it although the platform’s teardown is documented to leave them behind orphaned, and, worse, as the install remediation of a failed first install of the connectivity release: helm-controller uninstalls a failed first install before it retries, and that uninstall removed the kagent namespace and every kagent object in it while the kagent release believed itself installed (measured on the ATS kind smoke, where a PolicyException without its policy-exceptions namespace failed the connectivity install). Kept, the namespace survives both; the release adopts it on a reinstall as it adopts a namespace the cluster created. Management clusters see one annotation added to the kagent Namespace where kagent is on, nothing else.
    • The helm.sh/chart label of every object stays a valid label value at the versions the charts are actually installed under. helm-controller installs a chart with the OCI digest appended as build metadata (3.20.0+8c89e1be4cbf; the label helper turns + into _), and a branch build carries a long prerelease version (3.19.1-dev.<branch>.<date>.<time>.h<sha>, stamped by abs); the 63-character cut of <name>-<version> could land on that _, which the apiserver rejects (“must start and end with an alphanumeric character”) for every object of the release — measured on the self-management adoption of a branch build, whose failed revision then also poisoned helm uninstall (the hooks of the last revision carried the label). The chart helpers of both charts and of the flux-engine subchart trim _ after the cut as they trim - and .; make verify-labels (tests/verify-labels.py) packages both charts at versions whose cut lands on each separator and validates every label of the render. Published versions are short and were never affected; nothing changes in their render.
  • Fixed

    • The kagent controller metrics Service the connectivity chart renders (<release>-kagent-controller-metrics, the target of its ServiceMonitor) selected the controller pods with the connectivity release’s own app.kubernetes.io/instance label. Under the retired standalone umbrella every subchart shared one release name, so that matched; under the meta chart each component is its own release — the kagent pods carry app.kubernetes.io/instance: kagent, the component key the meta chart fixes as the release name — and the Service had no endpoints: the controller was never scraped on any installation with kagent and the monitors on (#305). The selector now names kagent’s release through the new connectivity helper agent-platform.kagent.releaseName; the fallbacks of the controller route’s backend host, the UI route’s Service and the oauth2-proxy Service (kagent.fullnameOverride / kagent.oauth2-proxy.fullnameOverride, pinned in values.yaml, else .Release.Name) go through agent-platform.kagent.fullname / the release-name helper too — the same wrong assumption, latent behind the pins. No compatibility knob: the meta chart is the chart’s only consumer. No installation needs to act: the Service’s selector changes (a Service update, no pod rolls) and kagent controller scraping starts working; every other object is byte-identical. make verify-global asserts the selector and that no kagent template falls back to .Release.Name.
    • A first helm install with components.kagent.enabled on a cluster without the kagent namespace deadlocked: the kagent chart installs into kagent.namespaceOverride while its HelmRelease targets the platform namespace (so install.createNamespace never creates kagent), and the one chart object that does create it — the connectivity chart’s Namespace — sits in a release that dependsOn kagent. Every kagent attempt failed with namespaces "kagent" not found until the retries were exhausted, everything behind kagent waited, helm install --wait timed out (#306). With the bundled engine the meta chart now runs one pre-install,pre-upgrade hook Job, <release>-kagent-namespace (weight -8, alpine/k8s, as the hook ServiceAccount, whose own hook events gain pre-install,pre-upgrade while this hook renders): it creates the namespace when it is missing, waits out one that is still terminating (the reinstall right after an uninstall), and leaves an existing one alone. The Namespace is deliberately not an object of the meta release — the connectivity release renders and tracks it, adopting the hook-created one exactly as it adopts the one Giant Swarm’s bases pre-create, and two Helm releases must never track one object. Not rendered with the engine off (the fleet’s pure render is byte-identical; a cluster’s own Flux gets the namespace out of band), with kagent off, with kagent.namespaceOverride empty or equal to the HelmReleases’ target namespace. Measured on kind (kagent on, no namespace): helm install --wait deployed, helm upgrade idempotent, helm uninstall --wait clean with the namespace gone through the connectivity release, reinstall clean. make verify-engine asserts the hook and its absences; the ATS kind smoke can turn kagent on from this version.
  • Fixed

    Full Changelog: https://github.com/giantswarm/muster/compare/v5.14.1...v5.14.2

  • Fixed

    Full Changelog: https://github.com/giantswarm/klaus-oci/compare/v0.0.72...v0.0.73

  • Fixed

    • The shared RemoteMCPServer agent-platform/muster reported Accepted=False (ReconcileFailed … Unauthorized) on every installation: the kagent controller lists a server’s tools with its own request, which carries no bearer, and muster (an OAuth resource server) answers 401 (#299). While muster runs with OAuth on (muster.muster.oauth.server.enabled, the default) the connectivity chart now labels the CR kagent.dev/discovery: disabled, the opt-out from controller-side tool discovery contributed upstream (kagent-dev/kagent#2752, 0.10.x backport kagent-dev/kagent#2753): a controller that knows the label reports Accepted=True (DiscoveryDisabled, empty inventory); an older one ignores the label and behaves as before. Agents are unaffected — they resolve tools at run time as the caller. A controller credential through spec.headersFrom was rejected on purpose: the kagent runtime applies static headers after the propagated caller token, so every agent would have acted as the controller’s ServiceAccount (docs/authentication.md, “Tool discovery by the kagent controller”). make verify-kagent-discovery asserts the label follows the OAuth toggle and stays off the operator-defined kagent.remoteMcpServers.
  • Added

    • Self-management through the bundled Flux, on by default (roadmap#4348, PRD Q5/Q5b): where the chart brings the engine, the engine also holds the chart. gitops.self.enabled (auto | true | false, default auto = follows components.flux.enabled; true with the engine off fails the render) renders the release’s own OCIRepository + HelmRelease (templates/self/release.yaml, both named after the release, in the release namespace, run as agent-platform-flux — gitops.serviceAccountName overrides it — chartRef to the OCIRepository, releaseName/targetNamespace the release’s, install/upgrade.disableWait, values from Secret agent-platform-values key values.yaml with optional: false). The OCIRepository follows <gitops.self.repository>/agent-platform (default oci://gsoci.azurecr.io/charts/giantswarm; gitops.self.insecure for a plain-HTTP lab registry) at >=<installed version> <next major>.0.0, derived from the running chart at render time (gitops.self.versionRange overrides; an offline helm template derives it from the placeholder Chart.yaml version), on gitops.self.interval (default 10m). The bundled helm-controller adopts the CLI-installed release with one extra revision and rolls the chart forward inside its major without a helm upgrade. Measured on kind: install --wait 87 s, adoption in the second the release went deployed, a pushed version applied 47 s after the push with the new chart default in effect and the user’s values intact, a rewritten values Secret applied within 20 s, uninstall --wait with the self HelmRelease present in under a minute, the upgrade from an engine-on 3.18.0 into this version adopted with the bracket held.
    • The install-time bracket: the self HelmRelease is rendered suspend: true on helm install and whenever it does not exist yet (a guarded lookup — the upgrade of an engine-on installation into this version), never on a controller-driven upgrade; the post-install/post-upgrade hook <release>-self-values (weight 0) writes the user-supplied values of the revision in flight (helm get values, never the merged tree) into Secret agent-platform-values, labelled reconcile.fluxcd.io/watch=Enabled, by server-side apply, and — only while the HelmRelease is suspended — starts the detached Job <release>-self-resume, which polls helm status and resumes the HelmRelease once the release is deployed (or failed); still pending after 300 s it exits 1 without resuming. Neither is a chart object Helm’s kstatus --wait would wait on. helm template renders suspend: true.
    • The Helm CLI is day-0 only — a ValidatingAdmissionPolicy (templates/self/policy.yaml: <release>-self-managed-<namespace> + a binding of the same name scoped to the release namespace, failurePolicy: Fail) on the CREATE of Secrets of type helm.sh/release.v1 labelled name=<release>: admitted for system:serviceaccount:<namespace>:agent-platform-flux (the identity the bundled helm-controller impersonates) or while the namespace carries agent-platform.giantswarm.io/helm-cli=allow. helm upgrade and helm rollback fail in about a second with the reason in Helm’s own error output (the message names Secret agent-platform-values and the hand-back) and write no revision; UPDATE/DELETE pass, so does helm uninstall. Needs Kubernetes ≥ 1.30: the render refuses an older cluster while self-management is on. A render guard also refuses the annotation together with self-management on (two writers on one release). The guard-Job alternative (a pre-upgrade hook reading the pending storage Secret’s managedFields manager) stays documented in UPGRADE.md as the fallback should the policy ever be ruled out.
    • The hand-back / escape hatch: kubectl annotate namespace <ns> agent-platform.giantswarm.io/helm-cli=allow, then helm upgrade … --set gitops.self.enabled=false --force-conflicts (once; helm-controller co-owns the fields) hands the release back to the CLI without an uninstall — the new pre-upgrade hooks suspend the self HelmRelease before Helm deletes it, so helm-controller drops its finalizer; the value stays in the values file (README “Self-management”).
    • Teardown extended (templates/hooks/self.yaml, rendered whenever the engine is on): weight -6 <release>-self-stop-resumer (one plain kubectl delete of the resumer Job, --ignore-not-found) and weight -5 <release>-self-suspend (suspends the self HelmRelease if present, removes the hook-written values Secret by its labels) at pre-delete — and at pre-upgrade too while self-management is off (the hand-back and lab renders). They run as the new namespaced identity <release>-self (ServiceAccount + Role + RoleBinding, templates/self/rbac.yaml, regular objects: the detached resumer runs after a hook-managed account is gone), as do the values hook and the resumer. gitops.hooks.helmImage (docker.io/alpine/k8s:1.36.4 — kubectl, helm, jq, a shell; Renovate-tracked like gitops.hooks.image) runs the hooks that need helm or have to tolerate a missing object; templates/hooks/_job.tpl takes script, image and serviceAccountName.
    • Lab shape: gitops.self.enabled: false renders none of it (no self objects, no values hook, no policy) but the -6/-5 hooks and their identity; tests/test-values.yaml sets it — the ATS installs an unreleased chart, which a self HelmRelease would replace with the published one — and tests/ats/test_smoke.py asserts the lab shape on the cluster. make verify-self (tests/verify-self.py, in CI with Helm 3.17.3) asserts the three shapes, the guards and the knobs; make verify-engine renders its engine-on shape with self-management off. No installation needs to act: the fleet (engine off) renders byte-identically; an engine-on CLI installation becomes self-managed on its next helm upgrade — see UPGRADE.md.