Skip to content

kyvvu changelog

All notable changes to the Kyvvu Python client (kyvvu) are documented in this file.

Versions before 0.11.0 were early development releases with frequent breaking changes. The changelog starts from the current beta.


[1.0.0] - 2026-10-30

  • Changed: depends on kyvvu-templates>=1.0.0,<2.0.0, which now ships the behaviour templates the engine no longer exports.

  • Changed: the kyvvu command now ships in the Apache-2.0 kyvvu-cli distribution. Install it separately; the client no longer installs a console script or exposes a serve extra.

  • Changed: Kyvvu.register_agent() now delegates registration transport, retries and caching to PlatformClient and evaluates the profile accepted by the platform, including an owner assigned in its response.
  • Removed: the client's _registration_cache.py and duplicate HTTP transport, __min_api_version__, SDK_API_VERSION, KV_SKIP_API_VERSION_CHECK, and KyvvuIncompatibleAPIError. Platform engine and policy schema compatibility checks remain.

  • Changed: the Apache-2.0 baseline manifests, profiles, and loader now ship in the kyvvu-baseline dependency. Existing kyvvu.baseline imports remain available as re-exports.

  • Breaking: 1.0. Requires kyvvu-engine>=1.0.0,<2.0.0 and a platform at 1.0.0 or newer: the steps carry hash, which API 0.22 refuses with 422. Classified Development Status :: 5 - Production/Stable.

  • Breaking: requires Python 3.11 or newer (requires-python = ">=3.11"), with kyvvu-engine 1.0. The 3.10 classifier is gone. On 3.10, pip keeps resolving the newest 0.x release.

  • Changed: traces sent to the platform carry seq and hash on every step: the engine computes the hash chain and the platform verifies it. Nothing changes in the client's own API.

