Skip to content

Compatibility matrix

This guide tells you which kneo-client release supports which kneo_serv platform version, what forward and backward compatibility mean in practice, and how to use the drop-to-transport escape hatch when you need access to an endpoint the current kneo-client release doesn't wrap yet.

What "compatibility" means in kneo-client

The Kneo Agent Platform's /v1 HTTP API is a stability boundary — a kneo-client release pinned to one kneo_serv minor works against any patch-level kneo_serv release on the same minor line, and against newer minors that don't break /v1.

The pinning is explicit and committed: schemas/openapi.json is a /v1-filtered copy of one specific kneo_serv release's published OpenAPI spec. Bumping the pin is a deliberate PR (via scripts/bump_schemas.py), reviewed in isolation, and never happens automatically. See ADR-004 for the rationale.

Current matrix

kneo-client Pinned to kneo_serv Tested against Python Status
1.1.0 v1.2.0 (info.version 1.2.0) kneo_serv 1.2.x line (E2E) + 1.0.x/1.1.x per the floors below >=3.12 (3.12 / 3.13 / 3.14 in CI) Current
1.0.0 v1.0.0 (info.version 1.0.0) kneo_serv 1.0.x line >=3.12 (3.12 / 3.13 / 3.14 in CI) Previous
0.10.0 v1.0.0 (info.version 1.0.0) kneo_serv 1.0.x line >=3.12 (3.12 / 3.13 / 3.14 in CI) Older
0.9.0 v1.0.0 (info.version 1.0.0) kneo_serv 1.0.x line >=3.12 (3.12 / 3.13 / 3.14 in CI) Older
0.8.0 v0.11.0 (info.version 0.11.0) kneo_serv 0.11.x line >=3.12 (3.12 / 3.13 / 3.14 in CI) Older
0.7.0 v0.9.0 (info.version 0.9.0) kneo_serv 0.9.x line >=3.12 (3.12 / 3.13 / 3.14 in CI) Older
0.6.0 v0.7.0 (info.version 0.7.0) kneo_serv 0.7.x line >=3.12 (3.12 / 3.13 / 3.14 in CI) Older
0.5.0 v0.5.0 (info.version 0.5.0) kneo_serv 0.5.x line >=3.12 (3.12 / 3.13 / 3.14 in CI) Older
0.4.0 v0.4.0 (info.version 0.4.0) kneo_serv 0.4.x line >=3.12 (3.12 / 3.13 / 3.14 in CI) Older

kneo_serv declared general availability at v1.0.0 — the /v1 HTTP API and kneo CLI are stable contracts across the 1.x line. kneo-client is GA at 1.0.0: it pins that v1.0.0 contract and freezes its own public Python API (the curated __all__) + SemVer across the 1.x line (see contract_stability.md). The GA surface was landed across 0.9.0 (the v1.0.0 contract uptake) and 0.10.0 (the pre-freeze surface corrections); 1.0.0 is the posture declaration over that corrected, machine-pinned surface.

The pinned kneo_serv version is recorded in schemas/SOURCE.md — that's the source of truth for which platform version generated the committed _generated/ tree.

How pinning works in practice

The pin is the input to the generated layer. When you bump it:

  1. scripts/bump_schemas.py fetches the new openapi.json from the target kneo_serv ref (or a local checkout).
  2. The spec is filtered to /v1 paths only — kneo_serv mounts every route at both /v1/… and /…; we drop the unprefixed mounts.
  3. The filtered spec replaces schemas/openapi.json, schemas/SOURCE.md is updated, and _generated/ is regenerated.
  4. The hand-rolled adapter layer (platform/, agent/) may need updates if endpoints were added, renamed, or had their shapes change. The contract test tests/contract/test_path_coverage.py catches both sides of that drift.

A kneo-client minor release ships exactly one kneo_serv pin. A pin bump lands in a minor; patches never change pins (aligned with contract_stability.md — the earlier "any minor / patch" wording here contradicted it).

Forward compatibility — newer kneo_serv than the pin

kneo-client ships an explicit list of every (method, path) it wraps. A newer kneo_serv that adds endpoints will still work for everything kneo-client already wraps — you just won't have wrappers for the new endpoints until the next kneo-client release.

If you need access to a new endpoint before that release, use the public KneoClient.request() escape hatch:

async with KneoClient.from_profile() as client:
    # Call an endpoint that doesn't have a wrapper yet:
    resp = await client.request("GET", "/v1/some/new/endpoint")
    payload = resp.json()

