diff --git a/.github/workflows/update-ref-docs.yaml b/.github/workflows/update-ref-docs.yaml index d919b659..f286cb2a 100644 --- a/.github/workflows/update-ref-docs.yaml +++ b/.github/workflows/update-ref-docs.yaml @@ -426,7 +426,16 @@ jobs: echo "Extracted from kagent ${KAGENT_COMMIT}:" check SUBSTRATE_VERSION "$SUBSTRATE_VERSION" "$SEMVER" - check KMCP_VERSION "$KMCP_VERSION" "$SEMVER" + # kagent stopped bundling kmcp at 1.0.0-alpha14 (kagent#3223), so its + # go.mod no longer requires it. An absent require is a real answer, + # not a parse failure: KMCP_VERSION stays empty and versions/kmcp.md + # is left alone. A require that IS present must still parse. + if grep -q 'github\.com/kagent-dev/kmcp' "$GOMOD"; then + check KMCP_VERSION "$KMCP_VERSION" "$SEMVER" + else + KMCP_VERSION="" + echo " KMCP_VERSION = (not bundled; go.mod does not require kmcp)" + fi check KAGENT_TOOLS_VERSION "$KAGENT_TOOLS_VERSION" "$SEMVER" check MAX_KUBE "$MAX_KUBE" '^[0-9]+\.[0-9]+$' @@ -566,7 +575,10 @@ jobs: # than warned about. It is still worth saying out loud: it means the # kagent support matrix and the kmcp guides name different versions on # purpose, which looks like a bug to anyone who has not read this. - if [ "$KMCP_LATEST" != "$KMCP_VERSION" ]; then + if [ -z "$KMCP_VERSION" ]; then + echo "kmcp: kagent no longer bundles kmcp; kmcp's latest release is $KMCP_LATEST." + echo "KMCP_MISMATCH=" >> $GITHUB_ENV + elif [ "$KMCP_LATEST" != "$KMCP_VERSION" ]; then echo "kmcp split: kagent pins $KMCP_VERSION, kmcp's latest release is $KMCP_LATEST. Writing both." echo "KMCP_MISMATCH=\`versions/kmcp.md\` is \`$KMCP_VERSION\` (what kagent bundles) and \`versions/kmcp-latest.md\` is \`$KMCP_LATEST\` (kmcp's newest release). The two naming different versions is expected -- the kagent support matrix and the standalone kmcp guides are answering different questions." >> $GITHUB_ENV else @@ -682,7 +694,12 @@ jobs: } echo "Flat conrefs:" - write_conref "$VERSIONS_DIR/kmcp.md" "$KMCP_VERSION" + # Empty when kagent no longer bundles kmcp; see the extraction step. + if [ -n "$KMCP_VERSION" ]; then + write_conref "$VERSIONS_DIR/kmcp.md" "$KMCP_VERSION" + else + echo " kmcp.md: SKIPPED, kagent does not bundle kmcp. Left at $(cat "$VERSIONS_DIR/kmcp.md")" + fi # Only when it resolved; see the cross-check step above. if [ -n "${KMCP_LATEST:-}" ]; then write_conref "$VERSIONS_DIR/kmcp-latest.md" "$KMCP_LATEST" @@ -728,7 +745,7 @@ jobs: echo "KMCP_SOURCE_LINE=- **kmcp**: [\`${KMCP_TAG}\`](https://github.com/${{ github.repository_owner }}/kmcp/releases/tag/${KMCP_TAG}) (\`${KMCP_COMMIT}\`)" echo "LINE_SCOPE_NOTE=" echo "FLAT_CONREF_ROWS<}} -A **WorkerPool** keeps **Workers** running and ready, and a Worker hosts the **Actor** alongside other Actors while its Session is active. Suspending this Actor writes its full state to an immutable **ActorSnapshot** and releases the capacity that it held on its Worker. +A **WorkerPool** keeps **Workers** running and ready, and a Worker hosts the **Actor** alongside other Actors while its Session is active. Suspending this Actor writes its durable data to an immutable **ActorSnapshot** and releases the capacity that it held on its Worker. The diagram forks at this snapshot, because a snapshot serves two purposes. -- **Resume** restores the same Actor onto **any Worker in the pool with room**, which is not necessarily the Worker that it ran on before. Because the snapshot captures the Actor's full state, the conversation continues from where it left off. Every idle agent takes this path. +- **Resume** restores the same Actor onto **any Worker in the pool with room**, which is not necessarily the Worker that it ran on before. Agent Substrate starts new containers for the Actor and restores the durable data from the snapshot. The conversation continues from where it left off. Every idle agent takes this path. - A **{{< gloss "Tag" >}}Tag{{< /gloss >}}** pins this snapshot so that Agent Substrate does not collect it. To fork a Session from a checkpoint, kagent seeds a **New Actor** from that tag. Resuming an existing Actor never goes through a tag. A tag gives a snapshot a stable name, so callers do not track Agent Substrate's internal snapshot identity. The tag names one snapshot permanently, and only its visibility scope can change afterward. A tag is also a retention pin, so Agent Substrate does not delete a snapshot while a tag still names it. 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 e407b411..5f5d3598 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 @@ -66,10 +66,7 @@ Listing behaves the same way. A list returns the caller's own Sessions by defaul A share lets a Session's owner give another account access to that one conversation. Creating a share produces a token, and a caller presenting that token reaches the shared Session without becoming its creator. -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. +Every share is read-only. The holder can read the conversation, but cannot send a message, answer an approval, cancel a task, or retry a turn. kagent enforces the restriction in its services and transports. A shared conversation in the UI does not show controls for those actions. 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. 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 6e6116b9..c0928a26 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 @@ -34,7 +34,9 @@ An {{< gloss "ActorTemplate" >}}ActorTemplate{{< /gloss >}}'s snapshot configura - **`Full`**: Captures process memory, the root filesystem changes layered on top of the container image, and any attached durable volumes. A `Full` snapshot holds everything that is needed to resume the Actor hot, with its in-memory state intact. - **`Data`**: Captures only the contents of attached durable volumes. Process memory and the rest of the root filesystem are discarded, which makes the snapshot much cheaper to write and store. -kagent sets the scope automatically on every ActorTemplate that it generates, and it sets `Full` so that process memory survives both a pause and a suspend. Each Actor also gets a single durable volume named `data`. The one snapshot setting that you author is the storage location, which you set on the {{< gloss "Harness" >}}Harness{{< /gloss >}}. +kagent sets the `Data` scope on every ActorTemplate that it generates, for both agents and standalone Sandboxes. Each Actor gets one durable volume named `data`, mounted at `/data`. The only snapshot setting that you configure is the storage location, in the `substrate.snapshotPolicy` section of the {{< gloss "Harness" >}}Harness{{< /gloss >}}. + +A suspend keeps the durable volume and discards process memory. A resume therefore starts new containers from the image and restores the saved durable directories. The agent runtimes store what a turn needs in the durable volume, including a pending approval or question, and continue the conversation after the restart. One scope governs both operations. The ActorTemplate's `onCommit` setting selects what a suspend uploads to snapshot storage, and a pause captures this same scope on the node. @@ -46,8 +48,8 @@ A **DurableDir volume** is the per-Actor application data surface. Its contents Two kinds of snapshot serve different purposes, and both appear in a normal installation. -- The **golden snapshot** is shared by every Actor of one ActorTemplate. Agent Substrate captures it once by booting the workload temporarily when the template is created. Every new Actor of this template starts from this snapshot rather than from a cold start. kagent waits for the golden snapshot before an {{< gloss "AgentTemplate" >}}AgentTemplate{{< /gloss >}} becomes ready, and the controller reports `waiting for the ActorTemplate golden snapshot` while the capture runs. -- The **last snapshot** belongs to one Actor. Agent Substrate writes it on every suspend and restores this same Actor from it on the next resume. It keeps the files and memory that this one Session built up, so the Session continues from where it stopped. +- The **golden snapshot** is shared by every Actor of one ActorTemplate. Agent Substrate captures it once by booting the workload temporarily when the template is created. Every new Actor of this template starts from this snapshot. kagent waits for the golden snapshot before an {{< gloss "AgentTemplate" >}}AgentTemplate{{< /gloss >}} becomes ready, and the controller reports `waiting for the ActorTemplate golden snapshot` while the capture runs. +- The **last snapshot** belongs to one Actor. Agent Substrate writes it on every suspend and restores this same Actor from it on the next resume. It keeps the durable files that this one Session built up, so the Session continues from where it stopped. Agent Substrate writes every snapshot to object storage, so Actor state is durable and portable across the cluster. The supported backends are Google Cloud Storage and Amazon Simple Storage Service (S3), and the S3 client also reaches S3-compatible stores that accept a custom endpoint and path-style addressing. For the settings that select and configure each client, see [Configure snapshot storage]({{< link path="operations/tune-agent-substrate#configure-snapshot-storage" >}}). A {{< gloss "Harness" >}}Harness{{< /gloss >}} names the location for its Actors' snapshots in its `substrate.snapshotPolicy` section. 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 263ec8ae..93f7ed98 100644 --- a/docs-site/content/kagent/1.x/agents/agent-harness.md +++ b/docs-site/content/kagent/1.x/agents/agent-harness.md @@ -11,7 +11,7 @@ This page covers the Harness schema, the four harnesses, and what each one suppo ## Configure a Harness -The following configuration shows a complete Harness resource. Only `workload`, `substrate`, and one harness block are required. +The following configuration shows a complete Harness resource. Only `substrate` and one harness block are required. The `byo` harness also requires `workload.image` and `workload.command`. ```yaml kubectl apply -f - <}}). Limits and reserved names both apply. For more information, see [Environment variables on an Actor](#environment-variables-on-an-actor). | @@ -52,6 +52,30 @@ A Harness and an AgentTemplate do not reference each other. An {{< gloss "Agent" The image, command, arguments, environment, and resolved AgentTemplate together form a {{< gloss "Revision" >}}revision{{< /gloss >}}, which kagent identifies by a digest of that content. Editing any of them produces a new revision at the next reconcile. A {{< gloss "Session" >}}Session{{< /gloss >}} runs the revision that it was created from until that Session ends, so only Sessions that start after the edit use the new revision. +### Built-in runtime images + +Each kagent release ships a runtime image for the `kagent`, `codex`, and `claude` harnesses, pinned by digest. If a Harness of one of these types omits `workload.image`, it runs the image of the installed release. If the Harness explicitly sets `workload.image`, this image overrides the release image. After the Agent compiles, `status.workloadImage` reports the image that the Agent's desired revision runs. Existing Sessions keep both the revision and the image that they were created from. + +To read the image that an Agent runs, check its status. + +```bash +kubectl get agent my-agent -n kagent -o jsonpath='{.status.workloadImage}' +``` + +To replace the release defaults for the whole installation, set `config.harnessImages` in your Helm values, such as in the following example. Your values merge over the image catalog that the chart packages. `global.imageRegistry` rewrites both the packaged defaults and your overrides, so a mirrored registry needs no further configuration. + +```yaml +config: + harnessImages: + kagent: registry.example.com/kagent/golang-adk@sha256: + codex: registry.example.com/kagent/codex-harness@sha256: + claude: registry.example.com/kagent/claude-harness@sha256: +``` + +The `kagent`, `codex`, and `claude` fields set the digest-pinned default image for each harness. Omit a key to keep the release default for that harness. An empty string disables the default, and a Harness of that type must then set `workload.image`. + +The chart also sets `config.harnessImages.release` to the release that the packaged images belong to. When that value does not match the controller's release, the controller fails at startup. + ## Choose a harness A Harness resource names exactly one of the following four harnesses, and that choice decides what executes an agent and how much of kagent's feature set the agent can use. 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 8459827b..64022706 100644 --- a/docs-site/content/kagent/1.x/agents/agent-memory.md +++ b/docs-site/content/kagent/1.x/agents/agent-memory.md @@ -27,35 +27,40 @@ Memories are scoped to the agent and to the user who created them, and carry the Agent memory requires your own PostgreSQL database with the [pgvector](https://github.com/pgvector/pgvector) extension installed. The default database that `kagent install` deploys runs the stock `postgres` image, which omits pgvector, so that database cannot store memories. -1. Install the pgvector extension in your database, in the schema that kagent reads it from. The `config.database.vectorSchema` value names this schema and defaults to `extensions`. Grant `USAGE` on the schema to the role that kagent assumes after it connects, which the `config.database.role` value names. When you leave `config.database.role` empty, grant `USAGE` to the login user. +1. Prepare the database for kagent and create its two connection Secrets. Follow steps 1 - 3 of [Use an external PostgreSQL instance]({{< link path="operations/operational-considerations#use-an-external-postgresql-instance" >}}) to create the `kagent_owner` and `kagent_readwrite` roles and the `my-postgres-owner` and `my-postgres-readwrite` Secrets. + +2. Install the pgvector extension in the schema that kagent reads it from. The `config.database.vectorSchema` value names this schema and defaults to `extensions`. Migrations and runtime queries both use the extension, so grant `USAGE` on the schema to both kagent roles. ```sql CREATE SCHEMA IF NOT EXISTS extensions; CREATE EXTENSION IF NOT EXISTS vector SCHEMA extensions; - GRANT USAGE ON SCHEMA extensions TO ; + GRANT USAGE ON SCHEMA extensions TO kagent_owner, kagent_readwrite; ``` The kagent migrations create the memory tables, and they never create the extension. Be sure to install it yourself before you enable memory. -2. Store the connection string for your database in a Kubernetes Secret in the same namespace as the kagent controller. - ```bash - kubectl create secret generic my-postgres-url-secret -n kagent \ - --from-literal=connectionString='postgres://:@:5432/?sslmode=require' - ``` - 3. Save the following Helm values to point kagent at your database and run the migration that creates the memory tables. ```yaml cat > kagent-memory-values.yaml <}}Harness{{< /gloss >}} and the AgentTemplate, and creating a {{< gloss "Session" >}}Session{{< /gloss >}} from it. - Running the {{< gloss "Actor" >}}Actor{{< /gloss >}} that hosts your image inside a sandbox. The {{< gloss "WorkerPool" >}}WorkerPool{{< /gloss >}} that the Harness references sets the class: `gvisor`, which every installation provides, or `microvm`, which needs its own Agent Substrate provisioning. For more information, see [Sandbox classes]({{< link path="about/substrate-runtime/sandboxing#sandbox-classes" >}}). -- [Suspending and resuming]({{< link path="about/substrate-runtime/suspend-and-resume#suspension-between-turns" >}}) the Actor between turns, including its in-memory state. +- [Suspending and resuming]({{< link path="about/substrate-runtime/suspend-and-resume#suspension-between-turns" >}}) the Actor between turns. A suspend keeps only the durable volume mounted at `/data`, so an image must write any state that it needs across turns to that directory. - Routing every conversation through the A2A gateway, so callers address the {{< gloss "Agent" >}}Agent{{< /gloss >}} rather than the Actor behind it. - Delivering the agent configuration and agent card to the container, which the kagent helpers write to `/config/config.json` and `/config/agent-card.json` at startup. @@ -228,7 +228,7 @@ app = KAgentApp( `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`. -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. +To bind the address that the configuration names, parse `/config/config.json` with `kagent.adk.types.parse_config` and pass the result as the `config` argument. The `a2a_grpc_address` argument overrides both the default and the configured address. {{% /tab %}} {{% tab name="Any other language" %}} Implement the contract directly: @@ -245,6 +245,7 @@ The kagent helpers read `KAGENT_CONFIG_JSON` and materialize its contents into ` - **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 only HTTP JSON-RPC cannot run on this harness. - **The LangGraph, CrewAI, and OpenAI adapters do not satisfy the contract.** The `kagent-langgraph`, `kagent-crewai`, and `kagent-openai` packages each build a FastAPI application with A2A JSON-RPC routes, without a gRPC server. Running LangGraph, CrewAI, or the OpenAI Agents SDK under `byo` means serving A2A over gRPC yourself, as the **Any other language** tab in [Build the image](#build-the-image) describes. +- **Skills load only when the image reads them.** kagent mounts each skill and plugin that an AgentTemplate attaches, and describes them in the agent configuration. The Go helper reads that description. The Python helper does not yet read it, so a Python image runs without its skills. - **Long-term memory is unavailable.** Memory is configured under `spec.kagent.memory`, and only the `kagent` harness wires it up. A `byo` Harness does not take an equivalent setting. For what a BYO image would need to replace, see [Agent memory]({{< link path="agents/agent-memory" >}}). - **A broken port mapping looks like 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 95ebb19b..fe378905 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 @@ -195,7 +195,9 @@ An `ask_user_request` carries an `id` and a list of `questions`. The response ec A paused task waits for the client to send a message that carries the response payload, on the same task and context. kagent rejects a resume attempt on a task that is not waiting. -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. A harness that holds a live process across the wait, such as `codex` or `claude`, therefore 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" >}}). +While the task waits, kagent suspends the {{< gloss "Actor" >}}Actor{{< /gloss >}} to a durable {{< gloss "Snapshot" >}}snapshot{{< /gloss >}} in the same way as at the end of a turn. The snapshot keeps the durable volume and discards the running process. Each harness records the pending request in that volume and continues the turn when the answer arrives. + +The `claude` harness resumes its deferred tool call. The `codex` harness runs the approved MCP call itself and continues with the result. The {{< gloss "Worker" >}}Worker{{< /gloss >}} is released in the meantime, so a conversation that sits at `INPUT_REQUIRED` does not consume pool capacity. The wait also survives the loss of the node. For more information, 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/examples/a2a-agents.md b/docs-site/content/kagent/1.x/examples/a2a-agents.md index e9f0c31e..6faa830e 100644 --- a/docs-site/content/kagent/1.x/examples/a2a-agents.md +++ b/docs-site/content/kagent/1.x/examples/a2a-agents.md @@ -209,6 +209,8 @@ A task that is still running reports `TASK_STATE_WORKING` and has no artifacts y A task callback lets a webhook learn when a task needs input or reaches a final state. The callback receives a status update for future `INPUT_REQUIRED`, `AUTH_REQUIRED`, `COMPLETED`, `FAILED`, `CANCELED`, and `REJECTED` states. After the callback arrives, the receiver calls authenticated `GetTask` to read the current state and artifacts. +kagent keeps one pending delivery for each callback, and a newer state replaces a delivery that has not yet succeeded. When a task resumes, kagent discards the pending delivery for the state that the task left, so a receiver does not see every state. + The kagent CLI wraps the three push notification configuration methods, so the steps that follow use the CLI rather than building each request by hand. The CLI defaults to port 8083, which the prerequisites already forward. 1. To register a callback before a new task is dispatched, add `--push-url` to the kagent CLI invocation. The command returns immediately with the task ID and callback ID. @@ -266,15 +268,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 `config.gateway.push.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.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 config.gateway.push.issuer=https://kagent.example + --set config.push.issuer=https://kagent.example ``` - 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. + The issuer that a receiver trusts must match the host that it fetches the JWKS from. When you omit `config.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 @@ -290,7 +292,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 `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. +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.push.allowHttp=true` or `config.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/agent-substrate.md b/docs-site/content/kagent/1.x/examples/agent-substrate.md index 58cb8280..a474b176 100644 --- a/docs-site/content/kagent/1.x/examples/agent-substrate.md +++ b/docs-site/content/kagent/1.x/examples/agent-substrate.md @@ -100,7 +100,7 @@ Each suspend writes a {{< gloss "Snapshot" >}}snapshot{{< /gloss >}}, and Agent ```bash grpcurl -plaintext \ -d '{"sessionId":"'"$SESSION_ID"'","expectedHeadTaskId":"'"$TASK_ID"'","requestId":"'"$(uuidgen)"'"}' \ - localhost:8083 kagent.api.v1alpha1.CheckpointService/CreateCheckpoint + localhost:8083 kagent.api.v1.CheckpointService/CreateCheckpoint ``` Example output: @@ -134,7 +134,7 @@ Each suspend writes a {{< gloss "Snapshot" >}}snapshot{{< /gloss >}}, and Agent ```bash grpcurl -plaintext \ -d '{"sessionId":"'"$SESSION_ID"'","page":{"limit":50}}' \ - localhost:8083 kagent.api.v1alpha1.CheckpointService/ListCheckpoints + localhost:8083 kagent.api.v1.CheckpointService/ListCheckpoints ``` Underneath, the checkpoint attaches a {{< gloss "Tag" >}}Tag{{< /gloss >}} named `checkpoint-` to the snapshot, and Agent Substrate does not collect a snapshot while a tag names it. You can see the tag by running `kubectl ate get tags --atespace kagent`. @@ -147,7 +147,7 @@ A fork starts a new Session from a checkpoint. The new Session replays the conve ```bash grpcurl -plaintext \ -d '{"checkpointId":"'"$CHECKPOINT_ID"'","requestId":"'"$(uuidgen)"'"}' \ - localhost:8083 kagent.api.v1alpha1.CheckpointService/ForkSession + localhost:8083 kagent.api.v1.CheckpointService/ForkSession ``` The fork takes the checkpoint's name, and runs the revision that the source Session was created on. Example output: @@ -200,7 +200,7 @@ A fork starts a new Session from a checkpoint. The new Session replays the conve ```bash grpcurl -plaintext \ -d '{"checkpointId":"'"$CHECKPOINT_ID"'"}' \ - localhost:8083 kagent.api.v1alpha1.CheckpointService/DeleteCheckpoint + localhost:8083 kagent.api.v1.CheckpointService/DeleteCheckpoint ``` ## Next steps 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 48e77cd8..ef28df02 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 @@ -495,7 +495,7 @@ You can now safely [clean up these resources](#clean-up). ```bash grpcurl -plaintext \ -d '{"checkpointId":""}' \ - localhost:8083 kagent.api.v1alpha1.CheckpointService/DeleteCheckpoint + localhost:8083 kagent.api.v1.CheckpointService/DeleteCheckpoint ``` 2. Remove the server entry from your client. In Claude Code, run `claude mcp remove kagent`. In Cursor, delete the `kagent` entry from your MCP settings. diff --git a/docs-site/content/kagent/1.x/examples/skills.md b/docs-site/content/kagent/1.x/examples/skills.md index ce349eac..3aa2eee5 100644 --- a/docs-site/content/kagent/1.x/examples/skills.md +++ b/docs-site/content/kagent/1.x/examples/skills.md @@ -11,11 +11,13 @@ For the fields that attach a skill and the rules that govern their names, see [S ## About skills at run time -kagent does not fetch a skill when you apply an AgentTemplate. The compiled revision records where each skill comes from, and the {{< gloss "Actor" >}}Actor{{< /gloss >}} fetches it when the agent starts. Every artifact is unpacked under `/plugins`, whether it contains a single skill or a plugin, and each enabled skill is then copied to `/skills/`. The agent reads skills only from `/skills`. +kagent does not fetch a skill when you apply an AgentTemplate. The compiled revision records the source of each skill, and the skill arrives when the agent starts. For an `oci` source, Agent Substrate pulls the image from the Worker and mounts it as read-only into the {{< gloss "Actor" >}}Actor{{< /gloss >}} before the runtime starts. For a `git` or `bucket` source, the Actor fetches the files itself. The runtime then copies each enabled skill into the directory that its harness reads, which is `/skills/` on a `kagent` Harness. Because kagent fetches a skill this late, review the following considerations. * **A wrong source still compiles.** kagent validates skill names before it accepts an AgentTemplate, but it never checks that the artifact exists or that it holds a `SKILL.md` file. A bad digest produces a revision that reports `Ready`, and the agent then fails to start. +* **A private registry needs Worker credentials.** Agent Substrate pulls an `oci` source with the registry credentials that the Worker uses for workload images. Kubernetes `imagePullSecrets` on a pod do not configure that pull. +* **Each Actor mounts up to 29 distinct skill images.** Images shared by several skills or plugins count once. A revision that needs more fails when kagent builds its ActorTemplate. * **Scripts run in the runtime image.** A skill's scripts get whatever the Harness image provides. The kagent runtime image is Alpine Linux with `bash`, `git`, and the standard Alpine utilities, and it does **not** include Python. ### Skill tools @@ -32,8 +34,20 @@ 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 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. `list_files` and `grep_file` reach only the skills and session roots that `read_file` reads. 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`. +The `config.runtimeDefaults.skillTools` Helm values change this tool set for every agent in the installation, because a Harness has no per-agent equivalent. +```yaml +config: + runtimeDefaults: + skillTools: + fileSearchTools: true + disableBash: true +``` + +| Setting | Description | +| ------- | ----------- | +| `fileSearchTools` | Set 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. Both tools reach only the skills and session roots that `read_file` reads. These settings are off by default because they give an agent broad visibility of the filesystem. When `bash` is disabled, these tools keep that visibility available to the agent. | +| `disableBash` | Set to `true` to remove the `bash` tool from the root agent and from every subagent. The agent still loads skills and keeps the file tools. Defaults to `false`. | 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. > [!IMPORTANT] @@ -53,7 +67,7 @@ Attaching a skill also changes what the agent is told. The runtime appends the n ``` > [!WARNING] - > kagent pulls a skill image over HTTPS with certificate verification, and v1alpha3 has no option to disable it. kagent 0.x accepted an `insecureSkipVerify` flag for a local registry, but that field does not exist in 1.x. A plain HTTP registry, and a `localhost` registry that only the host can reach, both fail at agent startup. + > Agent Substrate pulls a skill image on the Worker. API version v1alpha3 does not have an option to skip certificate verification. kagent 0.x accepted an `insecureSkipVerify` flag for a local registry, but that field does not exist in 1.x. A `localhost` registry that only the host can reach fails at agent startup. ## Build the skill @@ -140,7 +154,7 @@ A skill is a directory whose root holds a `SKILL.md` file. Everything else in th ## Publish the skill as an OCI image -kagent pulls an `oci` source as a container image and unpacks its flattened filesystem, so the image holds the skill directory and nothing else. Build it from `scratch`, which produces an image whose root **is** the skill root. +Agent Substrate mounts an `oci` source as a container image. The image must hold the skill directory and nothing else. Build it from `scratch`, which produces an image whose root **is** the skill root. 1. Create the Dockerfile. ```bash @@ -345,6 +359,7 @@ A skill that kagent cannot fetch stops the agent from starting at all, rather th > This condition does not name the skill, and reports the same reason for any Actor that has not yet produced a snapshot. An agent that is merely still starting looks identical to one whose skill cannot be fetched. 2. Find the WorkerPool that the Harness runs on, and read its Workers' logs. The Actor writes the failure there rather than to the Agent. + ```bash export WORKER_POOL=$(kubectl get harness my-first-harness -n kagent \ -o jsonpath='{.spec.substrate.workerPoolRef.name}') diff --git a/docs-site/content/kagent/1.x/observability/metrics.md b/docs-site/content/kagent/1.x/observability/metrics.md index 2a2ac34e..8b07b1b2 100644 --- a/docs-site/content/kagent/1.x/observability/metrics.md +++ b/docs-site/content/kagent/1.x/observability/metrics.md @@ -36,7 +36,7 @@ Besides the standard Go runtime and process metrics, the controller reports the | Metric | Type | Measures | | ------ | ---- | -------- | -| `kagent_grpc_server_requests_total` | Counter | Requests to the controller's gRPC API, by `method`, `code`, and `rpc_type`. The `method` label names the full gRPC method, such as `/kagent.api.v1alpha1.SessionService/CreateSession`. | +| `kagent_grpc_server_requests_total` | Counter | Requests to the controller's gRPC API, by `method`, `code`, and `rpc_type`. The `method` label names the full gRPC method, such as `/kagent.api.v1.SessionService/CreateSession`. | | `kagent_grpc_server_request_duration_seconds` | Histogram | Latency of requests to the controller's gRPC API, with the same labels. | | `controller_runtime_reconcile_total`, `controller_runtime_reconcile_errors_total`, `controller_runtime_reconcile_time_seconds` | Counter, counter, histogram | Reconciliations of kagent resources, by controller, and how long each one takes. A rising error count means that the controller cannot bring a resource to its desired state. | | `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. | 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 53fe2601..a1d2ebdf 100644 --- a/docs-site/content/kagent/1.x/operations/operational-considerations.md +++ b/docs-site/content/kagent/1.x/operations/operational-considerations.md @@ -61,21 +61,27 @@ Each `config.runtimeDefaults` setting is the installation's default. A Harness c ## Choose a database -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. +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. kagent connects to the database with two logins, and reads the connection string for each login from a Kubernetes Secret. + +* The **owner** login creates the schema and applies migrations at startup. `database.postgres.ownerConnectionStringSecretRef` references the Secret and key that hold its connection string. +* The **read/write** login serves every runtime query. It can read and write rows, but it cannot change the schema. `database.postgres.readWriteConnectionStringSecretRef` references the Secret and key that hold its connection string. ```yaml database: postgres: - connectionStringSecretRef: - name: kagent-postgres - key: connectionString + ownerConnectionStringSecretRef: + name: kagent-postgres-owner + key: ownerConnectionString + readWriteConnectionStringSecretRef: + name: kagent-postgres-readwrite + key: readWriteConnectionString ``` `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`. +`kagent install` creates a development database 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-owner` and `kagent-postgres-readwrite` in the kagent namespace, and `substrate-postgres-owner` and `substrate-postgres-readwrite` in `ate-system`. 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. @@ -84,29 +90,63 @@ Connections to this database do not use a password. When `database.postgres.clie ### Use an external PostgreSQL instance -For production, run PostgreSQL outside the cluster's lifecycle so that a kagent uninstall cannot delete the data. +For production, run PostgreSQL outside the cluster's lifecycle so that a kagent uninstall cannot delete the data. kagent requires a new, empty database, and the roles and the schema must exist before the controller starts. kagent ships a `psql` script that creates them. + +Agent Substrate connects to the same database with its own owner and read/write logins, which its own setup script creates. The Agent Substrate chart reads the connection strings for those logins from the Secrets that its `postgres.ownerConnectionStringSecretRef` and `postgres.readWriteConnectionStringSecretRef` values reference. For a procedure that prepares the database for both, see [Install kagent 1.0]({{< link path="operations/upgrade-from-0x#install-kagent-10" >}}). + +1. Download the setup script for your kagent version. + + ```bash + curl -sSLo setup.sql https://raw.githubusercontent.com/kagent-dev/kagent/refs/tags/v{{< reuse "kagent-docs/versions/kagent.md" >}}/go/core/pkg/postgressetup/setup.sql + ``` + +2. Run the script as a database administrator, with `psql` version 15 or later. The script reads the passwords from environment variables, which earlier `psql` versions cannot do. The script creates a `NOLOGIN` owner role that owns the schema, a `NOLOGIN` read/write role, a login user for each role, and the schema. It also revokes `CREATE` on the `public` schema from `PUBLIC`. Every variable is required. The script must run in one transaction. -1. Create a Secret that holds the connection string. + ```bash + export KAGENT_OWNER_PASSWORD= KAGENT_READWRITE_PASSWORD= + psql "$ADMIN_CONNECTION_STRING" --no-psqlrc --set=ON_ERROR_STOP=1 --single-transaction \ + --set=kagent_schema=kagent \ + --set=kagent_owner_role=kagent_owner \ + --set=kagent_owner_user=kagent_owner_user \ + --set=kagent_readwrite_role=kagent_readwrite \ + --set=kagent_readwrite_user=kagent_readwrite_user \ + --command='\getenv kagent_owner_password KAGENT_OWNER_PASSWORD' \ + --command='\getenv kagent_readwrite_password KAGENT_READWRITE_PASSWORD' \ + --file=setup.sql + ``` + + The script is safe to run again. Each run sets each login's password to the value of its variable, and an empty value removes the password. To keep the existing passwords, pass them again on every run. The passwords reach the server as statement text. A server that logs every statement therefore records them. To keep a plain password out of the server log, pass a SCRAM-SHA-256 verifier, which PostgreSQL stores as given. + +3. Create a Secret for each login. Leave role settings out of the connection strings, because kagent assumes each role after it connects. These names do not collide with the `kagent-postgres-owner` and `kagent-postgres-readwrite` Secrets that `kagent install` creates for the development database. ```bash - kubectl create secret generic my-postgres \ + kubectl create secret generic my-postgres-owner \ --namespace kagent \ - --from-literal=connectionString="postgresql://user:password@postgres.example.com:5432/kagent?sslmode=verify-full" + --from-literal=ownerConnectionString="postgresql://kagent_owner_user:@postgres.example.com:5432/kagent?sslmode=verify-full" + kubectl create secret generic my-postgres-readwrite \ + --namespace kagent \ + --from-literal=readWriteConnectionString="postgresql://kagent_readwrite_user:@postgres.example.com:5432/kagent?sslmode=verify-full" ``` -2. Point the chart at your Secret in your Helm values file. +4. In your Helm values file, configure the chart to reference your Secrets and roles. ```yaml database: postgres: - connectionStringSecretRef: - name: my-postgres - key: connectionString + ownerConnectionStringSecretRef: + name: my-postgres-owner + key: ownerConnectionString + readWriteConnectionStringSecretRef: + name: my-postgres-readwrite + key: readWriteConnectionString clientCertificate: enabled: false config: database: - role: kagent_owner + owner: + role: kagent_owner + readWrite: + role: kagent_readwrite schema: kagent vectorEnabled: true skipMigrations: false @@ -117,25 +157,27 @@ For production, run PostgreSQL outside the cluster's lifecycle so that a kagent replicas: 3 ``` - `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). + `database.postgres` configures the Kubernetes packaging, including the Secret references 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 | | ------- | ----------- | - | `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`. | - | `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`. | + | `database.postgres.ownerConnectionStringSecretRef` | The Secret and key that hold the owner login's connection string. When `config.database.skipMigrations` is set to `true`, the controller does not read this Secret. | + | `database.postgres.readWriteConnectionStringSecretRef` | The Secret and key that hold the read/write login's connection string. Required. The controller reads both Secrets only at startup. After a Secret changes, restart the controller. | + | `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. Both connection strings must then point their `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`. | + | `config.database.owner.role` and `config.database.readWrite.role` | The roles that the two logins assume after they connect. Match the role names that you passed to the setup script. Give each installation that shares one database distinct roles. | + | `config.database.schema` | The schema that holds kagent's tables. Match the `kagent_schema` value that you passed to the setup script. `config.database.vectorSchema` names the schema that holds the shared `pgvector` extension, and both kagent roles need `USAGE` on it. | + | `config.database.vectorEnabled` | Set to `true` only when the instance has the `pgvector` extension installed in `config.database.vectorSchema`. 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. | + | `config.database.skipMigrations` | Set to `true` to stop the controller from running migrations at startup. The controller then verifies through the read/write login that the database is already migrated, and fails if it is not. Apply the migrations from a pipeline before you install or upgrade. The controller then does not need the owner Secret or role. | -3. Apply the values to the kagent Helm release. +5. Apply the values to the kagent Helm release. The `--reuse-values` flag keeps the values that an existing release was installed with, such as the provider key and the Agent Substrate settings that `kagent install` sets. On a first installation, the flag has no effect. ```bash helm upgrade --install 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 \ --values kagent.yaml ``` @@ -178,15 +220,14 @@ controller: signing: secretName: kagent-push-signing config: - gateway: - push: - issuer: https://kagent.example.com + 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. 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. | +| `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.push.signingPrivateKeyFile` to `/etc/kagent/secrets/push-private-key.pem`. | +| `config.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. @@ -255,7 +296,7 @@ A fork is the case where the creation time decides. A fork replays its source Se Work in progress is never deleted. A Session with a running task survives past the interval, and so does one with a turn waiting at `INPUT_REQUIRED` or `AUTH_REQUIRED`. A pending lifecycle operation or a checkpoint being captured also holds a Session open. > [!IMPORTANT] -> **Expiry deletes the Session, not its history.** Deleting an idle Session removes the Session, its shares, its runtime row, and its creation receipt, so `GetSession` returns not-found afterward. The A2A context, its tasks, its event history, and any explicit {{< gloss "Checkpoint" >}}checkpoints{{< /gloss >}} stay in PostgreSQL, and a retained checkpoint outlives the Session it was taken on. kagent has no setting that bounds this retained history, so size a database for a long-running installation with the audit history planned separately. +> **Expiry deletes the Session, not its history.** Deleting an idle Session removes the Session, its shares, its runtime row, and its creation receipt. `GetSession` then returns not-found. An A2A message that continues the expired Session by its `contextId` fails with `TaskNotFound` (`-32001`, or gRPC `NOT_FOUND`). The A2A context, its tasks, its event history, and any explicit {{< gloss "Checkpoint" >}}checkpoints{{< /gloss >}} stay in PostgreSQL, and a retained checkpoint outlives the Session it was taken on. kagent has no setting that bounds this retained history, so size a database for a long-running installation with the audit history planned separately. 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 b2ed0ea3..5520a11c 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 @@ -40,7 +40,7 @@ Read the current state of the runtime before you change it. The rest of this gui 3. Call `GetSubstrateStatus`, which joins the WorkerPools, {{< gloss "ActorTemplate" >}}ActorTemplates{{< /gloss >}}, {{< gloss "Actor" >}}Actors{{< /gloss >}}, and Workers that kagent knows about into one response. ```bash grpcurl -plaintext -d '{"namespace":"kagent"}' \ - localhost:8083 kagent.api.v1alpha1.SystemService/GetSubstrateStatus + localhost:8083 kagent.api.v1.SystemService/GetSubstrateStatus ``` Example output, abbreviated to one Worker and one Actor: ```json diff --git a/docs-site/content/kagent/1.x/operations/uninstall.md b/docs-site/content/kagent/1.x/operations/uninstall.md index 95978576..73819542 100644 --- a/docs-site/content/kagent/1.x/operations/uninstall.md +++ b/docs-site/content/kagent/1.x/operations/uninstall.md @@ -17,6 +17,7 @@ Helm leaves behind: - The development PostgreSQL Deployment, Service, Secret, and PersistentVolumeClaim that `kagent install` created in the kagent namespace - The actor identity pools and the `ate-api-authentication` ConfigMap that the install created in `ate-system` - The service DNS, pod identity, and PostgreSQL certificate authority pools that the install created in `podcertificate-controller-system` +- The cluster-scoped ClusterTrustBundles that publish those certificate authorities - The `kagent`, `ate-system`, and `podcertificate-controller-system` namespaces This guide includes both Helm steps to uninstall its owned components, and manual steps to remove separate material that can block a later reinstallation. @@ -86,13 +87,23 @@ Error: while uploading pool state to secret: secrets "actor-id-jwt-pool" already service-dns-ca-pool pod-identity-ca-pool postgres-ca-pool ``` -3. Delete the development PostgreSQL resources that `kagent install` created. +3. Delete the cluster-scoped trust bundles that publish those pools' certificate authorities. Deleting a namespace does not remove a ClusterTrustBundle. A later install that finds a stale bundle might start Agent Substrate components that trust the old authority. Every agent turn then fails with `invalid peer certificate: UnknownIssuer`. + ```bash + kubectl delete clustertrustbundle \ + egress-mitm.ate.dev:mitm:primary-bundle \ + podidentity.podcert.ate.dev:identity:primary-bundle \ + postgres.podcert.ate.dev:identity:primary-bundle \ + servicedns.podcert.ate.dev:identity:primary-bundle + ``` + +4. Delete the development PostgreSQL resources that `kagent install` created. ```bash kubectl delete -n kagent \ deployment/kagent-postgresql \ service/kagent-postgresql \ configmap/kagent-postgresql \ - secret/kagent-postgres \ + secret/kagent-postgres-owner \ + secret/kagent-postgres-readwrite \ pvc/kagent-postgresql \ --ignore-not-found ``` 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 7c77721d..dac4c26c 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 @@ -17,7 +17,7 @@ Three independent changes each rule out `helm upgrade`, so working around any on | The custom resources moved to a new API group | 1.0 serves its resources under `api.kagent.dev`, where 0.10.x serves them under `kagent.dev`. The groups share no conversion, so nothing stored under the old group is visible through the new CRDs. | | The resource model is replaced | 0.10.x's `Agent` is gone. What it described is now split between an AgentTemplate and a {{< gloss "Harness" >}}Harness{{< /gloss >}}, paired by a new {{< gloss "Agent" >}}Agent{{< /gloss >}} resource in the `api.kagent.dev` group, and a conversation is a {{< gloss "Session" >}}Session{{< /gloss >}} created against that Agent. The two `Agent` kinds share a name and nothing else. For the model itself, see [Core concepts]({{< link path="about/core-concepts" >}}). | -The two releases also cannot run side by side on one cluster. The group rename separates most of the custom resources. Both releases bundle kmcp, though, and each one ships its own version of the `mcpservers.kagent.dev` CRD. A CRD is cluster-scoped, so installing 1.0's CRDs replaces the version that 0.10.x installed. A second cluster keeps the old installation intact while you work. +The two releases also cannot run side by side on one cluster. The group rename separates the custom resources, but both charts name their cluster-scoped ClusterRoles after the release, such as `kagent-getter-role`. When both releases use the default `kagent` release name, the 1.0 install collides with the ClusterRoles that 0.10.x owns. A second cluster keeps the old installation intact while you work. > [!WARNING] > Downgrading from 1.0 back to 0.10.x is unsupported. Treat the cutover as one-way, and keep the 0.10.x installation running until you have verified the new one. @@ -58,28 +58,97 @@ Your Kubernetes resources do carry forward, so export them before anything repla ## Install kagent 1.0 -Install 1.0 in the same way as a first-time installation, with one addition: point it at the empty database that you provisioned. The full procedure installs Agent Substrate, kagent, and the identity material that they need. +Install 1.0 against the empty database that you provisioned. kagent and Agent Substrate both store their state in that database, each with an owner login that applies migrations and a read/write login that serves runtime queries. `kagent install` normally creates a development database for both. The `--skip-database-setup` flag skips that database. The releases then read the connection Secrets that you create in this section. -1. Store the connection string for the empty database in a Secret in the new cluster. +Before you start, complete the [Before you begin]({{< link path="setup/installation#before-you-begin" >}}) steps of the installation guide. Those steps prepare the kagent CLI, your provider API key, and a cluster at Kubernetes 1.37 or later. + +1. Create the namespaces that hold the connection Secrets. `kagent install` uses the namespaces when they already exist. + ```bash + kubectl create namespace kagent + kubectl create namespace ate-system + ``` + +2. Prepare the database for kagent. Follow steps 1 to 3 of [Use an external PostgreSQL instance]({{< link path="operations/operational-considerations#use-an-external-postgresql-instance" >}}), which create the `kagent_owner` and `kagent_readwrite` roles and the `my-postgres-owner` and `my-postgres-readwrite` Secrets. + +3. Prepare the same database for Agent Substrate. Agent Substrate ships its own setup script, which creates the `substrate_owner` and `substrate_readwrite` roles and the `substrate` schema. Run it in the same way as the kagent script. ```bash - kubectl create secret generic kagent-upgrade-postgres -n kagent \ - --from-literal=connectionString='postgres://:@:5432/?sslmode=require' + curl -sSLo substrate-setup.sql https://raw.githubusercontent.com/kagent-dev/substrate/refs/tags/v{{< reuse "kagent-docs/versions/agent-substrate.md" >}}/pkg/postgressetup/setup.sql + export SUBSTRATE_OWNER_PASSWORD= SUBSTRATE_READWRITE_PASSWORD= + psql "$ADMIN_CONNECTION_STRING" --no-psqlrc --set=ON_ERROR_STOP=1 --single-transaction \ + --set=substrate_schema=substrate \ + --set=substrate_owner_role=substrate_owner \ + --set=substrate_owner_user=substrate_owner_user \ + --set=substrate_readwrite_role=substrate_readwrite \ + --set=substrate_readwrite_user=substrate_readwrite_user \ + --command='\getenv substrate_owner_password SUBSTRATE_OWNER_PASSWORD' \ + --command='\getenv substrate_readwrite_password SUBSTRATE_READWRITE_PASSWORD' \ + --file=substrate-setup.sql ``` -2. Follow all steps in the [1.0 installation guide]({{< link path="setup/installation" >}}). Set the following Helm values on the kagent install, rather than changing them afterward. +4. Create the Agent Substrate connection Secrets. The Agent Substrate chart reads these names and keys by default, so its release needs no extra values. + ```bash + kubectl create secret generic substrate-postgres-owner \ + --namespace ate-system \ + --from-literal=ownerConnectionString="postgresql://substrate_owner_user:@:5432/?sslmode=verify-full" + kubectl create secret generic substrate-postgres-readwrite \ + --namespace ate-system \ + --from-literal=readWriteConnectionString="postgresql://substrate_readwrite_user:@:5432/?sslmode=verify-full" + ``` + +5. Save the kagent Helm values that configure kagent to use your Secrets and roles. + ```bash + cat > kagent-values.yaml <}}) depends on. Your database must have the pgvector extension installed. | + | `database.postgres.ownerConnectionStringSecretRef.name` and `database.postgres.readWriteConnectionStringSecretRef.name` | Read the two connection strings from the Secrets that you created. The keys default to `ownerConnectionString` and `readWriteConnectionString`. | + | `config.database.owner.role` and `config.database.readWrite.role` | The roles that the kagent setup script created. If you passed other role names to the script, set those names instead. | + | `config.database.vectorEnabled` | Applies the pgvector migrations, which [long-term memory]({{< link path="agents/agent-memory" >}}) depends on. If your database does not have the pgvector extension installed, omit the value. | + | `config.server.grpcReflection` | Lets the guides that use `grpcurl` discover the controller API, as in the installation guide. | - 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" >}}). + The controller applies migrations at startup. To apply them from a pipeline instead, set `config.database.skipMigrations=true` and run `kagent db migrate up` with the owner login yourself. The controller then verifies the schema and fails if it is not already current. -3. Verify the schema before continuing. +6. Save a CLI configuration file that references those values. ```bash - kagent db migrate status --db-url "postgres://:@:5432/" + cat > kagent-config.yaml <@:5432/?sslmode=verify-full" \ + --db-role kagent_owner + ``` + + Example output: + ```console + 1 migration(s) applied (at v1), 0 pending ``` ## Recreate your resources 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 ee74f70c..c4520886 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 @@ -32,11 +32,11 @@ The single 0.10.x `Agent` resource described both what an agent does and how it Nothing pairs a Harness and an AgentTemplate implicitly. Neither resource carries a field naming the other, so an Agent is what makes a template runnable on a Harness. For the full model, see [Core concepts]({{< link path="about/core-concepts#agent" >}}). -The CRD API group also moves from `kagent.dev/v1alpha2` to `api.kagent.dev/v1alpha3`, with no conversion between the two. The only CRD name that 1.0 still shares with 0.10.x is kmcp's `mcpservers.kagent.dev`, so the two releases cannot run side by side on one cluster. +The CRD API group also moves from `kagent.dev/v1alpha2` to `api.kagent.dev/v1alpha3`, with no conversion between the two. 1.0 no longer bundles kmcp, so it does not install the `mcpservers.kagent.dev` CRD. The two releases still cannot run side by side on one cluster, because both charts name their ClusterRoles after the release. ### Removed and replaced resources -Four resource kinds are removed or replaced and three carry forward, and the distinction matters when you plan a move: a replaced kind has somewhere for its configuration to go, while a removed kind has none. +Some resource kinds are removed or replaced and others carry forward. The distinction matters when you plan a move, because a replaced kind has somewhere for its configuration to go, while a removed kind has none. | 0.10.x resource | Status in 1.0 | | --------------- | ------------- | @@ -44,6 +44,7 @@ Four resource kinds are removed or replaced and three carry forward, and the dis | `ToolServer` | Replaced by {{< gloss "RemoteMCPServer" >}}RemoteMCPServer{{< /gloss >}}, which 0.10.x already served alongside it. | | `Memory` | Replaced by `Harness.spec.kagent.memory`, so memory is a property of the runtime rather than of one agent. | | `SandboxAgent` | Removed, and no replacement is needed. Every 1.0 agent runs sandboxed by default. | +| `MCPServer` | Removed, because 1.0 no longer bundles kmcp or its controller. Run the MCP server outside of kagent, and set its URL on a RemoteMCPServer. The `kagent mcp` commands are removed with it. | | `AgentHarness` | Removed, with no equivalent. It provisioned OpenClaw and Hermes coding-agent sandboxes with Slack and Telegram channels. | | `ModelConfig`, `ModelProviderConfig`, `RemoteMCPServer` | Carried forward. `v1alpha3` keeps every field that `v1alpha2` sets, so an exported resource applies unchanged apart from its `apiVersion`. | 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 fc2b8c7b..1117c82d 100644 --- a/docs-site/content/kagent/1.x/reference/tools-ecosystem.md +++ b/docs-site/content/kagent/1.x/reference/tools-ecosystem.md @@ -48,8 +48,6 @@ 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` 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. diff --git a/docs-site/content/kagent/1.x/reference/versions.md b/docs-site/content/kagent/1.x/reference/versions.md index 4faf08a9..4d64ec2c 100644 --- a/docs-site/content/kagent/1.x/reference/versions.md +++ b/docs-site/content/kagent/1.x/reference/versions.md @@ -9,13 +9,12 @@ Only the latest {{< reuse "kagent-docs/snippets/name-product.md" >}} release is ## What a kagent release includes -The kagent Helm chart pulls in {{< gloss "Agent Substrate" >}}Agent Substrate{{< /gloss >}} and two other components as subcharts, each on its own release cadence. Because a kagent release pins one version of each, the pinned versions are the combination that kagent is built and tested against. +A kagent release depends on {{< gloss "Agent Substrate" >}}Agent Substrate{{< /gloss >}} and on the kagent-tools subchart, each on its own release cadence. Because a kagent release pins one version of each, the pinned versions are the combination that kagent is built and tested against. | Component | Version | What it provides | | --------- | ------- | ---------------- | | kagent | {{< reuse "kagent-docs/versions/kagent.md" >}} | The controller, the gRPC API, and the agent runtimes. | | Agent Substrate | {{< reuse "kagent-docs/versions/agent-substrate.md" >}} | The {{< gloss "Actor" >}}Actor{{< /gloss >}} runtime that every agent runs on, including sandboxing, snapshots, and the {{< gloss "WorkerPool" >}}WorkerPool{{< /gloss >}}. | -| kmcp | {{< reuse "kagent-docs/versions/kmcp.md" >}} | The MCP server toolkit and its controller. | | kagent-tools | {{< reuse "kagent-docs/versions/kagent-tools.md" >}} | The bundled tool server that the built-in RemoteMCPServer points at. | > [!IMPORTANT] @@ -42,7 +41,7 @@ kagent 1.0 is a deliberate clean break from the 0.10.x line, and the compatibili Moving from 0.10.x therefore means standing up a new installation and recreating your resources on it, rather than running `helm upgrade`. For the procedure, see [Upgrade from 0.x]({{< link path="operations/upgrade-from-0x#in-place-upgrade-blockers" >}}). For the resources that 1.0 replaces or removes, see [Release notes]({{< link path="reference/release-notes/1.0#removed-and-replaced-resources" >}}).