[0.23.0] - 2026-10-17

  • Fixed: kyvvu doctor with both a CLI session and an agent API key checks both, on the Credential line, and fails when the key is rejected; a valid session used to show ✓ beside ✗ Policies … (401). The key is checked where an agent started here would use it. Policies names the URL it fetched from and, on a 401/403, names KV_API_KEY as the fix; when that URL differs from the CLI session's, Policies and Trace sink say so and suggest --api-url / KV_API_URL (Trace sink warns). The probe runner's own stderr notices are silenced, so nothing precedes the report.

  • Fixed: the manifest CLI could not be used end to end. kyvvu list-assignments has an ID column and kyvvu assign-manifest prints the new assignment id, which is what kyvvu unassign-manifest --assignment-id takes. A refused request prints the platform's reason on one line, Error (409 manifest_already_assigned): Manifest '...' is already assigned ..., and exits 1, instead of the raw JSON body followed by Failed to assign manifest: 1 (a catch-all intercepted typer.Exit, which is not a SystemExit). Every CLI command reports platform errors in that shape. --agent-id on assign-manifest, list-assignments and list-policies takes the agent key as well as the agent_… id; a key used to answer 404 Agent not found.

  • Fixed: kyvvu doctor verifies the declared kyvvu-engine range without packaging, which the SDK does not depend on: a clean install now prints ✓ Versions … (satisfies <1.0.0,>=0.13.0) instead of • Versions … not verified (packaging not installed). The built-in comparison covers the >=X,<Y release-number ranges the SDK declares and abstains (falling back to packaging, else "not verified") on anything else.

  • Fixed: with KV_POLICY_FILE set, kyvvu doctor's trace-sink hint claimed that with KV_API_KEY and an agent key set auto would resolve to the platform, even with KV_API_KEY set. It now says auto follows the local policy file, and names only what is actually missing.

  • Fixed: a plain kyvvu try printed two "path ... resolved on none of the trial steps" warnings (the typo hint) about properties.target.trust, which the bundled demo path leaves out on purpose. The warning now appears only when you supply the policies (--manifest), where a missing path may be a misspelling of yours; the bundled baseline's paths are never reported, including under --steps alone.

  • Fixed: two threads reaching Kyvvu.runner first at the same time each built a runner, the second replacing the first; a task started on the replaced one was unknown to the one that stayed, so its next step raised TaskStateError: task ... is not open. The lazy construction is now locked.

  • Changed: the bundled eu-ai-act-minimal baseline is 1.1.0. "AI-generated content must be marked before delivery" no longer demands a step.gate immediately before every step.model -- before the content exists -- and so no longer warns on every model call. It now fires on delivery: an outbound step.message POST, or a step.resource POST with properties.target.trust: external, in a task that holds a completed step.model, warns unless a step.gate was recorded after the most recent model step. The citation is corrected to Article 50(2). kyvvu try and the kyvvu init scaffold gain a delivery step, send_reply (step.message POST), between the model call and run_script, and now go allow, allow, warn, block: the warn lands on the delivery (content marking, Art. 50(2), and chatbot disclosure, Art. 50(1)) rather than on the model call, and the block is still OWASP ASI05 on the ungated step.exec.

  • Added: Kyvvu.registration_result, the EvalResult of the last register_agent() policy evaluation, set on the fresh, cached and stale-cache paths and on a block (the exception's own result). A call that fails before evaluating clears it. A warn outcome used to reach only warnings.warn and the incident sink, and register_agent() returns the platform's response, so a caller had no way to read which registration policies warned.

  • Added: a manifest assigned to a running agent applies without calling register_agent() again. When a policy refresh changes the agent's agent_registration policies, the registration last sent is evaluated again: warnings file a registration incident, and a blocking violation makes every following step raise KyvvuBlockedError naming the policy until a later refresh lets the registration pass. kv.policy_status() reports it as registration_blocked / registration_blocked_by.

  • Breaking: identifiers have a grammar (see docs/concepts/tasks.md, "Identifier rules"). A task_id passed to start_task(), resume_task(), end_task(), idle_task(), error_task() or add_step(), and an action_id or step_name passed to add_step(), outside its rule raises KyvvuValidationError naming the field and the rule, before anything is recorded. An explicitly passed task_id is refused, never mapped; an empty string is no longer treated as "omitted".
  • Changed: names and ids that come from code or a framework are made to comply instead of raising: @kv.step function names, template-rendered task-boundary names (context["name"], crew and chain names) and framework tool names have control characters replaced by U+FFFD and are cut to 255 characters; a framework task or run id outside the grammar is mapped to x- plus 32 hex digits of its SHA-256 (deterministic), with the raw task id kept as properties.raw.task_id. New Kyvvu.capture_step_name(), Kyvvu.compliant_task_id() and Kyvvu.compliant_action_id() for adapters.
  • Fixed: kyvvu try records a blocked step as a blocked step, as a real run does, so a rule with count_attempts sees the same history in a trial. record_blocked_step() coerces the error message to I-JSON.

  • Breaking: A write into a task this client does not have open now raises KyvvuConfigError instead of starting a second record of the task numbered from 1. This covers add_step(), end_task(), idle_task(), @kv.step (including a context still naming a task that another context ended), and FrameworkAdapter.emit_step(). A step completion arriving after its task closed is dropped with a warning, never raised: a framework completion, and an @kv.step completion, failure or block record when the task closed while the step ran (the function itself called end_task(), or another context closed the task while an async step awaited). start_task() refuses an id that is already open. add_step() with a task boundary kind now has the matching lifecycle effect (task.start opens the task, task.end/task.error close and flush it). In 1.0 a task cannot be continued after it ends or from another process; a task_id must never be reused.

  • Changed: Captured values are coerced to I-JSON, which the engine now requires, so recording never fails the host's call: decorated arguments and return values, add_step() input/output, and framework callback payloads and template-extracted properties, and the exception text a failed or blocked step records. Tuples and sets become lists, datetimes ISO strings, Decimal/UUID strings, NaN/Infinity the strings "NaN"/"Infinity"/"-Infinity", integers beyond 2^53-1 strings, bytes a "<bytes len=N>" marker, non-string keys str(key), pydantic models their model_dump(), anything else its repr. Explicit add_step() properties and meta are not coerced (policies read them): a non-I-JSON value raises KyvvuValidationError naming its path. FrameworkAdapter.emit_task_start() takes properties=; Kyvvu.capture_value() / capture_payload() expose the coercion to integrations.

  • Breaking: A context without a selected task no longer resumes the client's only open task. Call kv.resume_task(task_id) in that context, or pass task_id= to programmatic step and task methods. Framework tool callbacks retain their adapter task context.

  • Breaking: Framework tool callbacks now record POST by default. Declare reads with Kyvvu(read_only_tools=[...]); write_tools explicitly classifies writes. Tools carrying data.* or target.trust require one of these declarations.

  • Removed: The inherited policy_hmac_secret setting and HMAC policy verification. Ed25519 signing is planned for a future release.

  • Breaking: Decorated gates record properties.kind and output.decision; a normal return passes, while GateDenied or a crash denies. Manual gates need output={"decision": "pass"} to satisfy the bundled baseline. Ship this SDK and the matching engine before the platform serves updated gate policies.

  • Removed: The inherited cross-execution rate-limit rule is no longer available. Policies naming it fail to load as an unknown rule.

  • Breaking: Exported trace steps carry the per-task ordinal as seq in place of step, and a recorded Behavior exposes it as .seq.

  • Breaking: The behaviour field is kind and its enum is ActionKind; the retired field and enum names are rejected. Policy kind parameters are target_kinds, trigger_kinds, forbidden_kind, forbidden_kinds, required_kind, taint_kind, and forbidden_predecessor_kinds; old parameter names are rejected. A step policy's field needs a complete path such as input.recipient or properties.message.recipient (that is, input.<name> or properties.<name>); bare data field names are rejected.

  • Breaking: SDK model calls omit the verb. Passing Verb.POST to a step.model decorator now raises. Policies matching POST without a step-type filter no longer match model calls; select step.model explicitly. Existing POST log rows remain as history.

[0.22.1] - 2026-10-01

Fixed

  • A policy block raised from on_chain_start or the root's on_chain_end left the task open (LangChain, LangGraph). A block normally leaves the task to the framework's error callback, but a block on the first step.message (raised out of on_chain_start, so the run never starts) or on the final one (raised after the run finished) is followed by no error callback. The handler now closes the task as task.error itself; before, it stayed open until the sweeper evicted it an hour later.

Added

  • KyvvuCrewAIListener.blocked_exception: the KyvvuBlockedError that tripped the circuit breaker, for a caller to re-raise after kickoff() (CrewAI's event bus swallows it). blocked_reason still gives the message.

[0.22.0] - 2026-10-01

Breaking. Requires the matching kyvvu-engine release. Several integration defaults changed; read the breaking list before upgrading.

Breaking changes

  • The template argument is a sentinel, and None means no template. The default is kyvvu.default_template ("the built-in, or KV_TEMPLATE_LOCATION"). A BehaviorTemplate you pass replaces the built-in and governs both the steps and the task boundaries the integration opens; nothing falls through to the built-in. The template is checked at construction and must cover every boundary: all four task.* types for Kyvvu, and on_chain_start / on_chain_end / on_agent_finish / on_chain_error (LangChain, LangGraph) or the three crew_kickoff_* events (CrewAI). template=None is accepted by Kyvvu for @kv.step and add_step, refused by start_task, and refused by the callback handlers.
  • function_name and function_name_pattern are removed. One name key, name / name_pattern, on every surface. step_name is an output field, never a match key. Templates are validated strictly at load (see the engine).
  • scope: is gone from manifests and policies and policy load failures raise PolicyLoadError. kv.dropped_policies, PolicyStatus.dropped and PolicyLoadState.all_dropped are removed; four load states remain. kyvvu try, doctor and serve exit non-zero on an unloadable set.
  • KV_ENVIRONMENT works through Kyvvu(...) and the default is production. Precedence is the argument, then KV_ENVIRONMENT, then production (the SDK used to default to development). Set KV_ENVIRONMENT=development for local work; the kyvvu init scaffold does. kyvvu try prints the environment it evaluated as.
  • A policy block no longer ends the task on any surface. The decorator and the framework adapters used to close the task on a block. Every surface now records the blocked step, raises KyvvuBlockedError, and leaves the task open; call error_task(), end_task(), or record an approval gate and retry. error_task() on a closed task raises KyvvuConfigError.
  • A decorated step.gate passes unless it raises GateDenied. The decorator template no longer hard-codes guard.result = "pass"; the decorator records pass on a normal return and fail on GateDenied (new, exported from kyvvu, not a KyvvuBlockedError). A gate function that only returns a denial records a pass: deny by raising.
  • Callback handlers own their task. They raise KyvvuConfigError if a task is already active and close only tasks they opened. Do not wrap a handler in kv.start_task().
  • @kv.step on a tool a handler is running raises, because the call would be recorded twice. Decorated helpers and add_step inside a tool are fine.
  • domain_allowlist requires target_step_types; regexes use re.search; risk_classification means "this class or above"; cross_execution_rate_limit is refused at load (engine changes). The bundled decorator template now leaves target.host out when a function has no url argument (| omit_empty).

Added

  • kyvvu template show <name> prints a built-in template (decorator, langchain, langgraph, crewai) from the package data, to copy and edit.
  • An unmatched callback event is recorded as step.unknown.
  • Asynchronous reporting. Incident reports and trace flushes are queued and sent by a worker thread; kv.flush(timeout) and kv.report_status(); settings KV_REPORT_QUEUE_SIZE (1000) and KV_REPORT_EXIT_FLUSH_SECONDS (2). The stdout, file and callback sinks stay inline.
  • A kyvvu[serve] extra. It is not on PyPI until this release is published; until then install pip install kyvvu "kyvvu-engine[serve]".

Changed

  • kyvvu try's closing text follows the outcome of the gate replay and it prints the environment.
  • The CrewAI ImportError names pip install "crewai>=1.15.21,<2"; there is no kyvvu[crewai] extra.

[0.19.0] - 2026-08-27

Breaking. Follows kyvvu-engine 0.10.0 in restoring the task.* action-type namespace. Requires kyvvu-engine>=0.10.0.

Breaking changes

  • The four task-lifecycle step types return to the task.* namespace: step.task_start → task.start, step.task_end → task.end, step.task_error → task.error, step.task_idle → task.idle, with the StepType members renamed to match. All four built-in templates (decorator, crewai, langchain, langgraph) are updated. A custom .btmpl still naming the old spelling must be updated.
  • Task-boundary markers no longer carry an output. start_task, end_task, error_task, the blocked-start path, and _flush_task_error used to record output={"status": ...} on a lifecycle marker. Per the AAG spec a marker carries no input and no output — that absence is what lets a consumer read a step with no output as an intended action rather than waiting for a result that never comes. The status and error text now live under properties.task.
  • A template that does not cover a task-boundary type now raises KyvvuConfigError instead of silently synthesizing a fallback behavior. The fallback cost the caller the template's property extraction and changed the resolved step_name, with no log line — the framework adapters warned on an unmatched context, but the decorator path did not.

Added

  • kv.idle_task(), and a task_idle rule in the decorator template. The engine has always handled task.idle, but the SDK had no way to emit it and the decorator template had no rule for it, so every idle marker fell through to the silent fallback. The task stays open: memory persists across the idle period and the next action signals resumption, so idle_task() does not end the task or clear the active-task context.
  • StepType.task_idle is now in the decorator's task-step set, with both the sync and async handlers wired.

[0.11.0] - 2026-04-28

First tracked release. Establishes the public API surface and the two integration patterns (decorator, LangChain).

Breaking changes from earlier versions

  • Constructor: agent_key and risk_classification are now parameters on Kyvvu(), not register_agent(). Passing them to register_agent() raises TypeError.
  • LangChain handler: KyvvuLangChainHandler is now a pure adapter that accepts an already-constructed Kyvvu instance. The old pattern (KyvvuLangChainHandler(api_key=..., agent_key=...)) no longer works. Use Kyvvu() + register_agent() + KyvvuLangChainHandler(kv).
  • kv.enrich(): Removed as instance/static method. Use the module-level from kyvvu import enrich or inline the dict.
  • capture_output kwarg: Removed from @kv.step(). The task_end decorator now flushes automatically.
  • Task lifecycle: start_task() replaces begin_task(). Calling start_task() twice on the same task without end_task() raises KyvvuConfigError.

Added

  • Kyvvu class with two identity-acquisition paths: constructor (agent_id) or registration (register_agent()).
  • Programmatic task API: start_task(), resume_task(), end_task(), error_task().
  • @kv.step decorator integration via FrameworkAdapter base class.
  • KyvvuLangChainHandler callback handler (pure adapter pattern).
  • YAML behaviour templates with first-match-wins rule evaluation.
  • Built-in templates: decorator.template.yaml, langchain.template.yaml.
  • BehaviorTemplate.from_builtin() and BehaviorTemplate.from_path().
  • KV_TEMPLATE_LOCATION env var for custom templates.
  • CLI: kyvvu auth, kyvvu agents, kyvvu policies, kyvvu config, kyvvu init, kyvvu serve.
  • Exception hierarchy: KyvvuError, KyvvuAPIError, KyvvuBlockedError, KyvvuConfigError, KyvvuKeyRevokedError, KyvvuRateLimitError, KyvvuTimeoutError, KyvvuValidationError.
  • 351 tests across decorator, LangChain, templates, CLI, and core.