KneoClient.request(method, path, ...) is part of the stable 1.x contract — the supported forward-compatibility path. It runs the full pipeline (auth, retries, idempotency, request-ID injection, and error mapping, just like the wrapped methods) and hands back the raw httpx.Response for you to parse — you just lose the typed response model. Prefer a typed .platform / .agent wrapper whenever one exists. (The older client._transport.request(...) still works but is internal and unsupported; migrate to client.request(...).)

Backward compatibility — older kneo_serv than the pin

kneo-client X.Y.Z is not guaranteed against kneo_serv releases older than its pin. Wrappers may rely on response fields that older kneo_serv versions don't emit, and the client's error mapping assumes the current platform error shape. A KeyError on from_dict() is the typical symptom — the wrapper expects a field that wasn't in the older platform's response.

If you have to talk to an older kneo_serv, pin to a matching kneo-client minor:

Need to talk to kneo_serv Use kneo-client
1.0.x / 1.1.x 1.1.0 (pinned to v1.2.0) is deliberately older-serv-safe for this uptake: the credential inventory is normalized client-side (status derived from present against pre-1.2.0 servers), and the new surfaces degrade explicitly per the server-version floors below (422 unknown_query_parameters for filter kwargs the server doesn't know, 404 for the graph/preview endpoints, absent usage/trace flags). 1.0.0 (pinned to v1.0.0) also works for everything it wraps.
0.12.x / 1.0.x 0.9.0 (pinned to v1.0.0). Takes up the GA contract: async_mode now honored on POST /v1/specs/run (202), the spec_path_confined 422 from default-on spec-path confinement, and consolidated client API-stability docs. A v0.11.0-pinned client (0.8.0) also works against 0.12.x/1.0.x for everything it wraps — specs.run async just surfaces the queued run via the same RunCreateResponse, and an out-of-root spec_path draws the 422 either way.
0.10.x / 0.11.x 0.8.0 (pinned to v0.11.0). Absorbs the kneo_serv 0.11.0 /v1 breaks (async run-create 202, unknown-query-param 422 rejection, guardrail-block run terminalization) and adds a versioned User-Agent + Retry-After HTTP-date parsing. v0.10.0 introduced no /v1 change, so a v0.9.0-pinned client (0.7.0) also works against 0.10.x for everything it wraps.
0.8.x / 0.9.x 0.7.0 (pinned to v0.9.0). Adds the skill-catalog wrapper (agent.skills.list), the per-request skills overlay on runs.create, Page.window, the typed 400 / 413 / 422 errors, and code-aware 409 classification. A v0.7.0-pinned client (0.6.0) still works against 0.8.x/0.9.x for everything it wraps — it just won't expose those additions.
0.6.x / 0.7.x 0.6.0 (pinned to v0.7.0). Adds the audit-events pagination + sorting kwargs (offset / sort_by / sort_order, server-honored since kneo_serv 0.6.0), the standardized error envelope, and the HumanTaskResponse.messages thread projection. A v0.5.0-pinned client (0.5.x) still works against 0.6.x/0.7.x for everything it wraps — it just won't expose those additions.
0.5.x 0.5.0 (pinned to v0.5.0). Because the /v1 contract is byte-identical to v0.4.0, a v0.4.0-pinned client (0.1.x0.4.x) also works against 0.5.x — you just won't surface the typed KneoServiceUnavailableError for the new 503 backpressure path.
0.4.x 0.1.x, 0.2.x, 0.3.x, or 0.4.x (all pinned to v0.4.0; pick 0.4.x to drop the long-deprecated audit.list kwargs, 0.3.x for the additive filter kwargs on runs.checkpoints / credentials.list, 0.2.x for the Page + Map return-type ergonomics)

(More rows added as the project ships.)

Server-version floors and behavior changes

Some wrapped surfaces depend on the server actually implementing them — the client sends the request either way, so know your deployment's kneo_serv version:

  • overlays are applied only from kneo_serv 0.9.0. Older servers accepted the overlays (and overrides / strict on the specs routes) fields but silently ignored them. From 0.9.0 they are actually applied server-side (and replayed on resume). A deployment upgrading kneo_serv from 0.7.x to 0.9.x will see previously-ignored overlay fields in stored request payloads take effect — audit those payloads before upgrading.
  • Skill catalog requires kneo_serv >= 0.8.0. client.agent.skills.list() 404s against older servers. The per-request skills overlay on runs.create requires >= 0.9.0.
  • Human-task status filter requires kneo_serv >= 0.8.0. human_tasks.list(status=...) is silently ignored by older servers (honored from 0.8.0, contractual in the 0.9.0 spec).
  • Spec-explain envelope requires kneo_serv >= 0.9.0. The environment / overlays / overrides fields on specs.explain (including the explain leg of dry_run) draw a 422 from older servers.
  • Run-list filters, session_id, and the graph endpoints require kneo_serv >= 1.1.0; q, usage, policy preview, the typed credential-inventory fields, and the trace complete/dropped flags require >= 1.2.0. Older servers hard-reject unknown query params with 422 unknown_query_parameters (they never silently ignore them), 404 the graph/preview endpoints, and simply don't emit the new response fields. A body session_id on runs.create/specs.run draws a body-validation 422 from kneo_serv 1.0.x (extra='forbid'), and POST /v1/specs/run silently dropped it until the 1.2.0 parity fix.
  • Timestamp filters compare lexically on kneo_serv 1.1.x. created_after/created_before are validated + UTC-normalized (malformed values → 422 invalid_timestamp) only from 1.2.0; on 1.1.x a malformed or non-+00:00-form value silently misfilters. Pass datetime.isoformat() of a tz-aware UTC datetime.
  • Resuming an expired human task raises KneoHumanTaskExpiredError from kneo_serv >= 1.1.0 (409 human_task_expired; tasks with on_timeout: escalate stay resumable late). Older servers could let a late resume race the expiry sweep and succeed.
  • kneo_serv behavior changes worth auditing before a server upgrade to 1.1.x/1.2.x (no client code change; listed here because runs that used to pass may fail): spec content now rejects unknown keys (extra='forbid'E_SCHEMA diagnostics; provider-specific params belong under model.extra); tool allowed_domains/denied_domains are enforced at call time (422 guardrail_violation sync / failed async); a run's spec + skills are frozen at create (edit-then-resume no longer applies the edit, from 1.1.0); the per-run policy-report scope changed specs:readruns:read; GET /v1/skills path values are now root-relative.
  • Spec-path confinement is default-on from kneo_serv >= 1.0.0. A caller-supplied spec_path, overlays entry, or skills[].source that resolves outside the server's allow-listed root (KNEO_SERV_SPEC_ROOT, defaulting to the server's working directory) — including absolute, ..-traversal, ~, or symlink-escape paths — is rejected 422 spec_path_confined (surfaced as KneoValidationError with .code == "spec_path_confined"). Through 0.12.x this was opt-in (out-of-root paths only logged a deprecation warning); at 1.0.0 the warn-and-allow path is removed. The client sends these fields unchanged either way — keep your specs under an allow-listed root, or have the operator set KNEO_SERV_SPEC_ROOT. See error handling.

