Upgrade guide¶
Conventions for upgrading Kneo Agent Platform (kneo-serv) between
releases, plus version-specific notes when a release has breaking changes.
For the release process itself (gates, tagging, artifacts), see
release_checklist.md. For the supported
kneo_agent SDK range, see sdk_alignment.md.
Versioning¶
Kneo Agent Platform follows semantic versioning:
- Patch (
0.1.0→0.1.1): bug fixes; persistence schemas, route contracts, CLI commands, and env-var names do not change. - Minor (
0.1.x→0.2.0): additive changes. Persistence schemas may add new tables or columns with migrations; routes and CLI may add new surfaces. Existing surfaces remain available with the same shape unless the release notes call out an exception. - Major (
1.x→2.0): may remove or change surfaces. Read the release notes before upgrading; expect to update calling code.
As of 1.0.0 (GA), the /v1 HTTP API and the kneo CLI are stable contracts
under semantic versioning: additive changes within the major, deprecation windows
before any removal, and no silent /v1 breaks. (Pre-1.0, behavior corrections
could land in a minor under the fixes-vs-breaks policy; that latitude is retired.)
The HTTP API is also versioned at the URL prefix (/v1); legacy unversioned
routes remain available alongside /v1. See
design.md § 13 and
contract_stability.md.
Standard upgrade procedure¶
- Read the release notes for every minor/major version between your current and target version. Patch upgrades only need the latest patch's notes. The reading-order index (newest first) is releases/README.md; the current release's notes are release_notes_1.2.0.md.
- Pin the target version in your dependency manifest:
- Stop traffic to the service (or drain via a load balancer). Background runs that are queued will be reclaimed by the worker after restart; in-flight runs that complete during the drain will record normally.
- Back up persistence. Follow
backup_and_recovery.md(pg_dumpfor PostgreSQL,backup_sqlite_database()for SQLite). Keep the backup until you have verified the new version through at least one business cycle. - Install the new version in your deployment image or environment.
- Restart the service. Migrations apply automatically at startup.
Watch the structured log for
migrationevents and anymigration_failederrors. - Verify with
GET /readyzand the deployment smoke script: - Resume traffic.
If GET /readyz does not return 200 within a few seconds of restart, see
troubleshooting.md § 1.2.
Persistence migrations¶
Every store that has a schema (SQLiteRunStateStore,
PostgresRunStateStore) tracks its schema version and applies
forward-only migrations on first connection. Migrations are
idempotent and never drop columns or rows on their own. The
file-based stores have no schema; they tolerate older record shapes
through the row decoder.
If a migration fails, the service refuses to serve requests rather than running on a partially-migrated schema. Fix the underlying cause (usually a permissions or disk-space problem), then restart.
Downgrades are not supported. Restore from backup if you need to revert.
For contributors authoring new migrations (conventions, the dialect
portability rules, the test patterns), see
docs/dev/migrations.md.
Spec migrations¶
The YAML spec format is versioned at version: v1. The compiler accepts
older shapes through automatic normalization, but for clarity the CLI can
write upgraded specs to disk:
kneo spec migrate legacy_agent.yaml --output migrated_agent.yaml
kneo spec migrate migrated_agent.yaml --check --json
Specs that pass kneo spec validate on the source version will
continue to compile after upgrading; specs that hit deprecation warnings
should be migrated proactively before a future release removes the
fallback.
Signed bundles created with kneo spec bundle sign are tied to the
signing key, not the kneo-serv version, so bundles signed before an
upgrade continue to verify after as long as the signing key is unchanged.
SDK compatibility¶
kneo-serv declares a kneo-agent range in pyproject.toml. When
upgrading kneo-serv, let pip resolve the matching SDK; do not pin SDK
versions outside that range. The compatibility tests
(tests/test_sdk_compatibility.py) assert the SDK surface used by the
service, so a version mismatch surfaces as a test failure.
If you maintain custom runtimes or middlewares that import directly from
kneo_agent, run those compatibility tests after upgrading and update
imports in lockstep.
Configuration changes¶
Environment-variable names and defaults are part of the public surface. Changes are recorded in environment.md and called out in release notes:
- New variables default to behavior consistent with the previous release.
- Renamed variables retain a deprecation alias for at least one minor release; a startup warning is emitted when the alias is used.
- Removed variables are removed only at major versions.
After upgrading, diff your env file against the latest
deploy/production.env.example (or
staging.env.example) to spot any new optional variables.
CLI changes¶
The kneo CLI is regenerated each release; see
cli_reference.md for the current shape. New
subcommands are additive within minor releases. Subcommand behavior may
change at major releases — check the release notes.
CLI profiles stored at ~/.kneo_serv/profiles.json carry forward across
releases. The profile schema is itself versioned and migrated in place.
Version-specific notes¶
This section grows as releases ship. Each entry should describe what changed, what action operators must take, and how to verify the upgrade.
0.1.0 — initial release¶
No upgrade applies; this is the first published version. See release_notes_0.1.0.md for scope, capabilities, and verified release-candidate steps.
0.2.0 — first public distribution¶
This is the first cut to publish a real kneo-serv package. 0.1.0
and 0.1.1 shipped as GitHub Release artifacts only; 0.2.0 is the first
version available via pip install kneo-serv and docker pull
ghcr.io/kneo-agent/kneo-serv.
Version trajectory on PyPI: 0.0.0 → 0.2.0. The kneo-serv 0.0.0
placeholder published on 2026-05-14 reserved the distribution name; it
shipped an empty importable module with no kneo CLI binary (no
[project.scripts] entry). Any user who tried pip install kneo-serv
&& kneo --version during the placeholder window saw kneo: command
not found — 0.2.0 is the first cut to install the binary. The
placeholder is yanked once 0.2.0 ships; existing explicit ==0.0.0
pins still resolve, but default pip install kneo-serv jumps straight
to 0.2.0.
Install paths:
- pip install kneo-serv — first time this works end-to-end.
- docker pull ghcr.io/kneo-agent/kneo-serv:0.2.0 (and :0.2, and
:latest) — first time the image is available without a local
build.
Deployment migration for operators on 0.1.x using compose.yaml
with the bundled build: context: .:
- Default flow becomes docker compose pull && docker compose up -d
against the GHCR image.
- The build: block stays in compose.yaml for contributors and the
CI smoke test (docker compose up --build).
- No required changes to deploy/production.env or
deploy/staging.env from 0.1.1.
Persistence schemas: unchanged from 0.1.1. No migrations required.
Feature additions visible to operators (full per-feature detail in
release_notes_0.2.0.md):
- kneo spec lint — CI-friendly validator subcommand that exits
non-zero on any warnings or errors.
- Retention windows now live in .kneo/config.yaml under a
retention: block, with env vars as the operator override.
- Human-task expiration via
PlatformManager.prune_expired_human_tasks() — paused runs whose
human-step deadline has passed transition to a new expired status
and emit human.expired audit events.
- Two new reference example specs:
concurrent_review_workflow.yaml and group_chat_workflow.yaml.
- Docker-based local PostgreSQL integration testing via
python scripts/postgres_test.py.
No breaking changes to spec syntax, HTTP API contracts, CLI command names, env-var names, or persistence schemas. Specs that validated under 0.1.1 continue to validate under 0.2.0.
0.2.1 — /healthz version and Docker /app permission fix¶
Patch release fixing two regressions discovered while smoke-testing the published 0.2.0 image. Both are bug fixes; no new features, no contract changes.
Upgrade:
- pip install -U kneo-serv (resolves to 0.2.1).
- docker pull ghcr.io/kneo-agent/kneo-serv:0.2.1 — :0.2 and :latest
now resolve to the 0.2.1 digest.
What was broken in 0.2.0:
- GET /healthz returned "version":"0.1.0" from the 0.2.0 image
because HealthResponse.version was a hardcoded string literal. 0.2.1
resolves the field dynamically via importlib.metadata.version("kneo-serv").
- Plain docker run -p 8000:8000 ghcr.io/kneo-agent/kneo-serv:0.2.0
crashed on startup with
PermissionError: [Errno 13] Permission denied: '.kneo' because /app
was root-owned but the container drops to the non-root kneo user
before creating the SQLite-fallback path. 0.2.1 adds
chown -R kneo:kneo /app to the install layer. The Docker Compose
deployment path was unaffected (it pins KNEO_SERV_DATABASE_URL to
PostgreSQL).
Persistence schemas: unchanged from 0.2.0. No migrations required.
No breaking changes to spec syntax, HTTP API contracts, CLI command names, env-var names, or persistence schemas.
0.2.2 — FastAPI info.version fix + post-0.2.0 docs sweep¶
Patch release fixing one regression in the same family as 0.2.1 plus a documentation sweep. No feature changes, no contract changes, no schema changes.
Upgrade:
- pip install -U kneo-serv (resolves to 0.2.2).
- docker pull ghcr.io/kneo-agent/kneo-serv:0.2.2 — :0.2 and
:latest now resolve to the 0.2.2 digest.
What was broken in 0.2.1:
- GET /openapi.json returned info.version: "0.1.0" from the 0.2.1
image because the FastAPI app constructor in
kneo_serv/service/app.py still pinned a hardcoded literal. The
0.2.1 cut fixed HealthResponse.version but missed this parallel
occurrence. 0.2.2 resolves both via the same
importlib.metadata.version("kneo-serv") helper, called at
app-construction time.
Documentation:
- Forward-looking plan docs and "as of 0.1.0" framing in user/dev
docs swept to match the 0.2.x shipped reality. No content lost —
historical files (CHANGELOG entries, shipped release notes, the
archived 0.2.0 tracker, ADRs) are unchanged.
Persistence schemas: unchanged from 0.2.1. No migrations required.
0.3.0¶
Next additive minor on the 0.2.x line. No breaking changes to spec
syntax, HTTP API contracts, CLI command names, env-var names, or
persistence schemas. Full narrative in
release_notes_0.3.0.md.
Upgrade:
- pip install -U kneo-serv (resolves to 0.3.0).
- docker pull ghcr.io/kneo-agent/kneo-serv:0.3.0 — :0.3 and
:latest now resolve to the 0.3.0 digest. The image is now
signed (cosign keyless via Sigstore) and ships with a CycloneDX
SBOM attestation; verification commands are in
supply_chain_review.md § Verification commands.
SDK floor bump:
- The kneo-agent SDK floor moves from >=1.1.1 to >=1.2.0.
Pip auto-resolves on pip install -U kneo-serv, but operators
pinning the SDK separately (e.g. via a constraints file or a
monorepo lockfile) must ensure their install is on 1.2.0 or
newer. The compat test suite passed against kneo-agent 1.2.0
throughout the 0.2.x line; the floor was kept low to avoid
forcing 0.1.x users to upgrade. 0.3.0 is the natural inflection
point to lift it.
New timed_out lifecycle status:
- Runs that hit their run-level deadline transition to a new
terminal timed_out status (alongside completed, failed,
cancelled, expired). Operator tooling that switches on
state.status should accept it as terminal — e.g. dashboards,
alerting rules, retention sweeps (which the platform's own
RetentionPolicy.run_statuses already includes).
- The error.type field on a timed-out run is run_timed_out,
distinct from human_task_expired (which the existing expired
status uses).
New runtime surfaces:
- start_run_from_spec(..., timeout_seconds=N) and
run_from_spec(..., timeout_seconds=N) accept an optional
wall-clock deadline. Operator-callable
PlatformManager.prune_timed_out_runs() walks runs and force-cancels
those past their deadline. Same operator-cron pattern as
prune_retention() and prune_expired_human_tasks() — no
built-in scheduler.
- The human-task on_timeout: continue and on_timeout: escalate
literals are now wired in the runtime (they were accepted by the
spec but silently treated as fail in 0.2.x). Operators with
specs that declared these literals will see the documented
behaviour for the first time. Audit consumers should expect new
event types: human.continued, human.continue_failed,
human.escalated, run.timed_out.
- New route GET /v1/runs/{run_id}/policy-report returns the spec
policy report for a stored run, no spec bundle required
client-side. Auth: specs:read scope (same as the existing
POST /v1/specs/policy-report).
New observability surfaces:
- Three new platform-side OpenTelemetry spans
(kneo.queue.dispatch, kneo.worker.lease,
kneo.continuation.lock) join the SDK's agent-boundary spans
when KNEO_SERV_OTEL_ENABLED=true. Pre-existing OTel pipelines
pick them up automatically once telemetry is enabled — no extra
configuration required. See
observability.md § Platform-side spans.
Persistence schemas: unchanged. The new RunState.deadline_at
and Checkpoint.iteration fields default to None and 1
respectively in the dataclass, so existing rows round-trip cleanly
through the JSON-payload SQLite / PostgreSQL stores.
0.4.0¶
Next additive minor on the 0.3.x line. No breaking changes to
spec syntax, HTTP API contracts, CLI command names, env-var names,
or persistence schemas. Specs that validated under 0.3.x continue to
validate under 0.4.0. The cut is a docs + tooling release —
runtime semantics are identical to 0.3.0. Full narrative in
release_notes_0.4.0.md.
Upgrade:
- pip install -U kneo-serv (resolves to 0.4.0).
- docker pull ghcr.io/kneo-agent/kneo-serv:0.4.0 — :0.4 and
:latest now resolve to the 0.4.0 digest. Image continues to be
signed (cosign keyless via Sigstore) and ships with a CycloneDX
SBOM attestation; the 0.4.0 cut adds a Trivy CVE scan report
attached to the GitHub Release. Verification commands are in
supply_chain_review.md § Verification commands.
SDK floor: unchanged. The kneo-agent floor stays at >=1.2.0
— same as 0.3.0. No operator action required for operators pinning
the SDK separately.
New auto-generated API reference: the docs site at
kneo-agent.github.io/kneo-serv/ gains a new top-level API
Reference nav section with 17 pages (16 subpackages + sdk),
rendered at build time by mkdocstrings from the Python
docstrings. Operator surface unchanged — the API ref is a
developer lookup surface, not a runtime change. See
docs/api/README.md for the index.
Image vulnerability scanning (Trivy): the release pipeline now
scans the pushed GHCR image with Trivy under the CVSS≥7 policy
(HIGH/CRITICAL findings block the publish step). On every
release-tag build, the JSON scan report is attached to the GitHub
Release as the trivy-report-<version> artifact, 90-day
retention. Deployers can re-run the scan locally with
trivy image ghcr.io/kneo-agent/kneo-serv:<tag>; full policy +
escape hatch documented in
security_hardening.md § Image vulnerability scanning.
Developer-facing changes (no operator surface impact):
- Ratcheting ruff D-rule gate (D100/D101/D102) now enforced
project-wide for kneo_serv/**/*.py. New public classes /
methods without docstrings fail CI. Forks adding code should
follow the Google docstring convention; the chain-reference
files are
security/secrets.py and
platform/manager.py.
- Full mypy strict coverage across kneo_serv/. The
[[tool.mypy.overrides]] block in
pyproject.toml now covers every public
module. Forks that subclass or extend public types should expect
disallow_untyped_defs + warn_return_any + strict_equality.
- mkdocstrings[python]>=0.27 added to the docs optional-dep
block. Operators using pip install kneo-serv (without
[docs]) are unaffected — the dep is build-time only for the
rendered site.
New 0.3.0-feature worked examples:
- examples.md picked up a Timeout branches
subsection on the human_approval_workflow.yaml entry covering
the on_timeout: fail/continue/escalate literals (all wired
since 0.3.0).
- New examples/run_with_timeout.py
walks through start_run_from_spec(..., timeout_seconds=N) +
prune_timed_out_runs(). Companion to the human-task timeout
example above.
Persistence schemas: unchanged. No new fields, no migrations.
0.5.0¶
Next additive minor on the 0.4.x line. No breaking changes to spec syntax, HTTP API contracts, CLI command names, env-var names, or persistence schemas. Specs that validated under 0.4.x continue to validate under 0.5.0.
Upgrade:
- pip install -U kneo-serv (resolves to 0.5.0).
- docker pull ghcr.io/kneo-agent/kneo-serv:0.5.0 — :0.5 and :latest
resolve to the 0.5.0 digest; image signing, SBOM attestation, and the
Trivy scan gate are unchanged from 0.4.0.
SDK floor: unchanged. The kneo-agent floor stays at >=1.2.0.
Bug fix — checkpoint-callback metadata now survives on the final run
record: in 0.4.x and earlier, execute_run and continue_run saved a
stale in-memory RunState at the end of a run, overwriting the
step_iteration_counts and execution_context that the checkpoint
callback had written to RunState.metadata during the run. The values
survived on the checkpoints but were missing from the run record.
0.5.0 re-reads the persisted run before the final save. Operator
impact: if you read per-step iteration metadata off the final
RunState (via GET /v1/runs/{id} metadata or the PlatformManager
API) and worked around its absence, that metadata is now present.
Checkpoint contents are unchanged. No migration or data backfill — the
fix only affects runs executed under 0.5.0 onward.
New performance and capacity guide: performance.md
documents throughput, latency, the SQLite-vs-PostgreSQL capacity
trade-off, and the tuning knobs, with a reproducible bench harness
(scripts/bench). No runtime change — guidance
and tooling only.
Single-team on-prem operability (all additive; defaults preserve 0.4.x
behaviour):
- Worker concurrency. KNEO_SERV_WORKER_CONCURRENCY (default 1) runs a
pool of in-process worker threads; KNEO_SERV_WORKER_LEASE_SECONDS
(default 300) sets the queue lease. Default 1 reproduces the prior
single-worker behaviour. Sizing guidance in performance.md.
- Prometheus /metrics. New unauthenticated GET /metrics (root path only),
opt-out via KNEO_SERV_METRICS_ENABLED=false. Operator action: restrict it
to your monitoring network (reverse proxy or the env flag). See
observability.md § Prometheus /metrics.
- Overload backpressure. KNEO_SERV_MAX_QUEUE_DEPTH (default 0 = unlimited,
i.e. unchanged). When set, POST /v1/runs (async) returns 503 with
Retry-After: 5 once the queue is full — make async callers retry on 503.
- Poison-run dead-letter. KNEO_SERV_QUEUE_MAX_ATTEMPTS (default 5) fails a
run re-leased past the cap with a dead_letter error + run.dead_lettered
audit event. Behaviour change: before 0.5.0 a run that repeatedly crashed
its worker was re-leased indefinitely; it is now dead-lettered after 5
attempts. Set 0 to restore unbounded retries.
- Graceful drain. On SIGTERM the worker pool finishes in-flight runs and
stops claiming new ones, so container rollouts no longer interrupt a run.
Persistence schemas: unchanged. No new fields, no migrations.
0.6.0¶
Multi-process PostgreSQL hardening + uptake of the Kneo Agent SDK 2.x line.
Additive persistence migrations only (a new checkpoint uniqueness index);
no breaking spec-syntax, CLI, or env-var-removal changes. Two intentional
observable changes (error codes, RunConfig defaults) are called out below.
Upgrade:
- pip install -U kneo-serv (resolves to 0.6.0).
- docker pull ghcr.io/kneo-agent/kneo-serv:0.6.0 — :0.6 and :latest
resolve to the 0.6.0 digest; image signing, SBOM attestation, and the Trivy
scan gate are unchanged.
SDK floor bumped to kneo-agent>=2.2.0,<3.0.0 (from >=1.2.0,<2.0.0). The
service installs this for you; if you pin kneo-agent yourself, move your pin
onto the 2.x line. The bump picks up SDK security fixes (MCP cross-origin
redirect refusal + URL-scheme validation; broader secret redaction). Two SDK
2.x behaviour deltas a deployer may notice:
- The SDK no longer auto-retries tool calls. kneo-serv's own retry knobs
(KNEO_SERV_PROVIDER_RETRIES, KNEO_SERV_MCP_RETRIES, …) are unchanged and
still apply; only the SDK's internal per-tool retry default flipped off.
- Spec/agent with_defaults for temperature / max_iterations now actually
apply — see the RunConfig change below.
Observable change — error-code remap (action may be required). The public
error field in error responses is now a stable, snake_case code decoupled from
internal Python class names:
| Status | Old error |
New error |
|---|---|---|
| 404 | KeyError |
not_found |
| 400 | ValueError / FileNotFoundError |
invalid_request |
| 500 | <ExceptionClassName> (+ raw message) |
internal_error (generic message) |
queue_full, resource_locked, unauthorized, forbidden, and the
idempotency codes are unchanged. If a client matched on the old class-name codes
(KeyError, ValueError, …), update it to the new codes. The error envelope —
{"detail": {"error": "...", "message": "..."}} — is otherwise unchanged and is
now published in the OpenAPI schema as ErrorResponse / ErrorDetail. 500
responses no longer echo the exception message, and /readyz probe failures no
longer leak the underlying error detail (both are logged server-side instead).
Observable change — RunConfig defaults now merge. Before 0.6.0, a run that
didn't specify max_iterations / temperature had them force-set to 10 /
0.7, silently overriding a spec author's with_defaults(...). 0.6.0 leaves
them unspecified so the agent/skill defaults apply (SDK 2.x merge semantics). If
a spec set temperature: 0.2 (or a custom max_iterations) and you relied on
the run ignoring it, the spec value now takes effect. To force a value
regardless of the spec, set it explicitly on the run config. A malformed
temperature (non-numeric / bool / NaN / inf) now returns 400
invalid_request instead of a 500.
New operator knobs (all additive; defaults preserve 0.5.x behaviour):
- KNEO_SERV_RETENTION_AUDIT_DAYS (and project-config retention.audit_days)
— prune audit events older than N days. The audit table is otherwise unbounded;
set this on long-lived deployments.
- KNEO_SERV_SHUTDOWN_TIMEOUT_SECONDS (default 30) — how long SIGTERM
shutdown waits for in-flight runs to finish. A run still executing past the
timeout is interrupted by process exit but stays claimed and is re-leased /
retried (not lost); set this and your orchestrator's termination grace
period ≥ your longest run step to drain without a restart.
- Token-usage metrics. /metrics now exposes kneo_tokens_input_total,
kneo_tokens_output_total, and kneo_tokens_total counters; usage is also on
the run record and run.created audit metadata when the runtime reports it.
- Idempotency in-progress. A duplicate POST arriving while the first
same-key request is still in flight now returns 409 idempotency_key_in_progress
(previously the two could race). Treat 409 as "retry shortly".
Persistence schemas: one additive migration — a UNIQUE(run_id, sequence)
index on checkpoints (migration v3), which de-duplicates any pre-existing
duplicate (run_id, sequence) rows on first start. PostgreSQL queue/lease
timestamp columns are widened REAL → DOUBLE PRECISION in place (a precision
fix). No data backfill or operator action required; both apply automatically on
the first start under 0.6.0.
0.7.0¶
Finishing the 0.6.0 lease-liveness story plus an on-prem operability cluster.
No breaking changes, no persistence migration, no SDK-floor change — the
schema version is unchanged from 0.6.0 and the SDK floor stays
kneo-agent>=2.2.0,<3.0.0. Two behaviour notes below are worth reading before
you upgrade; everything else is additive and default-off / default-unset.
Upgrade:
- pip install -U kneo-serv (resolves to 0.7.0).
- docker pull ghcr.io/kneo-agent/kneo-serv:0.7.0 — :0.7 and :latest
resolve to the 0.7.0 digest. The release pipeline now runs an in-pipeline
cosign verify self-check (cosign verify + verify-attestation against the
pushed digest); image signing, SBOM attestation, and the Trivy gate are
otherwise unchanged.
Behaviour note — worker_lease_seconds is now a liveness window, not a
run-time cap. A worker now renews its queue lease for the life of a run (a
heartbeat renewing at ~worker_lease_seconds / 3), so a healthy long run never
lets its lease lapse and get reclaimed mid-flight. The lease therefore no longer
bounds how long a run may take — it bounds how long a crashed worker's run
stays unreclaimable. If you raised worker_lease_seconds in 0.5.x/0.6.x to "fit"
your longest run, you can lower it back toward your crash-detection latency. No
action is required; the default is unchanged and shorter leases are now safe.
Behaviour note — the per-run token ceiling is a post-run boundary check, not a
mid-flight kill. A run can be capped at a maximum input+output token budget via
the SDK's TokenBudgetMiddleware, configured per agent with model.token_budget
in the spec (a positive integer — a non-positive value is rejected at spec
validation, not silently ignored) or deployment-wide with
KNEO_SERV_TOKEN_BUDGET (the spec field wins). The middleware checks reported
usage after each run and then raises
TokenBudgetExceeded, surfaced as 400 token_budget_exceeded. Consequences:
- A run that overshoots within a single step finishes that step before failing —
size the ceiling as a spend backstop, not a precise hard stop.
- on_missing="ignore": a runtime that doesn't report metadata["usage"] never
spuriously fails the ceiling. Unset (the default) means no ceiling.
New operator knobs (all additive; defaults preserve 0.6.x behaviour):
- KNEO_SERV_AUDIT_EXPORT_ENABLED — when set, every persisted (already
redacted) audit event is also emitted as a JSON line on the dedicated
kneo_serv.audit logger, from the single record_audit_event chokepoint.
Attach a logging handler to forward to a file / syslog / SIEM. Off by default;
export failures never break the run path.
- KNEO_SERV_TOKEN_BUDGET — deployment-wide per-run token ceiling (see the
behaviour note above). A spec's model.token_budget overrides it.
- Local / self-hosted LLM endpoints. The native (openai) runtime now reads
model.extra.base_url and an API key — model.extra.api_key_ref resolved
through the SecretResolver, or a literal api_key escape hatch — and threads
them into the OpenAI-compatible client, so a spec can target Ollama / vLLM /
llama.cpp / LocalAI. Unset fields preserve the hosted-OpenAI default; a literal
api_key is redacted from audit / list surfaces.
New spec fields (optional, additive — old specs are unaffected):
- Human-request taxonomy. components.humans.* accepts request_type
(approval / review / correction / selection / freeform), options,
default_option, context, and response_role. validate_semantics rejects
a default_option outside options and a selection without options. When
response_role is set, the reviewer's reply folds into the resumed run's
message thread with that role. GET /v1/human-tasks/{id} now also returns the
paused run's redacted messages thread alongside the pending request
(same auth scope; no new route). A client that only reads the existing
request field is unaffected.
Persistence schemas: no migration. The store schema version is unchanged
from 0.6.0; RunStateStore gains a schema_version / close Protocol surface
(behaviourally a no-op on the schema-less stores), but no on-disk change applies
on upgrade.
0.9.0¶
Reliability & retention. No breaking changes, no persistence migration,
no SDK-floor change — the spec schema version is unchanged and the SDK
floor stays kneo-agent>=2.2.0,<3.0.0. Persistence additions (the
idempotency prune, count queries) are additive-only; rollback to 0.8.0 is
safe (no persisted-field removals).
Behavior corrections to review before upgrading. Each corrects shipped behavior that contradicted its own documented/validated contract (the fixes-vs-breaks test in the new contract-stability policy, adopted this cut). If you built automation against the old behavior, adjust:
- Handoff
round_robinruns reportcompletedafter a full rotation (previously every successful rotation persisted asfailed/max_iterations). Alerts keyed on that false failure will go quiet. on_error: continue/fallbackexecute. Workflows that declared error tolerance but relied on the hard failure will now proceed:continuepasses the step's input through;fallbackruns the referenced step. See the run lifecycle guide.- List
totalis the true store count — it previously capped silently at the 10 000-row fetch window. Dashboards assertingtotal ≤ 10000should read the pagination block. Run list items now carrytrace_event_countinstead of each run's fulltrace_eventsarray (the trace lives atGET /runs/{id}/trace). - Token-usage metrics survive redaction (
input_tokensetc. were[REDACTED]everywhere). Cost dashboards start receiving real values. - Resume/continue are fenced: resuming a run that is not
blocked, or continuing a terminal-but-not-failed / live-leased run, returns409 run_state_conflictinstead of silently re-executing. Cancelling a blocked run removes its task fromGET /human-tasks. - A per-attempt timeout no longer retries by default — the abandoned
attempt may still be running, so the retry double-executed
non-idempotent calls. This applies to provider calls AND to workflow
steps/nodes that set
timeout_seconds+max_retries. Opt back in per surface:KNEO_SERV_PROVIDER_RETRY_ON_TIMEOUT=true(orretry_on_timeoutin spec retry config) for providers,KNEO_SERV_WORKFLOW_RETRY_ON_TIMEOUT=truefor workflow steps/nodes. MCP connect timeouts are the exception: they cancel the connect coroutine cleanly, so configured MCP retries do retry them. - Stricter validation (pure checks; previously these crashed at
runtime): graph
kind: humannodes (E_GRAPH_NODE_HUMAN_UNSUPPORTED), memory blocks withoutpolicy(E_MEMORY_POLICY_REQUIRED), guardrail items missingid/type(E_GUARDRAIL_FIELDS). Invalid path-based specs now return200 {valid: false}from/specs/validate(was 400). - Environment policies set via REST are enforced on run/compile when
the request names the environment — a deployment blocked by policy
returns
403 environment_policy_blocked. Verify your stored policies say what you mean before upgrading production. - Stricter env-var parsing: invalid numeric values in
KNEO_SERV_WORKER_*/ queue knobs now fail startup instead of silently running defaults; same for the new strictly-parsed knobs.
New knobs (all optional; see
environment.md): KNEO_SERV_RETENTION_IDEMPOTENCY_DAYS,
KNEO_SERV_RETENTION_RUN_STATUSES, KNEO_SERV_TRACE_MAX_EVENTS,
KNEO_SERV_MCP_CONNECT_TIMEOUT_SECONDS,
KNEO_SERV_IDEMPOTENCY_LOCK_TTL_SECONDS,
KNEO_SERV_PROVIDER_RETRY_ON_TIMEOUT,
KNEO_SERV_WORKFLOW_RETRY_ON_TIMEOUT, KNEO_SERV_ARTIFACT_PATH /
KNEO_SERV_LOG_PATH.
0.8.0¶
Declarative spec parity along the tools / MCP / skills axis. No breaking
changes, no persistence migration, no SDK-floor change — the schema version is
unchanged and the SDK floor stays kneo-agent>=2.2.0,<3.0.0. One behaviour
note below is worth reading before you upgrade; everything else is additive and
default-unset.
Upgrade:
- pip install -U kneo-serv (resolves to 0.8.0).
- docker pull ghcr.io/kneo-agent/kneo-serv:0.8.0 — :0.8 and :latest
resolve to the 0.8.0 digest. Image signing, SBOM attestation, and the Trivy
gate are unchanged from 0.7.0.
Behaviour note — overlays is no longer silently ignored. POST /v1/runs
(sync + async) and the /v1/specs/run / /compile / /validate /
/policy-report routes accepted an overlays list but dropped it without
applying it. From 0.8.0 the overlays are threaded through compile/run,
persisted in run metadata, and replayed on resume. If any stored client request
or automation passes overlays, audit it before upgrading — those overlays now
actually change the compiled spec. overrides / strict are likewise now
honored on the /specs/* routes that previously dropped them.
Trust note — spec_path and overlays are filesystem-trusted inputs. Both
name paths the server reads at compile time. Grant runs:write /
specs:read-scoped keys to callers you trust with that read surface, and see
security_hardening.md for the posture before exposing
these fields to semi-trusted callers.
New spec surface (all optional, additive — old specs are unaffected):
- Declarative MCP transports. A top-level
mcp_serversblock (transport: stdio | http | sse, withcommand/args/env/cwdorurl/sse_url/message_url/headers/timeout, plusmax_response_bytes/sse_read_timeoutknobs andverify/ca_bundle/client_cert/client_keyTLS fields) and atool.mcp = {server: <name>, name?: <remote_tool>}reference. Construction happens at build time; the connection is lazy on first tool call, so the spec compiles offline. Preferclient_key_ref(resolved via theSecretResolver) over inlineclient_key— the inline spec is persisted unredacted into run metadata, and the TLS field names are redaction terms only on audit/list surfaces.verify: falsedraws a validation warning. - Agent-as-tool.
tool.agent: <name>backs a tool with another declared agent. A tool must be backed by exactly one ofimplementation/mcp/agent— a tool with none is now a validation error (E_TOOL_NO_BACKING) instead of being silently dropped at build. - Workflow-as-agent.
agent.as_agent: <workflow>backs an agent with a declared workflow; onlyname/description/system_promptare legal alongside it. Cyclic or dangling references across all of these fail at/specs/validate(E_BUILD_CYCLEetc.), not at runtime.
New API surface (additive):
GET /v1/skills— read-only catalog of declared + default discoverable skills;specs:readscope, standard pagination, no side effects.RunCreateRequest.skills— per-request{add, disable}skills overlay.addonly enables skills already declared in the spec; out-of-scope overlays are rejected; every overlay is audited (run.skills_overlay) and preserved across resume.GET /v1/human-tasks?status=pending|escalated— a real filter now; an unknown value returns 422 where it was previously a silent no-op.POST /runs/{id}/continueaccepts anIdempotency-Keyand replays the stored response on retry; concurrent/continuecalls are serialized under a per-run lock.POST /v1/specs/runnow holds the same idempotency lock as/runs(409idempotency_key_in_progresson contention).- Invalid specs on sync
POST /v1/runsreturn400with diagnostics where they previously surfaced as an opaque 500. 413 (payload_too_large) is now published in the OpenAPI error responses.
Persistence schemas: no migration. No new persisted fields; rollback to 0.7.x after running 0.8.0 is persistence-safe (the new request/spec fields are request-scoped or compile-scoped only).
0.10.0¶
Theme: performance & capacity / 1.0 runway. A correctness/security/
hardening cut. Additive-only; no migration; SDK floor held at
kneo-agent>=2.2.0,<3.0.0. 0.10.0 is a normal additive minor — not the
1.0 cut.
Intentional behavior changes (each corrects provably-wrong shipped behavior per the contract-stability policy; act if you keyed on the old behavior):
tool-stageredact/warnguardrails now actually enforce. Before 0.10.0 a declaredtool-stage guardrail validated, satisfied the productionrequire_guardrailsgate, and deployed — but was never wired into the runtime, so it never ran.redact/warnnow execute in the tool-call chain. Action: if a deployment declared atool-stageredact/warnguardrail, it was unprotected until now (disclosed on fix) — re-review it and confirm the now-live behavior is what you want (e.g. aredactaction will now actually redact tool output).- Raising tool-stage guardrail actions are now rejected at
/specs/validate(E_GUARDRAIL_ACTION_UNSUPPORTED). A tool-stage guardrail with the defaultblock(or escalate/human_review/retry/revise) cannot abort the run yet — the SDK bridge executor's per-tool-failure contract converts the raised violation into a recoverable result, so it would fail open. Rather than ship that, such specs now fail validation. Action: for tool-stage guardrails useredact/warn, or move a blocking check to theinput/outputstage (those enforceblockcorrectly). True tool-stage block-enforcement is planned for 0.11.0. Note: a tool-stage guardrail with no explicitactiondefaults toblock, so addaction: redact(orwarn) to such specs. - Guardrails with a non-
middlewaremodeare now rejected at/specs/validate(E_GUARDRAIL_MODE_UNSUPPORTED). Only themiddlewareattachment is wired; other modes (runtime/tool/workflow) were silently dropped. Action: removemode(it defaults tomiddleware) or set it tomiddleware. workflow-stage guardrails are now rejected at/specs/validate(E_GUARDRAIL_STAGE_UNSUPPORTED). No runtime hook enforces them yet, so a spec declaring one previously validated green and silently did nothing. Action: removeworkflow-stage guardrail blocks (or move the control to atool/input/outputstage); such specs will now fail validation.- A
kind: workflowstep containing a human-approval step is rejected at/specs/validate(E_STEP_WORKFLOW_NESTED_HUMAN). Such specs used to validate and then complete the run with the unapproved output. Action: lift the human-approval step to the top-level workflow (the supported pattern — it blocks and resumes correctly). - Secret redaction now covers pluralized credential keys (
api_keys,refresh_tokens,KNEO_SERV_API_KEYS). Single-segment usage counters (input_tokens,max_tokens) are unaffected. Action: none expected; if you scraped a redacted log/trace/audit field expecting a plural credential key to appear in the clear, it no longer will. - Release packaging: the container image's public tags (
:X.Y.Z/:X.Y/:latest) and the GitHub Release are now gated behind the Trivy CVE scan and the coverage/postgres lanes (therelease → scan → gated shipsplit). No operator action; relevant only if you build the image from this repo's workflow.
Persistence schemas: no migration. Terminal-write atomicity, the persistent idle-poll worker, and checkpoint-prune liveness are behavior-internal; no new persisted fields. Rollback to 0.9.x is persistence-safe.
0.11.0¶
0.11.0 is a breaking, 1.0-runway cut: it ships the two held 1.0-register
/v1 contract changes, plus guardrail-enforcement that turns some
previously-rejected specs into accepted-and-enforced ones.
Breaking — /v1 contract:
- Async run-create returns
202 Accepted(was200).POST /runs/POST /v1/runswithasync_mode=truenow returns 202; synchronous creates (async_mode=false) still return 200. The response body is unchanged. Action: if your client assertsstatus_code == 200on async create, accept202(or treat2xxas success); keep pollingGET /runs/{run_id}exactly as before. Idempotent replays preserve the 202. - Unknown query parameters are rejected with
422. Any query-string parameter a route does not declare now returns422 {"error": "unknown_query_parameters", "unknown": [...]}on the authenticated/v1(and root) surface; through 0.10.x they were silently ignored./healthz,/readyz,/metricsare exempt. Action: remove stray/misspelled query params from API calls; a typo that was previously a silent no-op now errors (which is the point — it surfaces the bug). Request bodies already rejected unknown fields, so this only changes query strings.
Behavior — guardrail enforcement (specs rejected at 0.10.0 now validate):
- Tool-stage guardrails with a raising action (
block/escalate/etc.) are now enforced — a violation aborts the run (sync →422, async →failed) instead of failing open.E_GUARDRAIL_ACTION_UNSUPPORTEDis no longer raised at/specs/validate. workflow-stage guardrails are now accepted and enforced per step (each step's output is checked; block aborts, redact/revise rewrite).E_GUARDRAIL_STAGE_UNSUPPORTEDis no longer raised. Action: if you relied on these being rejected as a lint, note they now run — audit anytool/workflow-stage guardrail blocks you had declared "for later."- Guardrails now also apply to streaming runs (
Agent.stream): input guardrails run before the stream; an outputrevisebuffers and rewrites the caller-received text (so a stream with an output guardrail yields the revised result as one chunk rather than token-by-token).
Persistence schemas: no migration — all changes are API-surface or behavior-internal; no new persisted fields. Rollback to 0.10.x is persistence-safe (but clients depending on the new 202/422 contract must roll back too).
Downstream: kneo_client (and anything pinning the /v1 contract) needs a
coordinated uptake for the 202 + reject-unknown-query-params changes — see its
TODO-0.8.0.
0.12.0¶
0.12.0 is an additive, production-ready minor (the GA candidate). No breaking
/v1 change ships in this cut; the one deliberate break (spec-path confinement
default-on) is staged here as a deprecation warning and lands at 1.0.0.
Behavior change — POST /specs/run honors async_mode:
- Through 0.11.x,
POST /specs/runsilently ignoredasync_modeand always ran the spec inline, returning200. It now mirrorsPOST /runs: withasync_mode=trueit dispatches to the worker queue and returns202 Acceptedwith the queuedrun_id(pollGET /runs/{run_id}); the synchronous default (async_mode=false) still returns200; idempotent replay preserves the original status. Action: a client that sentasync_mode=trueto/specs/runand relied on getting a completed run back at200will now get202+ a queued id — switch to polling (as/runscallers already do). Clients that only used the synchronous default are unaffected.
Deprecation (becomes a default-on break at 1.0.0) — spec-path confinement:
spec_pathandoverlaysare caller-supplied filesystem paths the service reads at compile time. 0.12.0 adds an opt-inKNEO_SERV_SPEC_ROOTenv var: set it to an allow-listed root and any path resolving outside it (absolute,..-traversal, symlink escape) is rejected422 spec_path_confined. WhileKNEO_SERV_SPEC_ROOTis unset, behavior is unchanged except that an absolute path now logs aDeprecationWarning. At1.0.0confinement becomes default-on and absolute / out-of-root paths are rejected by default. Action: setKNEO_SERV_SPEC_ROOTto the directory that holds your specs now — this both closes the path-disclosure surface today and adopts the GA behavior ahead of the1.0.0flip. (Held 1.0-register change; see../dev/contract_stability.md.)
Also in this cut (no action needed): human-approval (kind=human) gates now
pause + resume in every workflow shape (graph, handoff, group-chat,
concurrent — previously sequential only); the kneo spec explain CLI command;
an enforced seeded backup/restore release gate; and internal correctness/security
fixes (overlay path-confinement, a tool-policy fail-open close, file-store
retention-race hardening). See the CHANGELOG.
Persistence schemas: no migration — additive only; rollback to 0.11.x is persistence-safe. (A run blocked inside a graph/orchestration workflow on 0.12.0 cannot be resumed after a rollback to 0.11.x, which lacks the continuation support — drain in-flight blocked runs before rolling back.)
1.0.0¶
BREAKING — spec-path confinement is default-on. Through 0.12.x,
KNEO_SERV_SPEC_ROOT was opt-in: with it unset, an absolute or out-of-root
spec_path/overlays was accepted and only logged a DeprecationWarning. At
1.0.0 the default flips to reject: a caller-supplied path that resolves
outside the confinement root is refused with 422 spec_path_confined. When
KNEO_SERV_SPEC_ROOT is unset, the confinement root is the process working
directory.
This break now also covers skills[].source — a declared skill bundle's
filesystem path, which through 0.12.x was read unconfined (the sibling path
that bypassed the spec_path/overlays confinement and left an authenticated
arbitrary-file-read oracle open). At 1.0.0 an out-of-root skill source is
rejected like any other spec read, and a ../~ traversal in a skill source is
rejected at spec validation.
Action. Pick one:
- Set
KNEO_SERV_SPEC_ROOTto the directory that holds your specs, overlays, and skill bundles (the recommended posture — an explicit allow-listed root). Everything you load byspec_path/overlays/skills[].sourcemust resolve inside it. - Or keep specs under the service's working directory and leave
KNEO_SERV_SPEC_ROOTunset (the working directory is the default root).
If you deploy with out-of-tree spec or skill paths (e.g. absolute paths to a
shared bundle directory), move them under the root or add the root to
KNEO_SERV_SPEC_ROOT before upgrading — otherwise those requests begin
returning 422 spec_path_confined. Inline specs (spec in the request body) and
per-run skill overlays are unaffected; only caller-supplied filesystem paths are
confined.
Local CLI is operator-trusted. Spec-path confinement applies to the
service's reads of caller-supplied paths (the /v1 surface, run, resume). The
local kneo CLI reads the operator's own filesystem directly and is not
confined to KNEO_SERV_SPEC_ROOT — a local operator already owns the filesystem,
so kneo spec validate /any/path.yaml keeps working from any directory. (When the
CLI targets a remote service with --service-url, it sends the resolved spec
inline; the service applies its own confinement.)
kneo spec validate now exits 1 on an invalid spec (was exit 0, with the
diagnostics only in the body). It now works as a CI gate, consistent with
kneo spec lint; --json still prints valid + diagnostics. Update any pipeline
that relied on exit 0 for an invalid spec.
Persistence schemas: no migration — these are request-validation + CLI changes only.
1.1.0¶
A normal additive minor on the 1.x line — no /v1 contract break. It does
tighten spec validation under the reject-don't-drop policy: controls that
were previously accepted and then silently dropped, ignored, or crashed at runtime
now fail fast at /v1/specs/validate (and at run-create). All are validation
behavior, not /v1 shape changes; a spec that was already correct is unaffected.
GET /v1/runs/{id}/policy-report now requires the runs:read scope (was
specs:read), aligning it with every other per-run read (get / recovery /
replay / trace / checkpoints / graph). The operator, service, and
viewer roles already carry both scopes and are unaffected; the reviewer role
(runs:read, no specs:read) gains access. Action: a custom API key that
read this endpoint with only specs:read must add runs:read.
Unknown spec keys are now rejected (extra='forbid'). A typo'd or stray key
anywhere in a spec block (tols:, systme_prompt:, an unknown sub-block) was
silently discarded — so an agent could ship missing the tool/prompt/governance the
author intended. Every v1 spec block now rejects unknown keys with an E_SCHEMA
validation error. Blocks that are intentionally open carry a dedicated field for
loose values — put provider-specific inference params under model.extra, not as
unknown keys on model:.
Action: run kneo spec validate (or kneo spec lint) over your specs before
upgrading; fix or remove any flagged stray keys. A common one: a runtime selector
must use runtime_preferences.preferred_mode (+ allowed_modes), not a bare
runtime: key (which never took effect and is now rejected).
Guardrail action is validated against its stage. An action the stage's
runtime does not honor now fails validation (E_GUARDRAIL_ACTION_UNSUPPORTED)
instead of crashing (HTTP 500) or silently degrading at runtime. Specifically:
redact is rejected on the input/output stages (it is a tool/workflow
action), and revise is rejected on input (it only applies on output/tool/
workflow). Supported per stage: input — warn/block/retry/escalate/human_review;
output — those + revise; tool/workflow — those + redact. action still defaults
to block. Action: for output PII handling use block (fail-closed) or
revise; redaction belongs on the tool/workflow stage.
Participant fallback is rejected. A concurrent / handoff / group-chat
participant declaring on_error: fallback or fallback_ref now fails validation
(E_PARTICIPANT_FALLBACK_UNSUPPORTED) — orchestration never honored participant
fallback, so it was a silent no-op. Action: remove the participant fallback, or
use a sequential / graph workflow where fallback is enforced.
Tool domain gating is now enforced; binary capability flags are advisory.
tools.permissions.allowed_domains (and the newly-wired denied_domains) are now
enforced at tool-call time: a URL-shaped argument whose host is in
denied_domains — or, when allowed_domains is set, is not in it — fails the call
(422 sync / failed async). Action: if a tool legitimately calls a host, add it
to allowed_domains before upgrading. The binary capability flags
(allow_network / allow_filesystem_read|write / allow_shell) remain a
declared static-governance posture — they drive the policy report and the
deny_unrestricted_tools env-policy gate, but are not runtime-enforced against
arbitrary in-process tool code (a real sandbox is a future 2.0 item; see
capability_enforcement_design.md). Treat them as governance signals, not a
runtime sandbox.
Run spec is frozen at create. A run now captures its resolved base + overlay
spec inputs at creation and compiles every execute/resume from that snapshot.
Editing, moving, or deleting the source spec/overlay files after a run is created
— or restarting the service against a changed tree — no longer changes or breaks
that run (it ran against a possibly-mutated file before). The reload-on-resume
behavior is gone: editing a spec file and resuming will not apply the edit to
an existing run; start a new run instead. Runs created before 1.1.0 keep the old
recompile-from-source behavior (no snapshot). The snapshot now also captures the
declared skill bundles' content (instructions + metadata) at create, so a
run's skill prompts are durable too — editing, moving, or deleting a skill
bundle after a run is created no longer changes that run. Remaining limit: tool
implementation code (Python import paths) and a skill bundle's tool callables
are code, not data, and are still resolved at build — a tool whose code changes
under a restart can still change behavior (the package-version pin / fingerprint
mitigations remain the deferred audit/drift work).
Resuming an expired human task is now refused. POST
/v1/human-tasks/{id}/resume returns 409 human_task_expired when the
task's deadline (expires_at) has already passed — previously a resume landing
in the window between expiry and the next prune_expired_human_tasks sweep
could complete a task the deadline said was over, and whether the resume or the
sweep won was a race. The deadline is now authoritative: the configured
on_timeout policy (fail / continue) governs an expired task. Exception:
on_timeout: escalate tasks remain resumable past their deadline — escalation
deliberately keeps the run blocked for a manual late resume. Action: none for
well-behaved reviewers; clients that resumed past the deadline must handle the
409 (the task timed out — inspect the run's terminal state).
Persistence schemas: a single automatic, backward-compatible SQLite/
PostgreSQL migration (v4) adds a nullable runs.session_id column (W9 #6) plus
filter indexes; it applies on first connect with no operator action and leaves
existing rows intact. The remaining changes are additive run-metadata +
validation/gating behavior.
1.2.0¶
A normal additive minor on the 1.x line — no /v1 contract break. Theme:
sibling-parity & contract-fidelity hardening. It does tighten a few
validate-/config-/runtime behaviors under the reject-don't-drop policy;
controls previously accepted-then-dropped, silently-ignored, or a prod no-op now
fail fast or take effect. A deployment that was already correctly configured is
unaffected.
allow_filesystem_read now requires human approval like allow_filesystem_write.
An agent whose tool policy sets allow_filesystem_read is now flagged as a
privileged surface (W_TOOL_FILESYSTEM_READ_ALLOWED) and, with no human-review
step, trips W_HUMAN_APPROVAL_MISSING — matching the write flag and the policy
report, which already counted read-any-file as unrestricted. Action: add a human
approval step to such specs (or document the exemption), as you already do for
write-capable agents.
A mistyped role/scope — or a stray : — in KNEO_SERV_API_KEYS now fails at
startup. A token that is neither a known role nor a scope some role grants
(e.g. reviewr, runs:reed, or a : inside the key value, which is the field
delimiter) previously parsed as an inert explicit scope, silently giving the key
fewer privileges than intended. It now raises a RuntimeError at config parse.
Action: if a key fails to load, fix the typo; keys themselves must not contain a
:.
Retention status overrides must be terminal. KNEO_SERV_RETENTION_RUN_STATUSES
/ _QUEUE_STATUSES (and programmatic RetentionPolicy) now reject any
non-terminal status — a value like running would have let a prune pass delete
live in-flight records. Action: set only terminal statuses
(completed/failed/cancelled/timed_out/expired for runs;
completed/failed for the queue).
redact_tool_results is now enforced. The tools.permissions.redact_tool_results
policy flag was only honored by an unwired middleware — a no-op in production. It
now runs on the live tool path and scrubs secrets from tool output via redact_data
(per its docstring), not a blanket marker. Action: none required; if you set this
flag expecting it to take effect, it now does (secret values in tool results are
scrubbed).
Unknown query parameters: 403 now precedes 422. An authenticated but
under-scoped caller sending an unrecognized query param now gets 403 (missing
scope) before the 422 (unknown param), so the error no longer discloses which
params a route accepts to a caller not authorized for it. Action: none.
New additive surface (no action; opt-in): GET /v1/runs?q= bounded content
search; a first-class usage field on the run status; a typed
GET /v1/security/credentials inventory with a health status;
POST /v1/policies/environment/{env}/preview; and dropped/complete on
GET /v1/runs/{id}/trace.
SDK floor: unchanged (kneo-agent>=2.2.0,<3.0.0). 1.2.0 consumes no new SDK
surface.
Rolling back¶
Schema-forward migrations make in-place downgrade unsafe; the only supported rollback path is restore from the pre-upgrade backup, then re-install the previous version.
For the full step-by-step procedure — stop, restore, re-install, restart,
verify with the deployment smoke — see
backup_and_recovery.md § Rolling back after a failed upgrade.
Keep the pre-upgrade backup until you have verified the new version through at least one business cycle.
Reporting upgrade issues¶
Capture the same context listed in troubleshooting.md § What to capture before opening a bug, plus:
- Source version (
pip show kneo-servbefore the upgrade). - Target version (after the upgrade).
- Migration log lines from the first start on the new version.
- The exact env file or compose
.env(with secrets redacted).