From bbb8f7787dd82cabf745222bb468b1ddf272d57f Mon Sep 17 00:00:00 2001 From: Rachael Graham Date: Tue, 13 Oct 2026 17:33:32 -0500 Subject: [PATCH 1/5] Docs for alpha13 Signed-off-by: Rachael Graham --- .../assets/kagent-docs/versions/kagent.md | 2 +- .../content/kagent/1.x/about/core-concepts.md | 2 +- .../1.x/about/substrate-runtime/identity.md | 6 +- .../substrate-runtime/standalone-sandboxes.md | 29 +++--- .../kagent/1.x/agents/agent-harness.md | 46 +++++++--- .../content/kagent/1.x/agents/agent-memory.md | 4 +- .../kagent/1.x/agents/bring-your-own-agent.md | 91 +++++++++++++------ .../content/kagent/1.x/examples/a2a-agents.md | 8 +- .../content/kagent/1.x/examples/a2a-byo.md | 21 ++++- .../kagent/1.x/examples/agents-via-mcp.md | 2 +- .../content/kagent/1.x/examples/skills.md | 2 +- .../kagent/1.x/observability/audit-prompts.md | 20 ++-- .../observability/lightweight-otel-stack.md | 25 +++-- .../kagent/1.x/observability/metrics.md | 6 +- .../kagent/1.x/observability/otel-stack.md | 32 ++++--- .../kagent/1.x/observability/tracing.md | 46 +++++----- .../operations/operational-considerations.md | 81 ++++++++++++++--- .../1.x/operations/tune-agent-substrate.md | 2 +- .../kagent/1.x/operations/upgrade-from-0x.md | 6 +- .../content/kagent/1.x/setup/installation.md | 25 ++++- .../1.x/setup/model-providers/azure-openai.md | 17 ++++ 21 files changed, 327 insertions(+), 146 deletions(-) diff --git a/docs-site/assets/kagent-docs/versions/kagent.md b/docs-site/assets/kagent-docs/versions/kagent.md index 3399c9ac..5e47b10a 100644 --- a/docs-site/assets/kagent-docs/versions/kagent.md +++ b/docs-site/assets/kagent-docs/versions/kagent.md @@ -1 +1 @@ -{{< version include-if="0.x" >}}0.10.2{{< /version >}}{{< version include-if="1.x" >}}1.0.0-alpha12{{< /version >}} \ No newline at end of file +{{< version include-if="0.x" >}}0.10.2{{< /version >}}{{< version include-if="1.x" >}}1.0.0-alpha13{{< /version >}} \ No newline at end of file diff --git a/docs-site/content/kagent/1.x/about/core-concepts.md b/docs-site/content/kagent/1.x/about/core-concepts.md index db49a76b..79a22833 100644 --- a/docs-site/content/kagent/1.x/about/core-concepts.md +++ b/docs-site/content/kagent/1.x/about/core-concepts.md @@ -134,7 +134,7 @@ kagent agent session create --agent assistant -n kagent After it is created, a Session talks to callers over the {{< gloss "A2A" >}}A2A{{< /gloss >}} (Agent-to-Agent) protocol, through kagent's A2A gateway. Callers address the Agent rather than the Session. The HTTP endpoint is `/agents/{namespace}/{name}` and gRPC carries the same `namespace/name` in the standard A2A `tenant` field. The Session's ID is the A2A `contextId`, so a message that carries no context identifier starts a new conversation, and a message that repeats one continues that conversation. -An expiration worker deletes a Session that records no task activity for seven days. The `controller.sessionIdleTTL` Helm value sets that window. A value of `0` turns the expiration worker off. For more information about what the deletion retains, see [Expire idle Sessions]({{< link path="operations/operational-considerations#expire-idle-sessions" >}}). +An expiration worker deletes a Session that records no task activity for seven days. The `config.sessions.idleTtl` Helm value sets that window. A value of `0s` turns the expiration worker off. For more information about what the deletion retains, see [Expire idle Sessions]({{< link path="operations/operational-considerations#expire-idle-sessions" >}}). ## Actor diff --git a/docs-site/content/kagent/1.x/about/substrate-runtime/identity.md b/docs-site/content/kagent/1.x/about/substrate-runtime/identity.md index 4cc382e1..ead860c3 100644 --- a/docs-site/content/kagent/1.x/about/substrate-runtime/identity.md +++ b/docs-site/content/kagent/1.x/about/substrate-runtime/identity.md @@ -29,12 +29,12 @@ Every call on the Session API carries a principal. An authenticator resolves one ### Controller authentication modes -The `controller.auth.mode` Helm value selects which authenticator the controller installs. The chart defaults to `insecure`, and enabling the bundled oauth2-proxy does not change it, because browser sign-in and controller authentication are separate boundaries. To authenticate both, enable oauth2-proxy and set `controller.auth.mode` to `trusted-proxy`. +The `config.server.auth.mode` Helm value selects which authenticator the controller installs. The chart defaults to `insecure`, and enabling the bundled oauth2-proxy does not change it, because browser sign-in and controller authentication are separate boundaries. To authenticate both, enable oauth2-proxy and set `config.server.auth.mode` to `trusted-proxy`. | Mode | How a principal is resolved | | ---- | --------------------------- | | `insecure` | Every request is admitted. The caller's identity is read from the `X-User-Id` header, or from a `user_id` query parameter that takes precedence over it. A caller that supplies neither is named `admin@kagent.dev`. | -| `trusted-proxy` | The request must carry a bearer token in the `Authorization` header. The identity comes from the claim that `controller.auth.userIdClaim` names, defaulting to `sub`. A missing custom claim falls back to `sub`, and a request with no identity is rejected. | +| `trusted-proxy` | The request must carry a bearer token in the `Authorization` header. The identity comes from the claim that `config.server.auth.userIdClaim` names, defaulting to `sub`. A missing custom claim falls back to `sub`, and a request with no identity is rejected. | Any other value fails controller startup rather than falling back to a default. @@ -73,7 +73,7 @@ A share carries one of two permissions. A client that creates a share can set `CreateSessionShareRequest.ttl` to a positive lifetime, which starts when the share is created. Once that lifetime ends, the token grants no access and a token lookup returns not found. The owner still sees the expired share in `ListSessionShares` and can revoke it. -The returned `SessionShare.expiresAt` field records when the token stops granting access. Omit `ttl` and `expiresAt` stays unset. The share then stays valid until it is revoked or the Session is deleted. An operator caps share lifetimes with `controller.sessionShareMaxTTL`. For more information, see [Expire idle Sessions]({{< link path="operations/operational-considerations#expire-idle-sessions" >}}). +The returned `SessionShare.expiresAt` field records when the token stops granting access. Omit `ttl` and `expiresAt` stays unset. The share then stays valid until it is revoked or the Session is deleted. An operator caps share lifetimes with `config.sessions.shareMaxTtl`. For more information, see [Expire idle Sessions]({{< link path="operations/operational-considerations#expire-idle-sessions" >}}). A share widens what the holder can reach to what the owner can see, and the underlying record is read as the owner rather than as the visitor. Revoking the share withdraws that access. diff --git a/docs-site/content/kagent/1.x/about/substrate-runtime/standalone-sandboxes.md b/docs-site/content/kagent/1.x/about/substrate-runtime/standalone-sandboxes.md index d562000b..2afa0df2 100644 --- a/docs-site/content/kagent/1.x/about/substrate-runtime/standalone-sandboxes.md +++ b/docs-site/content/kagent/1.x/about/substrate-runtime/standalone-sandboxes.md @@ -20,27 +20,26 @@ An agent can create a Sandbox of its own through the kagent {{< gloss "MCP" >}}M ## Before you begin -1. [Install kagent]({{< link path="setup/installation" >}}) and set the following `controller.sandbox` values on the installation. Sandbox preparation needs a guest image digest, and the chart ships no default, so a SandboxTemplate never becomes ready until you set one. The remaining values allocate the compute that each Sandbox runs on and bound how long it lives. +1. [Install kagent]({{< link path="setup/installation" >}}) and set the following `config.sandboxes` values on the installation. Sandbox preparation needs a guest image, and the chart ships no default, so a SandboxTemplate never becomes ready until you set one. The remaining values allocate the compute that each Sandbox runs on and bound how long it lives. ```yaml - controller: - sandbox: - guestImage: - digest: sha256:1821780ef01958f63cb9a1a1d9a175f7e386e7c72859dd29ab86d8644a47d2d4 - cpu: 1 + config: + sandboxes: + guestImage: {{< reuse "kagent-docs/versions/sandbox-guest-image.md" >}} + cpu: "1" memory: 1Gi - defaultTTL: 1h - maxTTL: 24h + defaultTtl: 3600s + maxTtl: 86400s ``` A SandboxTemplate cannot override these values, so they are the installation's policy rather than a per-template choice. | Value | Default | Description | | ----- | ------- | ----------- | - | `controller.sandbox.guestImage.digest` | `""` | The digest-pinned guest image. Required: preparation fails without it. The controller passes the digest to Agent Substrate unchanged and resolves no tags, so supply a digest rather than a tag. | - | `controller.sandbox.cpu` | `1` | CPU allocated to each Sandbox. | - | `controller.sandbox.memory` | `1Gi` | Memory allocated to each Sandbox. | - | `controller.sandbox.defaultTTL` | `1h` | Lifetime applied when `kagent sandbox create` omits `--ttl`. | - | `controller.sandbox.maxTTL` | `24h` | Upper bound on any requested lifetime. Note that activity does not extend a Sandbox's lifetime. A Sandbox expires the configured interval after it is created, however recently a command ran in it. | + | `config.sandboxes.guestImage` | `""` | The digest-pinned guest image, as a complete reference. Required: preparation fails without it. The controller passes the reference to Agent Substrate unchanged and resolves no tags, so pin a digest rather than a tag. Setting `global.imageRegistry` rewrites the registry host of this reference. | + | `config.sandboxes.cpu` | `1` | CPU allocated to each Sandbox. | + | `config.sandboxes.memory` | `1Gi` | Memory allocated to each Sandbox. | + | `config.sandboxes.defaultTtl` | `3600s` | Lifetime applied when `kagent sandbox create` omits `--ttl`. Durations in the `config` block take protobuf seconds notation, so `3600s` rather than `1h`. | + | `config.sandboxes.maxTtl` | `86400s` | Upper bound on any requested lifetime, which the controller caps at `86400s`. Note that activity does not extend a Sandbox's lifetime. A Sandbox expires the configured interval after it is created, however recently a command ran in it. | 2. Download the kagent CLI. The `--version` flag matches the CLI to the release that these docs cover. ```bash @@ -85,7 +84,7 @@ A SandboxTemplate is a namespaced `api.kagent.dev/v1alpha3` resource that prepar | `substrate.workerPoolRef.name` | Yes | The WorkerPool that this template's Actors are scheduled onto. | | `substrate.snapshotPolicy.location` | Yes | The object storage location for Actor {{< gloss "Snapshot" >}}snapshots{{< /gloss >}}. | - A SandboxTemplate takes no startup command and no guest toggle. kagent supplies the guest entrypoint from the image that `controller.sandbox.guestImage.digest` names. That image replaces the tools image's own entrypoint. Your tools image contributes the installed programs and nothing else. + A SandboxTemplate takes no startup command and no guest toggle. kagent supplies the guest entrypoint from the image that `config.sandboxes.guestImage` names. That image replaces the tools image's own entrypoint. Your tools image contributes the installed programs and nothing else. 2. Confirm that the template prepared a revision before you create a Sandbox from it. ```bash @@ -163,7 +162,7 @@ tools: - list_sandboxes ``` -An agent that creates a Sandbox owns it under whatever identity the MCP connection authenticated, not under the identity of the person it is talking to. A Session share token grants no access to a Sandbox. To have an agent act for the person who invoked it, configure credential propagation. The Go kagent runtime reads `KAGENT_PROPAGATE_TOKEN=true` to pass the caller's credentials and identity to the MCP servers that you trust. +An agent that creates a Sandbox owns it under whatever identity the MCP connection authenticated, not under the identity of the person it is talking to. A Session share token grants no access to a Sandbox. To have an agent act for the person who invoked it, configure credential propagation. Set `config.runtimeDefaults.tokenPropagation.enabled` to `true`, and the kagent runtime passes the caller's credentials and identity to the MCP servers that you trust. The MCP transfer limits are tighter than the command line's. A gRPC file transfer is bounded at 64 MiB. An MCP transfer or output read is bounded at 1 MiB, encodes bytes as base64, and returns a continuation offset for reading more. diff --git a/docs-site/content/kagent/1.x/agents/agent-harness.md b/docs-site/content/kagent/1.x/agents/agent-harness.md index 49070c4a..a3f47a49 100644 --- a/docs-site/content/kagent/1.x/agents/agent-harness.md +++ b/docs-site/content/kagent/1.x/agents/agent-harness.md @@ -24,8 +24,8 @@ spec: workload: image: {{< reuse "kagent-docs/versions/runtime-image.md" >}} env: - - name: KAGENT_LOG_LEVEL - value: info + - name: OTEL_PROPAGATORS + value: tracecontext,baggage substrate: workerPoolRef: name: kagent-default @@ -42,7 +42,7 @@ EOF | `workload.image` | Yes | The runtime image, pinned by `sha256` digest. A tag alone is rejected, because a revision must be reproducible. | | `workload.command` | For `byo` | Overrides the image entrypoint, up to 32 entries. Required for the `byo` runtime, optional otherwise. Every runtime honors an explicit value, the `kagent` runtime included, whatever language its image is written in. | | `workload.args` | No | Overrides the image arguments, up to 64 entries. An override that you omit stays unset rather than taking a default. | -| `env` | No | Environment variables for the runtime, up to 100. Each entry sets a literal `value`, which is required and can be an empty string. The schema defines no secret-backed source, so the API server rejects a `credentialRef` entry as an unknown field. Put credentials on a ModelConfig or a RemoteMCPServer instead. For more information, see [About model providers]({{< link path="setup/model-providers/about-model-providers#credentials-that-do-not-compile" >}}). | +| `env` | No | Environment variables for the runtime, up to 100. Each entry sets a literal `value`, which is required and can be an empty string. An installation's own settings, such as the runtime log level, come from the chart's `config.runtimeDefaults` block rather than from here. The schema defines no secret-backed source, so the API server rejects a `credentialRef` entry as an unknown field. Put credentials on a ModelConfig or a RemoteMCPServer instead. For more information, see [About model providers]({{< link path="setup/model-providers/about-model-providers#credentials-that-do-not-compile" >}}). | | `substrate.workerPoolRef.name` | Yes | The {{< gloss "WorkerPool" >}}WorkerPool{{< /gloss >}} that this Harness's Actors are scheduled onto. An operator must provision one before any agent can run. | | `substrate.snapshotPolicy.location` | Yes | The object storage location for Actor {{< gloss "Snapshot" >}}snapshots{{< /gloss >}}. | @@ -257,18 +257,21 @@ The `kagent` and `byo` runtimes take the full set. For more information about wh Tracing carries the prompts and replies that an agent exchanges with a model, and so does log export on the `claude` runtime. Two settings in the kagent Helm chart decide whether that content leaves the runtime, and each one reaches a different set of runtimes. Both default to `false`, and both take effect only where tracing or log export is already enabled. ```yaml -otel: - capture: - messageContent: false - rawApiBodies: false +config: + runtimeDefaults: + telemetry: + captureContent: false + captureRawApiBodies: false + maxCaptureBytes: 16384 ``` | Setting | What it includes | Applies to | | ------- | ---------------- | ---------- | -| `otel.capture.messageContent` | Prompts and assistant replies in the runtime's telemetry. On the `kagent` runtime, the content appears in the span for each model call. That runtime records a tool call's arguments and reply on the span for the tool call instead, whatever this setting holds. On the `claude` runtime, tool results require tracing, and assistant replies require log export through `otel.logs`. | `kagent`, `codex`, `claude` | -| `otel.capture.rawApiBodies` | The complete provider API request and response bodies. This setting returns more than `otel.capture.messageContent` does, and it takes effect only when `otel.logs.enabled` is `true`. | `claude` | +| `config.runtimeDefaults.telemetry.captureContent` | Prompts and assistant replies in the runtime's telemetry. On the `kagent` runtime, the content appears in the span for each model call. That runtime records a tool call's arguments and reply on the span for the tool call instead, whatever this setting holds. On the `claude` runtime, tool results require tracing, and assistant replies require log export through `config.runtimeDefaults.telemetry.logs`. | `kagent`, `codex`, `claude` | +| `config.runtimeDefaults.telemetry.captureRawApiBodies` | The complete provider API request and response bodies. This setting returns more than `captureContent` does, and it takes effect only when you set the logs endpoint. | `claude` | +| `config.runtimeDefaults.telemetry.maxCaptureBytes` | The budget for each captured prompt or reply, in bytes. Defaults to `16384`, with a maximum of `65536`. | `codex`, `claude` | -The controller sets `OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT` in every compiled runtime from `otel.capture.messageContent`, so a Harness cannot change the capture decision for itself. Naming that variable in the Harness `spec.env` field fails the `claude` and `codex` runtimes outright, and is discarded on the `kagent` runtime. For the rest of the variables that behave this way, see [Controller-owned telemetry variables](#controller-owned-telemetry-variables). +The controller sets `OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT` in every compiled runtime from `config.runtimeDefaults.telemetry.captureContent`, so a Harness cannot change the capture decision for itself. Naming that variable in the Harness `spec.env` field fails the `claude` and `codex` runtimes outright, and is discarded on the `kagent` runtime. For the rest of the variables that behave this way, see [Controller-owned telemetry variables](#controller-owned-telemetry-variables). The controller compiles the telemetry environment into the `byo` runtime as well whenever any signal is enabled, so `OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT` reaches a `byo` image. The `byo` runtime is the exception to the ownership rule. The controller applies the Harness `spec.env` field after the telemetry environment, so a `spec.env` entry wins where the two name the same variable, and an image that exports to its own backend keeps doing so. Whether the image acts on either setting depends on its own instrumentation. For more information, see [Tracing]({{< link path="observability/tracing#about-trace-coverage" >}}). @@ -299,7 +302,28 @@ Every other `OTEL_*` variable, such as the `OTEL_BSP_*` batch span processor set ### Baggage propagation -kagent runtimes set `OTEL_PROPAGATORS` to `tracecontext` when the environment leaves it unset, so a caller's baggage reaches neither the tools an agent calls nor its model provider. Trace context still propagates. To carry baggage as well, set `OTEL_PROPAGATORS` to `tracecontext,baggage` in the Harness `spec.env` field. +kagent runtimes set `OTEL_PROPAGATORS` to `tracecontext` when the environment leaves it unset, so a caller's baggage reaches neither the tools an agent calls nor its model provider. Trace context still propagates. To carry baggage as well, set `OTEL_PROPAGATORS` to `tracecontext,baggage` in the Harness `spec.env` field. The controller reads its own value from the `controller.env` Helm value, and each process reads only its own, so enabling baggage on a runtime does not enable it on the controller. + +`OTEL_PROPAGATORS` governs both directions. A format that the list omits is neither read from an incoming request nor written to an outgoing one, including the members that the runtime adds itself. + +With `baggage` listed, the `kagent` runtime adds the request's identity to the baggage of each model call and each MCP tool call. + +| Member | Model calls | MCP tool calls | +| ------ | ----------- | -------------- | +| `gen_ai.agent.name` | Yes | Yes | +| `gen_ai.conversation.id` | Yes | Yes | +| `a2a.task.id` | Yes | Yes | +| `gen_ai.tool.name` | No | Yes | +| `gen_ai.tool.call.id` | No | Yes | + +These members are identifiers rather than content or credentials, and the runtime replaces a caller's member of the same name, so a caller cannot claim another conversation's identity downstream. + +> [!WARNING] +> **Enabling baggage sends the caller's own members to your model provider.** A caller baggage member that kagent does not replace is forwarded unchanged in the `baggage` header of each model API request and each MCP request, so a caller controls what reaches those external services. Enable baggage only where you trust what your callers attach. + +Two limits are worth planning around. The `claude` and `codex` runtimes receive `OTEL_PROPAGATORS`, but kagent adds no identity to their model and tool calls, and neither does the Python runtime, so those five members reach the `kagent` runtime alone. kagent also never copies a baggage member onto a span, so a backend that needs these values to be searchable must add a baggage-to-attributes span processor, in the service that receives the request or in its OpenTelemetry Collector. + +An MCP session is opened by one request and shared by every later one. The session makes three requests of its own: the event stream, the replies to server requests, and the close. None of the three carries trace context or baggage, because either would name a request that has already ended. ## Check that a Harness is ready diff --git a/docs-site/content/kagent/1.x/agents/agent-memory.md b/docs-site/content/kagent/1.x/agents/agent-memory.md index 29677570..02774e30 100644 --- a/docs-site/content/kagent/1.x/agents/agent-memory.md +++ b/docs-site/content/kagent/1.x/agents/agent-memory.md @@ -43,6 +43,8 @@ Memories are scoped to the agent and to the user who created them, and carry the connectionStringSecretRef: name: my-postgres-url-secret key: connectionString + config: + database: # Use the login user directly. To assume a role instead, create the role in PostgreSQL and set its name here. role: "" # Runs the vector migration, which creates the pgvector extension and the memory tables @@ -196,7 +198,7 @@ The SQL query only reads the table. To list or clear memories, call the `MemoryS No CLI command wraps the service yet, so these examples call it with [grpcurl](https://github.com/fullstorydev/grpcurl), and both calls take the `agent_name` exactly as the memory table stores it. Be sure to copy the value out of the table rather than assembling it by hand, because an incorrect name fails silently. -1. Port-forward the controller's gRPC port, and confirm that your kagent installation sets `controller.grpc.reflection=true`. +1. Port-forward the controller's gRPC port, and confirm that your kagent installation sets `config.server.grpcReflection=true`. ```bash kubectl port-forward -n kagent svc/kagent-controller 8083:8083 ``` diff --git a/docs-site/content/kagent/1.x/agents/bring-your-own-agent.md b/docs-site/content/kagent/1.x/agents/bring-your-own-agent.md index cc743b87..385999bc 100644 --- a/docs-site/content/kagent/1.x/agents/bring-your-own-agent.md +++ b/docs-site/content/kagent/1.x/agents/bring-your-own-agent.md @@ -17,7 +17,7 @@ kagent owns the lifecycle, the isolation, and the routing: - Wraps the {{< gloss "Actor" >}}Actor{{< /gloss >}} that the image runs in with a [gVisor sandbox]({{< link path="about/substrate-runtime/sandboxing#sandbox-classes" >}}). - [Suspends and resumes]({{< link path="about/substrate-runtime/suspend-and-resume#suspension-between-turns" >}}) the Actor between turns, including its in-memory state. - Routes every conversation through the A2A gateway, so callers address the Agent rather than the Actor behind it. -- Delivers the compiled agent configuration and agent card to the container as environment variables. +- Delivers the compiled agent configuration and agent card to the container, which the kagent helpers write to `/config/config.json` and `/config/agent-card.json` at startup. Your image owns the agent's behavior and the two endpoints that expose it: @@ -39,28 +39,52 @@ A `byo` image must meet four requirements to run as an agent. kagent enforces on | The A2A service speaks gRPC, not JSON-RPC | The agent card fixes the protocol binding to gRPC. An image that serves A2A over JSON-RPC alone is never reached, whatever port it listens on. | > [!WARNING] -> **A `byo` Harness injects no `KAGENT_PORT` variable, and an image that listens elsewhere still reports `READY`.** Readiness is probed on port 8081, which succeeds no matter what the A2A service does, so nothing surfaces the mismatch until an invoke fails with `Connect: tunnel failed`. Only the `kagent` compiler sets `KAGENT_PORT=80`. Either pin port 80 in the image, or set `KAGENT_PORT` in the Harness's `spec.env` as the examples on this page do. This gap is tracked as [kagent#2758](https://github.com/kagent-dev/kagent/issues/2758). +> **An image that listens on another port still reports `READY`.** Readiness is probed on port 8081, which succeeds no matter what the A2A service does, so nothing surfaces the mismatch until an invoke fails with `Connect: tunnel failed`. The compiled configuration sets port `80` in its `runtime.port` field, so an image that reads the configuration binds the right port. An image that ignores the configuration must pin port 80 itself. This gap is tracked as [kagent#2758](https://github.com/kagent-dev/kagent/issues/2758). ## Opaque and configured agents -kagent compiles an AgentTemplate for a `byo` Harness in the same way as for the `kagent` runtime, then hands the result to the container as environment variables. The two ways of using this runtime differ only in whether the image reads them. +kagent compiles an AgentTemplate for a `byo` Harness in the same way as for the `kagent` runtime, then hands the result to the container. The two ways of using this runtime differ only in whether the image reads it. - **An opaque agent ignores the compiled configuration.** The AgentTemplate exists to give the agent an identity and a description, and the image decides everything else, including which model to call and which tools to offer. Every field on an opaque AgentTemplate is optional, `modelConfig` included. - **A configured agent reads the compiled configuration.** The image honors the AgentTemplate's system prompt, ModelConfig, tool bindings, skills, and plugins, so an operator changes the agent's behavior by editing the AgentTemplate rather than by rebuilding the image. The `kagent` and `byo` runtimes compile through the same path, so a configured BYO agent accepts the same model providers and the same AgentTemplate features as the built-in runtime. For the provider matrix, see [Model provider support]({{< link path="agents/agent-harness#model-provider-support" >}}). For what an AgentTemplate can bind, see [Tool and skill support]({{< link path="agents/agent-harness#tool-and-skill-support" >}}). -Both kinds of agent receive the same variables. +Both kinds of agent receive the same inputs. Agent Substrate delivers the two JSON payloads as private environment variables, and the kagent helpers write them to files before the agent starts. An image in another language either reads the files that `MaterializeFromEnv` writes or parses the variables itself. + +| Input | Contents | +| ----- | -------- | +| `/config/config.json` | The compiled agent configuration: the resolved system prompt, the model and its settings, every tool, skill, and plugin that the AgentTemplate binds, and a `runtime` object that sets the process settings. | +| `/config/agent-card.json` | The agent card that kagent advertises for this agent, rendered as JSON. | +| Anything in `spec.env` | Literal values and `credentialRef` lookups from the Harness, resolved before the Actor starts. Agent Substrate accepts at most 32 environment variables on an Actor, and the compiled configuration counts toward that limit. A revision that exceeds it fails to compile rather than starting and misbehaving. | + +The `runtime` object is the root of the configuration document, and subagents inherit it. It includes the settings that a process needs before it serves a request. + +```json +{ + "runtime": { + "control_plane_url": "http://kagent-controller.kagent.svc:8083", + "gateway_url": "http://kagent-controller.kagent.svc:8083", + "name": "my-byo-agent", + "namespace": "kagent", + "log_level": "info", + "port": "80", + "max_content_length": 0 + } +} +``` -| Variable | Contents | -| -------- | -------- | -| `KAGENT_CONFIG_JSON` | The compiled agent configuration: the resolved system prompt, the model and its settings, and every tool, skill, and plugin that the AgentTemplate binds. | -| `KAGENT_AGENT_CARD_JSON` | The agent card that kagent advertises for this agent, rendered as JSON. | -| `KAGENT_NAMESPACE` and `KAGENT_NAME` | The AgentTemplate's namespace and name, which kagent's own helpers use to scope sessions. | -| Anything in `spec.env` | Literal values and `credentialRef` lookups from the Harness, resolved before the Actor starts. | +| Field | Contents | +| ----- | -------- | +| `control_plane_url` | The kagent API that the agent reports task state to. The Go and Python helpers both require it. | +| `gateway_url` | The A2A gateway that the agent reaches other agents and MCP servers through. | +| `name` and `namespace` | The Agent's name and the AgentTemplate's namespace, which kagent's own helpers use to scope sessions. | +| `port` | The port to serve A2A on. The controller sets `80` for every runtime, and the agent card advertises that same port. | +| `log_level` | `debug`, `info`, `warn`, or `error`, from the `config.runtimeDefaults.logging.level` Helm value. | +| `max_content_length` | The largest A2A request body to accept, in bytes. Zero means unlimited, and an absent field keeps the helper's own default. | -> [!NOTE] -> Agent Substrate accepts at most 32 environment variables on an Actor, and the compiled configuration counts toward that limit. A revision that exceeds it fails to compile rather than starting and misbehaving. +> [!IMPORTANT] +> The process settings moved into the configuration document in {{< reuse "kagent-docs/versions/kagent.md" >}}. An image that reads `KAGENT_PORT`, `KAGENT_NAME`, `KAGENT_NAMESPACE`, `KAGENT_API_URL`, or `KAGENT_A2A_GRPC_ADDRESS` finds none of them set, and naming one in `spec.env` sets a variable that nothing reads. Read the `runtime` object instead. ## Configure a BYO Harness @@ -78,9 +102,6 @@ spec: workload: image: /my-agent@sha256: command: ["/my-agent"] - env: - - name: KAGENT_PORT - value: "80" substrate: workerPoolRef: name: kagent-default @@ -96,7 +117,7 @@ EOF | `byo` | Yes | Selects this runtime. The object is always empty, and naming a second runtime alongside it is rejected. | | `workload.image` | Yes | Your image, pinned by `sha256` digest. A tag alone is rejected, because a revision must be reproducible. | | `workload.command` | Yes | The entrypoint to run, up to 32 entries. Required for `byo` and optional for every other runtime. | -| `env` | No | Set `KAGENT_PORT` here unless the image pins port 80 itself. Each entry takes a literal `value`. | +| `env` | No | Provider credentials and any setting that your image reads. Each entry takes a literal `value` or a `credentialRef`. | An opaque agent's AgentTemplate carries only a description for the agent card. An Agent then pairs it with the Harness. @@ -124,7 +145,7 @@ spec: EOF ``` -To configure the agent from Kubernetes instead, add the fields that any AgentTemplate takes, and read `KAGENT_CONFIG_JSON` in the image. For what those fields mean, see [Your first agent]({{< link path="get-started/your-first-agent#create-a-harness-an-agenttemplate-and-an-agent" >}}). +To configure the agent from Kubernetes instead, add the fields that any AgentTemplate takes, and read `/config/config.json` in the image. For what those fields mean, see [Your first agent]({{< link path="get-started/your-first-agent#create-a-harness-an-agenttemplate-and-an-agent" >}}). ## Build the image @@ -135,18 +156,29 @@ The A2A contract is a gRPC service and a readiness endpoint, so any language wit {{< tabs >}} {{% tab name="Go" %}} -`github.com/kagent-dev/kagent/go/adk/pkg/app` serves the A2A gRPC service, the readiness endpoint on 8081, and the agent card, given any type that implements `a2asrv.AgentExecutor`. The helper is framework-agnostic, so the executor is the only part that you write. +`github.com/kagent-dev/kagent/go/adk/pkg/app` serves the A2A gRPC service, the readiness endpoint on 8081, and the agent card, given any type that implements `a2asrv.AgentExecutor`. The helper is framework-agnostic, so the executor is the only part that you write. `config.MaterializeFromEnv` writes the payloads that Agent Substrate delivers to `/config`, and `config.LoadAgentConfig` reads the document back. ```go +if err := config.MaterializeFromEnv("/config"); err != nil { + return err +} +compiled, err := config.LoadAgentConfig("/config/config.json") +if err != nil { + return err +} +runtime := compiled.ProcessConfig() + application, err := app.New(app.AppConfig{ AgentCard: a2atype.AgentCard{ Name: "my-agent", Version: "v1", Capabilities: a2atype.AgentCapabilities{Streaming: true}, }, - Port: "80", - AppName: "my-agent", - Logger: logger, + ControlPlaneURL: runtime.ControlPlaneURL, + MaxContentLength: runtime.MaxContentLength, + Port: cmp.Or(runtime.Port, "80"), + AppName: cmp.Or(runtime.Name, "my-agent"), + Logger: logger, }, myExecutor{}) if err != nil { return err @@ -154,32 +186,35 @@ if err != nil { return application.Run() ``` -Setting `Port` to `80` keeps this image working without a `KAGENT_PORT` variable on the Harness. Omitting it falls back to the `KAGENT_PORT` environment variable and then to a default of `8080`, which kagent never dials. +`ControlPlaneURL` is required unless you supply a `ControllerClient` of your own, because the helper reports task state to the kagent API. `Port` defaults to `8080` when both the field and `runtime.port` are empty, and kagent never dials that port, so keep the `cmp.Or` fallback to `80`. For a complete executor, see [`go/core/test/byoa2a/main.go`](https://github.com/kagent-dev/kagent/blob/main/go/core/test/byoa2a/main.go) in the kagent repository. {{% /tab %}} {{% tab name="Python" %}} -The `kagent-adk` package serves A2A over gRPC, defaulting its listener to `[::]:80` and its readiness endpoint to 8081. The default address matches what kagent dials, so a Python image needs no `KAGENT_PORT` variable on the Harness. +The `kagent-adk` package serves A2A over gRPC and its readiness endpoint on 8081. `KAgentConfig.from_file` materializes the payloads that Agent Substrate delivers and reads the `runtime` object back. The listener defaults to `[::]:80`, which matches what kagent dials. ```python from kagent.adk import KAgentApp +from kagent.core import KAgentConfig + +config = KAgentConfig.from_file() app = KAgentApp( root_agent_factory=build_agent, agent_card=card, - kagent_api_url=os.environ["KAGENT_API_URL"], - app_name="my-agent", + kagent_api_url=config.api_url, + app_name=config.app_name, ).build() ``` -`KAgentApp` wraps a Google Agent Development Kit (ADK) agent rather than an arbitrary framework. Override the listener with the `KAGENT_A2A_GRPC_ADDRESS` environment variable, or the `a2a_grpc_address` argument. +`from_file` raises when `control_plane_url`, `gateway_url`, `name`, or `namespace` is missing, so a standalone process that runs outside Agent Substrate must supply its own `/config/config.json`. `KAgentApp` wraps a Google Agent Development Kit (ADK) agent rather than an arbitrary framework. To bind the address that the configuration names rather than the default, pass the parsed document as the `agent_config` argument. The `a2a_grpc_address` argument overrides both. {{% /tab %}} {{% tab name="Any other language" %}} Implement the contract directly: 1. Serve `lf.a2a.v1.A2AService` over gRPC on port 80. Generate the stubs from the [A2A protocol](https://a2a-protocol.org) definitions. 2. Serve `GET /readyz` over HTTP on port 8081, returning `200`. -3. Read `KAGENT_CONFIG_JSON` if the agent should honor its AgentTemplate, or ignore it and run an opaque agent. +3. Read the compiled configuration from the `KAGENT_CONFIG_JSON` environment variable if the agent should honor its AgentTemplate, or ignore it and run an opaque agent. The variable is the delivery mechanism that the kagent helpers materialize into `/config/config.json`, and an image without those helpers parses it directly. {{% /tab %}} {{< /tabs >}} @@ -188,7 +223,7 @@ Implement the contract directly: - **The A2A interface is fixed.** kagent advertises `http://127.0.0.1:80` over gRPC, and `spec.byo` takes no field to change the address, the port, or the protocol. An image that serves A2A over HTTP JSON-RPC alone cannot run on this runtime. - **The `kagent-langgraph` and `kagent-crewai` adapters do not qualify yet.** Both build a FastAPI application with A2A JSON-RPC routes and no gRPC server, so neither satisfies the contract as shipped. Running LangGraph or CrewAI under `byo` currently means serving A2A over gRPC yourself. - **Long-term memory is unavailable.** Memory is configured under `spec.kagent.memory` and wired only by the `kagent` runtime's compiler. A `byo` Harness has no equivalent setting. For what a BYO image would need to replace, see [Agent memory]({{< link path="agents/agent-memory" >}}). -- **A broken port mapping presents as a healthy agent.** Readiness passes on 8081 regardless of the A2A service, so the Session reports `READY` and every invoke fails. This is [kagent#2758](https://github.com/kagent-dev/kagent/issues/2758). +- **A broken port mapping presents as a healthy agent.** For the failure and what avoids it, see [The A2A contract](#the-a2a-contract). ## Next steps diff --git a/docs-site/content/kagent/1.x/examples/a2a-agents.md b/docs-site/content/kagent/1.x/examples/a2a-agents.md index c2f66c9f..50540429 100644 --- a/docs-site/content/kagent/1.x/examples/a2a-agents.md +++ b/docs-site/content/kagent/1.x/examples/a2a-agents.md @@ -260,15 +260,15 @@ kagent does not generate its own signing key, so you are responsible for creatin --from-file=private-key.pem=private-key.pem ``` -3. Name the Secret in `controller.push.signing.secretName`, and set `controller.push.signing.issuer` to a URL that reaches the controller. Use the Helm release name and version that you installed kagent with. +3. Name the Secret in `controller.push.signing.secretName`, and set `config.gateway.push.issuer` to a URL that reaches the controller. Use the Helm release name and version that you installed kagent with. ```bash helm upgrade kagent oci://ghcr.io/kagent-dev/kagent/helm/kagent \ --namespace kagent --reuse-values \ --set controller.push.signing.secretName=kagent-push-signing \ - --set controller.push.signing.issuer=https://kagent.example + --set config.gateway.push.issuer=https://kagent.example ``` - The issuer that a receiver trusts must match the host that it fetches the JWKS from. When `controller.push.signing.issuer` is empty, Helm falls back to `ui.externalUrl` and then to the in-cluster A2A gateway URL. Either fallback can name a host that does not serve the JWKS. + The issuer that a receiver trusts must match the host that it fetches the JWKS from. When you omit `config.gateway.push.issuer`, Helm derives it from `ui.externalUrl` and then from the in-cluster A2A gateway URL. Either fallback can name a host that does not serve the JWKS. 4. Confirm that the controller publishes a verification key. Before you set the Secret, this path returns `404`. ```bash @@ -284,7 +284,7 @@ Receivers verify a signed delivery against the JSON Web Key Set (JWKS). The cont Receivers must not send a token or credential when they register a callback. kagent signs each delivery attempt with a fresh five-minute JWT whose audience is the exact callback URL, including the query string. Before you acknowledge the request with a 2xx response, verify the JWT signature, issuer, audience, expiry, and `taskId` claim. The `taskId` claim repeats the `statusUpdate.taskId` value in the body, so a receiver that compares them rejects a token replayed against a different task. -By default, callback URLs must use HTTPS. Callback URLs must not resolve to private, loopback, link-local, unspecified, or multicast addresses. To reach a test receiver inside the cluster, set `controller.push.allowHTTP=true` or `controller.push.allowPrivateNetworks=true`. kagent does not follow redirects, and failed attempts are retried from the database-backed queue. +By default, callback URLs must use HTTPS. Callback URLs must not resolve to private, loopback, link-local, unspecified, or multicast addresses. To reach a test receiver inside the cluster, set `config.gateway.push.allowHttp=true` or `config.gateway.push.allowPrivateNetworks=true`. kagent does not follow redirects, and failed attempts are retried from the database-backed queue. ## When an agent needs a person diff --git a/docs-site/content/kagent/1.x/examples/a2a-byo.md b/docs-site/content/kagent/1.x/examples/a2a-byo.md index edb67231..5d8af041 100644 --- a/docs-site/content/kagent/1.x/examples/a2a-byo.md +++ b/docs-site/content/kagent/1.x/examples/a2a-byo.md @@ -90,7 +90,7 @@ A `byo` Harness carries two fields that the other runtimes do not need: an empty ``` > [!NOTE] - > This Harness sets no `KAGENT_PORT` variable, because `byoa2a/main.go` pins `Port: "80"` in the image. An image that leaves the port to kagent listens on the wrong one and still reports `READY`. For that trap and its workaround, see [Bring your own agent]({{< link path="agents/bring-your-own-agent#the-a2a-contract" >}}). + > This Harness sets no port, because `byoa2a/main.go` reads `runtime.port` from the compiled configuration and falls back to `80`. An image that does neither listens on the wrong port and still reports `READY`. For that trap, see [Bring your own agent]({{< link path="agents/bring-your-own-agent#the-a2a-contract" >}}). 2. Create the AgentTemplate and an Agent that pairs it with the Harness. `description` is the only field that this template needs. ```yaml @@ -196,9 +196,24 @@ func (executor) Cancel(context.Context, *a2asrv.ExecutorContext) iter.Seq2[a2aty Two events make a complete turn. `NewSubmittedTask` acknowledges the message and opens the task, and a `TaskStateCompleted` status update carrying an agent message ends it. Between them, a real agent yields whatever its work produces. -`app.New` serves the A2A gRPC service and the readiness endpoint on your behalf, and the `Port: "80"` line keeps the listener where kagent expects it. Both stay as they are in an agent of your own. +`main` sets the rest up. `config.MaterializeFromEnv` writes the payloads that Agent Substrate delivers to `/config`, `config.LoadAgentConfig` reads the document back, and `ProcessConfig` returns its `runtime` object. `app.New` then serves the A2A gRPC service and the readiness endpoint on your behalf. -To read the AgentTemplate rather than ignore it, parse the `KAGENT_CONFIG_JSON` variable that kagent sets on the container. For what that variable holds, see [Bring your own agent]({{< link path="agents/bring-your-own-agent#opaque-and-configured-agents" >}}). +```go +compiled, err := config.LoadAgentConfig("/config/config.json") +runtime := compiled.ProcessConfig() + +application, err := app.New(app.AppConfig{ + ControlPlaneURL: runtime.ControlPlaneURL, + AgentCard: a2atype.AgentCard{Name: "opaque-byo", Version: "v1", Capabilities: a2atype.AgentCapabilities{Streaming: true}}, + Port: cmp.Or(runtime.Port, "80"), + AppName: cmp.Or(runtime.Name, "opaque-byo"), + Logger: logger, +}, executor{}) +``` + +`ControlPlaneURL` is required, because the helper reports task state to the kagent API. The `cmp.Or` fallbacks keep the image runnable outside Agent Substrate, where no configuration document exists. All of this stays as it is in an agent of your own. + +This agent ignores the rest of the compiled configuration. To honor the AgentTemplate instead, read the other fields of the document that `LoadAgentConfig` returns. For what it holds, see [Bring your own agent]({{< link path="agents/bring-your-own-agent#opaque-and-configured-agents" >}}). ## Build and run your own agent diff --git a/docs-site/content/kagent/1.x/examples/agents-via-mcp.md b/docs-site/content/kagent/1.x/examples/agents-via-mcp.md index 7a5fdb22..a1b17820 100644 --- a/docs-site/content/kagent/1.x/examples/agents-via-mcp.md +++ b/docs-site/content/kagent/1.x/examples/agents-via-mcp.md @@ -27,7 +27,7 @@ The MCP server is part of the controller's HTTP port rather than a separate depl ## Before you begin -1. [Install kagent]({{< link path="setup/installation" >}}), and confirm that your installation sets `controller.grpc.reflection=true`. Reflection lets a gRPC client discover the controller's methods without a local copy of kagent's protocol buffer definitions. +1. [Install kagent]({{< link path="setup/installation" >}}), and confirm that your installation sets `config.server.grpcReflection=true`. Reflection lets a gRPC client discover the controller's methods without a local copy of kagent's protocol buffer definitions. 2. [Create your first agent]({{< link path="get-started/your-first-agent" >}}), so that you have at least one Session in the `READY` state. The MCP server lists ready Sessions only. diff --git a/docs-site/content/kagent/1.x/examples/skills.md b/docs-site/content/kagent/1.x/examples/skills.md index 00df36fd..c4290973 100644 --- a/docs-site/content/kagent/1.x/examples/skills.md +++ b/docs-site/content/kagent/1.x/examples/skills.md @@ -32,7 +32,7 @@ On a `kagent` Harness, attaching a skill adds seven tools to the agent, whether | `edit_file` | Replaces an exact string in a file that the agent has already read. | | `bash` | Runs a shell command in the session directory `/tmp/kagent//`. Commands time out after 30 seconds. | -Two further tools are available but off by default. Set `KAGENT_ENABLE_FILE_SEARCH_TOOLS` to `true` on the agent's env to add `list_files`, which lists the contents of a directory, and `grep_file`, which searches file contents for a pattern and can recurse. Both are confined to the same skills and session roots as `read_file`. They stay opt-in because they give an agent broad visibility of the filesystem, and they are how an agent keeps that visibility when a deployment disables `bash`. +Two further tools are available but off by default. Set the `config.runtimeDefaults.fileSearchTools` Helm value to `true` to add `list_files`, which lists the contents of a directory, and `grep_file`, which searches file contents for a pattern and can recurse. The setting applies to every agent in the installation, because a Harness has no per-agent equivalent. Both are confined to the same skills and session roots as `read_file`. They stay opt-in because they give an agent broad visibility of the filesystem, and they are how an agent keeps that visibility when a deployment disables `bash`. Attaching a skill also changes what the agent is told. The runtime appends the name and description of every attached skill to the model request, along with an instruction to call `load_skill` before acting on one, so a skill reaches the model even before any tool is called. diff --git a/docs-site/content/kagent/1.x/observability/audit-prompts.md b/docs-site/content/kagent/1.x/observability/audit-prompts.md index 45fba48d..108f4651 100644 --- a/docs-site/content/kagent/1.x/observability/audit-prompts.md +++ b/docs-site/content/kagent/1.x/observability/audit-prompts.md @@ -15,14 +15,14 @@ The `kagent` runtime does not write prompts or replies to log records, so a logg ### What each runtime records -Each runtime records the content on its own instrumentation, so where the content lands differs by runtime. For the available runtimes, see [Choose a runtime]({{< link path="agents/agent-harness#choose-a-runtime" >}}). +Each runtime records the content on its own instrumentation, so where the content lands differs by runtime. The settings in the last column are the fields of the `config.runtimeDefaults.telemetry` Helm block. For the available runtimes, see [Choose a runtime]({{< link path="agents/agent-harness#choose-a-runtime" >}}). | Runtime | Where the content goes | Settings | | ------- | ---------------------- | -------- | -| `kagent` | The `generate_content` span of each model call, in three attributes. See [What a record holds](#what-a-record-holds). | `otel.capture.messageContent` | -| `codex` | The runtime's own spans. | `otel.capture.messageContent` | -| `claude` | Prompts and tool details on spans, and assistant replies in the runtime's own log records. With `otel.capture.rawApiBodies`, the log records also carry the complete provider request and response bodies, which is a fuller record than the spans give you. | `otel.capture.messageContent`, `otel.capture.rawApiBodies` | -| `byo` | Wherever the image's own instrumentation records it. The controller sets `OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT` in this runtime, but an image that does not read the variable records nothing. | `otel.capture.messageContent` | +| `kagent` | The `generate_content` span of each model call, in three attributes. See [What a record holds](#what-a-record-holds). | `captureContent` | +| `codex` | The runtime's own spans. | `captureContent` | +| `claude` | Prompts and tool details on spans, and assistant replies in the runtime's own log records. With `captureRawApiBodies`, the log records also carry the complete provider request and response bodies, which is a fuller record than the spans give you. | `captureContent`, `captureRawApiBodies` | +| `byo` | Wherever the image's own instrumentation records it. The controller sets `OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT` in this runtime, but an image that does not read the variable records nothing. | `captureContent` | For each setting, see the agent harness [telemetry content settings]({{< link path="agents/agent-harness#telemetry-content-settings" >}}). @@ -40,11 +40,11 @@ The record holds more than the prompts that your team wrote. The system instruction holds the instructions that the runtime appends as well as the prompt your team wrote, and the message history holds the person's messages, the agent's earlier turns, and the model's tool calls. -A tool's own arguments and reply are recorded separately, on the `execute_tool` span of each tool call, in the `gcp.vertex.agent.tool_call_args` and `gcp.vertex.agent.tool_response` attributes. `otel.capture.messageContent` does not govern those two attributes. The runtime records them whenever tracing is on. Treat them as part of the audit record, and as a disclosure to account for on an agent whose tools return sensitive data. +A tool's own arguments and reply are recorded separately, on the `execute_tool` span of each tool call, in the `gcp.vertex.agent.tool_call_args` and `gcp.vertex.agent.tool_response` attributes. `config.runtimeDefaults.telemetry.captureContent` does not govern those two attributes. The runtime records them whenever tracing is on. Treat them as part of the audit record, and as a disclosure to account for on an agent whose tools return sensitive data. Each model call carries the full history again, so a long conversation repeats its earlier messages in every span. Account for that volume when you set a retention period. -The `kagent` runtime does not shorten these attributes. The `claude` and `codex` runtimes bound each captured prompt and reply to the `otel.capture.maxBytes` budget, which defaults to 16384 bytes, and report a shortened capture with the `kagent.capture.input_truncated` and `kagent.capture.output_truncated` attributes. Your tracing backend might also limit the size of an attribute, so check its limits before you rely on it for long conversations. +The `kagent` runtime does not shorten these attributes. The `claude` and `codex` runtimes bound each captured prompt and reply to the `config.runtimeDefaults.telemetry.maxCaptureBytes` budget, which defaults to 16384 bytes, and report a shortened capture with the `kagent.capture.input_truncated` and `kagent.capture.output_truncated` attributes. Your tracing backend might also limit the size of an attribute, so check its limits before you rely on it for long conversations. ### Delivery @@ -83,10 +83,10 @@ Turn on content capture in the kagent Helm release, then create a Session that p --version {{< reuse "kagent-docs/versions/kagent.md" >}} \ --namespace kagent \ --reuse-values \ - --set otel.capture.messageContent=true + --set config.runtimeDefaults.telemetry.captureContent=true ``` - For an agent on the `claude` runtime, also set `otel.logs.enabled` to `true`, and send the logs to a backend that stores them, such as Loki in the [OTel stack]({{< link path="observability/otel-stack" >}}). Without log export, the replies of a `claude` agent are not recorded. + For an agent on the `claude` runtime, also set `config.runtimeDefaults.telemetry.logs.endpoint` to the address of your collector, and send the logs to a backend that stores them, such as Loki in the [OTel stack]({{< link path="observability/otel-stack" >}}). Without log export, the replies of a `claude` agent are not recorded. 3. Wait for the controller to roll out. ```bash @@ -174,7 +174,7 @@ Turn off content capture, then create a new Session so that the change takes eff {{< reuse "kagent-docs/snippets/helm-path.md" >}}/{{< reuse "kagent-docs/snippets/helm-kagent.md" >}} \ --version {{< reuse "kagent-docs/versions/kagent.md" >}} \ --namespace kagent --reuse-values \ - --set otel.capture.messageContent=false + --set config.runtimeDefaults.telemetry.captureContent=false ``` 2. Create a new Session, because an existing Actor keeps the configuration that it started with. The spans of a Session that still captures content keep carrying it until you delete the Session. diff --git a/docs-site/content/kagent/1.x/observability/lightweight-otel-stack.md b/docs-site/content/kagent/1.x/observability/lightweight-otel-stack.md index 10aa2b9c..2aa1d8d6 100644 --- a/docs-site/content/kagent/1.x/observability/lightweight-otel-stack.md +++ b/docs-site/content/kagent/1.x/observability/lightweight-otel-stack.md @@ -204,15 +204,22 @@ Turn on the kagent trace and log exporters, and point both at the collector. Als --namespace kagent \ --reuse-values \ --values - <}}/{{< reuse "kagent-docs/snippets/helm-kagent.md" >}} \ --version {{< reuse "kagent-docs/versions/kagent.md" >}} \ --namespace kagent --reuse-values \ - --set otel.traces.enabled=false \ - --set otel.logs.enabled=false \ + --set config.telemetry=null \ + --set config.runtimeDefaults.telemetry=null \ --set controller.metrics.enabled=false helm upgrade substrate \ {{< reuse "kagent-docs/snippets/helm-substrate.md" >}} \ diff --git a/docs-site/content/kagent/1.x/observability/metrics.md b/docs-site/content/kagent/1.x/observability/metrics.md index 083cd61b..698f7cbe 100644 --- a/docs-site/content/kagent/1.x/observability/metrics.md +++ b/docs-site/content/kagent/1.x/observability/metrics.md @@ -17,11 +17,13 @@ The controller serves its metrics over HTTPS, and accepts a scrape only from a S controller: metrics: enabled: true +config: + metrics: bindAddress: ":8443" - secureServing: true + secure: true ``` -Change the port through `bindAddress` rather than through `controller.env`, because the chart derives the Service `targetPort` and the pod `containerPort` from `bindAddress` at template time. Overriding `KAGENT_METRICS_BIND_ADDRESS` directly moves the listener and leaves the Service pointing at the old port. +The two blocks divide the listener from its Kubernetes packaging. `config.metrics` sets the address that the controller binds and whether it serves over HTTPS, and `controller.metrics` creates the Service, the ClusterRoles, and the optional ServiceMonitor. Setting `controller.metrics.enabled` to `false` overrides `config.metrics.bindAddress` with `0`, which disables the listener as well. Change the port through `config.metrics.bindAddress`, because the chart derives the Service `targetPort` and the pod `containerPort` from it at template time. Grant your Prometheus instance access in one of the following ways. diff --git a/docs-site/content/kagent/1.x/observability/otel-stack.md b/docs-site/content/kagent/1.x/observability/otel-stack.md index a979f06c..75bf0eb0 100644 --- a/docs-site/content/kagent/1.x/observability/otel-stack.md +++ b/docs-site/content/kagent/1.x/observability/otel-stack.md @@ -339,15 +339,22 @@ Turn on the kagent trace and log exporters, and point both at the collector. Als --namespace kagent \ --reuse-values \ --values - <}}). | - | `otel.traces` | Exports traces from the controller and from the agent runtimes that it starts. | - | `otel.logs` | Exports log records from the agent runtimes. The `kagent` runtime never puts prompts or replies in a log record. For where that content goes, see [Audit prompts]({{< link path="observability/audit-prompts" >}}). | + | `config.telemetry` | The endpoint and protocol that the controller's own signals export to. An `http://` endpoint sends plaintext. For each field, see [Tracing]({{< link path="observability/tracing#enable-tracing" >}}). | + | `config.runtimeDefaults.telemetry` | The same pair for every agent runtime that the controller starts. The controller's own settings do not propagate to a runtime, so both blocks name the collector. | + | `telemetry.traces` | Exports traces. A signal exports when you set its endpoint, and stays off when you omit the endpoint, so neither block takes an `enabled` field. | + | `telemetry.logs` | Exports log records. The `kagent` runtime never puts prompts or replies in a log record. For where that content goes, see [Audit prompts]({{< link path="observability/audit-prompts" >}}). | | `controller.metrics.enabled` | Serves the controller's Prometheus metrics on port `8443` over HTTPS. | | `controller.metrics.serviceMonitor` | Creates a ServiceMonitor for the metrics endpoint. The `prometheusServiceAccount` setting binds the metrics reader role to the Prometheus ServiceAccount, which authorizes the scrape. | @@ -495,8 +503,8 @@ Log in to Grafana, and query each backend from the **Explore** view. {{< reuse "kagent-docs/snippets/helm-path.md" >}}/{{< reuse "kagent-docs/snippets/helm-kagent.md" >}} \ --version {{< reuse "kagent-docs/versions/kagent.md" >}} \ --namespace kagent --reuse-values \ - --set otel.traces.enabled=false \ - --set otel.logs.enabled=false \ + --set config.telemetry=null \ + --set config.runtimeDefaults.telemetry=null \ --set controller.metrics.enabled=false \ --set controller.metrics.serviceMonitor.enabled=false helm upgrade substrate \ diff --git a/docs-site/content/kagent/1.x/observability/tracing.md b/docs-site/content/kagent/1.x/observability/tracing.md index e7bbac42..d9361e0c 100644 --- a/docs-site/content/kagent/1.x/observability/tracing.md +++ b/docs-site/content/kagent/1.x/observability/tracing.md @@ -83,7 +83,7 @@ A trace tells you which request you are looking at through attributes on its spa The runtime also adds each scalar value in the A2A message's metadata as an `a2a.message.metadata.` attribute, so a client can tag a request and search for it later. Unlike the correlation attributes, these tags stay on the `a2a.request` span alone, so a search on one returns that span instead of the whole subtree. > [!WARNING] -> When the `otel.capture.messageContent` Helm setting is `true`, prompts and replies reach your tracing backend. On the `kagent` runtime, the `generate_content` span of each model call then carries the conversation as the `gen_ai.input.messages` and `gen_ai.output.messages` attributes, and the Agent's system prompt as `gen_ai.system_instructions`. The setting defaults to `false`, which omits all three. +> When the `config.runtimeDefaults.telemetry.captureContent` Helm setting is `true`, prompts and replies reach your tracing backend. On the `kagent` runtime, the `generate_content` span of each model call then carries the conversation as the `gen_ai.input.messages` and `gen_ai.output.messages` attributes, and the Agent's system prompt as `gen_ai.system_instructions`. The setting defaults to `false`, which omits all three. > > The setting does not govern tool content. An `execute_tool` span carries the call's arguments and the tool's reply in `gcp.vertex.agent.tool_call_args` and `gcp.vertex.agent.tool_response` whether the setting is `true` or `false`, so a tool that returns sensitive data sends it to your tracing backend on the default settings. Turn tracing off for an agent whose tools return data that must not leave the cluster. For how to use this content as an audit record, see [Audit prompts]({{< link path="observability/audit-prompts" >}}). @@ -98,7 +98,7 @@ The runtime also adds each scalar value in the A2A message's metadata as an `a2a Tracing is off by default. Turning it on is a Helm change, because the controller reads its tracing configuration from the environment and passes that configuration to the agent runtimes that the controller starts. The following steps send traces to the collector that both stack guides install. To send traces to another OTLP backend, change the endpoint. > [!IMPORTANT] -> The telemetry values changed shape in 1.0. `otel.tracing.*`, `otel.logging.*`, `otel.captureSensitiveContent`, and the `insecure` flag were removed, and `otel.exporter.otlp.*`, `otel.traces.*`, `otel.logs.*`, and `otel.capture.*` replace them. The chart defines no check that rejects a removed setting, so neither Helm flag that reuses stored values carries you across. `--reuse-values` restores the previous chart's defaults, which leaves `otel.traces` and `otel.metrics` undefined and fails the upgrade while the controller ConfigMap renders. `--reset-then-reuse-values` completes, but the chart ignores the removed keys, so a release that set `otel.tracing.enabled` to `true` turns tracing off without reporting an error. If your release still holds the old settings, write the replacements into a values file and upgrade with `--values`, as shown in the following steps. On subsequent upgrades, `--reuse-values` works again. +> The telemetry values moved again in {{< reuse "kagent-docs/versions/kagent.md" >}}. The whole top-level `otel` block was removed, and `config.telemetry` and `config.runtimeDefaults.telemetry` replace it. A release that still sets `otel.traces.enabled` or `otel.exporter.otlp.endpoint` keeps a value that no template reads, and tracing turns off without reporting an error. Write the replacements into a values file and upgrade with `--values`, as shown in the following steps. For the rest of the move and the Helm flags that do not carry you across it, see [Controller configuration]({{< link path="operations/operational-considerations#controller-configuration" >}}). 1. Save the current revision of your Agent. A later step uses it to tell when kagent recompiles the Agent with the new settings. The command first waits for any recompile that is still in progress, such as one from an earlier Helm upgrade, so that it saves a finished revision. ```bash @@ -119,29 +119,33 @@ Tracing is off by default. Turning it on is a Helm change, because the controlle 3. Add the tracing settings to the values file. ```yaml - otel: - exporter: - otlp: + config: + telemetry: + traces: endpoint: http://otel-collector.telemetry.svc.cluster.local:4317 protocol: grpc - timeout: 15000 - traces: - enabled: true + timeout: 15s + runtimeDefaults: + telemetry: + traces: + endpoint: http://otel-collector.telemetry.svc.cluster.local:4317 + protocol: grpc + timeout: 15s ``` {{< reuse "kagent-docs/snippets/review-table.md" >}} | Field | Description | | ----- | ----------- | - | `traces.enabled` | Whether to export traces at all. Defaults to `false`. | - | `exporter.otlp.endpoint` | The OTLP endpoint that every signal exports to, as an `http://` or `https://` URL. An `http://` endpoint sends plaintext. Empty by default. When a signal is enabled and neither this setting nor its per-signal counterpart holds an endpoint, the controller disables that signal and reports `OTLP traces endpoint is required when traces export is enabled`. | - | `exporter.otlp.protocol` | `grpc` or `http/protobuf`. Defaults to `grpc`, which matches the port `4317` in the example endpoint. Point `http/protobuf` at port `4318` instead. | - | `exporter.otlp.timeout` | The export timeout in milliseconds. Empty by default, which keeps the OTel SDK default. | - | `traces.endpoint`, `traces.protocol` | Send traces somewhere other than the other signals. Each one overrides its `exporter.otlp` counterpart for traces alone. Both are empty by default. | + | `config.telemetry.traces` | The traces of the kagent controller itself. | + | `config.runtimeDefaults.telemetry.traces` | The traces of every agent runtime that the controller compiles. The controller's own settings do not propagate to a runtime, so set both blocks to trace the whole path of a request. | + | `traces.endpoint` | Where the traces go, as an `http://` or `https://` URL. An `http://` endpoint sends plaintext. On the `http/protobuf` protocol, include the signal path, as in `http://otel-collector.telemetry.svc.cluster.local:4318/v1/traces`. | + | `traces.protocol` | `grpc` or `http/protobuf`. Defaults to `grpc`, which matches the port `4317` in the example endpoint. Point `http/protobuf` at port `4318` instead. | + | `traces.timeout` | The export timeout, as a duration in seconds with an `s` suffix. Defaults to `10s`. | - An endpoint is an absolute `http://` or `https://` URL, and one that carries credentials, a query, or a fragment leaves its signal disabled in the same way. An invalid telemetry setting never fails the upgrade or the Harness. The controller reports each one as a warning when it starts, as the log record `invalid agent telemetry configuration; disabling signal`, and turns off only the signal that the setting belongs to. Check that log after you change these values, because no other surface reports the problem and an agent with a disabled signal runs normally. + A signal has no separate switch. The controller exports a signal when you set its endpoint, and compiles an explicit off state when you omit the endpoint. Omit the key rather than leaving it empty: an empty endpoint fails validation. - The two endpoint settings differ in how the controller treats the path. A per-signal endpoint such as `traces.endpoint` is used exactly as you write it. The shared `exporter.otlp.endpoint` is used as written on the `grpc` protocol, and gains a `/v1/traces` suffix on `http/protobuf`, so point the shared setting at the collector's root rather than at a signal path. + The controller validates its configuration file at startup and exits when a setting is invalid, so a malformed endpoint stops the controller rather than disabling one signal. An endpoint must be an absolute `http://` or `https://` URL. The controller rejects one that includes credentials, a query, or a fragment. Check the controller's logs after you change these values. 4. Upgrade the kagent Helm release. ```bash @@ -161,7 +165,7 @@ Tracing is off by default. Turning it on is a Helm change, because the controlle sleep 5 done ``` - If the command finishes without printing `Recompiled`, the upgrade did not change the settings that kagent compiles into the Agent. Either the settings were already in place, or the chart did not recognize the `otel` keys. Helm accepts a key that a chart does not define without an error, so check that you upgraded to version {{< reuse "kagent-docs/versions/kagent.md" >}} of the chart, which uses the keys on this page. + If the command finishes without printing `Recompiled`, the upgrade did not change the settings that kagent compiles into the Agent. Either the settings were already in place, or the chart did not recognize the `config` keys. Helm accepts a key that a chart does not define without an error, so check that you upgraded to version {{< reuse "kagent-docs/versions/kagent.md" >}} of the chart, which uses the keys on this page. 6. Create a new Session, so that its Actor starts from a runtime that has the tracing configuration. ```bash @@ -253,20 +257,18 @@ The flush lets a kagent trace arrive promptly rather than on the exporter's own ## Turn tracing off -Turn off the trace exporter, then create a new Session so that the change takes effect. +Turn off the trace exporter, then create a new Session so that the change takes effect. Turning tracing off compiles an explicit off state rather than an absent one. The controller sets the trace exporter to `none` in each runtime, so an agent never falls back to the OpenTelemetry SDK's own default of exporting to `localhost`. The controller sets `OTEL_SDK_DISABLED` to `true` only when traces, metrics, and logs are all off, and both stack guides turn logs on, so the SDK stays enabled. -1. Disable tracing in the kagent Helm release. +1. Remove the telemetry settings from the kagent Helm release. Null the whole `telemetry` block rather than one signal inside it. A `--set config.telemetry.traces=null` renders the literal `traces: null` into the controller's configuration file, and the controller rejects a null value and fails to start. ```bash helm upgrade kagent \ {{< reuse "kagent-docs/snippets/helm-path.md" >}}/{{< reuse "kagent-docs/snippets/helm-kagent.md" >}} \ --version {{< reuse "kagent-docs/versions/kagent.md" >}} \ --namespace kagent --reuse-values \ - --set otel.traces.enabled=false + --set config.telemetry=null \ + --set config.runtimeDefaults.telemetry=null ``` - > [!NOTE] - > Turning tracing off compiles an explicit off state rather than an absent one. The controller sets the trace exporter to `none` in each runtime, so an agent never falls back to the OpenTelemetry SDK's own default of exporting to `localhost`. The controller sets `OTEL_SDK_DISABLED` to `true` only when traces, metrics, and logs are all off. Both stack guides turn logs on, so the SDK stays enabled after this step. - 2. Create a new Session to pick up the change, because an existing Actor keeps the configuration it started with. 3. To remove the tracing backend, follow the cleanup steps in the [OTel stack]({{< link path="observability/otel-stack#clean-up" >}}) or [Lightweight OTel stack]({{< link path="observability/lightweight-otel-stack#clean-up" >}}) guide. diff --git a/docs-site/content/kagent/1.x/operations/operational-considerations.md b/docs-site/content/kagent/1.x/operations/operational-considerations.md index 3df3cabf..ba103624 100644 --- a/docs-site/content/kagent/1.x/operations/operational-considerations.md +++ b/docs-site/content/kagent/1.x/operations/operational-considerations.md @@ -7,6 +7,48 @@ author: kagent.dev A default {{< reuse "kagent-docs/snippets/name-product.md" >}} installation runs one controller replica against the development database that `kagent install` creates, and schedules agents onto any node in the cluster. Each default works for a trial installation, and each needs a decision before you run {{< reuse "kagent-docs/snippets/name-product.md" >}} in production: which database to run, how many controller replicas to keep, and how to treat the nodes that host Workers. +## Controller configuration + +Every application setting that the controller reads lives under the chart's `config` block, and the chart renders that block into one configuration file at `/etc/kagent/controller.yaml`. Settings that describe how Kubernetes runs the controller, such as images, probes, volumes, Services, and Secret references, stay in the Helm values beside `config` rather than inside it. The two groups meet where a Secret stores a value that the controller reads, and the chart then projects the Secret and writes its path into the configuration file. + +```yaml +config: + logging: + level: info + server: + grpcReflection: false + auth: + mode: insecure + sessions: + idleTtl: 604800s + runtimeDefaults: + logging: + level: info +``` + +Three rules govern the whole block, and each one rejects a value that the Helm keys it replaced accepted. + +| Rule | What it means | +| ---- | ------------- | +| Durations are seconds | A duration takes protobuf JSON notation. The value is a number with an `s` suffix, as in `604800s` or `0.5s`, and a Go duration string such as `168h` fails validation. | +| The document is strict | The controller rejects an unknown field, a duplicate key, a null value, a YAML alias or merge, and a file with more than one document. | +| Settings load once | The controller validates the whole file at startup, passes the result to its owners, and reads nothing from the environment afterward. A change therefore needs a restart. The chart triggers that restart by checksumming the ConfigMap into the pod. | + +An invalid setting stops the controller from starting rather than disabling one feature, so check the controller's logs after you change these values. To check a file before you apply it, run the CLI against the rendered document. The command connects to no services. + +```bash +kagent config validate --component controller --config controller.yaml +``` + +Starting the controller with `--check-config` does the same from inside the image, and also confirms that the Secret files the document references are readable. + +> [!IMPORTANT] +> The `config` block replaced the `controller`, `database.postgres`, and `otel` application settings in {{< reuse "kagent-docs/versions/kagent.md" >}}. Helm accepts a key that a chart does not define, so a release that still sets `controller.sessionIdleTTL` or `otel.traces.enabled` keeps a value that no template reads, and the setting reverts to its default without reporting an error. Neither Helm flag that reuses stored values carries you across, so write the replacements into a values file and upgrade with `--values`. + +### Runtime defaults + +`config.runtimeDefaults` is the one part of the block that the controller does not apply to itself. The controller compiles those settings into each agent runtime that it starts, so a setting such as the log level, the telemetry endpoints, or the content capture decision must appear in both `config` and `config.runtimeDefaults` to govern the controller and the agents together. A Harness overrides a few of them through its `spec.env` field, and owns none of the telemetry ones. For which variables a Harness may set, see [Controller-owned telemetry variables]({{< link path="agents/agent-harness#controller-owned-telemetry-variables" >}}). + ## Choose a database kagent stores {{< gloss "Session" >}}Sessions{{< /gloss >}} and compiled {{< gloss "Revision" >}}revisions{{< /gloss >}} in PostgreSQL. The kagent chart deploys no database of its own. It reads a connection string from a Kubernetes Secret, and `database.postgres.connectionStringSecretRef` names that Secret and the key inside it. @@ -52,6 +94,8 @@ For production, run PostgreSQL outside the cluster's lifecycle so that a kagent key: connectionString clientCertificate: enabled: false + config: + database: role: kagent_owner schema: kagent vectorEnabled: true @@ -63,15 +107,17 @@ For production, run PostgreSQL outside the cluster's lifecycle so that a kagent replicas: 3 ``` + The two blocks divide along the same line as the rest of the chart. `database.postgres` covers the Kubernetes packaging, meaning the Secret reference and the client certificate. `config.database` sets what the controller reads from its configuration file. For that division, see [Controller configuration](#controller-configuration). + | Setting | Description | | ------- | ----------- | | `database.postgres.connectionStringSecretRef` | The Secret and key that hold the connection string. Keeping the string in a Secret keeps the credentials out of Helm values. The controller restarts to pick up a changed Secret. | | `database.postgres.clientCertificate` | Set `enabled` to `true` only for a database that authenticates clients with Pod Certificates, such as the one that `kagent install` creates. The connection string must then point its `sslcert` and `sslkey` parameters at the files under `/run/postgres.podcert.ate.dev`. An external instance that authenticates with a password, such as the one in the preceding example, leaves `enabled` at `false`. | - | `database.postgres.role` | The role that kagent assumes on each connection. Give each installation that shares one database a distinct role. | - | `database.postgres.schema` | The schema that holds kagent's tables. `database.postgres.vectorSchema` names the schema that holds the shared `pgvector` extension, and kagent needs `USAGE` on it. | - | `database.postgres.vectorEnabled` | Set to `true` only when the instance has the `pgvector` extension installed. The setting enables the vector migration, and features that depend on it, such as [long-term memory]({{< link path="agents/agent-memory" >}}), fail without the extension. The development database does not include `pgvector`. | - | `database.postgres.pool` | Connection pool sizing, through `maxConns`, `minConns`, `maxConnIdleTime`, and `maxConnLifetime`. Omit the fields to keep the pgx library defaults. | - | `database.postgres.skipMigrations` | Set to `true` to stop the controller from running migrations at startup. The controller then verifies that the database is already migrated and fails if it is not. Apply the migrations from a pipeline before you install or upgrade. | + | `config.database.role` | The role that kagent assumes on each connection. Give each installation that shares one database a distinct role. | + | `config.database.schema` | The schema that holds kagent's tables. `config.database.vectorSchema` names the schema that holds the shared `pgvector` extension, and kagent needs `USAGE` on it. | + | `config.database.vectorEnabled` | Set to `true` only when the instance has the `pgvector` extension installed. The setting enables the vector migration, and features that depend on it, such as [long-term memory]({{< link path="agents/agent-memory" >}}), fail without the extension. The development database does not include `pgvector`. | + | `config.database.pool` | Connection pool sizing, through `maxConns`, `minConns`, `maxConnIdleTime`, and `maxConnLifetime`. The two durations take protobuf seconds notation, as in `1800s`. Omit the fields to keep the pgx library defaults. | + | `config.database.skipMigrations` | Set to `true` to stop the controller from running migrations at startup. The controller then verifies that the database is already migrated and fails if it is not. Apply the migrations from a pipeline before you install or upgrade. | 3. Apply the values to the kagent Helm release. @@ -121,13 +167,16 @@ controller: push: signing: secretName: kagent-push-signing +config: + gateway: + push: issuer: https://kagent.example.com ``` | Setting | Description | | ------- | ----------- | -| `controller.push.signing.secretName` | The Secret that holds `private-key.pem`. Leave it empty to send push notifications without JSON Web Token (JWT) authentication. Helm references a Secret that you create, and neither generates nor rotates signing keys. An invalid key stops the controller from starting. | -| `controller.push.signing.issuer` | The stable URL that webhook receivers trust as the JWT issuer. Leave it empty to fall back to `ui.externalUrl`, and then to the in-cluster A2A gateway URL. | +| `controller.push.signing.secretName` | The Secret that holds `private-key.pem`. Leave it empty to send push notifications without JSON Web Token (JWT) authentication. Helm references a Secret that you create, and neither generates nor rotates signing keys. An invalid key stops the controller from starting. Naming a Secret here projects it into the controller as the `config.gateway.push.signingPrivateKeyFile` setting. | +| `config.gateway.push.issuer` | The stable URL that webhook receivers trust as the JWT issuer. Omit the key, and the chart derives the issuer from `ui.externalUrl`, and then from the in-cluster A2A gateway URL. An empty string is rejected, because the controller requires an absolute `http://` or `https://` URL. | With a signing key configured, each callback attempt carries a fresh five-minute Ed25519 JWT in an `Authorization: Bearer` header, and the `/.well-known/jwks.json` endpoint publishes the matching public key. @@ -175,15 +224,18 @@ When you scale a serving WorkerPool down, it removes pods without suspending the Every {{< gloss "Session" >}}Session{{< /gloss >}} holds database records and pins a runtime revision for as long as it exists, including a Session that is never opened again. The kagent controller deletes idle Sessions on a timer. Deleting idle sessions bounds the records that a kagent installation stores as well as the revisions that it holds open. -A leader-only worker sweeps Sessions once a minute and deletes every Session that has been idle past the limit, using the same deletion workflow that a client delete uses. +A leader-only worker sweeps Sessions once a minute and deletes every Session that has been idle past the limit, using the same deletion workflow that a client delete uses. `config.sessions.expirationInterval` sets that sweep interval, and it must be positive. -To choose how long a Session can be idle before the worker deletes it, set `controller.sessionIdleTTL` in your {{< reuse "kagent-docs/snippets/name-product.md" >}} Helm values. The value is a Go duration, and it defaults to `168h`, or seven days. Setting it to `0` turns the worker off, retries included, and a negative value is rejected. +To choose how long a Session can be idle before the worker deletes it, set `config.sessions.idleTtl` in your {{< reuse "kagent-docs/snippets/name-product.md" >}} Helm values. The value is a duration in seconds with an `s` suffix, and it defaults to `604800s`, or seven days. Setting it to `0s` turns the worker off, retries included, and a negative value is rejected. ```yaml -controller: - sessionIdleTTL: "720h" +config: + sessions: + idleTtl: 2592000s ``` +Durations in the `config` block follow protobuf JSON notation, so seconds are the only unit. A Go duration string such as `720h` fails validation and stops the controller from starting. + No per-agent override exists, so this one value governs every Session in the installation. No maximum exists either, so a longer value is accepted. Idle time runs from whichever is later: the Session's creation, or its most recently stored A2A event. Reads, renames, lifecycle calls, and retried writes do not reset it, so a Session that is only ever listed still expires. @@ -197,11 +249,12 @@ Work in progress is never deleted. A Session with a running task survives past t -A Session share carries its own lifetime, separate from the Session's idle limit. A client sets `CreateSessionShareRequest.ttl` when it creates a share, and `controller.sessionShareMaxTTL` caps what a client may ask for. A positive cap rejects any request that exceeds it, and supplies the lifetime for a request that omits `ttl`. The default `0` leaves a share unbounded unless the client sets `ttl`, and a negative value is rejected at startup. +A Session share carries its own lifetime, separate from the Session's idle limit. A client sets `CreateSessionShareRequest.ttl` when it creates a share, and `config.sessions.shareMaxTtl` caps what a client may ask for. A positive cap rejects any request that exceeds it, and supplies the lifetime for a request that omits `ttl`. The default `0s` leaves a share unbounded unless the client sets `ttl`, and a negative value is rejected at startup. ```yaml -controller: - sessionShareMaxTTL: "24h" +config: + sessions: + shareMaxTtl: 86400s ``` To watch the sweep, scrape `kagent_session_expired_total`, which counts the Sessions that it removed. Each removal also logs `expired idle session` at debug level with the Session's ID and how long it had been idle. diff --git a/docs-site/content/kagent/1.x/operations/tune-agent-substrate.md b/docs-site/content/kagent/1.x/operations/tune-agent-substrate.md index dd9fdd3b..54aa44c2 100644 --- a/docs-site/content/kagent/1.x/operations/tune-agent-substrate.md +++ b/docs-site/content/kagent/1.x/operations/tune-agent-substrate.md @@ -11,7 +11,7 @@ This single Worker reports capacity for 1000 Actors, so a default pool serves ma ## Before you begin -1. [Install kagent]({{< link path="setup/installation" >}}), and confirm that your installation sets `controller.grpc.reflection=true`. Reflection lets a gRPC client discover the controller's methods without a local copy of kagent's protocol buffer definitions. +1. [Install kagent]({{< link path="setup/installation" >}}), and confirm that your installation sets `config.server.grpcReflection=true`. Reflection lets a gRPC client discover the controller's methods without a local copy of kagent's protocol buffer definitions. 2. Confirm that you have administrative access to the cluster, because this guide scales WorkerPools and reads cluster-scoped resources. diff --git a/docs-site/content/kagent/1.x/operations/upgrade-from-0x.md b/docs-site/content/kagent/1.x/operations/upgrade-from-0x.md index 3addd6c2..dcd02cbd 100644 --- a/docs-site/content/kagent/1.x/operations/upgrade-from-0x.md +++ b/docs-site/content/kagent/1.x/operations/upgrade-from-0x.md @@ -72,10 +72,10 @@ Install 1.0 the same way as a first-time installation, with one addition: point | ----- | --- | | `database.postgres.connectionStringSecretRef.name=kagent-upgrade-postgres` | Reads the connection string from the Secret that you created. | | `database.postgres.connectionStringSecretRef.key=connectionString` | Names the key in the Secret that holds the connection string. | - | `database.postgres.role=""` | Uses the database login user directly. If you created a separate role such as `kagent_owner`, set that role name instead. | - | `database.postgres.vectorEnabled=true` | Applies the pgvector migrations, which [long-term memory]({{< link path="agents/agent-memory" >}}) depends on. Your database must have the pgvector extension installed. | + | `config.database.role=""` | Uses the database login user directly. If you created a separate role such as `kagent_owner`, set that role name instead. | + | `config.database.vectorEnabled=true` | Applies the pgvector migrations, which [long-term memory]({{< link path="agents/agent-memory" >}}) depends on. Your database must have the pgvector extension installed. | - The controller applies migrations at startup. To apply them from a pipeline instead, set `database.postgres.skipMigrations=true` and run `kagent db migrate up` yourself. The controller then verifies the schema and fails if it is not already current. For sizing and connection guidance on the database itself, see [Use an external PostgreSQL instance]({{< link path="operations/operational-considerations#use-an-external-postgresql-instance" >}}). + The controller applies migrations at startup. To apply them from a pipeline instead, set `config.database.skipMigrations=true` and run `kagent db migrate up` yourself. The controller then verifies the schema and fails if it is not already current. For sizing and connection guidance on the database itself, see [Use an external PostgreSQL instance]({{< link path="operations/operational-considerations#use-an-external-postgresql-instance" >}}). 3. Verify the schema before continuing. ```bash diff --git a/docs-site/content/kagent/1.x/setup/installation.md b/docs-site/content/kagent/1.x/setup/installation.md index 49f5564d..f62be5f8 100644 --- a/docs-site/content/kagent/1.x/setup/installation.md +++ b/docs-site/content/kagent/1.x/setup/installation.md @@ -58,15 +58,32 @@ author: kagent.dev The kagent CLI installs the CRDs, Agent Substrate, the Agent Substrate PodCertificate controller, a development PostgreSQL database, and kagent in the correct order. It also creates the Substrate identity material and database connection Secrets that the releases need. -1. Install kagent. The following command enables gRPC server reflection so that guides that use `grpcurl` can discover the controller API. +1. Save a Helm values file that enables gRPC server reflection, so that guides that use `grpcurl` can discover the controller API. ```bash - KAGENT_HELM_EXTRA_ARGS="--set controller.grpc.reflection=true" \ - kagent install --namespace kagent + cat > kagent-values.yaml < kagent-config.yaml <}}) needs. -2. Wait for the controller to roll out. +4. Wait for the controller to roll out. ```bash kubectl rollout status deployment/kagent-controller -n kagent --timeout=300s ``` diff --git a/docs-site/content/kagent/1.x/setup/model-providers/azure-openai.md b/docs-site/content/kagent/1.x/setup/model-providers/azure-openai.md index 757da8f0..0603fcdc 100644 --- a/docs-site/content/kagent/1.x/setup/model-providers/azure-openai.md +++ b/docs-site/content/kagent/1.x/setup/model-providers/azure-openai.md @@ -92,6 +92,23 @@ spec: azureDeployment: gpt-4o-mini ``` +## Send a file to the model + +The `responses` format is the only one that sends a file with a prompt. It sends a link rather than the bytes, and Azure fetches that link itself, both on the turn that sends the link and on every turn that replays the conversation. A link must therefore stay reachable from Azure for as long as the conversation lives. + +What reaches Azure depends on whether the message gives a link or the bytes, and on the media type. The following table covers every combination that a message can include. + +| What the message includes | What the runtime sends | +| ------------------------ | ---------------------- | +| An `http://` or `https://` link with an `image/` media type | An `input_image` part with the link. | +| An `http://` or `https://` link with any other media type, such as a PDF | An `input_file` part with the link. | +| Inline image bytes | An `input_image` part with the bytes as a `data:` URL. | +| Inline bytes of any other type, such as a PDF or an Office document | Nothing. Azure OpenAI takes no inline bytes outside an image. | + +The runtime refuses a link on any other scheme. When the newest user message includes such a link, the turn fails with `cannot send link "" with media type "": this provider integration can only pass http and https links`. When an older message in the replayed conversation includes one, the runtime replaces the link with a text note that keeps the URL, so a model with a fetch tool can still retrieve the file. + +The runtime sends a file's display name as a text part of its own, placed before the file, because Azure rejects a filename next to a file URL. + ## Authentication | Configuration | Credential | From 9d4290b964f0680e7a411a0a3914030bd03199fa Mon Sep 17 00:00:00 2001 From: Rachael Graham Date: Tue, 13 Oct 2026 22:02:53 -0500 Subject: [PATCH 2/5] edits 1 Signed-off-by: Rachael Graham --- .../content/kagent/1.x/about/core-concepts.md | 8 +++--- .../1.x/about/substrate-runtime/sandboxing.md | 2 +- .../substrate-runtime/standalone-sandboxes.md | 2 +- .../substrate-runtime/suspend-and-resume.md | 2 +- .../kagent/1.x/agents/agent-harness.md | 25 +++++++++++-------- .../kagent/1.x/agents/bring-your-own-agent.md | 22 +++++++++------- .../kagent/1.x/agents/human-in-the-loop.md | 2 +- .../kagent/1.x/agents/system-prompts.md | 2 +- .../content/kagent/1.x/examples/a2a-agents.md | 4 +-- .../content/kagent/1.x/examples/a2a-byo.md | 8 +++--- .../kagent/1.x/examples/agent-delegation.md | 2 +- .../kagent/1.x/examples/agent-substrate.md | 6 ++--- .../kagent/1.x/examples/agentgateway.md | 10 ++++---- .../kagent/1.x/examples/agents-via-mcp.md | 2 +- .../1.x/get-started/your-first-agent.md | 2 +- .../1.x/get-started/your-first-mcp-tool.md | 2 +- .../kagent/1.x/observability/audit-prompts.md | 2 +- .../kagent/1.x/observability/launch-ui.md | 4 +-- .../observability/lightweight-otel-stack.md | 2 +- .../kagent/1.x/observability/metrics.md | 4 +-- .../1.x/observability/substrate-telemetry.md | 2 +- .../kagent/1.x/observability/tracing.md | 13 +++------- .../content/kagent/1.x/operations/debug.md | 6 ++--- .../operations/operational-considerations.md | 15 ++++++----- .../1.x/operations/tune-agent-substrate.md | 6 ++--- .../kagent/1.x/operations/upgrade-from-0x.md | 2 +- .../kagent/1.x/reference/tools-ecosystem.md | 2 +- .../content/kagent/1.x/setup/installation.md | 2 +- .../model-providers/about-model-providers.md | 6 ++--- .../1.x/setup/model-providers/azure-openai.md | 4 +-- .../setup/model-providers/byo-agentgateway.md | 2 +- .../1.x/setup/model-providers/ollama.md | 6 ++--- .../1.x/setup/model-providers/openai.md | 2 +- .../kagent/1.x/skills-and-mcp/about-tools.md | 2 +- 34 files changed, 92 insertions(+), 91 deletions(-) diff --git a/docs-site/content/kagent/1.x/about/core-concepts.md b/docs-site/content/kagent/1.x/about/core-concepts.md index 79a22833..2856d842 100644 --- a/docs-site/content/kagent/1.x/about/core-concepts.md +++ b/docs-site/content/kagent/1.x/about/core-concepts.md @@ -92,7 +92,7 @@ For the complete AgentTemplate schema, see the [API reference]({{< link path="re An **Agent** is a Kubernetes custom resource that pairs _one AgentTemplate with one Harness_. Both sides are required. Under `spec.template`, the Agent sets exactly one of `ref`, which names an existing AgentTemplate, or `inline`, which carries a complete template spec. Under `spec.harnessRef`, the Agent names an existing Harness. A Harness has no inline form. -An inline template is a complete definition rather than an override of a named AgentTemplate, and kagent creates no Kubernetes resource to back one. Every name resolves in the Agent's own namespace, including a name nested inside an inline template. For the fields that express each choice, see the [API reference]({{< link path="reference/api-ref#agent" >}}). +An inline template is a complete definition rather than an override of a named AgentTemplate, and kagent does not create a Kubernetes resource to back one. Every name resolves in the Agent's own namespace, including a name nested inside an inline template. For the fields that express each choice, see the [API reference]({{< link path="reference/api-ref#agent" >}}). The following Agent resource references both sides: @@ -110,7 +110,7 @@ spec: name: kagent ``` -The Agent owns readiness. Its status reports the desired revision, the latest revision that compiled and prepared successfully, any non-blocking compatibility warnings, and the `Accepted`, `ResolvedRefs`, `Compatible`, and `Ready` conditions. An AgentTemplate and a Harness are shared configuration and report no runtime status of their own. +The Agent owns readiness. Its status reports the desired revision, the latest revision that compiled and prepared successfully, any non-blocking compatibility warnings, and the `Accepted`, `ResolvedRefs`, `Compatible`, and `Ready` conditions. An AgentTemplate and a Harness are shared configuration and do not report runtime status of their own. Deleting an Agent retires its definition. Existing Sessions keep the revisions that they pinned. Recreating an Agent under the same name creates a new identity, which cannot inherit the deleted Agent's last successful revision. @@ -132,7 +132,7 @@ You can create a Session with the following command: kagent agent session create --agent assistant -n kagent ``` -After it is created, a Session talks to callers over the {{< gloss "A2A" >}}A2A{{< /gloss >}} (Agent-to-Agent) protocol, through kagent's A2A gateway. Callers address the Agent rather than the Session. The HTTP endpoint is `/agents/{namespace}/{name}` and gRPC carries the same `namespace/name` in the standard A2A `tenant` field. The Session's ID is the A2A `contextId`, so a message that carries no context identifier starts a new conversation, and a message that repeats one continues that conversation. +After it is created, a Session talks to callers over the {{< gloss "A2A" >}}A2A{{< /gloss >}} (Agent-to-Agent) protocol, through kagent's A2A gateway. Callers address the Agent rather than the Session. The HTTP endpoint is `/agents/{namespace}/{name}` and gRPC carries the same `namespace/name` in the standard A2A `tenant` field. The Session's ID is the A2A `contextId`, so a message that does not include a context identifier starts a new conversation, and a message that repeats one continues that conversation. An expiration worker deletes a Session that records no task activity for seven days. The `config.sessions.idleTtl` Helm value sets that window. A value of `0s` turns the expiration worker off. For more information about what the deletion retains, see [Expire idle Sessions]({{< link path="operations/operational-considerations#expire-idle-sessions" >}}). @@ -169,7 +169,7 @@ Each Actor runs in its own sandbox, with its own filesystem and its own durable An agent tool binding can point at another AgentTemplate instead of an MCP server, so a broad-scope agent can hand part of a task to a specialist. For example, a release agent delegates a database question to another agent, gets the answer, and carries on. The specialist keeps its own prompt and its own tools, and the conversation stays with the agent that the caller addressed. -Each subagent binding sets `tools[].subAgent.templateRef` to name an AgentTemplate in the same namespace. The named template compiles under the parent Agent's Harness and runs inside the parent's Actor, so the two agents share one sandbox and the nesting creates no second Actor. A subagent needs no Agent of its own and no matching Harness reference. +Each subagent binding sets `tools[].subAgent.templateRef` to name an AgentTemplate in the same namespace. The named template compiles under the parent Agent's Harness and runs inside the parent's Actor, so the two agents share one sandbox and the nesting does not create a second Actor. A subagent does not need an Agent of its own or a matching Harness reference. Because a subagent runs inside its parent's runtime boundary, nesting stops at one level, and a bound template cannot bind a third. Every agent therefore runs either in its own Actor or in the Actor of the agent that bound it, never deeper. For the remaining rules that a tree must satisfy, see [What a subagent tree allows]({{< link path="skills-and-mcp/about-tools#what-a-subagent-tree-allows" >}}). diff --git a/docs-site/content/kagent/1.x/about/substrate-runtime/sandboxing.md b/docs-site/content/kagent/1.x/about/substrate-runtime/sandboxing.md index e3eb13e9..b62e77ce 100644 --- a/docs-site/content/kagent/1.x/about/substrate-runtime/sandboxing.md +++ b/docs-site/content/kagent/1.x/about/substrate-runtime/sandboxing.md @@ -103,6 +103,6 @@ Agent Substrate creates a Kubernetes NetworkPolicy for each WorkerPool, selectin That policy governs inbound traffic only. Outbound traffic is governed separately by an egress policy that the gateway enforces on every connection that an Actor opens. That policy is default-deny. An Actor reaches a destination only when a rule allows it, and the gateway denies the connection when no rule does. -kagent derives the egress policy from the {{< gloss "AgentTemplate" >}}AgentTemplate{{< /gloss >}}, so an agent needs no egress configuration of its own. The destinations it allows come from what the AgentTemplate already names, such as its model provider and its MCP servers. You author neither policy. Agent Substrate owns the ingress policy, and kagent regenerates the egress policy whenever the AgentTemplate changes. For what the policy holds, how a rule is matched, and how to diagnose a denied request, see [Egress control]({{< link path="about/substrate-runtime/networking-and-egress" >}}). +kagent derives the egress policy from the {{< gloss "AgentTemplate" >}}AgentTemplate{{< /gloss >}}, so an agent does not need egress configuration of its own. The destinations it allows come from what the AgentTemplate already names, such as its model provider and its MCP servers. You author neither policy. Agent Substrate owns the ingress policy, and kagent regenerates the egress policy whenever the AgentTemplate changes. For what the policy holds, how a rule is matched, and how to diagnose a denied request, see [Egress control]({{< link path="about/substrate-runtime/networking-and-egress" >}}). diff --git a/docs-site/content/kagent/1.x/about/substrate-runtime/standalone-sandboxes.md b/docs-site/content/kagent/1.x/about/substrate-runtime/standalone-sandboxes.md index 2afa0df2..a15b7e76 100644 --- a/docs-site/content/kagent/1.x/about/substrate-runtime/standalone-sandboxes.md +++ b/docs-site/content/kagent/1.x/about/substrate-runtime/standalone-sandboxes.md @@ -84,7 +84,7 @@ A SandboxTemplate is a namespaced `api.kagent.dev/v1alpha3` resource that prepar | `substrate.workerPoolRef.name` | Yes | The WorkerPool that this template's Actors are scheduled onto. | | `substrate.snapshotPolicy.location` | Yes | The object storage location for Actor {{< gloss "Snapshot" >}}snapshots{{< /gloss >}}. | - A SandboxTemplate takes no startup command and no guest toggle. kagent supplies the guest entrypoint from the image that `config.sandboxes.guestImage` names. That image replaces the tools image's own entrypoint. Your tools image contributes the installed programs and nothing else. + A SandboxTemplate does not take a startup command or a guest toggle. kagent supplies the guest entrypoint from the image that `config.sandboxes.guestImage` names. That image replaces the tools image's own entrypoint. Your tools image contributes the installed programs and nothing else. 2. Confirm that the template prepared a revision before you create a Sandbox from it. ```bash diff --git a/docs-site/content/kagent/1.x/about/substrate-runtime/suspend-and-resume.md b/docs-site/content/kagent/1.x/about/substrate-runtime/suspend-and-resume.md index bf92097e..ada2c8e5 100644 --- a/docs-site/content/kagent/1.x/about/substrate-runtime/suspend-and-resume.md +++ b/docs-site/content/kagent/1.x/about/substrate-runtime/suspend-and-resume.md @@ -79,7 +79,7 @@ A suspended Session resumes when the next message arrives for it. The kagent con Agent Substrate resolves the Actor's address on every request, so the Actor stays reachable at one name no matter which Worker it is placed on. For more information, see [Sandboxing]({{< link path="about/substrate-runtime/sandboxing#how-traffic-reaches-a-sandboxed-actor" >}}). -Because kagent suspends at every turn boundary, every message to a Session pays for one resume. An idle Session costs no compute, and the next message waits for its Actor to come back. +Because kagent suspends at every turn boundary, every message to a Session pays for one resume. An idle Session does not consume compute, and the next message waits for its Actor to come back. diff --git a/docs-site/content/kagent/1.x/agents/agent-harness.md b/docs-site/content/kagent/1.x/agents/agent-harness.md index a3f47a49..f51bdbbd 100644 --- a/docs-site/content/kagent/1.x/agents/agent-harness.md +++ b/docs-site/content/kagent/1.x/agents/agent-harness.md @@ -42,7 +42,7 @@ EOF | `workload.image` | Yes | The runtime image, pinned by `sha256` digest. A tag alone is rejected, because a revision must be reproducible. | | `workload.command` | For `byo` | Overrides the image entrypoint, up to 32 entries. Required for the `byo` runtime, optional otherwise. Every runtime honors an explicit value, the `kagent` runtime included, whatever language its image is written in. | | `workload.args` | No | Overrides the image arguments, up to 64 entries. An override that you omit stays unset rather than taking a default. | -| `env` | No | Environment variables for the runtime, up to 100. Each entry sets a literal `value`, which is required and can be an empty string. An installation's own settings, such as the runtime log level, come from the chart's `config.runtimeDefaults` block rather than from here. The schema defines no secret-backed source, so the API server rejects a `credentialRef` entry as an unknown field. Put credentials on a ModelConfig or a RemoteMCPServer instead. For more information, see [About model providers]({{< link path="setup/model-providers/about-model-providers#credentials-that-do-not-compile" >}}). | +| `env` | No | Environment variables for the runtime, up to 100. Each entry sets a literal `value`, which is required and can be an empty string. The schema defines no secret-backed source, so the API server rejects a `credentialRef` entry as an unknown field. Put credentials on a ModelConfig or a RemoteMCPServer instead. For more information, see [About model providers]({{< link path="setup/model-providers/about-model-providers#credentials-that-do-not-compile" >}}). Note that the kagent installation's settings, such as the runtime log level, come from the Helm chart's `config.runtimeDefaults` block rather than from this setting. | | `substrate.workerPoolRef.name` | Yes | The {{< gloss "WorkerPool" >}}WorkerPool{{< /gloss >}} that this Harness's Actors are scheduled onto. An operator must provision one before any agent can run. | | `substrate.snapshotPolicy.location` | Yes | The object storage location for Actor {{< gloss "Snapshot" >}}snapshots{{< /gloss >}}. | @@ -231,7 +231,7 @@ Some supported combinations still carry restrictions. | Combination | Restriction | | ----------- | ----------- | -| `codex` with `OpenAI` | Requires `openAI.apiFormat: responses`, and accepts no other `openAI` settings beyond `baseUrl`. | +| `codex` with `OpenAI` | Requires `openAI.apiFormat: responses`, and does not accept any other `openAI` setting beyond `baseUrl`. | | `codex` with `Bedrock` | Accepts only OpenAI `gpt-*` model IDs, and no `bedrock` settings beyond `region`. | | `claude` with `Anthropic` | Accepts no `anthropic` settings beyond `baseUrl`. | | `claude` with `Bedrock` | Accepts no `bedrock` settings beyond `region`. | @@ -302,28 +302,31 @@ Every other `OTEL_*` variable, such as the `OTEL_BSP_*` batch span processor set ### Baggage propagation -kagent runtimes set `OTEL_PROPAGATORS` to `tracecontext` when the environment leaves it unset, so a caller's baggage reaches neither the tools an agent calls nor its model provider. Trace context still propagates. To carry baggage as well, set `OTEL_PROPAGATORS` to `tracecontext,baggage` in the Harness `spec.env` field. The controller reads its own value from the `controller.env` Helm value, and each process reads only its own, so enabling baggage on a runtime does not enable it on the controller. +Baggage is a W3C header that travels with a request alongside its trace context. Each entry in that header is a member, which pairs a name with a string value. Every service that the request reaches can read the members that it carries. -`OTEL_PROPAGATORS` governs both directions. A format that the list omits is neither read from an incoming request nor written to an outgoing one, including the members that the runtime adds itself. +When the environment leaves `OTEL_PROPAGATORS` unset, kagent runtimes set it to `tracecontext`. A caller's baggage does not reach the tools that an agent calls or the agent's model provider, but trace context still propagates. To carry baggage as well, set `OTEL_PROPAGATORS` to `tracecontext,baggage` in the Harness `spec.env` field. The kagent controller reads its own value only from the `controller.env` Helm value, so enabling baggage on a runtime does not enable it on the kagent controller. -With `baggage` listed, the `kagent` runtime adds the request's identity to the baggage of each model call and each MCP tool call. +`OTEL_PROPAGATORS` is a list of wire formats that apply to both incoming and outgoing requests. If an incoming request has a format that is not in the list, the format is not read. Similarly, the runtime does not write an omitted format to an outgoing one. When the list omits `baggage`, the runtime's own members are dropped too. + +When `baggage` is listed, the `kagent` runtime adds the request's identity to the baggage of each model call and each MCP tool call. | Member | Model calls | MCP tool calls | | ------ | ----------- | -------------- | | `gen_ai.agent.name` | Yes | Yes | | `gen_ai.conversation.id` | Yes | Yes | | `a2a.task.id` | Yes | Yes | -| `gen_ai.tool.name` | No | Yes | | `gen_ai.tool.call.id` | No | Yes | -These members are identifiers rather than content or credentials, and the runtime replaces a caller's member of the same name, so a caller cannot claim another conversation's identity downstream. +These members serve as identifiers only, and the runtime can replace a caller's member of the same name. > [!WARNING] -> **Enabling baggage sends the caller's own members to your model provider.** A caller baggage member that kagent does not replace is forwarded unchanged in the `baggage` header of each model API request and each MCP request, so a caller controls what reaches those external services. Enable baggage only where you trust what your callers attach. +> **Enabling baggage sends the caller's own members to your model provider.** A caller baggage member that kagent does not replace is forwarded unchanged in the `baggage` header of each model API request and each MCP request. This means that a caller controls what reaches those external services in the header. Enable baggage only where you trust what your callers attach. -Two limits are worth planning around. The `claude` and `codex` runtimes receive `OTEL_PROPAGATORS`, but kagent adds no identity to their model and tool calls, and neither does the Python runtime, so those five members reach the `kagent` runtime alone. kagent also never copies a baggage member onto a span, so a backend that needs these values to be searchable must add a baggage-to-attributes span processor, in the service that receives the request or in its OpenTelemetry Collector. +Review and plan for the following considerations. +* The `claude` and `codex` runtimes receive `OTEL_PROPAGATORS`, but kagent does not add identity to their model and tool calls. The Python runtime does not add any either, so the four members reach the `kagent` runtime alone. +* kagent never copies a baggage member onto a span. To make these values searchable, add a baggage-to-attributes span processor to the service that receives the request or to its OpenTelemetry Collector. -An MCP session is opened by one request and shared by every later one. The session makes three requests of its own: the event stream, the replies to server requests, and the close. None of the three carries trace context or baggage, because either would name a request that has already ended. +When an agent requires an MCP server, the first tool listing or tool call opens a session with the MCP server, and every later request to that server shares it. The session makes three requests: the event stream, the replies to server requests, and the close. These requests do not carry trace context or baggage, because these name a request that has already ended. ## Check that a Harness is ready @@ -334,7 +337,7 @@ kubectl get harness -n kagent A Harness that is not `Ready` most often names a WorkerPool that does not exist yet. For the specific reason, read its conditions with `kubectl describe harness -n kagent`. -`Ready` covers the Harness's own dependencies, not whether a given agent runs on it. Whether a template compiles against this Harness is reported on the Agent that pairs the two, because an AgentTemplate carries no status of its own. For that check and the conditions it reports, see [Your first agent]({{< link path="get-started/your-first-agent" >}}). +`Ready` covers the Harness's own dependencies, not whether a given agent runs on it. Whether a template compiles against this Harness is reported on the Agent that pairs the two, because an AgentTemplate does not report status of its own. For that check and the conditions it reports, see [Your first agent]({{< link path="get-started/your-first-agent" >}}). ## Next steps diff --git a/docs-site/content/kagent/1.x/agents/bring-your-own-agent.md b/docs-site/content/kagent/1.x/agents/bring-your-own-agent.md index 385999bc..4c70760e 100644 --- a/docs-site/content/kagent/1.x/agents/bring-your-own-agent.md +++ b/docs-site/content/kagent/1.x/agents/bring-your-own-agent.md @@ -54,11 +54,11 @@ Both kinds of agent receive the same inputs. Agent Substrate delivers the two JS | Input | Contents | | ----- | -------- | -| `/config/config.json` | The compiled agent configuration: the resolved system prompt, the model and its settings, every tool, skill, and plugin that the AgentTemplate binds, and a `runtime` object that sets the process settings. | +| `/config/config.json` | The compiled agent configuration, including the resolved system prompt, the model and its settings, every tool, skill, and plugin that the AgentTemplate binds, and a `runtime` object that sets the process settings. | | `/config/agent-card.json` | The agent card that kagent advertises for this agent, rendered as JSON. | | Anything in `spec.env` | Literal values and `credentialRef` lookups from the Harness, resolved before the Actor starts. Agent Substrate accepts at most 32 environment variables on an Actor, and the compiled configuration counts toward that limit. A revision that exceeds it fails to compile rather than starting and misbehaving. | -The `runtime` object is the root of the configuration document, and subagents inherit it. It includes the settings that a process needs before it serves a request. +The `runtime` object is the root of the configuration document, and subagents inherit it. The object includes the settings that a process needs before it serves a request. ```json { @@ -76,15 +76,15 @@ The `runtime` object is the root of the configuration document, and subagents in | Field | Contents | | ----- | -------- | -| `control_plane_url` | The kagent API that the agent reports task state to. The Go and Python helpers both require it. | +| `control_plane_url` | The kagent API that the agent reports task state to. Required by the Go and Python helpers. | | `gateway_url` | The A2A gateway that the agent reaches other agents and MCP servers through. | -| `name` and `namespace` | The Agent's name and the AgentTemplate's namespace, which kagent's own helpers use to scope sessions. | +| `name` and `namespace` | The Agent's name and the AgentTemplate's namespace, which kagent's helpers use to scope sessions. | | `port` | The port to serve A2A on. The controller sets `80` for every runtime, and the agent card advertises that same port. | | `log_level` | `debug`, `info`, `warn`, or `error`, from the `config.runtimeDefaults.logging.level` Helm value. | | `max_content_length` | The largest A2A request body to accept, in bytes. Zero means unlimited, and an absent field keeps the helper's own default. | > [!IMPORTANT] -> The process settings moved into the configuration document in {{< reuse "kagent-docs/versions/kagent.md" >}}. An image that reads `KAGENT_PORT`, `KAGENT_NAME`, `KAGENT_NAMESPACE`, `KAGENT_API_URL`, or `KAGENT_A2A_GRPC_ADDRESS` finds none of them set, and naming one in `spec.env` sets a variable that nothing reads. Read the `runtime` object instead. +> The process settings moved into the configuration document in {{< reuse "kagent-docs/versions/kagent.md" >}}. An image that reads `KAGENT_PORT`, `KAGENT_NAME`, `KAGENT_NAMESPACE`, `KAGENT_API_URL`, or `KAGENT_A2A_GRPC_ADDRESS` reads them as unset, and naming one of these variables in `spec.env` sets a variable that nothing reads. Read the `runtime` object instead. ## Configure a BYO Harness @@ -186,7 +186,7 @@ if err != nil { return application.Run() ``` -`ControlPlaneURL` is required unless you supply a `ControllerClient` of your own, because the helper reports task state to the kagent API. `Port` defaults to `8080` when both the field and `runtime.port` are empty, and kagent never dials that port, so keep the `cmp.Or` fallback to `80`. +Because the helper reports task state to the kagent API, you must set either `ControlPlaneURL` or your own `ControllerClient` in `app.AppConfig`. When `runtime.port` is omitted or unset, `cmp.Or` supplies `80`. Without that fallback, the helper listens on its own `8080` by default, which kagent never dials. For a complete executor, see [`go/core/test/byoa2a/main.go`](https://github.com/kagent-dev/kagent/blob/main/go/core/test/byoa2a/main.go) in the kagent repository. {{% /tab %}} @@ -207,20 +207,24 @@ app = KAgentApp( ).build() ``` -`from_file` raises when `control_plane_url`, `gateway_url`, `name`, or `namespace` is missing, so a standalone process that runs outside Agent Substrate must supply its own `/config/config.json`. `KAgentApp` wraps a Google Agent Development Kit (ADK) agent rather than an arbitrary framework. To bind the address that the configuration names rather than the default, pass the parsed document as the `agent_config` argument. The `a2a_grpc_address` argument overrides both. +`from_file` raises `ValueError` when the document omits `control_plane_url`, `gateway_url`, `name`, or `namespace`, so a standalone process that runs outside Agent Substrate must supply its own `/config/config.json`. `KAgentApp` wraps a Google Agent Development Kit (ADK) agent rather than an arbitrary framework. + +To bind the address that the configuration names rather than the default, pass the parsed document as the `agent_config` argument. The `a2a_grpc_address` argument overrides both. {{% /tab %}} {{% tab name="Any other language" %}} Implement the contract directly: 1. Serve `lf.a2a.v1.A2AService` over gRPC on port 80. Generate the stubs from the [A2A protocol](https://a2a-protocol.org) definitions. 2. Serve `GET /readyz` over HTTP on port 8081, returning `200`. -3. Read the compiled configuration from the `KAGENT_CONFIG_JSON` environment variable if the agent should honor its AgentTemplate, or ignore it and run an opaque agent. The variable is the delivery mechanism that the kagent helpers materialize into `/config/config.json`, and an image without those helpers parses it directly. +3. To honor the AgentTemplate, read the compiled configuration from the `KAGENT_CONFIG_JSON` environment variable. An agent that ignores the variable runs as an opaque agen. + +The kagent helpers read `KAGENT_CONFIG_JSON` and materialize its contents into `/config/config.json`. An image without those helpers parses the variable directly. {{% /tab %}} {{< /tabs >}} ## Known limitations -- **The A2A interface is fixed.** kagent advertises `http://127.0.0.1:80` over gRPC, and `spec.byo` takes no field to change the address, the port, or the protocol. An image that serves A2A over HTTP JSON-RPC alone cannot run on this runtime. +- **The A2A interface is fixed.** kagent advertises `http://127.0.0.1:80` over gRPC, and `spec.byo` does not take a field that changes the address, the port, or the protocol. An image that serves A2A over HTTP JSON-RPC alone cannot run on this runtime. - **The `kagent-langgraph` and `kagent-crewai` adapters do not qualify yet.** Both build a FastAPI application with A2A JSON-RPC routes and no gRPC server, so neither satisfies the contract as shipped. Running LangGraph or CrewAI under `byo` currently means serving A2A over gRPC yourself. - **Long-term memory is unavailable.** Memory is configured under `spec.kagent.memory` and wired only by the `kagent` runtime's compiler. A `byo` Harness has no equivalent setting. For what a BYO image would need to replace, see [Agent memory]({{< link path="agents/agent-memory" >}}). - **A broken port mapping presents as a healthy agent.** For the failure and what avoids it, see [The A2A contract](#the-a2a-contract). diff --git a/docs-site/content/kagent/1.x/agents/human-in-the-loop.md b/docs-site/content/kagent/1.x/agents/human-in-the-loop.md index 784b6476..3ce4681e 100644 --- a/docs-site/content/kagent/1.x/agents/human-in-the-loop.md +++ b/docs-site/content/kagent/1.x/agents/human-in-the-loop.md @@ -178,7 +178,7 @@ An `ask_user_request` carries an `id` and a list of `questions`. The response ec A paused task waits. To resume, the client sends a message on the same task and context, carrying the response payload. kagent rejects a resume attempt on a task that is not waiting, with `task is not waiting for input`. -While the task waits, kagent pauses the {{< gloss "Actor" >}}Actor{{< /gloss >}} rather than suspending it. A pause keeps the running process in a full {{< gloss "Snapshot" >}}snapshot{{< /gloss >}} on node-local storage, so a runtime that holds a live process across the wait, such as `codex` or `claude`, continues the same turn on resume. The {{< gloss "Worker" >}}Worker{{< /gloss >}} is released in the meantime, so a conversation that sits at `INPUT_REQUIRED` costs no pool capacity. For more information on the suspend that a finished turn uses instead, see [Suspend and resume]({{< link path="about/substrate-runtime/suspend-and-resume" >}}). +While the task waits, kagent pauses the {{< gloss "Actor" >}}Actor{{< /gloss >}} rather than suspending it. A pause keeps the running process in a full {{< gloss "Snapshot" >}}snapshot{{< /gloss >}} on node-local storage, so a runtime that holds a live process across the wait, such as `codex` or `claude`, continues the same turn on resume. The {{< gloss "Worker" >}}Worker{{< /gloss >}} is released in the meantime, so a conversation that sits at `INPUT_REQUIRED` does not consume pool capacity. For more information on the suspend that a finished turn uses instead, see [Suspend and resume]({{< link path="about/substrate-runtime/suspend-and-resume" >}}). Because the {{< gloss "Transcript" >}}transcript{{< /gloss >}} only grows, the question and the answer both stay in the task history, so a later reader can see what was asked and what a person decided. diff --git a/docs-site/content/kagent/1.x/agents/system-prompts.md b/docs-site/content/kagent/1.x/agents/system-prompts.md index d6057f83..ebe5d227 100644 --- a/docs-site/content/kagent/1.x/agents/system-prompts.md +++ b/docs-site/content/kagent/1.x/agents/system-prompts.md @@ -127,7 +127,7 @@ spec: Prompt problems surface on the `ResolvedRefs` condition, because they are reference failures rather than runtime errors. No new revision compiles. An {{< gloss "Agent" >}}Agent{{< /gloss >}} that has never been ready cannot start a Session, and one that was ready before keeps starting new Sessions from its last ready revision. -An AgentTemplate carries no status of its own, so read the conditions from the Agent that names it. +An AgentTemplate does not report status of its own, so read the conditions from the Agent that names it. ```bash kubectl get agent my-first-agent -n kagent -o json \ diff --git a/docs-site/content/kagent/1.x/examples/a2a-agents.md b/docs-site/content/kagent/1.x/examples/a2a-agents.md index 50540429..4d77db2f 100644 --- a/docs-site/content/kagent/1.x/examples/a2a-agents.md +++ b/docs-site/content/kagent/1.x/examples/a2a-agents.md @@ -351,13 +351,13 @@ Answering a pause needs the human-in-the-loop extension, which a caller requests ``` > [!IMPORTANT] -> A caller that does not request the extension is still interrupted. The task stops at `TASK_STATE_INPUT_REQUIRED` exactly as before, and the question arrives as ordinary text on the status message, but the metadata carries no request `id`. Without that `id` there is nothing to answer, so the task waits until something cancels it. Request the extension on any call to an agent that can pause. +> A caller that does not request the extension is still interrupted. The task stops at `TASK_STATE_INPUT_REQUIRED` exactly as before, and the question arrives as ordinary text on the status message, but the metadata does not include a request `id`. Without that `id` there is nothing to answer, so the task waits until something cancels it. Request the extension on any call to an agent that can pause. A tool approval works the same way with a different payload, `tool_approval_request` answered by `tool_approval_response`. For the pause kinds, the approval model, and what a nested agent's pause looks like, see [Human in the loop]({{< link path="agents/human-in-the-loop" >}}). ## Clean up -* This example creates no Kubernetes resources, so you have nothing to delete. +* This example does not create Kubernetes resources, so you have nothing to delete. * You can stop the 8083 port-forward for the kagent-controller service with `Ctrl+C`. * The tasks that your messages created stay on the Session as part of its conversation, and deleting the Session removes them. diff --git a/docs-site/content/kagent/1.x/examples/a2a-byo.md b/docs-site/content/kagent/1.x/examples/a2a-byo.md index 5d8af041..62e844f9 100644 --- a/docs-site/content/kagent/1.x/examples/a2a-byo.md +++ b/docs-site/content/kagent/1.x/examples/a2a-byo.md @@ -90,7 +90,7 @@ A `byo` Harness carries two fields that the other runtimes do not need: an empty ``` > [!NOTE] - > This Harness sets no port, because `byoa2a/main.go` reads `runtime.port` from the compiled configuration and falls back to `80`. An image that does neither listens on the wrong port and still reports `READY`. For that trap, see [Bring your own agent]({{< link path="agents/bring-your-own-agent#the-a2a-contract" >}}). + > This Harness does not set a port, because `byoa2a/main.go` reads `runtime.port` from the compiled configuration and falls back to `80`. An image that neither reads `runtime.port` nor defaults to `80` listens on the wrong port and still reports `READY`. For that trap, see [Bring your own agent]({{< link path="agents/bring-your-own-agent#the-a2a-contract" >}}). 2. Create the AgentTemplate and an Agent that pairs it with the Harness. `description` is the only field that this template needs. ```yaml @@ -119,7 +119,7 @@ A `byo` Harness carries two fields that the other runtimes do not need: an empty `modelConfig` is absent on purpose. kagent requires one for the `kagent` runtime and treats it as optional for `byo`, because a BYO image chooses its own model, or no model at all. -3. Confirm that the Agent compiled. An AgentTemplate carries no status, so the Agent reports the state of the pair. +3. Confirm that the Agent compiled. An AgentTemplate does not report status, so the Agent reports the state of the pair. ```bash kubectl get agent byo-example-agent -n kagent \ -o jsonpath='{.status.conditions[*].type}{"\n"}{.status.conditions[*].status}{"\n"}' @@ -211,9 +211,9 @@ application, err := app.New(app.AppConfig{ }, executor{}) ``` -`ControlPlaneURL` is required, because the helper reports task state to the kagent API. The `cmp.Or` fallbacks keep the image runnable outside Agent Substrate, where no configuration document exists. All of this stays as it is in an agent of your own. +`ControlPlaneURL` is required because the helper reports task state to the kagent API. The `cmp.Or` fallbacks keep the image runnable outside Agent Substrate, where no configuration document exists. All of this stays as it is in an agent of your own. -This agent ignores the rest of the compiled configuration. To honor the AgentTemplate instead, read the other fields of the document that `LoadAgentConfig` returns. For what it holds, see [Bring your own agent]({{< link path="agents/bring-your-own-agent#opaque-and-configured-agents" >}}). +This agent ignores the rest of the compiled configuration. To honor the AgentTemplate instead, read the other fields of the document that `LoadAgentConfig` returns. To review what it holds, see [Bring your own agent]({{< link path="agents/bring-your-own-agent#opaque-and-configured-agents" >}}). ## Build and run your own agent diff --git a/docs-site/content/kagent/1.x/examples/agent-delegation.md b/docs-site/content/kagent/1.x/examples/agent-delegation.md index e7abd977..06a17d4e 100644 --- a/docs-site/content/kagent/1.x/examples/agent-delegation.md +++ b/docs-site/content/kagent/1.x/examples/agent-delegation.md @@ -70,7 +70,7 @@ Apply two ordinary AgentTemplates. Nothing marks them as bound, because a templa EOF ``` -2. Confirm that both templates exist. An AgentTemplate carries no status of its own, because the Agent that pairs it with a Harness owns readiness. +2. Confirm that both templates exist. An AgentTemplate does not report status of its own, because the Agent that pairs it with a Harness owns readiness. ```bash kagent agent template list ``` diff --git a/docs-site/content/kagent/1.x/examples/agent-substrate.md b/docs-site/content/kagent/1.x/examples/agent-substrate.md index 2a3ce519..e10b0415 100644 --- a/docs-site/content/kagent/1.x/examples/agent-substrate.md +++ b/docs-site/content/kagent/1.x/examples/agent-substrate.md @@ -38,7 +38,7 @@ Checkpoints have no kagent CLI commands yet, so this example calls `CheckpointSe kubectl ate get actors --atespace kagent ``` - Between turns, the Actor reports `ACTOR_STATE_SUSPENDED` and holds no Worker, so the `ATEOM POD` column reads `` and the `ATEOM IP` column is blank. The `VERSION` column is the Actor record's revision counter, which increases each time the record is updated. Example output: + Between turns, the Actor reports `ACTOR_STATE_SUSPENDED` and does not hold a Worker, so the `ATEOM POD` column reads `` and the `ATEOM IP` column is blank. The `VERSION` column is the Actor record's revision counter, which increases each time the record is updated. Example output: ```console ATESPACE NAME TEMPLATE STATE ATEOM POD ATEOM IP VERSION AGE kagent session-0198c3d7-4f2a-7b61-9c3e-5d8f7a2b4e10 kagent/my-first-agent-5f2b3c1a9e8d ACTOR_STATE_SUSPENDED 4 11m @@ -67,7 +67,7 @@ Checkpoints have no kagent CLI commands yet, so this example calls `CheckpointSe kubectl ate get actors --atespace kagent ``` - The Actor is back to `ACTOR_STATE_SUSPENDED` and holds no Worker again, at a higher `VERSION` than the listing in step 1. The `NAME` and `AGE` columns confirm that this is the same Actor throughout, rather than a new one per turn. Example output: + The Actor is back to `ACTOR_STATE_SUSPENDED` and again does not hold a Worker, at a higher `VERSION` than the listing in step 1. The `NAME` and `AGE` columns confirm that this is the same Actor throughout, rather than a new one per turn. Example output: ```console ATESPACE NAME TEMPLATE STATE ATEOM POD ATEOM IP VERSION AGE kagent session-0198c3d7-4f2a-7b61-9c3e-5d8f7a2b4e10 kagent/my-first-agent-5f2b3c1a9e8d ACTOR_STATE_SUSPENDED 8 13m @@ -78,7 +78,7 @@ Checkpoints have no kagent CLI commands yet, so this example calls `CheckpointSe kagent agent session list ``` - The Session reports `READY`, even though the Actor that runs it holds no Worker. Example output: + The Session reports `READY`, even though the Actor that runs it does not hold a Worker. Example output: ```console +--------------------------------------+----------------+-------+----------------------+ | ID | AGENT | STATE | CREATED | diff --git a/docs-site/content/kagent/1.x/examples/agentgateway.md b/docs-site/content/kagent/1.x/examples/agentgateway.md index cbb10910..6e796624 100644 --- a/docs-site/content/kagent/1.x/examples/agentgateway.md +++ b/docs-site/content/kagent/1.x/examples/agentgateway.md @@ -69,7 +69,7 @@ Only `policies.promptGuard` is new in this example. The routing fields and `poli 2. Set up [agentgateway model routing]({{< link path="setup/model-providers/byo-agentgateway#set-up-agentgateway-model-routing" >}}), which installs agentgateway with `--set agentgatewayModels.enabled=true` and creates a `Gateway` named `agentgateway-proxy` and an `AgentgatewayModel` named `gpt-4o-mini`. -3. Create the [ModelConfig]({{< link path="setup/model-providers/byo-agentgateway#create-the-modelconfig" >}}) that points at the gateway. This example uses a ModelConfig named `agentgateway-model-config` in the `kagent` namespace, and the deployment that it reaches enforces no API key authentication. +3. Create the [ModelConfig]({{< link path="setup/model-providers/byo-agentgateway#create-the-modelconfig" >}}) that points at the gateway. This example uses a ModelConfig named `agentgateway-model-config` in the `kagent` namespace, and the deployment that it reaches does not enforce API key authentication. ## Add the prompt guard to the model @@ -153,7 +153,7 @@ Two paths are worth checking in order. Calling the gateway directly isolates the The prompt contained personally identifiable information. ``` -3. To confirm that ordinary traffic still reaches the provider, send a prompt that carries no PII. +3. To confirm that ordinary traffic still reaches the provider, send a prompt that does not contain PII. ```bash curl -i http://$AGENTGATEWAY_ADDRESS/v1/chat/completions \ -H "Content-Type: application/json" \ @@ -219,7 +219,7 @@ Two paths are worth checking in order. Calling the gateway directly isolates the > [!CAUTION] > **A rejected turn strands the conversation permanently.** An agent is not a single-shot chat client: the {{< gloss "Transcript" >}}transcript{{< /gloss >}} is append-only, and a failed turn does not remove the message that failed. The Session resends the whole conversation on every turn, so the prompt that tripped the guard is sent again, and rejected again, for the rest of that Session's life. > -> The following sequence reproduces it. A fresh Session answers `Summarize the most recent ticket` normally. Send `Summarize the ticket from alex@example.com` next, and that turn fails with the `403`. Then send `What is 2+2?`, which carries no PII of its own: that turn fails with a `403` as well, and so does every turn after it. +> The following sequence reproduces it. A fresh Session answers `Summarize the most recent ticket` normally. Send `Summarize the ticket from alex@example.com` next, and that turn fails with the `403`. Then send `What is 2+2?`, which does not contain PII of its own: that turn fails with a `403` as well, and so does every turn after it. > > Creating a new Session is the only way to recover a conversation that a `Reject` guard has stopped. For agents, that cost is the strongest argument for [masking instead](#mask-instead-of-reject). @@ -241,7 +241,7 @@ Masking also repairs a conversation that a `Reject` guard already stopped. Chang --task "Summarize this ticket: alex@example.com reports that checkout returns 503 errors during peak hours." ``` - The turn succeeds this time. The agent answers, or asks a follow-up question, and the address never reaches the provider. The patch replaces the whole `request` list, so it also drops the `response.message` that the rejection used. A `Mask` guard returns no message, because it rejects nothing. + The turn succeeds this time. The agent answers, or asks a follow-up question, and the address never reaches the provider. The patch replaces the whole `request` list, so it also drops the `response.message` that the rejection used. A `Mask` guard does not return a message, because it rejects nothing. > [!WARNING] > **A guard on the response does not inspect streamed content unless you enable it.** Prompt guards default to skipping streaming responses to preserve throughput. Set `policies.promptGuard.streaming: Enabled` to guard them, and note that `Mask` is never applied to a streamed response even then: a guard can reject streamed content, and matched text in a stream that is not rejected passes through unmodified. @@ -362,7 +362,7 @@ An `AgentgatewayPolicy` holds the same `promptGuard` configuration, and one poli kubectl delete agenttemplate support-triage -n kagent ``` -2. Remove the guard from the model, leaving the routing and the provider credentials in place. The patch names `promptGuard` rather than `policies`, because `policies` also holds the `auth` that lets the gateway reach the provider. A merge patch also succeeds when the model carries no guard, which is the case if you followed [Attach the guard to a route instead](#attach-the-guard-to-a-route-instead). +2. Remove the guard from the model, leaving the routing and the provider credentials in place. The patch names `promptGuard` rather than `policies`, because `policies` also holds the `auth` that lets the gateway reach the provider. A merge patch also succeeds when the model does not name a guard, which is the case if you followed [Attach the guard to a route instead](#attach-the-guard-to-a-route-instead). ```bash kubectl patch agentgatewaymodel gpt-4o-mini -n agentgateway-system --type merge -p '{"spec":{"policies":{"promptGuard":null}}}' ``` diff --git a/docs-site/content/kagent/1.x/examples/agents-via-mcp.md b/docs-site/content/kagent/1.x/examples/agents-via-mcp.md index a1b17820..5805a02e 100644 --- a/docs-site/content/kagent/1.x/examples/agents-via-mcp.md +++ b/docs-site/content/kagent/1.x/examples/agents-via-mcp.md @@ -382,7 +382,7 @@ When an agent pauses to ask something, the task's status becomes `input_required }' ``` - The reply confirms only that the update was accepted, and carries no agent output. Example output: + The reply confirms only that the update was accepted, and does not include agent output. Example output: ```json { "resultType": "complete" } ``` diff --git a/docs-site/content/kagent/1.x/get-started/your-first-agent.md b/docs-site/content/kagent/1.x/get-started/your-first-agent.md index 5ac082f6..58a7406e 100644 --- a/docs-site/content/kagent/1.x/get-started/your-first-agent.md +++ b/docs-site/content/kagent/1.x/get-started/your-first-agent.md @@ -75,7 +75,7 @@ Three resources define a runnable agent: a Harness holds the runtime, an AgentTe name: my-first-harness ``` > [!NOTE] - > `spec.template` takes either a reference, as shown here, or a complete AgentTemplate spec under `inline`. An inline template is a complete value rather than an override, and kagent creates no Kubernetes object to back it. `spec.harnessRef` takes a reference only, so a Harness must exist before an Agent can name it. + > `spec.template` takes either a reference, as shown here, or a complete AgentTemplate spec under `inline`. An inline template is a complete value rather than an override, and kagent does not create a Kubernetes object to back it. `spec.harnessRef` takes a reference only, so a Harness must exist before an Agent can name it. 4. Confirm that the Agent is ready. `READY` reports whether kagent compiled and prepared a runtime {{< gloss "Revision" >}}revision{{< /gloss >}} for the pair. ```bash diff --git a/docs-site/content/kagent/1.x/get-started/your-first-mcp-tool.md b/docs-site/content/kagent/1.x/get-started/your-first-mcp-tool.md index 3b8f2150..29bb1050 100644 --- a/docs-site/content/kagent/1.x/get-started/your-first-mcp-tool.md +++ b/docs-site/content/kagent/1.x/get-started/your-first-mcp-tool.md @@ -37,7 +37,7 @@ kagent records what it discovered on the server's status, so the tool names come ``` > [!NOTE] - > The built-in server is installed only when the `kagent-tools.enabled` Helm value is `true`, which is the default. If the command returns no resources, either re-install with that value enabled, or use your own server as described in [Bind your own MCP server](#bind-your-own-mcp-server). + > The built-in server is installed only when the `kagent-tools.enabled` Helm value is `true`, which is the default. If the command does not return any resources, either re-install with that value enabled, or use your own server as described in [Bind your own MCP server](#bind-your-own-mcp-server). 2. Re-apply the `my-first-template` AgentTemplate with a `spec.tools` list and a system prompt that tells the model what the tools are for. ```yaml diff --git a/docs-site/content/kagent/1.x/observability/audit-prompts.md b/docs-site/content/kagent/1.x/observability/audit-prompts.md index 108f4651..69450a80 100644 --- a/docs-site/content/kagent/1.x/observability/audit-prompts.md +++ b/docs-site/content/kagent/1.x/observability/audit-prompts.md @@ -15,7 +15,7 @@ The `kagent` runtime does not write prompts or replies to log records, so a logg ### What each runtime records -Each runtime records the content on its own instrumentation, so where the content lands differs by runtime. The settings in the last column are the fields of the `config.runtimeDefaults.telemetry` Helm block. For the available runtimes, see [Choose a runtime]({{< link path="agents/agent-harness#choose-a-runtime" >}}). +Each runtime records the content on its own instrumentation, so where the content lands differs by runtime. The settings in the last column are the fields in the `config.runtimeDefaults.telemetry` Helm block. For the available runtimes, see [Choose a runtime]({{< link path="agents/agent-harness#choose-a-runtime" >}}). | Runtime | Where the content goes | Settings | | ------- | ---------------------- | -------- | diff --git a/docs-site/content/kagent/1.x/observability/launch-ui.md b/docs-site/content/kagent/1.x/observability/launch-ui.md index 41d5244e..7978b8d1 100644 --- a/docs-site/content/kagent/1.x/observability/launch-ui.md +++ b/docs-site/content/kagent/1.x/observability/launch-ui.md @@ -61,7 +61,7 @@ The UI service is a `ClusterIP` by default, so it is reachable only from inside {{% /tab %}} {{< /tabs >}} -The dashboard opens on a summary of what the controller has loaded. A fresh installation reports no agents and no conversations, and already counts the model configuration and the {{< gloss "Model Context Protocol" >}}Model Context Protocol{{< /gloss >}} (MCP) servers that the chart installed. +The dashboard opens on a summary of what the controller has loaded. A fresh installation counts zero agents and zero conversations, and already counts the model configuration and the {{< gloss "Model Context Protocol" >}}Model Context Protocol{{< /gloss >}} (MCP) servers that the chart installed. {{< reuse-image-light src="img/kagent-ui-dashboard.png" alt="The kagent dashboard after a fresh installation" caption="Figure: The kagent dashboard after a fresh installation" >}} {{< reuse-image-dark srcDark="img/kagent-ui-dashboard-dark.png" alt="The kagent dashboard after a fresh installation" caption="Figure: The kagent dashboard after a fresh installation" >}} @@ -134,7 +134,7 @@ The following limits apply to attachments: | gRPC message that the kagent API accepts | 16 MiB | The controller's gRPC server, and every kagent gRPC client. The limit is a compiled-in constant that no Helm value changes. | | Request body that the UI's nginx accepts | `ui.nginx.clientMaxBodySize`, default `20m` | The UI pod's nginx. The default sits just above the 16 MiB gRPC message limit. | -The UI falls back to the file extension when the browser reports no media type, or the wrong one, for a file. For example, browsers report no media type for `.md` files. +The UI falls back to the file extension when the browser does not report a media type for a file, or reports the wrong one. For example, browsers do not report a media type for `.md` files. If you host a reverse proxy in front of the UI, the proxy applies its own body-size limit, and a default nginx allows only `1m`. Raise the limit as shown in [Serve the UI under a sub-path]({{< link path="setup/reverse-proxy#proxy-shapes" >}}). diff --git a/docs-site/content/kagent/1.x/observability/lightweight-otel-stack.md b/docs-site/content/kagent/1.x/observability/lightweight-otel-stack.md index 2aa1d8d6..14766124 100644 --- a/docs-site/content/kagent/1.x/observability/lightweight-otel-stack.md +++ b/docs-site/content/kagent/1.x/observability/lightweight-otel-stack.md @@ -51,7 +51,7 @@ For what kagent and Agent Substrate each send, see [About the stack]({{< link pa ## Install Jaeger -Install [Jaeger](https://www.jaegertracing.io/) in all-in-one mode, which stores traces in memory and needs no other components. +Install [Jaeger](https://www.jaegertracing.io/) in all-in-one mode, which stores traces in memory and does not need other components. ```bash helm upgrade --install jaeger jaeger \ diff --git a/docs-site/content/kagent/1.x/observability/metrics.md b/docs-site/content/kagent/1.x/observability/metrics.md index 698f7cbe..93e247af 100644 --- a/docs-site/content/kagent/1.x/observability/metrics.md +++ b/docs-site/content/kagent/1.x/observability/metrics.md @@ -23,7 +23,7 @@ config: secure: true ``` -The two blocks divide the listener from its Kubernetes packaging. `config.metrics` sets the address that the controller binds and whether it serves over HTTPS, and `controller.metrics` creates the Service, the ClusterRoles, and the optional ServiceMonitor. Setting `controller.metrics.enabled` to `false` overrides `config.metrics.bindAddress` with `0`, which disables the listener as well. Change the port through `config.metrics.bindAddress`, because the chart derives the Service `targetPort` and the pod `containerPort` from it at template time. +These two sections divide the listener from its Kubernetes packaging. `config.metrics` sets the address that the controller binds and whether it serves over HTTPS. `controller.metrics` creates the Service, the ClusterRoles, and the optional ServiceMonitor. Setting `controller.metrics.enabled` to `false` overrides `config.metrics.bindAddress` with `0`, which disables the listener as well. Change the port through `config.metrics.bindAddress`, because the chart derives the Service `targetPort` and the pod `containerPort` from it at template time. Grant your Prometheus instance access in one of the following ways. @@ -42,7 +42,7 @@ Besides the standard Go runtime and process metrics, the controller reports the | `workqueue_depth`, `workqueue_queue_duration_seconds`, `workqueue_retries_total` | Gauge, histogram, counter | Work waiting for a reconciler, how long it waits, and how often it is retried. | | `leader_election_master_status` | Gauge | `1` on the controller replica that holds the leader lease. With several replicas, exactly one reports `1`. | | `rest_client_requests_total` | Counter | Requests from the controller to the Kubernetes API server, by status code. | -| `kagent_runtime_revision_gc_pending` | Gauge | Runtime revisions that the last successful discovery found eligible for cleanup. The controller reports a cached count, so a scrape costs no database or network call. The metric is absent until a discovery succeeds, and `0` reports a discovery that matched nothing. A failed discovery holds the previous value rather than clearing it. | +| `kagent_runtime_revision_gc_pending` | Gauge | Runtime revisions that the last successful discovery found eligible for cleanup. The controller reports a cached count, so a scrape does not make a database or network call. The metric is absent until a discovery succeeds, and `0` reports a discovery that matched nothing. A failed discovery holds the previous value rather than clearing it. | | `kagent_runtime_revision_gc_duration_seconds` | Histogram | Time that one revision cleanup attempt takes, by the `kagent_gc_stage` label: `discovery` or `collection`. A `collection` attempt covers the database claim, the Agent Substrate read and deletion, and the database finalization. A failed attempt carries an `error_type` label holding a gRPC status code name, or `_OTHER` for every other failure, so the series that carry the label give both the failure rate and the reason. | The Agent Substrate `atecontroller` component reports the same `controller_runtime_*` and `workqueue_*` metrics for its own reconcilers. To keep the two apart in a query, filter by the scrape job, such as `job="kagent-controller-metrics"` in the OTel stack. diff --git a/docs-site/content/kagent/1.x/observability/substrate-telemetry.md b/docs-site/content/kagent/1.x/observability/substrate-telemetry.md index c3cacfb9..de457448 100644 --- a/docs-site/content/kagent/1.x/observability/substrate-telemetry.md +++ b/docs-site/content/kagent/1.x/observability/substrate-telemetry.md @@ -153,7 +153,7 @@ The Agent Substrate API server, `ateapi`, writes an `Actor state changed` record | `resuming` | A turn woke the Actor, and a Worker is restoring it. | | `running` | The Actor is on a Worker and can serve the turn. | | `suspending` | The turn ended, and the Actor is writing its snapshot. | -| `suspended` | The snapshot is written, and the Actor holds no Worker. | +| `suspended` | The snapshot is written, and the Actor does not hold a Worker. | | `deleting`, `deleted` | The Session was deleted. `deleted` is the last record that an Actor gets. | `ateapi` runs more than one replica, and each replica writes only the changes that it handles. Read the logs of every replica. diff --git a/docs-site/content/kagent/1.x/observability/tracing.md b/docs-site/content/kagent/1.x/observability/tracing.md index d9361e0c..80fde703 100644 --- a/docs-site/content/kagent/1.x/observability/tracing.md +++ b/docs-site/content/kagent/1.x/observability/tracing.md @@ -66,7 +66,7 @@ The `kagent` runtime creates the same spans for every agent, and most span names | `invoke_agent ` | Once per request, named for the {{< gloss "Agent" >}}Agent{{< /gloss >}} that serves it, with each hyphen replaced by an underscore. The Agent `my-first-agent` produces `invoke_agent my_first_agent`, while its service name keeps the hyphens, so the two spellings differ. | | `generate_content ` | Once per model call, named for the model that was called. | | `execute_tool ` | Once per tool call, named for the tool that was called. Records the call's arguments and the tool's reply in the `gcp.vertex.agent.tool_call_args` and `gcp.vertex.agent.tool_response` attributes. | -| `execute_tool (merged)` | Once per model turn that calls more than one tool, as the parent of that turn's `execute_tool` spans. A turn that calls a single tool creates no merged span. | +| `execute_tool (merged)` | Once per model turn that calls more than one tool, as the parent of that turn's `execute_tool` spans. A turn that calls a single tool does not create a merged span. | ### Correlation attributes @@ -97,9 +97,6 @@ The runtime also adds each scalar value in the A2A message's metadata as an `a2a Tracing is off by default. Turning it on is a Helm change, because the controller reads its tracing configuration from the environment and passes that configuration to the agent runtimes that the controller starts. The following steps send traces to the collector that both stack guides install. To send traces to another OTLP backend, change the endpoint. -> [!IMPORTANT] -> The telemetry values moved again in {{< reuse "kagent-docs/versions/kagent.md" >}}. The whole top-level `otel` block was removed, and `config.telemetry` and `config.runtimeDefaults.telemetry` replace it. A release that still sets `otel.traces.enabled` or `otel.exporter.otlp.endpoint` keeps a value that no template reads, and tracing turns off without reporting an error. Write the replacements into a values file and upgrade with `--values`, as shown in the following steps. For the rest of the move and the Helm flags that do not carry you across it, see [Controller configuration]({{< link path="operations/operational-considerations#controller-configuration" >}}). - 1. Save the current revision of your Agent. A later step uses it to tell when kagent recompiles the Agent with the new settings. The command first waits for any recompile that is still in progress, such as one from an earlier Helm upgrade, so that it saves a finished revision. ```bash for i in $(seq 1 60); do @@ -143,9 +140,7 @@ Tracing is off by default. Turning it on is a Helm change, because the controlle | `traces.protocol` | `grpc` or `http/protobuf`. Defaults to `grpc`, which matches the port `4317` in the example endpoint. Point `http/protobuf` at port `4318` instead. | | `traces.timeout` | The export timeout, as a duration in seconds with an `s` suffix. Defaults to `10s`. | - A signal has no separate switch. The controller exports a signal when you set its endpoint, and compiles an explicit off state when you omit the endpoint. Omit the key rather than leaving it empty: an empty endpoint fails validation. - - The controller validates its configuration file at startup and exits when a setting is invalid, so a malformed endpoint stops the controller rather than disabling one signal. An endpoint must be an absolute `http://` or `https://` URL. The controller rejects one that includes credentials, a query, or a fragment. Check the controller's logs after you change these values. + A signal does not have a separate switch. When you set a signal's endpoint, the controller exports the signal. When you omit a signal's endpoint, the controller compiles an explicit off state for the signal. Omit the endpoint key rather than leaving it empty, because an empty endpoint fails validation. An endpoint must be an absolute `http://` or `https://` URL. The controller rejects one that includes credentials, a query, or a fragment. Check the controller's logs after you change these values. 4. Upgrade the kagent Helm release. ```bash @@ -257,9 +252,9 @@ The flush lets a kagent trace arrive promptly rather than on the exporter's own ## Turn tracing off -Turn off the trace exporter, then create a new Session so that the change takes effect. Turning tracing off compiles an explicit off state rather than an absent one. The controller sets the trace exporter to `none` in each runtime, so an agent never falls back to the OpenTelemetry SDK's own default of exporting to `localhost`. The controller sets `OTEL_SDK_DISABLED` to `true` only when traces, metrics, and logs are all off, and both stack guides turn logs on, so the SDK stays enabled. +Turn off the trace exporter, then create a new Session so that the change takes effect. The controller sets the trace exporter to `none` in each runtime, so an agent does not fall back to the OpenTelemetry SDK's default of exporting to `localhost`. -1. Remove the telemetry settings from the kagent Helm release. Null the whole `telemetry` block rather than one signal inside it. A `--set config.telemetry.traces=null` renders the literal `traces: null` into the controller's configuration file, and the controller rejects a null value and fails to start. +1. Remove the telemetry settings from the kagent Helm release. Null the whole `telemetry` block rather than one signal inside it, because the controller rejects a null value for a single signal and fails to start. ```bash helm upgrade kagent \ {{< reuse "kagent-docs/snippets/helm-path.md" >}}/{{< reuse "kagent-docs/snippets/helm-kagent.md" >}} \ diff --git a/docs-site/content/kagent/1.x/operations/debug.md b/docs-site/content/kagent/1.x/operations/debug.md index 15fec5f4..217b5552 100644 --- a/docs-site/content/kagent/1.x/operations/debug.md +++ b/docs-site/content/kagent/1.x/operations/debug.md @@ -22,7 +22,7 @@ NAME READY AGE kagent 30h ``` -The AgentTemplate list view carries no status column either, so a healthy template and a broken one look identical in it. +The AgentTemplate list view does not include a status column either, so a healthy template and a broken one look identical in it. ```bash kubectl get agenttemplate -n kagent @@ -65,7 +65,7 @@ When a stage fails, kagent marks that stage `False` with the real reason and mes **Read the first condition that is `False` and whose reason is not `Blocked`.** Everything after it is a consequence. -In the following example, one missing ModelConfig is the whole problem, and the two conditions after it carry no independent information. +In the following example, one missing ModelConfig is the whole problem, and the two conditions after it do not add independent information. ```console Harness: kagent @@ -77,7 +77,7 @@ Harness: kagent The same entry carries two revision fields that answer a different question. `desiredRevision` is the {{< gloss "Revision" >}}revision{{< /gloss >}} that the current spec should produce, and `latestSuccessfulRevision` is the newest one that finished preparing. A missing `latestSuccessfulRevision` means the template has never been ready, and two differing values mean the newest edit has not prepared yet. -## An AgentTemplate reports no conditions +## An AgentTemplate does not report conditions An AgentTemplate that no Harness admits gets no `status.harnesses[]` entry at all, so any command that ranges over that array prints nothing. diff --git a/docs-site/content/kagent/1.x/operations/operational-considerations.md b/docs-site/content/kagent/1.x/operations/operational-considerations.md index ba103624..97c5a880 100644 --- a/docs-site/content/kagent/1.x/operations/operational-considerations.md +++ b/docs-site/content/kagent/1.x/operations/operational-considerations.md @@ -9,7 +9,9 @@ A default {{< reuse "kagent-docs/snippets/name-product.md" >}} installation runs ## Controller configuration -Every application setting that the controller reads lives under the chart's `config` block, and the chart renders that block into one configuration file at `/etc/kagent/controller.yaml`. Settings that describe how Kubernetes runs the controller, such as images, probes, volumes, Services, and Secret references, stay in the Helm values beside `config` rather than inside it. The two groups meet where a Secret stores a value that the controller reads, and the chart then projects the Secret and writes its path into the configuration file. +Controller configuration fields are set in the `config` section of the {{< reuse "kagent-docs/snippets/name-product.md" >}} Helm chart values. The chart renders these settings into a configuration file at `/etc/kagent/controller.yaml`, which the kagent controller reads. + +Settings that describe how Kubernetes runs the controller stay in the Helm values outside of `config`. These include images, probes, volumes, Services, and Secret references. A Secret reference belongs to both groups. The chart projects the Secret into the controller pod, then writes the path of the projected file into the configuration file. ```yaml config: @@ -26,7 +28,7 @@ config: level: info ``` -Three rules govern the whole block, and each one rejects a value that the Helm keys it replaced accepted. +The controller enforces the following rules when it reads the settings at start time. | Rule | What it means | | ---- | ------------- | @@ -34,7 +36,7 @@ Three rules govern the whole block, and each one rejects a value that the Helm k | The document is strict | The controller rejects an unknown field, a duplicate key, a null value, a YAML alias or merge, and a file with more than one document. | | Settings load once | The controller validates the whole file at startup, passes the result to its owners, and reads nothing from the environment afterward. A change therefore needs a restart. The chart triggers that restart by checksumming the ConfigMap into the pod. | -An invalid setting stops the controller from starting rather than disabling one feature, so check the controller's logs after you change these values. To check a file before you apply it, run the CLI against the rendered document. The command connects to no services. +An invalid setting stops the controller from starting rather than disabling one feature, so check the controller's logs after you change these values. To check a file before you apply it, run the CLI against the rendered document. The command does not connect to any service. ```bash kagent config validate --component controller --config controller.yaml @@ -42,9 +44,6 @@ kagent config validate --component controller --config controller.yaml Starting the controller with `--check-config` does the same from inside the image, and also confirms that the Secret files the document references are readable. -> [!IMPORTANT] -> The `config` block replaced the `controller`, `database.postgres`, and `otel` application settings in {{< reuse "kagent-docs/versions/kagent.md" >}}. Helm accepts a key that a chart does not define, so a release that still sets `controller.sessionIdleTTL` or `otel.traces.enabled` keeps a value that no template reads, and the setting reverts to its default without reporting an error. Neither Helm flag that reuses stored values carries you across, so write the replacements into a values file and upgrade with `--values`. - ### Runtime defaults `config.runtimeDefaults` is the one part of the block that the controller does not apply to itself. The controller compiles those settings into each agent runtime that it starts, so a setting such as the log level, the telemetry endpoints, or the content capture decision must appear in both `config` and `config.runtimeDefaults` to govern the controller and the agents together. A Harness overrides a few of them through its `spec.env` field, and owns none of the telemetry ones. For which variables a Harness may set, see [Controller-owned telemetry variables]({{< link path="agents/agent-harness#controller-owned-telemetry-variables" >}}). @@ -61,13 +60,13 @@ database: key: connectionString ``` -`kagent install` creates a development database in the cluster, so an installation needs no external database to start. Run your own PostgreSQL instance for production, so that the data outlives the cluster that kagent runs on. +`kagent install` creates a development database in the cluster, so an installation does not need an external database to start. Run your own PostgreSQL instance for production, so that the data outlives the cluster that kagent runs on. ### The development database `kagent install` creates a database for you unless you pass `--skip-database-setup`. It deploys a single-replica `kagent-postgresql` Deployment backed by a 500Mi PersistentVolumeClaim, creates the roles that kagent and Agent Substrate connect as, and writes the connection Secrets for both: `kagent-postgres` in the kagent namespace, and `substrate-postgres-readwrite` and `substrate-postgres-owner` in `ate-system`. -Connections to this database carry no password. When `database.postgres.clientCertificate.enabled` is set to `true`, which `kagent install` does, each client authenticates with a Pod Certificate projected into its pod under `/run/postgres.podcert.ate.dev`. The connection string points its `sslcert` and `sslkey` parameters at those files. +Connections to this database do not use a password. When `database.postgres.clientCertificate.enabled` is set to `true`, which `kagent install` does, each client authenticates with a Pod Certificate projected into its pod under `/run/postgres.podcert.ate.dev`. The connection string points its `sslcert` and `sslkey` parameters at those files. > [!IMPORTANT] > The development database is a single pod backed by one PersistentVolumeClaim, and `kagent install` sizes that claim at 500Mi. Losing the pod's volume loses every Session and every compiled revision, so move a production installation to a PostgreSQL instance that you manage. diff --git a/docs-site/content/kagent/1.x/operations/tune-agent-substrate.md b/docs-site/content/kagent/1.x/operations/tune-agent-substrate.md index 54aa44c2..71ff1ccd 100644 --- a/docs-site/content/kagent/1.x/operations/tune-agent-substrate.md +++ b/docs-site/content/kagent/1.x/operations/tune-agent-substrate.md @@ -103,7 +103,7 @@ substrateWorkerPool: ### Recognize an undersized pool -Agent Substrate reports pool capacity through its metrics rather than through an error that reaches the caller. Each Worker publishes a state of `idle`, `partial`, `at_capacity`, or `unschedulable`, which together show how much of a pool is in use. A placement that finds no suitable Worker records a `no_capacity` outcome. Agent Substrate treats this outcome as a normal result rather than a failure, so it carries no error type and raises no alert on its own. Watch the share of Workers at `at_capacity` and the rate of `no_capacity` placements to tell a pool that is merely busy from one that is too small. +Agent Substrate reports pool capacity through its metrics rather than through an error that reaches the caller. Each Worker publishes a state of `idle`, `partial`, `at_capacity`, or `unschedulable`, which together show how much of a pool is in use. A placement that does not find a suitable Worker records a `no_capacity` outcome. Agent Substrate treats this outcome as a normal result rather than a failure, so it does not set an error type and does not raise an alert on its own. Watch the share of Workers at `at_capacity` and the rate of `no_capacity` placements to tell a pool that is merely busy from one that is too small. None of those signals reach the caller. A turn that cannot get a Worker waits for one, and a turn that waits too long fails with a generic timeout that names neither capacity nor the pool. @@ -117,7 +117,7 @@ Because short turns release their capacity quickly, a pool under mild pressure a ### Set resource requests on Workers -Generated Worker pods carry no resource requests or limits unless you supply them, and the effect on scheduling is easy to miss. +Generated Worker pods do not set resource requests or limits unless you supply them, and the effect on scheduling is easy to miss. A Worker with no limits reports its node's entire allocatable capacity as its own. Every Worker on that node reports the same figures, so Agent Substrate cannot use resource capacity to place Actors, and Kubernetes cannot stop you from packing more Workers onto a node than it can carry. Set requests and limits on any pool that carries production traffic. @@ -170,7 +170,7 @@ substrateWorkerPool: environment: production ``` -A pool that you manage outside the chart carries no labels of its own. Set both keys yourself, and match each value to the pool's name and namespace. +kagent does not label a pool that you manage outside the chart. Set both keys yourself, and match each value to the pool's name and namespace. ```yaml apiVersion: ate.dev/v1alpha1 diff --git a/docs-site/content/kagent/1.x/operations/upgrade-from-0x.md b/docs-site/content/kagent/1.x/operations/upgrade-from-0x.md index dcd02cbd..ee3d0d93 100644 --- a/docs-site/content/kagent/1.x/operations/upgrade-from-0x.md +++ b/docs-site/content/kagent/1.x/operations/upgrade-from-0x.md @@ -143,7 +143,7 @@ Confirm that the resources resolved before you retire anything, because a Harnes kubectl get harness -n kagent ``` -2. Check that each Agent compiled. An AgentTemplate carries no status, so the Agent that pairs it with a Harness is where readiness is reported. +2. Check that each Agent compiled. An AgentTemplate does not report status, so the Agent that pairs it with a Harness is where readiness is reported. ```bash kagent agent list ``` diff --git a/docs-site/content/kagent/1.x/reference/tools-ecosystem.md b/docs-site/content/kagent/1.x/reference/tools-ecosystem.md index 15133ea9..985046ce 100644 --- a/docs-site/content/kagent/1.x/reference/tools-ecosystem.md +++ b/docs-site/content/kagent/1.x/reference/tools-ecosystem.md @@ -34,7 +34,7 @@ kubectl get remotemcpserver kagent-tool-server -n kagent \ ### Turn tool discovery off for a server -The controller lists a server's tools with the credentials that the controller itself holds. A server that authenticates every caller individually, such as one that expects each agent's own propagated token, accepts no credential that the controller can present. Discovery against that server fails, and because the listing never succeeds, the RemoteMCPServer stays un-`Accepted` indefinitely even though agents can reach it at run time. +The controller lists a server's tools with the credentials that the controller itself holds. A server that authenticates every caller individually, such as one that expects each agent's own propagated token, does not accept any credential that the controller can present. Discovery against that server fails, and because the listing never succeeds, the RemoteMCPServer stays un-`Accepted` indefinitely even though agents can reach it at run time. To resolve this issue, label the server with `kagent.dev/discovery=disabled`. The controller accepts the server without listing its tools, and the agents that bind it resolve the tool list at runtime with the credentials that they carry. diff --git a/docs-site/content/kagent/1.x/setup/installation.md b/docs-site/content/kagent/1.x/setup/installation.md index f62be5f8..92633bb1 100644 --- a/docs-site/content/kagent/1.x/setup/installation.md +++ b/docs-site/content/kagent/1.x/setup/installation.md @@ -67,7 +67,7 @@ The kagent CLI installs the CRDs, Agent Substrate, the Agent Substrate PodCertif EOF ``` -2. Save a CLI configuration file that points the installer at those values. `kagent install` takes no Helm flags of its own, so chart values reach it through this file. A relative path in `valuesFiles` resolves against the configuration file's own directory. +2. Save a CLI configuration file that points the installer at those values. `kagent install` does not take Helm flags of its own, so chart values reach it through this file. A relative path in `valuesFiles` resolves against the configuration file's own directory. ```bash cat > kagent-config.yaml <}}AgentTemplate{{< /gloss >}} references a {{< gloss "ModelConfig" >}}ModelConfig{{< /gloss >}} by name in its `spec.modelConfig.name` field, and every agent compiled from that template calls the model that the ModelConfig names. -The kagent installation creates a `default-model-config` ModelConfig from the provider API key that you supply at install time, so a first agent needs no extra setup. To use a different provider, a different model, or a different set of credentials, create additional ModelConfigs. +The kagent installation creates a `default-model-config` ModelConfig from the provider API key that you supply at install time, so a first agent does not need extra setup. To use a different provider, a different model, or a different set of credentials, create additional ModelConfigs. ## How a ModelConfig reaches an agent @@ -18,7 +18,7 @@ Every ModelConfig shares the same three parts, regardless of the provider that i | `provider` | The provider to use. Accepted values are `OpenAI`, `Anthropic`, `AzureOpenAI`, `Ollama`, `Gemini`, `GeminiVertexAI`, `AnthropicVertexAI`, `Bedrock`, `SAPAICore`, `Foundry`, and `Mistral`. Defaults to `OpenAI`. The API accepts `Mistral`, but the controller does not resolve it, so a Mistral ModelConfig reports `unsupported model provider: Mistral` and compiles no revision. | | `model` | The model name, as the provider spells it. | | `stream` | Whether the agent streams its model calls. Defaults to `true`. Set `false` for an endpoint that rejects streaming, which otherwise fails every turn. | -| Provider block | A block named after the provider, such as `openAI` or `bedrock`, holding the settings that only that provider takes. An empty block is valid when the provider needs no extra settings. | +| Provider block | A block named after the provider, such as `openAI` or `bedrock`, holding the settings that only that provider takes. An empty block is valid when the provider does not take extra settings. | Credentials come from a Kubernetes Secret in the same namespace as the ModelConfig. The `apiKeySecret` field names the Secret, and `apiKeySecretKey` names the key within that Secret. To forward the bearer token from the incoming request to the provider instead, set `apiKeyPassthrough: true`. A ModelConfig cannot set both `apiKeyPassthrough` and `apiKeySecret`. For every ModelConfig field, including its type, default, and validation rules, see the [API reference]({{< link path="reference/api-ref#modelconfigspec" >}}). @@ -48,7 +48,7 @@ Changing `stream` compiles a new {{< gloss "Revision" >}}revision{{< /gloss >}}, A credential never enters the agent. kagent compiles the Secret that a ModelConfig names into a destination-scoped binding, and the {{< gloss "Agent Substrate" >}}Agent Substrate{{< /gloss >}} egress gateway fetches the Secret and writes the value into an outgoing HTTP header. Where an SDK requires an API key, the runtime receives the inert placeholder `kagent-credential-injected`. A compiled {{< gloss "Revision" >}}revision{{< /gloss >}} therefore records the Secret name, key, destination, and header, and never the credential itself. -To rotate a credential, update the Secret. The revision records the Secret name and key rather than the value, so a rotation needs no recompile. The gateway caches what it fetches, so allow a short delay before the new value is in use. +To rotate a credential, update the Secret. The revision records the Secret name and key rather than the value, so a rotation does not require a recompile. The gateway caches what it fetches, so allow a short delay before the new value is in use. When a request is made to a provider, the gateway puts the credential in the header that the provider expects. The destination is the endpoint that the ModelConfig resolves to. diff --git a/docs-site/content/kagent/1.x/setup/model-providers/azure-openai.md b/docs-site/content/kagent/1.x/setup/model-providers/azure-openai.md index 0603fcdc..3693ae35 100644 --- a/docs-site/content/kagent/1.x/setup/model-providers/azure-openai.md +++ b/docs-site/content/kagent/1.x/setup/model-providers/azure-openai.md @@ -71,7 +71,7 @@ The `azureOpenAI` block takes the following settings. For every field, including | Value | What the runtime calls | `azureOpenAI.apiVersion` | | ----- | ---------------------- | ------------------------ | | `chatCompletions` | The dated deployments path, which addresses `azureDeployment` under a pinned API version. This value is the default. | Required. The API server rejects a ModelConfig that omits it. | -| `responses` | The Azure Responses API at `/openai/v1/`. This surface takes no dated version. | Omit this field. | +| `responses` | The Azure Responses API at `/openai/v1/`. This surface does not take a dated version. | Omit this field. | The following ModelConfig calls the Responses API. @@ -103,7 +103,7 @@ What reaches Azure depends on whether the message gives a link or the bytes, and | An `http://` or `https://` link with an `image/` media type | An `input_image` part with the link. | | An `http://` or `https://` link with any other media type, such as a PDF | An `input_file` part with the link. | | Inline image bytes | An `input_image` part with the bytes as a `data:` URL. | -| Inline bytes of any other type, such as a PDF or an Office document | Nothing. Azure OpenAI takes no inline bytes outside an image. | +| Inline bytes of any other type, such as a PDF or an Office document | Nothing. Azure OpenAI does not take inline bytes outside an image. | The runtime refuses a link on any other scheme. When the newest user message includes such a link, the turn fails with `cannot send link "" with media type "": this provider integration can only pass http and https links`. When an older message in the replayed conversation includes one, the runtime replaces the link with a text note that keeps the URL, so a model with a fetch tool can still retrieve the file. diff --git a/docs-site/content/kagent/1.x/setup/model-providers/byo-agentgateway.md b/docs-site/content/kagent/1.x/setup/model-providers/byo-agentgateway.md index 4f74c5cf..fb943e1c 100644 --- a/docs-site/content/kagent/1.x/setup/model-providers/byo-agentgateway.md +++ b/docs-site/content/kagent/1.x/setup/model-providers/byo-agentgateway.md @@ -81,7 +81,7 @@ Choose the tab that matches how your agentgateway deployment authenticates calle {{< tabs >}} {{% tab name="No authentication" %}} -When your agentgateway deployment enforces no API key authentication, the ModelConfig needs no Secret. +When your agentgateway deployment does not enforce API key authentication, the ModelConfig does not need a Secret. ```yaml kubectl apply -f - < [!NOTE] -> This provider works on the `kagent`, `byo`, and `codex` runtimes, but not on `claude`. A `codex` Harness additionally requires `openAI.apiFormat: responses` and accepts no other `openAI` settings beyond `baseUrl`. For more information, see [Agent harness]({{< link path="agents/agent-harness#model-provider-support" >}}). It also backs every OpenAI-compatible endpoint, so several other providers in this section set `provider: OpenAI` and point `openAI.baseUrl` somewhere else. +> This provider works on the `kagent`, `byo`, and `codex` runtimes, but not on `claude`. A `codex` Harness additionally requires `openAI.apiFormat: responses` and does not accept any other `openAI` setting beyond `baseUrl`. For more information, see [Agent harness]({{< link path="agents/agent-harness#model-provider-support" >}}). It also backs every OpenAI-compatible endpoint, so several other providers in this section set `provider: OpenAI` and point `openAI.baseUrl` somewhere else. ## Create the ModelConfig diff --git a/docs-site/content/kagent/1.x/skills-and-mcp/about-tools.md b/docs-site/content/kagent/1.x/skills-and-mcp/about-tools.md index ea976a36..822e5387 100644 --- a/docs-site/content/kagent/1.x/skills-and-mcp/about-tools.md +++ b/docs-site/content/kagent/1.x/skills-and-mcp/about-tools.md @@ -66,7 +66,7 @@ Every field is required. For the complete schema, see the [API reference]({{< li | `subAgent.description` | The text that tells the parent agent when to route work here. | | `subAgent.templateRef.name` | The AgentTemplate to bind, in the same namespace. | -A bound template compiles under the parent {{< gloss "Agent" >}}Agent{{< /gloss >}}'s Harness and runs inside the parent's {{< gloss "Actor" >}}Actor{{< /gloss >}}, so nesting costs no extra compute and the two agents share one sandbox. A subagent needs no Agent of its own. +A bound template compiles under the parent {{< gloss "Agent" >}}Agent{{< /gloss >}}'s Harness and runs inside the parent's {{< gloss "Actor" >}}Actor{{< /gloss >}}, so nesting does not add compute and the two agents share one sandbox. A subagent does not need an Agent of its own. > [!NOTE] > Dedicated subagents, which would give a bound agent its own Harness, Session, and Actor and reach it over A2A, are not part of the served API. The `subAgent.agentRef` field that would select one is deferred until a dedicated subagent can create and invoke its own Session, so the only binding a reader can write today is `templateRef`. From e3273af97cd10813e75cbfed2a10bd72938b758c Mon Sep 17 00:00:00 2001 From: Rachael Graham Date: Wed, 14 Oct 2026 09:50:15 -0500 Subject: [PATCH 3/5] edits 2 Signed-off-by: Rachael Graham --- .../1.x/about/architecture/agent-substrate.md | 2 +- .../content/kagent/1.x/about/core-concepts.md | 2 +- .../1.x/about/substrate-runtime/identity.md | 4 +- .../networking-and-egress.md | 2 +- .../1.x/about/substrate-runtime/sandboxing.md | 4 +- .../substrate-runtime/standalone-sandboxes.md | 10 ++--- .../kagent/1.x/agents/agent-harness.md | 8 ++-- .../kagent/1.x/agents/bring-your-own-agent.md | 2 +- .../kagent/1.x/agents/human-in-the-loop.md | 2 +- .../kagent/1.x/agents/structured-output.md | 2 +- .../content/kagent/1.x/examples/a2a-byo.md | 4 +- .../kagent/1.x/examples/agents-via-mcp.md | 12 +++--- .../kagent/1.x/observability/audit-prompts.md | 2 +- .../content/kagent/1.x/operations/debug.md | 6 +-- .../operations/operational-considerations.md | 43 ++++++++++++------- .../1.x/operations/tune-agent-substrate.md | 18 ++++---- .../kagent/1.x/reference/release-notes/1.0.md | 2 +- .../kagent/1.x/reference/tools-ecosystem.md | 4 +- .../model-providers/about-model-providers.md | 6 +-- .../1.x/setup/model-providers/azure-openai.md | 6 ++- .../setup/model-providers/google-vertexai.md | 4 +- .../1.x/setup/model-providers/sap-ai-core.md | 4 +- 22 files changed, 81 insertions(+), 68 deletions(-) diff --git a/docs-site/content/kagent/1.x/about/architecture/agent-substrate.md b/docs-site/content/kagent/1.x/about/architecture/agent-substrate.md index 263323b9..cdd2976e 100644 --- a/docs-site/content/kagent/1.x/about/architecture/agent-substrate.md +++ b/docs-site/content/kagent/1.x/about/architecture/agent-substrate.md @@ -40,7 +40,7 @@ Agent Substrate checks two things before it places an Actor on a Worker: * Whether the Worker has a free Actor slot. This check always runs. * Whether the Worker has enough spare CPU and memory. This check runs only when the ActorTemplate states how much CPU and memory one Actor needs. -An ActorTemplate that the kagent controller compiles states no per-Actor CPU or memory, so the free Actor slot check is the only one that decides where a Session's Actor lands. [Worker resource limits]({{< link path="operations/tune-agent-substrate#set-resource-requests-on-workers" >}}) still matter, because a Worker with no limits reports its whole node as its own capacity. +An ActorTemplate that the kagent controller compiles does not state per-Actor CPU or memory, so the free Actor slot check is the only one that decides where a Session's Actor lands. [Worker resource limits]({{< link path="operations/tune-agent-substrate#set-resource-requests-on-workers" >}}) still matter, because a Worker with no limits reports its whole node as its own capacity. diff --git a/docs-site/content/kagent/1.x/about/core-concepts.md b/docs-site/content/kagent/1.x/about/core-concepts.md index 2856d842..408b6052 100644 --- a/docs-site/content/kagent/1.x/about/core-concepts.md +++ b/docs-site/content/kagent/1.x/about/core-concepts.md @@ -134,7 +134,7 @@ kagent agent session create --agent assistant -n kagent After it is created, a Session talks to callers over the {{< gloss "A2A" >}}A2A{{< /gloss >}} (Agent-to-Agent) protocol, through kagent's A2A gateway. Callers address the Agent rather than the Session. The HTTP endpoint is `/agents/{namespace}/{name}` and gRPC carries the same `namespace/name` in the standard A2A `tenant` field. The Session's ID is the A2A `contextId`, so a message that does not include a context identifier starts a new conversation, and a message that repeats one continues that conversation. -An expiration worker deletes a Session that records no task activity for seven days. The `config.sessions.idleTtl` Helm value sets that window. A value of `0s` turns the expiration worker off. For more information about what the deletion retains, see [Expire idle Sessions]({{< link path="operations/operational-considerations#expire-idle-sessions" >}}). +An expiration worker deletes a Session that does not record task activity for seven days. The `config.sessions.idleTtl` Helm value sets that window. A value of `0s` turns the expiration worker off. For more information about what the deletion retains, see [Expire idle Sessions]({{< link path="operations/operational-considerations#expire-idle-sessions" >}}). ## Actor diff --git a/docs-site/content/kagent/1.x/about/substrate-runtime/identity.md b/docs-site/content/kagent/1.x/about/substrate-runtime/identity.md index ead860c3..e407b411 100644 --- a/docs-site/content/kagent/1.x/about/substrate-runtime/identity.md +++ b/docs-site/content/kagent/1.x/about/substrate-runtime/identity.md @@ -51,7 +51,7 @@ Authorization is separate from both modes. kagent installs an authorizer that pe kagent's gRPC API reaches the same resources by a second path. The AgentTemplate service creates, updates, and deletes AgentTemplates, and the Harness service creates and deletes Harnesses, both through the kagent controller. The `kagent apply -f` command calls the AgentTemplate service, and any client that reaches the gRPC endpoint can call either service. The controller writes these resources with its own service account rather than the caller's, so Kubernetes RBAC never evaluates the caller. The authenticator above resolves the caller instead. > [!WARNING] -> By default, kagent neither authenticates nor authorizes this path. The `insecure` authenticator admits every request, and the authorizer it installs permits every check. Any caller that reaches the gRPC endpoint can author an agent's runtime and behavior, whatever their Kubernetes permissions are. Do not expose port `8083` outside the cluster. For the identity that this mode gives an anonymous caller, and for the mode that changes it, see [Controller authentication modes](#controller-authentication-modes). +> By default, kagent neither authenticates nor authorizes this path. The `insecure` authenticator admits every request, and the authorizer it installs permits every check. Any caller that reaches the gRPC endpoint can author an agent's runtime and behavior, regardless of their Kubernetes permissions. Do not expose port `8083` outside the cluster. For the identity that this mode gives an anonymous caller, and for the mode that changes it, see [Controller authentication modes](#controller-authentication-modes). ### Creator ownership @@ -71,7 +71,7 @@ A share carries one of two permissions. - **`READ_ONLY`**: The holder can read the conversation. kagent refuses any call that is not a read before the request reaches the service. - **`READ_WRITE`**: The holder can also send messages to the Session. -A client that creates a share can set `CreateSessionShareRequest.ttl` to a positive lifetime, which starts when the share is created. Once that lifetime ends, the token grants no access and a token lookup returns not found. The owner still sees the expired share in `ListSessionShares` and can revoke it. +A client that creates a share can set `CreateSessionShareRequest.ttl` to a positive lifetime, which starts when the share is created. Once that lifetime ends, the token does not grant access, and a token lookup returns not found. The owner still sees the expired share in `ListSessionShares` and can revoke it. The returned `SessionShare.expiresAt` field records when the token stops granting access. Omit `ttl` and `expiresAt` stays unset. The share then stays valid until it is revoked or the Session is deleted. An operator caps share lifetimes with `config.sessions.shareMaxTtl`. For more information, see [Expire idle Sessions]({{< link path="operations/operational-considerations#expire-idle-sessions" >}}). diff --git a/docs-site/content/kagent/1.x/about/substrate-runtime/networking-and-egress.md b/docs-site/content/kagent/1.x/about/substrate-runtime/networking-and-egress.md index eb1376fc..f047e0b8 100644 --- a/docs-site/content/kagent/1.x/about/substrate-runtime/networking-and-egress.md +++ b/docs-site/content/kagent/1.x/about/substrate-runtime/networking-and-egress.md @@ -84,7 +84,7 @@ The gateway caches each Actor's policy rather than fetching it per request, so p ## Diagnose a denied request -A denied request gives no explanation. The agent sees a rejected call, and its own error text names neither the destination nor the rule that refused it. The gateway records the reason in its log. +Agent Substrate does not explain why it denied a request. The agent sees a rejected call, and its own error text names neither the destination nor the rule that refused it. The gateway records the reason in its log. ```bash kubectl logs -n ate-system deploy/atenet-egress -c agentgateway --tail=100 diff --git a/docs-site/content/kagent/1.x/about/substrate-runtime/sandboxing.md b/docs-site/content/kagent/1.x/about/substrate-runtime/sandboxing.md index b62e77ce..9da0012d 100644 --- a/docs-site/content/kagent/1.x/about/substrate-runtime/sandboxing.md +++ b/docs-site/content/kagent/1.x/about/substrate-runtime/sandboxing.md @@ -45,7 +45,7 @@ A {{< gloss "WorkerPool" >}}WorkerPool{{< /gloss >}}'s `sandboxClass` field sets A **SandboxConfig** is a cluster-scoped resource that describes the runtime files a sandbox class requires to start. The resource stores none of those files. Instead, it records the location that the node agent fetches each file from, keyed by processor architecture. It also names the pause image, which holds the sandbox's namespaces as its root container. -Every ActorTemplate must name a SandboxConfig, and Agent Substrate supplies no name of its own. A template cannot be prepared until the configuration that it names exists in the cluster. Many ActorTemplates can share one SandboxConfig. The runtime version is then pinned in a single place, rather than repeated on every template. +Every ActorTemplate must name a SandboxConfig, and Agent Substrate does not supply a name of its own. A template cannot be prepared until the configuration that it names exists in the cluster. Many ActorTemplates can share one SandboxConfig. The runtime version is then pinned in a single place, rather than repeated on every template. A default installation creates one SandboxConfig, named `gvisor-default`. kagent writes this name onto every ActorTemplate that it generates, so each template names it explicitly. The configuration looks like the following: @@ -71,7 +71,7 @@ spec: | Field | Description | | ----- | ----------- | | `sandboxClass` | The sandbox runtime family that this configuration applies to, `gvisor` or `microvm`. An ActorTemplate only uses configurations whose class matches its own, and preparation fails if the two disagree. | -| `pauseImage` | The image for the root sandbox container, which holds the sandbox's namespaces and runs no workload code. It must be pinned to a digest, because the snapshot manifest records it, and changing the image invalidates the snapshots that were taken with it. | +| `pauseImage` | The image for the root sandbox container, which holds the sandbox's namespaces and does not run workload code. It must be pinned to a digest, because the snapshot manifest records it, and changing the image invalidates the snapshots that were taken with it. | | `assets` | The files that the node agent fetches, keyed first by processor architecture and then by asset name. A `gvisor` class expects one `gvisor` asset, the release archive that the node agent extracts. A `microvm` class expects several, such as `cloud-hypervisor`, `kata-kernel`, and `kata-image`. | | `assets...sha256` | The lowercase hex digest of the file. The node agent verifies each download against it, and caches the result under a path that includes the digest, so changing the digest fetches the new asset instead of reusing the cached one. To read the configuration that your own cluster installed, including the pinned digests, run `kubectl get sandboxconfig gvisor-default -o yaml`.| diff --git a/docs-site/content/kagent/1.x/about/substrate-runtime/standalone-sandboxes.md b/docs-site/content/kagent/1.x/about/substrate-runtime/standalone-sandboxes.md index a15b7e76..38db4ef1 100644 --- a/docs-site/content/kagent/1.x/about/substrate-runtime/standalone-sandboxes.md +++ b/docs-site/content/kagent/1.x/about/substrate-runtime/standalone-sandboxes.md @@ -20,7 +20,7 @@ An agent can create a Sandbox of its own through the kagent {{< gloss "MCP" >}}M ## Before you begin -1. [Install kagent]({{< link path="setup/installation" >}}) and set the following `config.sandboxes` values on the installation. Sandbox preparation needs a guest image, and the chart ships no default, so a SandboxTemplate never becomes ready until you set one. The remaining values allocate the compute that each Sandbox runs on and bound how long it lives. +1. [Install kagent]({{< link path="setup/installation" >}}) and set the following `config.sandboxes` values on the installation. Sandbox preparation needs a guest image, and the chart does not ship a default, so a SandboxTemplate never becomes ready until you set one. The remaining values allocate the compute that each Sandbox runs on and bound how long it lives. ```yaml config: sandboxes: @@ -35,7 +35,7 @@ An agent can create a Sandbox of its own through the kagent {{< gloss "MCP" >}}M | Value | Default | Description | | ----- | ------- | ----------- | - | `config.sandboxes.guestImage` | `""` | The digest-pinned guest image, as a complete reference. Required: preparation fails without it. The controller passes the reference to Agent Substrate unchanged and resolves no tags, so pin a digest rather than a tag. Setting `global.imageRegistry` rewrites the registry host of this reference. | + | `config.sandboxes.guestImage` | `""` | The digest-pinned guest image, as a complete reference. Required: preparation fails without it. The controller passes the reference to Agent Substrate unchanged and does not resolve tags, so pin a digest rather than a tag. Setting `global.imageRegistry` rewrites the registry host of this reference. | | `config.sandboxes.cpu` | `1` | CPU allocated to each Sandbox. | | `config.sandboxes.memory` | `1Gi` | Memory allocated to each Sandbox. | | `config.sandboxes.defaultTtl` | `3600s` | Lifetime applied when `kagent sandbox create` omits `--ttl`. Durations in the `config` block take protobuf seconds notation, so `3600s` rather than `1h`. | @@ -80,7 +80,7 @@ A SandboxTemplate is a namespaced `api.kagent.dev/v1alpha3` resource that prepar | Field | Required | Description | | ----- | -------- | ----------- | | `workload.image` | Yes | The tools image, pinned by `sha256` digest. A tag alone is rejected, because a prepared revision must be reproducible. | - | `env` | No | Environment defaults for the guest, up to 100. Each entry sets a literal `value`, which is required and may be an empty string. The schema defines no secret-backed source, so the API server rejects a `credentialRef` entry as an unknown field. | + | `env` | No | Environment defaults for the guest, up to 100. Each entry sets a literal `value`, which is required and may be an empty string. The schema does not define a secret-backed source, so the API server rejects a `credentialRef` entry as an unknown field. | | `substrate.workerPoolRef.name` | Yes | The WorkerPool that this template's Actors are scheduled onto. | | `substrate.snapshotPolicy.location` | Yes | The object storage location for Actor {{< gloss "Snapshot" >}}snapshots{{< /gloss >}}. | @@ -97,7 +97,7 @@ Editing a template prepares a new revision. A Sandbox that already exists keeps ## Run a command > [!WARNING] -> **The gateway denies every outbound connection from a Sandbox.** kagent compiles an empty [egress policy]({{< link path="about/substrate-runtime/networking-and-egress#policy-generation" >}}) as it creates a Sandbox, and the gateway rejects any destination that the policy does not name, so a command that fetches a package, clones a repository, or calls an API fails. The SandboxTemplate schema defines no destination field, so no configuration opens one. Move what a command needs into the Sandbox with `kagent sandbox upload`. +> **The gateway denies every outbound connection from a Sandbox.** kagent compiles an empty [egress policy]({{< link path="about/substrate-runtime/networking-and-egress#policy-generation" >}}) as it creates a Sandbox, and the gateway rejects any destination that the policy does not name, so a command that fetches a package, clones a repository, or calls an API fails. The SandboxTemplate schema does not define a destination field, so no configuration opens one. Move what a command needs into the Sandbox with `kagent sandbox upload`. Each `kagent sandbox` command makes one lifecycle attempt rather than retrying for you, so `create` takes a stable `--request-id` that you reuse to retry the same creation. @@ -162,7 +162,7 @@ tools: - list_sandboxes ``` -An agent that creates a Sandbox owns it under whatever identity the MCP connection authenticated, not under the identity of the person it is talking to. A Session share token grants no access to a Sandbox. To have an agent act for the person who invoked it, configure credential propagation. Set `config.runtimeDefaults.tokenPropagation.enabled` to `true`, and the kagent runtime passes the caller's credentials and identity to the MCP servers that you trust. +An agent that creates a Sandbox owns it under whatever identity the MCP connection authenticated, not under the identity of the person it is talking to. A Session share token does not grant access to a Sandbox. To have an agent act for the person who invoked it, configure credential propagation. Set `config.runtimeDefaults.tokenPropagation.enabled` to `true`, and the kagent runtime passes the caller's credentials and identity to the MCP servers that you trust. The MCP transfer limits are tighter than the command line's. A gRPC file transfer is bounded at 64 MiB. An MCP transfer or output read is bounded at 1 MiB, encodes bytes as base64, and returns a continuation offset for reading more. diff --git a/docs-site/content/kagent/1.x/agents/agent-harness.md b/docs-site/content/kagent/1.x/agents/agent-harness.md index f51bdbbd..eed3324c 100644 --- a/docs-site/content/kagent/1.x/agents/agent-harness.md +++ b/docs-site/content/kagent/1.x/agents/agent-harness.md @@ -40,9 +40,9 @@ EOF | ----- | -------- | ----------- | | One of `kagent`, `codex`, `claude`, `byo` | Yes | The runtime that executes the agent. Naming none, or more than one, is rejected. For the available runtimes, see [Choose a runtime](#choose-a-runtime). | | `workload.image` | Yes | The runtime image, pinned by `sha256` digest. A tag alone is rejected, because a revision must be reproducible. | -| `workload.command` | For `byo` | Overrides the image entrypoint, up to 32 entries. Required for the `byo` runtime, optional otherwise. Every runtime honors an explicit value, the `kagent` runtime included, whatever language its image is written in. | +| `workload.command` | For `byo` | Overrides the image entrypoint, up to 32 entries. Required for the `byo` runtime, optional otherwise. Every runtime honors an explicit value, the `kagent` runtime included, regardless of the language its image is written in. | | `workload.args` | No | Overrides the image arguments, up to 64 entries. An override that you omit stays unset rather than taking a default. | -| `env` | No | Environment variables for the runtime, up to 100. Each entry sets a literal `value`, which is required and can be an empty string. The schema defines no secret-backed source, so the API server rejects a `credentialRef` entry as an unknown field. Put credentials on a ModelConfig or a RemoteMCPServer instead. For more information, see [About model providers]({{< link path="setup/model-providers/about-model-providers#credentials-that-do-not-compile" >}}). Note that the kagent installation's settings, such as the runtime log level, come from the Helm chart's `config.runtimeDefaults` block rather than from this setting. | +| `env` | No | Environment variables for the runtime, up to 100. Each entry sets a literal `value`, which is required and can be an empty string. The schema does not define a secret-backed source, so the API server rejects a `credentialRef` entry as an unknown field. Put credentials on a ModelConfig or a RemoteMCPServer instead. For more information, see [About model providers]({{< link path="setup/model-providers/about-model-providers#credentials-that-do-not-compile" >}}). Note that the kagent installation's settings, such as the runtime log level, come from the Helm chart's `config.runtimeDefaults` block rather than from this setting. | | `substrate.workerPoolRef.name` | Yes | The {{< gloss "WorkerPool" >}}WorkerPool{{< /gloss >}} that this Harness's Actors are scheduled onto. An operator must provision one before any agent can run. | | `substrate.snapshotPolicy.location` | Yes | The object storage location for Actor {{< gloss "Snapshot" >}}snapshots{{< /gloss >}}. | @@ -225,7 +225,7 @@ The runtime that a Harness selects decides which ModelConfig an Agent that runs kagent rejects `AnthropicVertexAI`, `GeminiVertexAI`, and `SAPAICore` on every runtime, because each authenticates with a credential that the egress gateway cannot place in an HTTP header. The ModelConfig never compiles, so no agent can use these providers. For the alternatives, see [About model providers]({{< link path="setup/model-providers/about-model-providers#credentials-that-do-not-compile" >}}). -kagent rejects `Mistral` for a different reason. The controller does not resolve the provider at all, so a Mistral ModelConfig reports `unsupported model provider: Mistral` and compiles no revision. +kagent rejects `Mistral` for a different reason. The controller does not resolve the provider at all, so a Mistral ModelConfig reports `unsupported model provider: Mistral` and does not compile a revision. Some supported combinations still carry restrictions. @@ -267,7 +267,7 @@ config: | Setting | What it includes | Applies to | | ------- | ---------------- | ---------- | -| `config.runtimeDefaults.telemetry.captureContent` | Prompts and assistant replies in the runtime's telemetry. On the `kagent` runtime, the content appears in the span for each model call. That runtime records a tool call's arguments and reply on the span for the tool call instead, whatever this setting holds. On the `claude` runtime, tool results require tracing, and assistant replies require log export through `config.runtimeDefaults.telemetry.logs`. | `kagent`, `codex`, `claude` | +| `config.runtimeDefaults.telemetry.captureContent` | Prompts and assistant replies in the runtime's telemetry. On the `kagent` runtime, the content appears in the span for each model call. That runtime records a tool call's arguments and reply on the span for the tool call instead, regardless of this setting. On the `claude` runtime, tool results require tracing, and assistant replies require log export through `config.runtimeDefaults.telemetry.logs`. | `kagent`, `codex`, `claude` | | `config.runtimeDefaults.telemetry.captureRawApiBodies` | The complete provider API request and response bodies. This setting returns more than `captureContent` does, and it takes effect only when you set the logs endpoint. | `claude` | | `config.runtimeDefaults.telemetry.maxCaptureBytes` | The budget for each captured prompt or reply, in bytes. Defaults to `16384`, with a maximum of `65536`. | `codex`, `claude` | diff --git a/docs-site/content/kagent/1.x/agents/bring-your-own-agent.md b/docs-site/content/kagent/1.x/agents/bring-your-own-agent.md index 4c70760e..1e1eb0af 100644 --- a/docs-site/content/kagent/1.x/agents/bring-your-own-agent.md +++ b/docs-site/content/kagent/1.x/agents/bring-your-own-agent.md @@ -36,7 +36,7 @@ A `byo` image must meet four requirements to run as an agent. kagent enforces on | `spec.workload.command` is set on the Harness | A `byo` Harness must override the image entrypoint, because kagent does not infer it. | | The container serves gRPC on port 80 | kagent builds the agent card with a single interface, `http://127.0.0.1:80` bound to gRPC, and that address is not configurable. The service is `lf.a2a.v1.A2AService`. | | The container answers `GET /readyz` on port 8081 | Agent Substrate probes this path to decide when the Actor is ready, with a 30-second timeout. The probe is on a different port from the A2A service on purpose, so serve it independently. | -| The A2A service speaks gRPC, not JSON-RPC | The agent card fixes the protocol binding to gRPC. An image that serves A2A over JSON-RPC alone is never reached, whatever port it listens on. | +| The A2A service speaks gRPC, not JSON-RPC | The agent card fixes the protocol binding to gRPC. An image that serves A2A over JSON-RPC alone is never reached, regardless of the port it listens on. | > [!WARNING] > **An image that listens on another port still reports `READY`.** Readiness is probed on port 8081, which succeeds no matter what the A2A service does, so nothing surfaces the mismatch until an invoke fails with `Connect: tunnel failed`. The compiled configuration sets port `80` in its `runtime.port` field, so an image that reads the configuration binds the right port. An image that ignores the configuration must pin port 80 itself. This gap is tracked as [kagent#2758](https://github.com/kagent-dev/kagent/issues/2758). diff --git a/docs-site/content/kagent/1.x/agents/human-in-the-loop.md b/docs-site/content/kagent/1.x/agents/human-in-the-loop.md index 3ce4681e..19a2c1cb 100644 --- a/docs-site/content/kagent/1.x/agents/human-in-the-loop.md +++ b/docs-site/content/kagent/1.x/agents/human-in-the-loop.md @@ -61,7 +61,7 @@ The `kagent` and `codex` runtimes both raise `ask_user_request`. The `claude` ru ## Require approval for a tool -An agent pauses for a tool only when its binding asks for that. Set `requireApproval` on an `mcp` tool binding in the AgentTemplate, and the agent stops before each call to a tool that the binding exposes. +An agent pauses for a tool only when its binding asks for that. If you set `requireApproval` on an `mcp` tool binding in the AgentTemplate, the agent stops before each call to a tool that the binding exposes. ```yaml apiVersion: api.kagent.dev/v1alpha3 diff --git a/docs-site/content/kagent/1.x/agents/structured-output.md b/docs-site/content/kagent/1.x/agents/structured-output.md index 439f7e1a..98170e6c 100644 --- a/docs-site/content/kagent/1.x/agents/structured-output.md +++ b/docs-site/content/kagent/1.x/agents/structured-output.md @@ -74,7 +74,7 @@ Set `spec.outputSchema` to keep the schema in the AgentTemplate, so that the sch EOF ``` - The schema requires an object with an integer `answer` and a string `explanation`, and allows no other properties. The system prompt describes the same JSON, because some model providers do not receive the schema. + The schema requires an object with an integer `answer` and a string `explanation`, and does not allow other properties. The system prompt describes the same JSON, because some model providers do not receive the schema. 2. Confirm that the Agent is ready. ```bash diff --git a/docs-site/content/kagent/1.x/examples/a2a-byo.md b/docs-site/content/kagent/1.x/examples/a2a-byo.md index 62e844f9..7a22b605 100644 --- a/docs-site/content/kagent/1.x/examples/a2a-byo.md +++ b/docs-site/content/kagent/1.x/examples/a2a-byo.md @@ -7,7 +7,7 @@ author: kagent.dev The `byo` runtime runs a container image that you build, so long as the image implements kagent's {{< gloss "A2A" >}}A2A{{< /gloss >}} (Agent-to-Agent) contract. This example takes the shortest path through that contract: build the minimal BYO agent that kagent tests itself against, run it on a {{< gloss "Harness" >}}Harness{{< /gloss >}}, and invoke it. -The agent that you build here calls no model and binds no tools. It answers every message with a fixed string, which makes it a poor agent and a clear demonstration: everything that happens between `kagent agent invoke` and that reply is kagent's half of the contract. To review the contract and the configured agents that read their AgentTemplate instead of ignoring it, see [Bring your own agent]({{< link path="agents/bring-your-own-agent" >}}). +The agent that you build here does not call a model or bind tools. It answers every message with a fixed string, which makes it a poor agent and a clear demonstration: everything that happens between `kagent agent invoke` and that reply is kagent's half of the contract. To review the contract and the configured agents that read their AgentTemplate instead of ignoring it, see [Bring your own agent]({{< link path="agents/bring-your-own-agent" >}}). ## Before you begin @@ -228,7 +228,7 @@ An agent of your own takes the same path as the example image, with two differen docker push $DOCKER_REGISTRY/kagent-dev/kagent/my-agent:v1 ``` - That Dockerfile copies `api`, `core`, `adk`, `harness`, and `pkg` from the kagent module, so it suits an agent written inside a checkout. An agent in a module of your own needs a Dockerfile of your own. kagent places no requirement on how the image is built, only on what it serves. + That Dockerfile copies `api`, `core`, `adk`, `harness`, and `pkg` from the kagent module, so it suits an agent written inside a checkout. An agent in a module of your own needs a Dockerfile of your own. kagent constrains what the image serves rather than how you build it. 3. Resolve the digest of the new image, as in [Build the agent image](#build-the-agent-image). A Harness rejects an image that names only a tag. ```bash diff --git a/docs-site/content/kagent/1.x/examples/agents-via-mcp.md b/docs-site/content/kagent/1.x/examples/agents-via-mcp.md index 5805a02e..629da85d 100644 --- a/docs-site/content/kagent/1.x/examples/agents-via-mcp.md +++ b/docs-site/content/kagent/1.x/examples/agents-via-mcp.md @@ -15,7 +15,7 @@ You can do this work from an MCP client such as Claude Code or Cursor, which bui The MCP server is part of the controller's HTTP port rather than a separate deployment, so a default installation already serves it at `/mcp` on port `8083`. -- **Transport**: Streamable HTTP only. Server-Sent Events (SSE) as a standalone transport and stdio are both unsupported, so a client that offers a transport choice must use Streamable HTTP. +- **Transport**: Streamable HTTP only. Server-Sent Events (SSE) as a standalone transport and stdio are both unsupported. A client that offers a transport choice must use Streamable HTTP. - **Sessions**: The handler is stateless, so each request stands alone and a client does not need to establish a session first. - **Extensions**: The server advertises the `io.modelcontextprotocol/tasks` extension, which changes how invocations behave. For more information, see [Invoke without waiting](#invoke-without-waiting). @@ -31,7 +31,7 @@ The MCP server is part of the controller's HTTP port rather than a separate depl 2. [Create your first agent]({{< link path="get-started/your-first-agent" >}}), so that you have at least one Session in the `READY` state. The MCP server lists ready Sessions only. -3. Install [grpcurl](https://github.com/fullstorydev/grpcurl). Deleting a checkpoint has neither an MCP tool nor a kagent command, so cleaning one up calls `CheckpointService` directly. +3. Install [grpcurl](https://github.com/fullstorydev/grpcurl). Deleting a checkpoint has neither an MCP tool nor a kagent command. Cleaning one up calls `CheckpointService` directly. ## Connect a client @@ -254,7 +254,7 @@ Declaring the `io.modelcontextprotocol/tasks` extension changes the same tool's }' ``` - The task carries an opaque ID of the form `v1.` that identifies the namespace, the instance, and the A2A task together, so pass it back verbatim rather than parsing it. Example output: + The task carries an opaque ID of the form `v1.` that identifies the namespace, the instance, and the A2A task together. Pass it back verbatim rather than parsing it. Example output: ```json { "taskId": "v1.eyJuYW1lc3BhY2UiOiJrYWdlbnQiLCJpbnN0YW5jZUlkIjoi...", @@ -309,10 +309,10 @@ Declaring the `io.modelcontextprotocol/tasks` extension changes the same tool's Two more methods complete the set. `tasks/cancel` stops a run that is still working, and `tasks/update` answers an agent that is waiting on a person. > [!WARNING] -> Read `status` rather than `resultType` to decide that a run is over. A paused task reports `resultType` as `complete` while its `status` is still `input_required`, so `resultType` alone does not mean an answer is waiting. +> Read `status` rather than `resultType` to decide that a run is over. A paused task reports `resultType` as `complete` while its `status` is still `input_required`. `resultType` alone therefore does not mean an answer is waiting. > [!NOTE] -> `statusMessage` changes meaning with the status. On a `working` task it holds the raw task record rather than a readable sentence, because the agent has not produced any text yet, so do not present it to a person as progress text. On an `input_required` task it holds the agent's question, and on a `completed` task it holds the reply. +> `statusMessage` changes meaning with the status. On a `working` task it holds the raw task record rather than a readable sentence, because the agent has not produced any text yet. Do not present it to a person as progress text. On an `input_required` task it holds the agent's question, and on a `completed` task it holds the reply. ### Answer an agent's question @@ -408,7 +408,7 @@ For the pause types, the approval model, and what the agent receives, see [Human The checkpoint tools pin a Session's state. These tools do not need the tasks extension, so they do not carry `_meta`. -1. Create a checkpoint. `expected_head_task_id` names the terminal task to pin, so the call fails rather than pinning the wrong turn when the conversation has advanced past it. Use the `$TASK_ID` that an earlier invocation returned. The `request_id` is an idempotency key, so repeating the call with the same value returns the checkpoint that the first call created rather than pinning a second one. +1. Create a checkpoint. `expected_head_task_id` names the terminal task to pin. The call fails rather than pinning the wrong turn when the conversation has advanced past it. Use the `$TASK_ID` that an earlier invocation returned. The `request_id` is an idempotency key, so repeating the call with the same value returns the checkpoint that the first call created rather than pinning a second one. ```bash curl -s -X POST http://localhost:8083/mcp \ -H 'Content-Type: application/json' \ diff --git a/docs-site/content/kagent/1.x/observability/audit-prompts.md b/docs-site/content/kagent/1.x/observability/audit-prompts.md index 69450a80..12d2de8c 100644 --- a/docs-site/content/kagent/1.x/observability/audit-prompts.md +++ b/docs-site/content/kagent/1.x/observability/audit-prompts.md @@ -48,7 +48,7 @@ The `kagent` runtime does not shorten these attributes. The `claude` and `codex` ### Delivery -Agent Substrate suspends an Actor as soon as a response completes. The controller therefore sets the `kagent` and `codex` runtimes to flush their spans before each response completes, so that the records of a turn reach your backend before the Actor suspends. The `claude` runtime gets no such flush, so the records of a conversation's last turn can be lost. For more information, see [Traces from a suspended Actor]({{< link path="observability/tracing#traces-from-a-suspended-actor" >}}). +Agent Substrate suspends an Actor as soon as a response completes. The controller therefore sets the `kagent` and `codex` runtimes to flush their spans before each response completes, so that the records of a turn reach your backend before the Actor suspends. The `claude` runtime does not perform that flush, so the records of a conversation's last turn can be lost. For more information, see [Traces from a suspended Actor]({{< link path="observability/tracing#traces-from-a-suspended-actor" >}}). > [!IMPORTANT] > Traces are a best-effort record. An exporter drops spans without an error when the collector is unreachable or its queue is full, and nothing in the trace shows that a record is missing. Treat captured spans as a review aid, not as a complete or tamper-proof compliance log. diff --git a/docs-site/content/kagent/1.x/operations/debug.md b/docs-site/content/kagent/1.x/operations/debug.md index 217b5552..c7d7f52e 100644 --- a/docs-site/content/kagent/1.x/operations/debug.md +++ b/docs-site/content/kagent/1.x/operations/debug.md @@ -106,7 +106,7 @@ Example output: {"matchLabels":{"kagent.dev/harness":"kagent"}} ``` -Add the label that the selector expects, and the conditions appear within seconds. +If you add the label that the selector expects, the conditions appear within seconds. ```yaml apiVersion: api.kagent.dev/v1alpha3 @@ -123,7 +123,7 @@ metadata: ## An agent turn times out -A turn that fails with a timeout reports the same error whatever the underlying cause, so this symptom needs elimination rather than reading. +A turn that fails with a timeout reports the same error regardless of the underlying cause, so this symptom needs elimination rather than reading. ```console ERROR: @@ -133,7 +133,7 @@ ERROR: Work through the causes in this order. -1. **Check that the {{< gloss "WorkerPool" >}}WorkerPool{{< /gloss >}} has ready Workers.** A pool with too few Workers produces exactly this error and logs no capacity message anywhere. To learn how to recognize this problem and size the pool, see [Tune Agent Substrate]({{< link path="operations/tune-agent-substrate" >}}). +1. **Check that the {{< gloss "WorkerPool" >}}WorkerPool{{< /gloss >}} has ready Workers.** A pool with too few Workers produces exactly this error and does not log a capacity message anywhere. To learn how to recognize this problem and size the pool, see [Tune Agent Substrate]({{< link path="operations/tune-agent-substrate" >}}). ```bash kubectl get workerpools -n kagent ``` diff --git a/docs-site/content/kagent/1.x/operations/operational-considerations.md b/docs-site/content/kagent/1.x/operations/operational-considerations.md index 97c5a880..ca975cbb 100644 --- a/docs-site/content/kagent/1.x/operations/operational-considerations.md +++ b/docs-site/content/kagent/1.x/operations/operational-considerations.md @@ -29,28 +29,39 @@ config: ``` The controller enforces the following rules when it reads the settings at start time. +* Durations are in seconds. A duration takes protobuf JSON notation. The value is a number with an `s` suffix, as in `604800s` or `0.5s`. A Go duration string such as `168h` fails validation. +* The document is strict. The controller rejects an unknown field, a duplicate key, a null value, a YAML alias or merge, and a file with more than one document. +* Settings load once. The controller validates the whole file at startup, passes the result to its owners, and reads nothing from the environment afterward. A change therefore needs a restart. The chart triggers that restart by checksumming the ConfigMap into the pod. -| Rule | What it means | -| ---- | ------------- | -| Durations are seconds | A duration takes protobuf JSON notation. The value is a number with an `s` suffix, as in `604800s` or `0.5s`, and a Go duration string such as `168h` fails validation. | -| The document is strict | The controller rejects an unknown field, a duplicate key, a null value, a YAML alias or merge, and a file with more than one document. | -| Settings load once | The controller validates the whole file at startup, passes the result to its owners, and reads nothing from the environment afterward. A change therefore needs a restart. The chart triggers that restart by checksumming the ConfigMap into the pod. | +To check your settings before you apply them, render the document that the chart builds from your Helm values. -An invalid setting stops the controller from starting rather than disabling one feature, so check the controller's logs after you change these values. To check a file before you apply it, run the CLI against the rendered document. The command does not connect to any service. +```bash +helm template kagent \ + {{< reuse "kagent-docs/snippets/helm-path.md" >}}/{{< reuse "kagent-docs/snippets/helm-kagent.md" >}} \ + --version {{< reuse "kagent-docs/versions/kagent.md" >}} \ + --namespace kagent \ + --values kagent.yaml \ + --show-only templates/controller-configmap.yaml \ + | yq '.data."controller.yaml"' > controller.yaml +``` + +Then run `kagent config validate` against the rendered document. ```bash kagent config validate --component controller --config controller.yaml ``` -Starting the controller with `--check-config` does the same from inside the image, and also confirms that the Secret files the document references are readable. +After you change these values, you can verify their validity by checking the controller's logs, as an invalid field prevents the controller from starting. ### Runtime defaults -`config.runtimeDefaults` is the one part of the block that the controller does not apply to itself. The controller compiles those settings into each agent runtime that it starts, so a setting such as the log level, the telemetry endpoints, or the content capture decision must appear in both `config` and `config.runtimeDefaults` to govern the controller and the agents together. A Harness overrides a few of them through its `spec.env` field, and owns none of the telemetry ones. For which variables a Harness may set, see [Controller-owned telemetry variables]({{< link path="agents/agent-harness#controller-owned-telemetry-variables" >}}). +The only section of settings that the controller does not apply to itself is `config.runtimeDefaults`. The controller compiles those settings into each agent runtime that it starts. If you want a setting to apply to both the controller and the agents, such as the log level, the telemetry endpoints, or the content capture decision, you must configure that settings in both `config` and `config.runtimeDefaults`. + +A Harness can override some runtime defaults through its `spec.env` field. The telemetry settings are the exception, because the controller owns those variables in every runtime. For the list, see [Controller-owned telemetry variables]({{< link path="agents/agent-harness#controller-owned-telemetry-variables" >}}). ## Choose a database -kagent stores {{< gloss "Session" >}}Sessions{{< /gloss >}} and compiled {{< gloss "Revision" >}}revisions{{< /gloss >}} in PostgreSQL. The kagent chart deploys no database of its own. It reads a connection string from a Kubernetes Secret, and `database.postgres.connectionStringSecretRef` names that Secret and the key inside it. +kagent stores {{< gloss "Session" >}}Sessions{{< /gloss >}} and compiled {{< gloss "Revision" >}}revisions{{< /gloss >}} in PostgreSQL. The kagent chart does not deploy a database of its own. It reads a connection string from a Kubernetes Secret, and `database.postgres.connectionStringSecretRef` names that Secret and the key inside it. ```yaml database: @@ -106,7 +117,7 @@ For production, run PostgreSQL outside the cluster's lifecycle so that a kagent replicas: 3 ``` - The two blocks divide along the same line as the rest of the chart. `database.postgres` covers the Kubernetes packaging, meaning the Secret reference and the client certificate. `config.database` sets what the controller reads from its configuration file. For that division, see [Controller configuration](#controller-configuration). + `database.postgres` configures the Kubernetes packaging, including the Secret reference and the client certificate. `config.database` sets what the controller reads from its configuration file. For more information about these settings, see [Controller configuration](#controller-configuration). | Setting | Description | | ------- | ----------- | @@ -174,8 +185,8 @@ config: | Setting | Description | | ------- | ----------- | -| `controller.push.signing.secretName` | The Secret that holds `private-key.pem`. Leave it empty to send push notifications without JSON Web Token (JWT) authentication. Helm references a Secret that you create, and neither generates nor rotates signing keys. An invalid key stops the controller from starting. Naming a Secret here projects it into the controller as the `config.gateway.push.signingPrivateKeyFile` setting. | -| `config.gateway.push.issuer` | The stable URL that webhook receivers trust as the JWT issuer. Omit the key, and the chart derives the issuer from `ui.externalUrl`, and then from the in-cluster A2A gateway URL. An empty string is rejected, because the controller requires an absolute `http://` or `https://` URL. | +| `controller.push.signing.secretName` | The Secret that holds `private-key.pem`. Leave it empty to send push notifications without JSON Web Token (JWT) authentication. Helm references a Secret that you create, and neither generates nor rotates signing keys. If you set this field, the Secrey's name is added into the controller as the `config.gateway.push.signingPrivateKeyFile` setting. | +| `config.gateway.push.issuer` | The stable URL that webhook receivers trust as the JWT issuer. If you omit the key, the chart derives the issuer from `ui.externalUrl`, and then from the in-cluster A2A gateway URL. An empty string is rejected, because the controller requires an absolute `http://` or `https://` URL. | With a signing key configured, each callback attempt carries a fresh five-minute Ed25519 JWT in an `Authorization: Bearer` header, and the `/.well-known/jwks.json` endpoint publishes the matching public key. @@ -223,7 +234,7 @@ When you scale a serving WorkerPool down, it removes pods without suspending the Every {{< gloss "Session" >}}Session{{< /gloss >}} holds database records and pins a runtime revision for as long as it exists, including a Session that is never opened again. The kagent controller deletes idle Sessions on a timer. Deleting idle sessions bounds the records that a kagent installation stores as well as the revisions that it holds open. -A leader-only worker sweeps Sessions once a minute and deletes every Session that has been idle past the limit, using the same deletion workflow that a client delete uses. `config.sessions.expirationInterval` sets that sweep interval, and it must be positive. +A leader-only worker sweeps Sessions once a minute and deletes every Session that has been idle past the limit, using the same deletion workflow that a client delete uses. To change this sweep interval, you can configure `config.sessions.expirationInterval`. To choose how long a Session can be idle before the worker deletes it, set `config.sessions.idleTtl` in your {{< reuse "kagent-docs/snippets/name-product.md" >}} Helm values. The value is a duration in seconds with an `s` suffix, and it defaults to `604800s`, or seven days. Setting it to `0s` turns the worker off, retries included, and a negative value is rejected. @@ -233,11 +244,11 @@ config: idleTtl: 2592000s ``` -Durations in the `config` block follow protobuf JSON notation, so seconds are the only unit. A Go duration string such as `720h` fails validation and stops the controller from starting. +Durations in the `config` block follow protobuf JSON notation. Seconds are the only supported unit, and a Go duration string such as `720h` fails validation and stops the controller from starting. -No per-agent override exists, so this one value governs every Session in the installation. No maximum exists either, so a longer value is accepted. +This value governs every Session in the installation. kagent does not provide a per-agent override, and it does not cap the value. -Idle time runs from whichever is later: the Session's creation, or its most recently stored A2A event. Reads, renames, lifecycle calls, and retried writes do not reset it, so a Session that is only ever listed still expires. +Idle time runs from whichever is later: the Session's creation, or its most recently stored A2A event. Reads, renames, lifecycle calls, and retried writes do not reset it. A Session that is only ever listed still expires. diff --git a/docs-site/content/kagent/1.x/operations/tune-agent-substrate.md b/docs-site/content/kagent/1.x/operations/tune-agent-substrate.md index 71ff1ccd..b2ed0ea3 100644 --- a/docs-site/content/kagent/1.x/operations/tune-agent-substrate.md +++ b/docs-site/content/kagent/1.x/operations/tune-agent-substrate.md @@ -15,7 +15,7 @@ This single Worker reports capacity for 1000 Actors, so a default pool serves ma 2. Confirm that you have administrative access to the cluster, because this guide scales WorkerPools and reads cluster-scoped resources. -3. Install [grpcurl](https://github.com/fullstorydev/grpcurl). `GetSubstrateStatus` has no kubectl or kagent command equivalent, so calling it directly is the only way to see the full runtime state. +3. Install [grpcurl](https://github.com/fullstorydev/grpcurl). No kubectl or kagent command covers `GetSubstrateStatus`. Calling it directly is the only way to see the full runtime state. ## Inspect the runtime @@ -84,7 +84,7 @@ A {{< gloss "Worker" >}}Worker{{< /gloss >}} hosts many Actors at the same time, Each Worker reports an Actor capacity when it starts, fixed by the Worker build rather than by a field on the WorkerPool. The gVisor and micro-VM Workers that Agent Substrate ships both report a capacity of 1000 Actors, and no setting raises or lowers it. -Agent Substrate checks this capacity on every placement. It weighs a Worker's spare CPU and memory only when the ActorTemplate states how much CPU and memory one Actor needs. An ActorTemplate that the kagent controller compiles states no per-Actor CPU or memory, so a pool places a Session's Actor on a Worker's available slots alone, regardless of how loaded the Worker's node already is. Worker resource limits still matter, because they govern how Kubernetes packs Workers onto nodes. +Agent Substrate checks this capacity on every placement. It weighs a Worker's spare CPU and memory only when the ActorTemplate states how much CPU and memory one Actor needs. An ActorTemplate that the kagent controller compiles does not state per-Actor CPU or memory, so a pool places a Session's Actor on a Worker's available slots alone, regardless of how loaded the Worker's node already is. Worker resource limits still matter, because they govern how Kubernetes packs Workers onto nodes. Size the pool for the number of turns that run at the same time, not for the number of Agents or Sessions that you have created. A cluster with hundreds of Sessions that are each used occasionally needs far fewer Workers than the session count suggests. @@ -103,7 +103,7 @@ substrateWorkerPool: ### Recognize an undersized pool -Agent Substrate reports pool capacity through its metrics rather than through an error that reaches the caller. Each Worker publishes a state of `idle`, `partial`, `at_capacity`, or `unschedulable`, which together show how much of a pool is in use. A placement that does not find a suitable Worker records a `no_capacity` outcome. Agent Substrate treats this outcome as a normal result rather than a failure, so it does not set an error type and does not raise an alert on its own. Watch the share of Workers at `at_capacity` and the rate of `no_capacity` placements to tell a pool that is merely busy from one that is too small. +Agent Substrate reports pool capacity through its metrics rather than through an error that reaches the caller. Each Worker publishes a state of `idle`, `partial`, `at_capacity`, or `unschedulable`, which together show how much of a pool is in use. A placement that does not find a suitable Worker records a `no_capacity` outcome. Agent Substrate treats this outcome as a normal result rather than a failure. It does not set an error type, and it does not raise an alert on its own. Watch the share of Workers at `at_capacity` and the rate of `no_capacity` placements to tell a pool that is merely busy from one that is too small. None of those signals reach the caller. A turn that cannot get a Worker waits for one, and a turn that waits too long fails with a generic timeout that names neither capacity nor the pool. @@ -155,11 +155,11 @@ substrateWorkerPool: | `substrateWorkerPool.serviceAccount.create` | Whether the chart creates the Worker ServiceAccount. Defaults to `true`. Set it to `false` to use an account that you manage yourself. | | `substrateWorkerPool.serviceAccount.name` | The ServiceAccount that Worker pods run as. Omit it to use the pool name. | -The chart writes `serviceAccountName` into the pool's pod template. A `serviceAccountName` that you set under `substrateWorkerPool.template` is overwritten rather than merged, so set the account through `substrateWorkerPool.serviceAccount.name`. +The chart writes `serviceAccountName` into the pool's pod template. A `serviceAccountName` that you set under `substrateWorkerPool.template` is overwritten rather than merged. Set the account through `substrateWorkerPool.serviceAccount.name`. ### Keep placement scoped to the pool's namespace {#workerpool-placement-labels} -kagent selects a WorkerPool by name and namespace when it compiles an ActorTemplate, and the generated ActorTemplate matches Workers on the `kagent.dev/worker-pool-name` and `kagent.dev/worker-pool-namespace` labels. Agent Substrate matches those labels across every pool rather than checking the WorkerPool's namespace, so a pool that carries only the name label can be selected from another namespace. +kagent selects a WorkerPool by name and namespace when it compiles an ActorTemplate, and the generated ActorTemplate matches Workers on the `kagent.dev/worker-pool-name` and `kagent.dev/worker-pool-namespace` labels. Agent Substrate matches those labels across every pool rather than checking the WorkerPool's namespace. A pool that sets only the name label can therefore be selected from another namespace. The chart sets both labels from the pool's name and namespace, and applies them after `substrateWorkerPool.labels`. An extra label that reuses either key is overwritten rather than honored. @@ -186,7 +186,7 @@ spec: workerImage: "ghcr.io/kagent-dev/substrate/ateom-gvisor:v{{< reuse "kagent-docs/versions/agent-substrate.md" >}}" ``` -Earlier releases placed Actors with a single `kagent.dev/worker-pool` label, and the chart no longer sets it. A Session or Sandbox that is pinned to a revision from one of those releases still selects that key, so keep the old label on the pool until you recreate them. +Earlier releases placed Actors with a single `kagent.dev/worker-pool` label, and the chart no longer sets it. A Session or Sandbox that is pinned to a revision from one of those releases still selects that key. Keep the old label on the pool until you recreate them. ```yaml substrateWorkerPool: @@ -210,7 +210,7 @@ Because `rustfs.enabled` defaults to `true`, the Agent Substrate chart installs ### Move snapshots to an external bucket -1. Turn the bundled store off and select the backend in your Agent Substrate Helm values. Setting `atelet.storageBackend` to `s3` selects the S3 client, and setting it to `gcs` selects Google Cloud Storage. Agent Substrate matches only the `s3` value, so every other value, an empty one included, selects Google Cloud Storage. +1. Turn the bundled store off and select the backend in your Agent Substrate Helm values. Setting `atelet.storageBackend` to `s3` selects the S3 client, and setting it to `gcs` selects Google Cloud Storage. Agent Substrate matches only the `s3` value. Every other value, an empty one included, selects Google Cloud Storage. ```yaml rustfs: @@ -221,7 +221,7 @@ Because `rustfs.enabled` defaults to `true`, the Agent Substrate chart installs 2. Supply the credentials that your backend needs. Setting `rustfs.enabled` to `false` stops the chart from setting the `AWS_*` variables, so neither component receives an endpoint or a key from Helm. Each storage client reads its credentials from the pod's own environment instead. - - For Google Cloud Storage, the client uses application default credentials. Bind the components' ServiceAccounts through [Workload Identity Federation for GKE](https://cloud.google.com/kubernetes-engine/docs/concepts/workload-identity). The chart renders no Helm value for Google credentials, so set nothing further. + - For Google Cloud Storage, the client uses application default credentials. Bind the components' ServiceAccounts through [Workload Identity Federation for GKE](https://cloud.google.com/kubernetes-engine/docs/concepts/workload-identity). The chart does not render a Helm value for Google credentials. Set nothing further. - For Amazon S3, the client reads the standard AWS environment and uses a role that the pod assumes. No static key is needed. - For an S3-compatible store, pass two variables and the static credentials through `atelet.extraEnv`. `AWS_ENDPOINT_URL` names the store's API endpoint, which the client otherwise resolves to Amazon S3. `AWS_S3_USE_PATH_STYLE` puts the bucket name in the request path instead of the hostname, which most S3-compatible stores require. @@ -265,7 +265,7 @@ Reading these variables off a running pod helps when a snapshot fails to write. ### Snapshot layout in the bucket -Agent Substrate builds a path per Actor under the Harness prefix, so one bucket serves many Harnesses without collision. The `` segment is the Actor's `metadata.uid`, not the `actorId` that `GetSubstrateStatus` reports. Both values are UUIDs, so read `latestSnapshot` off the Actor rather than matching a path to an Actor by eye. +Agent Substrate builds a path per Actor under the Harness prefix, so one bucket serves many Harnesses without collision. The `` segment is the Actor's `metadata.uid`, not the `actorId` that `GetSubstrateStatus` reports. Both values are UUIDs. Read `latestSnapshot` off the Actor rather than matching a path to an Actor by eye. ```console s3://ate-snapshots/kagent/atespaces//actors//snapshots/ diff --git a/docs-site/content/kagent/1.x/reference/release-notes/1.0.md b/docs-site/content/kagent/1.x/reference/release-notes/1.0.md index 792a9f94..ee74f70c 100644 --- a/docs-site/content/kagent/1.x/reference/release-notes/1.0.md +++ b/docs-site/content/kagent/1.x/reference/release-notes/1.0.md @@ -9,7 +9,7 @@ Each section covers one patch release in the kagent 1.0 line, newest first. For ## 1.0.0 -kagent 1.0 is a clean break from the 0.10.x line rather than an incremental release. It changes how an agent runs, how you declare one, and which custom resources exist. Because 1.0 shares no database schema with 0.10.x, moving an existing installation means standing up a new one rather than running `helm upgrade`. +kagent 1.0 is a clean break from the 0.10.x line rather than an incremental release. It changes how an agent runs, how you declare one, and which custom resources exist. Because 1.0 does not share a database schema with 0.10.x, moving an existing installation means standing up a new one rather than running `helm upgrade`. For the component versions that this release pins and the Kubernetes version that it is tested against, see [Version support]({{< link path="reference/versions#what-a-kagent-release-includes" >}}). diff --git a/docs-site/content/kagent/1.x/reference/tools-ecosystem.md b/docs-site/content/kagent/1.x/reference/tools-ecosystem.md index 985046ce..6543d486 100644 --- a/docs-site/content/kagent/1.x/reference/tools-ecosystem.md +++ b/docs-site/content/kagent/1.x/reference/tools-ecosystem.md @@ -48,7 +48,7 @@ The label changes three things about the server: - `status.discoveredTools` stays empty, so the command in [Read the tools that a server serves](#read-the-tools-that-a-server-serves) returns nothing for this server. The server's own documentation becomes the only list of what it offers. - The catalog keeps the server but records it as disconnected, with no tools. -You can also use this label on a kmcp `MCPServer` when agentgateway fronts the server. Agents bind a RemoteMCPServer that points at the gateway rather than binding the `MCPServer` itself, so discovery against the `MCPServer` serves no purpose and the label turns it off. +You can also use this label on a kmcp `MCPServer` when agentgateway fronts the server. Agents bind a RemoteMCPServer that points at the gateway rather than binding the `MCPServer` itself, so discovery against the `MCPServer` is unnecessary and the label turns it off. > [!NOTE] > Use this label only when controller-side discovery cannot succeed. Binding specific tools by name in an AgentTemplate still works against a server with discovery off, but nothing validates those names at admission, so a typo surfaces as a failed tool call at runtime instead of a rejected binding. @@ -329,7 +329,7 @@ grafana-mcp: | `grafana.secretRef` | The name of an existing Secret that holds `GRAFANA_SERVICE_ACCOUNT_TOKEN`. Use this instead of `serviceAccountToken` to keep the token out of your Helm values. | > [!NOTE] -> Upstream publishes no tag other than `latest`, so this subchart tracks the `mcp/grafana:latest` image on Docker Hub. A kagent release therefore does not pin which tools this server serves, and the set can change without a kagent upgrade. Read `status.discoveredTools` on `kagent-grafana-mcp` for the tools that your installation has, rather than relying on a published list. +> Upstream does not publish a tag other than `latest`, so this subchart tracks the `mcp/grafana:latest` image on Docker Hub. A kagent release therefore does not pin which tools this server serves, and the set can change without a kagent upgrade. Read `status.discoveredTools` on `kagent-grafana-mcp` for the tools that your installation has, rather than relying on a published list. ## Community and contributed servers diff --git a/docs-site/content/kagent/1.x/setup/model-providers/about-model-providers.md b/docs-site/content/kagent/1.x/setup/model-providers/about-model-providers.md index 5968100e..0ea4ea71 100644 --- a/docs-site/content/kagent/1.x/setup/model-providers/about-model-providers.md +++ b/docs-site/content/kagent/1.x/setup/model-providers/about-model-providers.md @@ -15,7 +15,7 @@ Every ModelConfig shares the same three parts, regardless of the provider that i | Field | Description | | ----- | ----------- | -| `provider` | The provider to use. Accepted values are `OpenAI`, `Anthropic`, `AzureOpenAI`, `Ollama`, `Gemini`, `GeminiVertexAI`, `AnthropicVertexAI`, `Bedrock`, `SAPAICore`, `Foundry`, and `Mistral`. Defaults to `OpenAI`. The API accepts `Mistral`, but the controller does not resolve it, so a Mistral ModelConfig reports `unsupported model provider: Mistral` and compiles no revision. | +| `provider` | The provider to use. Accepted values are `OpenAI`, `Anthropic`, `AzureOpenAI`, `Ollama`, `Gemini`, `GeminiVertexAI`, `AnthropicVertexAI`, `Bedrock`, `SAPAICore`, `Foundry`, and `Mistral`. Defaults to `OpenAI`. The API accepts `Mistral`, but the controller does not resolve it, so a Mistral ModelConfig reports `unsupported model provider: Mistral` and does not compile a revision. | | `model` | The model name, as the provider spells it. | | `stream` | Whether the agent streams its model calls. Defaults to `true`. Set `false` for an endpoint that rejects streaming, which otherwise fails every turn. | | Provider block | A block named after the provider, such as `openAI` or `bedrock`, holding the settings that only that provider takes. An empty block is valid when the provider does not take extra settings. | @@ -67,14 +67,14 @@ Substrate matches a destination on the exact DNS hostname, without path, port, o ### Credentials that do not compile -Header injection accepts one shape of credential: a static string. A credential that requires a local signature, a token exchange, or a file mounted into the agent cannot be injected, so kagent rejects the configuration instead of passing the credential to the runtime. The Agent reports the `Compatible` condition as `False` with the reason `UnsupportedConfiguration`, and kagent compiles no revision from it. Any Session that already exists keeps running the last revision that compiled. +Header injection accepts one shape of credential: a static string. A credential that requires a local signature, a token exchange, or a file mounted into the agent cannot be injected, so kagent rejects the configuration instead of passing the credential to the runtime. The Agent reports the `Compatible` condition as `False` with the reason `UnsupportedConfiguration`, and kagent does not compile a revision from it. Any Session that already exists keeps running the last revision that compiled. | Configuration | Why it cannot be injected | What to use instead | | ------------- | ------------------------- | ------------------- | | `Bedrock` with `AWS_ACCESS_KEY_ID` and `AWS_SECRET_ACCESS_KEY` | IAM keys sign each request locally. | A Bedrock bearer token in `AWS_BEARER_TOKEN_BEDROCK`. See [Amazon Bedrock]({{< link path="setup/model-providers/amazon-bedrock" >}}). | | `AnthropicVertexAI` and `GeminiVertexAI` | A Google service account key is signed locally to obtain a token, and the `kagent` and `byo` runtimes also mount it as a file. | `Anthropic` or `Bedrock` for Claude models, and `Gemini` for Gemini models. See [Google Vertex AI]({{< link path="setup/model-providers/google-vertexai" >}}). | | `SAPAICore` | OAuth2 client credentials are exchanged for a token before any request. | A provider that authenticates with an API key. See [SAP AI Core]({{< link path="setup/model-providers/sap-ai-core" >}}). | -| A `credentialRef` in a Harness `spec.env` entry | An arbitrary variable names no destination and no header to bind it to. | A ModelConfig or a RemoteMCPServer, each of which carries a destination. See [Agent harness]({{< link path="agents/agent-harness#configure-a-harness" >}}). | +| A `credentialRef` in a Harness `spec.env` entry | An arbitrary variable does not name a destination or a header to bind it to. | A ModelConfig or a RemoteMCPServer, each of which carries a destination. See [Agent harness]({{< link path="agents/agent-harness#configure-a-harness" >}}). | | `openAI.tokenExchange` | The block reads a mounted service account file to acquire a token. | An endpoint that accepts a static API key. See [OpenAI]({{< link path="setup/model-providers/openai" >}}). | | `tls.caCertSecretRef`, on any provider | The CA bundle is mounted as a file. | An endpoint whose certificate chains to a public CA. Setting `tls.disableVerify: true` skips certificate verification entirely and belongs only in a test environment. | diff --git a/docs-site/content/kagent/1.x/setup/model-providers/azure-openai.md b/docs-site/content/kagent/1.x/setup/model-providers/azure-openai.md index 3693ae35..b39cc8fe 100644 --- a/docs-site/content/kagent/1.x/setup/model-providers/azure-openai.md +++ b/docs-site/content/kagent/1.x/setup/model-providers/azure-openai.md @@ -94,9 +94,9 @@ spec: ## Send a file to the model -The `responses` format is the only one that sends a file with a prompt. It sends a link rather than the bytes, and Azure fetches that link itself, both on the turn that sends the link and on every turn that replays the conversation. A link must therefore stay reachable from Azure for as long as the conversation lives. +The `responses` format attaches a file to a prompt as a link, or as inline bytes when the file is an image. Azure fetches a link both on the turn that sends it and on every turn that replays the conversation, so a file link must be reachable from Azure for as long as the conversation lives. -What reaches Azure depends on whether the message gives a link or the bytes, and on the media type. The following table covers every combination that a message can include. +A link reaches Azure regardless of its media type. Inline bytes reach Azure only for an image. | What the message includes | What the runtime sends | | ------------------------ | ---------------------- | @@ -109,6 +109,8 @@ The runtime refuses a link on any other scheme. When the newest user message inc The runtime sends a file's display name as a text part of its own, placed before the file, because Azure rejects a filename next to a file URL. +A link attachment with a display name gets an extra text part that reads `File: ` that is placed immediately before the file part. Azure rejects a filename beside a file URL, so the name reaches the model as ordinary text. + ## Authentication | Configuration | Credential | diff --git a/docs-site/content/kagent/1.x/setup/model-providers/google-vertexai.md b/docs-site/content/kagent/1.x/setup/model-providers/google-vertexai.md index 0704bf45..780d1797 100644 --- a/docs-site/content/kagent/1.x/setup/model-providers/google-vertexai.md +++ b/docs-site/content/kagent/1.x/setup/model-providers/google-vertexai.md @@ -5,9 +5,9 @@ weight: 20 author: kagent.dev --- -Neither Vertex AI provider runs on kagent 1.0. `AnthropicVertexAI` and `GeminiVertexAI` both authenticate with a Google service account key, and such a key is signed locally to obtain an access token. An agent reaches its model provider through the {{< gloss "Agent Substrate" >}}Agent Substrate{{< /gloss >}} egress gateway, which injects a static credential into an HTTP header and performs no signing, so kagent rejects a Vertex AI {{< gloss "ModelConfig" >}}ModelConfig{{< /gloss >}} rather than passing the key to the runtime. +Neither Vertex AI provider runs on kagent 1.0. `AnthropicVertexAI` and `GeminiVertexAI` both authenticate with a Google service account key, and such a key is signed locally to obtain an access token. An agent reaches its model provider through the {{< gloss "Agent Substrate" >}}Agent Substrate{{< /gloss >}} egress gateway, which injects a static credential into an HTTP header and does not sign the request, so kagent rejects a Vertex AI {{< gloss "ModelConfig" >}}ModelConfig{{< /gloss >}} rather than passing the key to the runtime. -The rejection happens at compile time. The AgentTemplate reports the `Compatible` condition as `False` with the reason `UnsupportedConfiguration`, and kagent compiles no revision from it. On a `claude` {{< gloss "Harness" >}}Harness{{< /gloss >}} the message names the credential. +The rejection happens at compile time. The AgentTemplate reports the `Compatible` condition as `False` with the reason `UnsupportedConfiguration`, and kagent does not compile a revision from it. On a `claude` {{< gloss "Harness" >}}Harness{{< /gloss >}} the message names the credential. ``` environment credential "KAGENT_CLAUDE_GOOGLE_CREDENTIALS_JSON" cannot use gateway header injection; local signing and arbitrary secret environment variables are unsupported diff --git a/docs-site/content/kagent/1.x/setup/model-providers/sap-ai-core.md b/docs-site/content/kagent/1.x/setup/model-providers/sap-ai-core.md index 275c1f85..27942d2a 100644 --- a/docs-site/content/kagent/1.x/setup/model-providers/sap-ai-core.md +++ b/docs-site/content/kagent/1.x/setup/model-providers/sap-ai-core.md @@ -5,9 +5,9 @@ weight: 20 author: kagent.dev --- -The `SAPAICore` provider does not run on kagent 1.0. SAP AI Core authenticates with OAuth2 client credentials, which a client exchanges at a token endpoint for a short-lived access token before it calls the API. An agent reaches its model provider through the {{< gloss "Agent Substrate" >}}Agent Substrate{{< /gloss >}} egress gateway, which injects a static credential into an HTTP header and performs no token exchange, so kagent rejects a SAP AI Core {{< gloss "ModelConfig" >}}ModelConfig{{< /gloss >}} rather than passing the client secret to the runtime. +The `SAPAICore` provider does not run on kagent 1.0. SAP AI Core authenticates with OAuth2 client credentials, which a client exchanges at a token endpoint for a short-lived access token before it calls the API. An agent reaches its model provider through the {{< gloss "Agent Substrate" >}}Agent Substrate{{< /gloss >}} egress gateway, which injects a static credential into an HTTP header and does not perform a token exchange, so kagent rejects a SAP AI Core {{< gloss "ModelConfig" >}}ModelConfig{{< /gloss >}} rather than passing the client secret to the runtime. -The rejection happens at compile time on every runtime. The AgentTemplate reports the `Compatible` condition as `False` with the reason `UnsupportedConfiguration`, and kagent compiles no revision from it. +The rejection happens at compile time on every runtime. The AgentTemplate reports the `Compatible` condition as `False` with the reason `UnsupportedConfiguration`, and kagent does not compile a revision from it. ``` environment credential "SAP_AI_CORE_CLIENT_ID" cannot use gateway header injection; local signing and arbitrary secret environment variables are unsupported From f6a0a972997e88945e0c030d31d3cdbde6cba72f Mon Sep 17 00:00:00 2001 From: Rachael Graham Date: Wed, 14 Oct 2026 10:41:32 -0500 Subject: [PATCH 4/5] Update metrics.md Signed-off-by: Rachael Graham --- docs-site/content/kagent/1.x/observability/metrics.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs-site/content/kagent/1.x/observability/metrics.md b/docs-site/content/kagent/1.x/observability/metrics.md index b2890bcd..a8b92317 100644 --- a/docs-site/content/kagent/1.x/observability/metrics.md +++ b/docs-site/content/kagent/1.x/observability/metrics.md @@ -49,7 +49,7 @@ The Agent Substrate `atecontroller` component reports the same `controller_runti ## Agent runtime metrics -Agent runtimes export metrics over OTLP when the kagent Helm release sets `otel.metrics.enabled` to `true`. The `claude` and `codex` runtimes are the only ones that report the histogram in this section. The stack guides convert these OpenTelemetry metrics to Prometheus names in the collector. +When the kagent Helm release sets an endpoint in `config.runtimeDefaults.telemetry.metrics`, agent runtimes export metrics over OTLP. The `claude` and `codex` runtimes are the only ones that report the histogram in this section. The stack guides convert these OpenTelemetry metrics to Prometheus names in the collector. | Metric | Type | Measures | | ------ | ---- | -------- | From ab049be5fd8766028731f040367ad89ef6ab193c Mon Sep 17 00:00:00 2001 From: Rachael Graham Date: Wed, 14 Oct 2026 12:46:15 -0500 Subject: [PATCH 5/5] review edits Signed-off-by: Rachael Graham --- docs-site/content/kagent/1.x/agents/agent-harness.md | 4 ++-- docs-site/content/kagent/1.x/agents/bring-your-own-agent.md | 6 +++--- docs-site/content/kagent/1.x/observability/tracing.md | 2 +- .../kagent/1.x/operations/operational-considerations.md | 6 +++--- .../kagent/1.x/setup/model-providers/azure-openai.md | 2 -- 5 files changed, 9 insertions(+), 11 deletions(-) diff --git a/docs-site/content/kagent/1.x/agents/agent-harness.md b/docs-site/content/kagent/1.x/agents/agent-harness.md index eed3324c..b0c72fd7 100644 --- a/docs-site/content/kagent/1.x/agents/agent-harness.md +++ b/docs-site/content/kagent/1.x/agents/agent-harness.md @@ -24,8 +24,8 @@ spec: workload: image: {{< reuse "kagent-docs/versions/runtime-image.md" >}} env: - - name: OTEL_PROPAGATORS - value: tracecontext,baggage + - name: OTEL_BSP_SCHEDULE_DELAY + value: "2000" substrate: workerPoolRef: name: kagent-default diff --git a/docs-site/content/kagent/1.x/agents/bring-your-own-agent.md b/docs-site/content/kagent/1.x/agents/bring-your-own-agent.md index 1e1eb0af..4b30bbfb 100644 --- a/docs-site/content/kagent/1.x/agents/bring-your-own-agent.md +++ b/docs-site/content/kagent/1.x/agents/bring-your-own-agent.md @@ -54,8 +54,8 @@ Both kinds of agent receive the same inputs. Agent Substrate delivers the two JS | Input | Contents | | ----- | -------- | -| `/config/config.json` | The compiled agent configuration, including the resolved system prompt, the model and its settings, every tool, skill, and plugin that the AgentTemplate binds, and a `runtime` object that sets the process settings. | -| `/config/agent-card.json` | The agent card that kagent advertises for this agent, rendered as JSON. | +| `KAGENT_CONFIG_JSON`, which the helpers write to `/config/config.json` | The compiled agent configuration, including the resolved system prompt, the model and its settings, every tool, skill, and plugin that the AgentTemplate binds, and a `runtime` object that sets the process settings. | +| `KAGENT_AGENT_CARD_JSON`, which the helpers write to `/config/agent-card.json` | The agent card that kagent advertises for this agent, rendered as JSON. | | Anything in `spec.env` | Literal values and `credentialRef` lookups from the Harness, resolved before the Actor starts. Agent Substrate accepts at most 32 environment variables on an Actor, and the compiled configuration counts toward that limit. A revision that exceeds it fails to compile rather than starting and misbehaving. | The `runtime` object is the root of the configuration document, and subagents inherit it. The object includes the settings that a process needs before it serves a request. @@ -216,7 +216,7 @@ Implement the contract directly: 1. Serve `lf.a2a.v1.A2AService` over gRPC on port 80. Generate the stubs from the [A2A protocol](https://a2a-protocol.org) definitions. 2. Serve `GET /readyz` over HTTP on port 8081, returning `200`. -3. To honor the AgentTemplate, read the compiled configuration from the `KAGENT_CONFIG_JSON` environment variable. An agent that ignores the variable runs as an opaque agen. +3. To honor the AgentTemplate, read the compiled configuration from the `KAGENT_CONFIG_JSON` environment variable. An agent that ignores the variable runs as an opaque agent. The kagent helpers read `KAGENT_CONFIG_JSON` and materialize its contents into `/config/config.json`. An image without those helpers parses the variable directly. {{% /tab %}} diff --git a/docs-site/content/kagent/1.x/observability/tracing.md b/docs-site/content/kagent/1.x/observability/tracing.md index c8254fc7..9d16e871 100644 --- a/docs-site/content/kagent/1.x/observability/tracing.md +++ b/docs-site/content/kagent/1.x/observability/tracing.md @@ -255,7 +255,7 @@ The flush lets a kagent trace arrive promptly rather than on the exporter's own Turn off the trace exporter, then create a new Session so that the change takes effect. The controller sets the trace exporter to `none` in each runtime, so an agent does not fall back to the OpenTelemetry SDK's default of exporting to `localhost`. -1. Remove the telemetry settings from the kagent Helm release. Null the whole `telemetry` block rather than one signal inside it, because the controller rejects a null value for a single signal and fails to start. +1. Remove the telemetry settings from the kagent Helm release. Remove the whole `telemetry` block rather than one signal inside it. The controller rejects a null value anywhere in its configuration and fails to start, so a values file that sets a single signal to `null` breaks the installation. A `--set` flag removes the key instead of nulling it. ```bash helm upgrade kagent \ {{< reuse "kagent-docs/snippets/helm-path.md" >}}/{{< reuse "kagent-docs/snippets/helm-kagent.md" >}} \ diff --git a/docs-site/content/kagent/1.x/operations/operational-considerations.md b/docs-site/content/kagent/1.x/operations/operational-considerations.md index ca975cbb..411363a2 100644 --- a/docs-site/content/kagent/1.x/operations/operational-considerations.md +++ b/docs-site/content/kagent/1.x/operations/operational-considerations.md @@ -55,9 +55,9 @@ After you change these values, you can verify their validity by checking the con ### Runtime defaults -The only section of settings that the controller does not apply to itself is `config.runtimeDefaults`. The controller compiles those settings into each agent runtime that it starts. If you want a setting to apply to both the controller and the agents, such as the log level, the telemetry endpoints, or the content capture decision, you must configure that settings in both `config` and `config.runtimeDefaults`. +The only section of settings that the controller does not apply to itself is `config.runtimeDefaults`. The controller compiles those settings into each agent runtime that it starts. If you want a setting to apply to both the controller and the agents, such as the log level, the telemetry endpoints, or the content capture decision, you must configure that setting in both `config` and `config.runtimeDefaults`. -A Harness can override some runtime defaults through its `spec.env` field. The telemetry settings are the exception, because the controller owns those variables in every runtime. For the list, see [Controller-owned telemetry variables]({{< link path="agents/agent-harness#controller-owned-telemetry-variables" >}}). +The controller compiles each `config.runtimeDefaults` setting into the runtime's configuration document rather than passing it as an environment variable, so a Harness cannot override one of these settings through its `spec.env` field. A Harness tunes only the standard `OTEL_*` variables that the controller does not own, such as the `OTEL_BSP_*` batch span processor settings. For the variables that the controller does own, see [Controller-owned telemetry variables]({{< link path="agents/agent-harness#controller-owned-telemetry-variables" >}}). ## Choose a database @@ -185,7 +185,7 @@ config: | Setting | Description | | ------- | ----------- | -| `controller.push.signing.secretName` | The Secret that holds `private-key.pem`. Leave it empty to send push notifications without JSON Web Token (JWT) authentication. Helm references a Secret that you create, and neither generates nor rotates signing keys. If you set this field, the Secrey's name is added into the controller as the `config.gateway.push.signingPrivateKeyFile` setting. | +| `controller.push.signing.secretName` | The Secret that holds `private-key.pem`. Leave it empty to send push notifications without JSON Web Token (JWT) authentication. Helm references a Secret that you create, and neither generates nor rotates signing keys. If you set this field, the chart mounts the key and sets `config.gateway.push.signingPrivateKeyFile` to `/etc/kagent/secrets/push-private-key.pem`. | | `config.gateway.push.issuer` | The stable URL that webhook receivers trust as the JWT issuer. If you omit the key, the chart derives the issuer from `ui.externalUrl`, and then from the in-cluster A2A gateway URL. An empty string is rejected, because the controller requires an absolute `http://` or `https://` URL. | With a signing key configured, each callback attempt carries a fresh five-minute Ed25519 JWT in an `Authorization: Bearer` header, and the `/.well-known/jwks.json` endpoint publishes the matching public key. diff --git a/docs-site/content/kagent/1.x/setup/model-providers/azure-openai.md b/docs-site/content/kagent/1.x/setup/model-providers/azure-openai.md index b39cc8fe..d5a77d9a 100644 --- a/docs-site/content/kagent/1.x/setup/model-providers/azure-openai.md +++ b/docs-site/content/kagent/1.x/setup/model-providers/azure-openai.md @@ -107,8 +107,6 @@ A link reaches Azure regardless of its media type. Inline bytes reach Azure only The runtime refuses a link on any other scheme. When the newest user message includes such a link, the turn fails with `cannot send link "" with media type "": this provider integration can only pass http and https links`. When an older message in the replayed conversation includes one, the runtime replaces the link with a text note that keeps the URL, so a model with a fetch tool can still retrieve the file. -The runtime sends a file's display name as a text part of its own, placed before the file, because Azure rejects a filename next to a file URL. - A link attachment with a display name gets an extra text part that reads `File: ` that is placed immediately before the file part. Azure rejects a filename beside a file URL, so the name reaches the model as ordinary text. ## Authentication