Breaking changes

A breaking change in either direction triggers a major bump:

  • kneo-client major — the public Python API (kneo_client.* namespace) changes incompatibly. Very rare.
  • kneo_serv major — i.e., introduction of /v2. kneo-client may need a major bump if /v1 is sunset; otherwise it ships a new minor that supports both.

Within a major, deprecated surfaces keep aliases for at least one minor. See the API stability contract for the full SemVer + deprecation policy.

Verifying compatibility yourself

The simplest smoke is the bundled examples. They ship in the repository (not in the wheel), so run them from a checkout:

# Install a specific kneo-client version
python -m pip install "kneo-client==X.Y.Z"

# Grab the MATCHING examples — check out the same release tag, or the
# examples may use surface the installed wheel doesn't have
git clone --branch vX.Y.Z https://github.com/kneo-agent/kneo-client.git && cd kneo-client

# Point it at your kneo_serv instance
export KNEO_URL=https://your-kneo-serv.example.com
export KNEO_API_KEY=...

# Run the smoke (touches health, runs, audit, agent specs, and human tasks)
python examples/01_basic_run.py YOUR_SPEC_ID

The seven examples/ scripts collectively touch the platform health, runs, audit, agent spec, and human-task surfaces — enough to catch obvious incompatibilities in seconds.

For deeper validation, the integration test suite is env-gated; set KNEO_TEST_URL and KNEO_TEST_API_KEY and run python -m pytest tests/integration -v.

What the pin does not guarantee

The pin guarantees the wire format and the path set of /v1. It does not guarantee:

  • Provider availability — whether your kneo_serv deployment has GPT-4 configured, an MCP server reachable, a specific runtime registered. The pin says nothing about deployment state.
  • Spec compatibility — a spec that works on one kneo_serv may fail on another if the spec relies on a specific provider, tool, or platform version. Validate specs against the target environment with client.agent.specs.validate(...).
  • Policy outcomespolicies.environment_* queries return whatever policy is configured on the target deployment.

These are platform-deployment concerns, not client-library concerns. The client gives you a clean wire to the platform; what the platform allows is a separate dimension.