diff --git a/CHANGELOG.md b/CHANGELOG.md index 965a968..ab25106 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,10 +6,10 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + and tag v{X.Y.Z}. The release workflow's preflight checks the section + header matches the tag. --> -## [8.0.0] - 2026-05-08 — Decision history API + telemetry simplification +## [8.0.0] - 2026-05-09 — Decision history API + telemetry simplification **Major release.** The headline feature is the new decision-history client API: `list_decisions` for paging through recorded decisions, alongside the @@ -20,88 +20,68 @@ telemetry contract — see `Removed` at the bottom of this entry for that. ### Added - **`client.list_decisions(opts)` method.** Pages over recorded decision - history from the orchestrator, mirroring `GET /api/v1/decisions`. - Companion to the v7.4.0 `get_decision_explain` method — callers can - now both list and drill in. Already shipped on `main` via PR #186 and - graduated into the v8.0 line with this release. See type - `ListDecisionsOptions` and `DecisionListItem` in `axonflow.decisions`. + history from the orchestrator, mirroring `GET /api/v1/decisions`. + Companion to the v7.4.0 `get_decision_explain` method — callers can + now both list and drill in. Already shipped on `main` and + graduated into the v8.0 line with this release. See type + `ListDecisionsOptions` and `DecisionListItem` in `axonflow.decisions`. ### Migration guide (v7 → v8) -- **`AxonFlow(...)` no longer accepts the `telemetry` keyword argument.** - Code passing `AxonFlow(..., telemetry=True)` or - `AxonFlow(..., telemetry=False)` will raise `TypeError` at construction - time. Migration: - - If you were using it to disable telemetry, set - `AXONFLOW_TELEMETRY=off` in the environment instead — that's the - sole opt-out lever as of v8.0. - - If you were using it to force-enable, the default is now ON for - every mode so the argument is no longer needed. +- **`AxonFlow(.)` no longer accepts the `telemetry` keyword argument.** + Code passing `AxonFlow(., telemetry=True)` or + `AxonFlow(., telemetry=False)` will raise `TypeError` at construction + time. Migration: + - If you were using it to disable telemetry, set + `AXONFLOW_TELEMETRY=off` in the environment instead — that's the + sole opt-out lever as of v8.0. + - If you were using it to force-enable, the default is now ON for + every mode so the argument is no longer needed. - **`AxonFlowConfig.telemetry` field removed.** Code that constructed - the dataclass directly with `AxonFlowConfig(..., telemetry=...)` will - fail to instantiate. Drop the field; rely on the env var. - -### Removed - -- **`AxonFlow(..., telemetry=...)` keyword argument** and the - corresponding `AxonFlowConfig.telemetry: bool | None` field. - `AXONFLOW_TELEMETRY=off` is now the sole opt-out path. Tests that - need to defend against contaminated dev environments should clear - the env var explicitly via `monkeypatch.setenv("AXONFLOW_TELEMETRY", "")`. -- **Sandbox-mode silent telemetry suppression.** Sandbox-mode clients - (constructed via `AxonFlow.sandbox()` or `mode=Mode.SANDBOX`) now fire - telemetry on the same heartbeat schedule as production-mode clients. - Pings are tagged `stream="sandbox"` so analytics can distinguish dev - pings from production heartbeat — see the checkpoint-service - `IsValidIncomingStream` allowlist for the wire-side gate. -- **`send_telemetry_ping` signature change.** The internal helper - `axonflow.telemetry.send_telemetry_ping` no longer accepts - `telemetry_enabled` or `has_credentials` parameters; `_is_telemetry_enabled` - takes no arguments. Callers should not have been depending on these - internals (they're underscore-prefixed), but the change is recorded - here for completeness. - -### Telemetry payload (v1 schema, axonflow-enterprise#2008) - -- New heartbeat fields: `telemetry_type: "sdk"`, `deployment_mode` aligned to `self_hosted | community_saas | unknown` via the new `_classify_deployment_mode` (host + `AXONFLOW_TRY=1` override). -- `_classify_endpoint` no longer returns `community-saas` — that value moved off endpoint_type onto deployment_mode; analytics queries on the legacy value must update. + the dataclass directly with `AxonFlowConfig(., telemetry=.)` will + fail to instantiate. Drop the field; rely on the env var. + +### Telemetry + +- **`AXONFLOW_TELEMETRY=off` is the sole opt-out.** `AxonFlow(..., telemetry=...)` keyword argument + `AxonFlowConfig.telemetry` field both removed; sandbox-mode clients now fire on the same 7-day heartbeat schedule as production (was suppressed pre-v8), tagged `stream="sandbox"` so dev pings stay distinguishable. +- **Heartbeat payload v1 schema additions** on the wire: new `telemetry_type` and `deployment_mode` fields. Existing receivers continue working unchanged — strictly additive. ## [7.1.0] - 2026-05-06 — X-Axonflow-Client header + scope-aware license validation **Companion release to platform v7.7.0.** The Python SDK now sends an `X-Axonflow-Client` identification header on every governed request, which the agent uses to derive the SDK request scope and validate it against any -license token's audience claim per the ADR-050 license matrix. +license token's audience claim per the license matrix. ### Added - **`X-Axonflow-Client: sdk-python/` header** on every governed - outbound request. Set automatically by the SDK transport; not - configurable. Agents at v7.7.0+ derive request scope from this header - and reject cross-quadrant token misuse (e.g. a SaaS Plugin Pro token - paired with an SDK request) at the validator boundary. Older agents - (pre-v7.7.0) ignore the header and continue to work unchanged. + outbound request. Set automatically by the SDK transport; not + configurable. Agents at v7.7.0+ derive request scope from this header + and reject cross-quadrant token misuse (e.g. a SaaS Plugin Pro token + paired with an SDK request) at the validator boundary. Older agents + (pre-v7.7.0) ignore the header and continue to work unchanged. ### Compatibility - **No public API changes.** Existing v7.0.x callers `pip install - --upgrade axonflow` and rebuild against v7.1.0 with no source changes. + --upgrade axonflow` and rebuild against v7.1.0 with no source changes. - **Backward-compatible against pre-v7.7.0 agents.** The header is - silently dropped by older agents; the SDK behaves identically against - v7.0.x / v7.1.x / v7.6.x agents as before. + silently dropped by older agents; the SDK behaves identically against + v7.0.x / v7.1.x / v7.6.x agents as before. - **Forward-compatible.** Future agent releases that require the header - on specific governed surfaces will work with this SDK without further - client changes. + on specific governed surfaces will work with this SDK without further + client changes. ### Companion releases (same day) - **Platform v7.7.0** — V1 SaaS Plugin Pro launch, license matrix, - per-tenant tier resolution, GDPR right-to-erasure - ([CHANGELOG](https://github.com/getaxonflow/axonflow/blob/main/CHANGELOG.md)) + per-tenant tier resolution, GDPR right-to-erasure + ([CHANGELOG](https://github.com/getaxonflow/axonflow/blob/main/CHANGELOG.md)) - **Go SDK v7.1.0** / **TypeScript SDK v7.1.0** / - **Java SDK v7.1.0** — same `X-Axonflow-Client` injection + **Java SDK v7.1.0** — same `X-Axonflow-Client` injection - **Plugins** — Claude Code / Cursor / Codex v1.2.0; OpenClaw v2.2.0 - with Pro license token paste activating Pro features + with Pro license token paste activating Pro features axonflow-sdk-rust remains at v0.1.0 (preview); SDK-Rust will gain the header in a future preview release. @@ -129,7 +109,7 @@ Major release across the AxonFlow SDK family. Companion releases ship the same d ### Changed - **Telemetry switched to a 7-day delivered-heartbeat.** At most one anonymous ping per environment every 7 days, with the stamp advanced only after the POST returns 2xx — a transient network failure doesn't silence telemetry until the next window. Concurrent threads are de-duplicated by an in-flight gate. Restricted environments where no cache dir is available (e.g. AWS Lambda) fall back transparently to the previous "one ping per process" behavior. -- `StaticPolicy` and `PolicyVersion` now serialize wire fields in snake_case to match the OpenAPI spec (`created_at`, `updated_at`, `organization_id`, `tenant_id`, `has_override`, `changed_at`, `changed_by`, `change_type`). camelCase aliases remain accepted on input via `validation_alias=AliasChoices(...)`. **Round-trip identity is no longer preserved** for callers that built these models from camelCase dicts — code that signs, hashes, or byte-compares serialized model bodies will see a one-time shape change. +- `StaticPolicy` and `PolicyVersion` now serialize wire fields in snake_case to match the OpenAPI spec (`created_at`, `updated_at`, `organization_id`, `tenant_id`, `has_override`, `changed_at`, `changed_by`, `change_type`). camelCase aliases remain accepted on input via `validation_alias=AliasChoices(.)`. **Round-trip identity is no longer preserved** for callers that built these models from camelCase dicts — code that signs, hashes, or byte-compares serialized model bodies will see a one-time shape change. ### Added @@ -137,7 +117,7 @@ Major release across the AxonFlow SDK family. Companion releases ship the same d ### Fixed -- The `DO_NOT_TRACK=1 is deprecated...` `logger.warning` is no longer emitted on every client construction when `DO_NOT_TRACK=1` is set. +- The `DO_NOT_TRACK=1 is deprecated.` `logger.warning` is no longer emitted on every client construction when `DO_NOT_TRACK=1` is set. ## [6.9.0] - 2026-04-28 — list_providers() + LLMProvider full shape @@ -167,15 +147,15 @@ Coordinated cycle: TypeScript v6.1.0 / Go v5.8.0 / Java v6.1.0 ship same day wit ### Added - **`MCPCheckInputResponse`** gains 5 optional Plugin Batch 1 fields: - - `decision_id: str | None` — audit correlator - - `risk_level: Literal["low", "medium", "high", "critical"] | None` - - `policy_matches: list[ExplainPolicy] | None` — per-policy explainability records - - `override_available: bool | None` — whether session override is permitted for the matched policies - - `override_existing_id: str | None` — already-active override consumed by this decision (if any) + - `decision_id: str | None` — audit correlator + - `risk_level: Literal["low", "medium", "high", "critical"] | None` + - `policy_matches: list[ExplainPolicy] | None` — per-policy explainability records + - `override_available: bool | None` — whether session override is permitted for the matched policies + - `override_existing_id: str | None` — already-active override consumed by this decision (if any) - **`MCPCheckOutputResponse`** gains 3 optional fields: - - `decision_id` - - `policy_matches: list[ExplainPolicy] | None` - - `redacted_message: str | None` — text-redaction counterpart to `redacted_data` (used when the connector returned a string message rather than tabular rows; e.g. execute-style responses) + - `decision_id` + - `policy_matches: list[ExplainPolicy] | None` + - `redacted_message: str | None` — text-redaction counterpart to `redacted_data` (used when the connector returned a string message rather than tabular rows; e.g. execute-style responses) - **`ExplainPolicy`** is now re-exported from `axonflow.types` (it was previously only in `axonflow.decisions`). Same Pydantic model — Python's snake_case convention naturally aligns wire-shape and SDK types, so no separate model is needed. All fields default to `None`. Pre-v7.1.0 platforms return `None` for every field; callers should treat absence as "context not available" rather than an error. @@ -227,40 +207,40 @@ Two platform-side spec corrections filed alongside this work, for issues the aud ### Fixed - **Runtime `axonflow.__version__` correctly reports the installed version.** - Wheels for v6.6.1 shipped with `axonflow.__version__` stuck at `"6.6.0"` - because the release workflow was targeting the wrong file when bumping - the version constant (it was rewriting `axonflow/__init__.py`, but the - real constant lives in `axonflow/_version.py` and `__init__.py` only - re-exports from there). Package metadata read by `pip show` was correct, - so install/upgrade worked fine; the drift only affected code that read - `axonflow.__version__` at runtime (telemetry self-identification, - version-gated feature detection in user code, log output). No functional - changes — this release ships the same binary behavior as v6.6.1 with - the runtime version correctly set to `6.6.2`. + Wheels for v6.6.1 shipped with `axonflow.__version__` stuck at `"6.6.0"` + because the release workflow was targeting the wrong file when bumping + the version constant (it was rewriting `axonflow/__init__.py`, but the + real constant lives in `axonflow/_version.py` and `__init__.py` only + re-exports from there). Package metadata read by `pip show` was correct, + so install/upgrade worked fine; the drift only affected code that read + `axonflow.__version__` at runtime (telemetry self-identification, + version-gated feature detection in user code, log output). No functional + changes — this release ships the same binary behavior as v6.6.1 with + the runtime version correctly set to `6.6.2`. ## [6.6.1] - 2026-04-24 ### Fixed - **Retire dead staging endpoint across the SDK.** The decommissioned - `staging-eu.getaxonflow.com` host was still referenced in 11 places, - including the public `AxonFlow.sandbox()` factory, every example's default - endpoint, the fixture-recording script, `SECURITY.md`, and `CONTRIBUTING.md`. - Callers hitting any of these defaults would silently connect to a dead host. - - `AxonFlow.sandbox()` now targets a local community docker-compose stack - at `http://localhost:8080` with `demo-client` / `demo-secret` credentials. - Docstring updated to point users at the hosted registration flow - (`POST /api/v1/register` + `AXONFLOW_TRY=1`) for the live community SaaS. - - `examples/quickstart.py`, `examples/gateway_mode.py`, - `examples/openai_integration.py`, and `scripts/record_fixtures.py` now - default `AXONFLOW_AGENT_URL` to `http://localhost:8080`. - - `SECURITY.md` "credentials in code" example uses the neutral - `https://axonflow.example.com` placeholder. - - `CONTRIBUTING.md` "Running Examples" section now documents the local - docker-compose setup, replaces the unused `AXONFLOW_LICENSE_KEY` - reference with the correct `AXONFLOW_CLIENT_ID` / `AXONFLOW_CLIENT_SECRET` - variables, and lists the four example files that actually exist - (previously referenced stale `basic_usage.py` and `interceptors.py`). + `staging-eu.getaxonflow.com` host was still referenced in 11 places, + including the public `AxonFlow.sandbox()` factory, every example's default + endpoint, the fixture-recording script, `SECURITY.md`, and `CONTRIBUTING.md`. + Callers hitting any of these defaults would silently connect to a dead host. + - `AxonFlow.sandbox()` now targets a local community docker-compose stack + at `http://localhost:8080` with `demo-client` / `demo-secret` credentials. + Docstring updated to point users at the hosted registration flow + (`POST /api/v1/register` + `AXONFLOW_TRY=1`) for the live community SaaS. + - `examples/quickstart.py`, `examples/gateway_mode.py`, + `examples/openai_integration.py`, and `scripts/record_fixtures.py` now + default `AXONFLOW_AGENT_URL` to `http://localhost:8080`. + - `SECURITY.md` "credentials in code" example uses the neutral + `https://axonflow.example.com` placeholder. + - `CONTRIBUTING.md` "Running Examples" section now documents the local + docker-compose setup, replaces the unused `AXONFLOW_LICENSE_KEY` + reference with the correct `AXONFLOW_CLIENT_ID` / `AXONFLOW_CLIENT_SECRET` + variables, and lists the four example files that actually exist + (previously referenced stale `basic_usage.py` and `interceptors.py`). - Telemetry pings now deliver reliably from short-lived processes (CLI, serverless, cold-starts). - Telemetry path is bounded at `_TIMEOUT_SECONDS` (3s) total; the `/health` probe and checkpoint POST share a single deadline instead of stacking. @@ -269,43 +249,43 @@ Two platform-side spec corrections filed alongside this work, for issues the aud ### Added - **Rich `ApproveStepResponse` / `RejectStepResponse`** — both pydantic models - now carry the same shape as the step-gate response: `decision` resolves to - `"allow"` / `"block"`, `retry_context` mirrors the gate response retry state, - `approved_by` / `approved_at` / `rejected_by` / `rejected_at` carry reviewer - identity, `approval_id` is the deterministic HITL queue UUID, and - `policies_matched` reconstructs the governance trail. Legacy fields - (`workflow_id`, `step_id`, `status`) remain for back-compat; every new field - is optional so older server responses still deserialize cleanly. + now carry the same shape as the step-gate response: `decision` resolves to + `"allow"` / `"block"`, `retry_context` mirrors the gate response retry state, + `approved_by` / `approved_at` / `rejected_by` / `rejected_at` carry reviewer + identity, `approval_id` is the deterministic HITL queue UUID, and + `policies_matched` reconstructs the governance trail. Legacy fields + (`workflow_id`, `step_id`, `status`) remain for back-compat; every new field + is optional so older server responses still deserialize cleanly. - **`plan_id` on approve/reject responses** — populated when the response - comes from the MAP plan-scoped endpoint; empty on WCP plane responses. - Same models work across both endpoints. + comes from the MAP plan-scoped endpoint; empty on WCP plane responses. + Same models work across both endpoints. - **`get_pending_plan_approvals`** — new client method that lists MAP-plane - pending approvals (`GET /api/v1/plans/approvals/pending`), the counterpart - of `get_pending_approvals` for the WCP plane. Accepts an optional - `plan_id` argument so reviewer tools can scope the listing to one plan. - Available on Evaluation+ licenses (same tier gate as the MAP step - approve/reject endpoints). Sync wrapper exposed via - `SyncAxonFlow.get_pending_plan_approvals`. + pending approvals (`GET /api/v1/plans/approvals/pending`), the counterpart + of `get_pending_approvals` for the WCP plane. Accepts an optional + `plan_id` argument so reviewer tools can scope the listing to one plan. + Available on Evaluation+ licenses (same tier gate as the MAP step + approve/reject endpoints). Sync wrapper exposed via + `SyncAxonFlow.get_pending_plan_approvals`. - **`PendingApproval.plan_id`** — populated on MAP-plane entries, `None` on - WCP-plane entries. Mirrors the approve/reject asymmetry. `PendingApproval` - also gains `step_index`, `decision`, `decision_reason`, `policies_matched`, - `step_input`, and `approval_status` so reviewer tools can render the full - approval context without a second request. + WCP-plane entries. Mirrors the approve/reject asymmetry. `PendingApproval` + also gains `step_index`, `decision`, `decision_reason`, `policies_matched`, + `step_input`, and `approval_status` so reviewer tools can render the full + approval context without a second request. ### Fixed - **`approve_step` / `reject_step` / `get_pending_approvals` endpoint URLs** — - all three previously targeted non-existent paths under - `/api/v1/workflow-control/` and would fail against a real AxonFlow server. - Corrected to the canonical `/api/v1/workflows/{id}/steps/{step_id}/(approve|reject)` - and `/api/v1/workflows/approvals/pending` routes. Customers using these - methods against a live deployment were receiving 404s; this release makes - them work. + all three previously targeted non-existent paths under + `/api/v1/workflow-control/` and would fail against a real AxonFlow server. + Corrected to the canonical `/api/v1/workflows/{id}/steps/{step_id}/(approve|reject)` + and `/api/v1/workflows/approvals/pending` routes. Customers using these + methods against a live deployment were receiving 404s; this release makes + them work. - **`PendingApprovalsResponse` field names aligned with the wire shape** — - the model previously declared `approvals` and `total`, which never matched - the server response (`pending_approvals` and `count`). Renamed fields. - Callers that read `response.approvals` or `response.total` must update to - `response.pending_approvals` / `response.count`. + the model previously declared `approvals` and `total`, which never matched + the server response (`pending_approvals` and `count`). Renamed fields. + Callers that read `response.approvals` or `response.total` must update to + `response.pending_approvals` / `response.count`. ### Deprecated @@ -314,42 +294,42 @@ Two platform-side spec corrections filed alongside this work, for issues the aud ### Unchanged - `approve_step(workflow_id, step_id)` / `reject_step(workflow_id, step_id, reason)` - method signatures are unchanged — only the response fields grew. + method signatures are unchanged — only the response fields grew. ## [6.5.0] - 2026-04-21 ### Added - **`retry_context` and `idempotency_key` support on the step gate** — - `StepGateResponse` now carries a `retry_context` object on every gate call with the - true `(workflow_id, step_id)` lifecycle: `gate_count`, `completion_count`, - `prior_completion_status` (`PriorCompletionStatus` enum — - `NONE` / `COMPLETED` / `GATED_NOT_COMPLETED`), `prior_output_available`, - `prior_output`, `prior_completion_at`, `first_attempt_at`, `last_attempt_at`, - `last_decision`, and `idempotency_key`. Prefer these fields to the legacy - `cached` / `decision_source` fields. -- **`client.step_gate(..., include_prior_output=False)`** — new keyword-only argument. - When `True`, the SDK sends `?include_prior_output=true` on the gate call and - `retry_context.prior_output` is populated when a prior `/complete` has landed. - Existing callers that omit the kwarg behave unchanged. + `StepGateResponse` now carries a `retry_context` object on every gate call with the + true `(workflow_id, step_id)` lifecycle: `gate_count`, `completion_count`, + `prior_completion_status` (`PriorCompletionStatus` enum — + `NONE` / `COMPLETED` / `GATED_NOT_COMPLETED`), `prior_output_available`, + `prior_output`, `prior_completion_at`, `first_attempt_at`, `last_attempt_at`, + `last_decision`, and `idempotency_key`. Prefer these fields to the legacy + `cached` / `decision_source` fields. +- **`client.step_gate(., include_prior_output=False)`** — new keyword-only argument. + When `True`, the SDK sends `?include_prior_output=true` on the gate call and + `retry_context.prior_output` is populated when a prior `/complete` has landed. + Existing callers that omit the kwarg behave unchanged. - **`StepGateRequest.idempotency_key`** — caller-supplied opaque business-level key - (max 255 chars). Immutable once recorded on the first gate call for a - `(workflow_id, step_id)`; subsequent gate/complete calls must pass the same key. + (max 255 chars). Immutable once recorded on the first gate call for a + `(workflow_id, step_id)`; subsequent gate/complete calls must pass the same key. - **`MarkStepCompletedRequest.idempotency_key`** — must match the key set on the - corresponding gate call, if any. Mismatch (including missing-vs-set on either side) - surfaces as a typed `IdempotencyKeyMismatchError`. + corresponding gate call, if any. Mismatch (including missing-vs-set on either side) + surfaces as a typed `IdempotencyKeyMismatchError`. - **`IdempotencyKeyMismatchError`** — typed exception raised by `step_gate` and - `mark_step_completed` when the platform returns HTTP 409 with - `error.code == "IDEMPOTENCY_KEY_MISMATCH"`. Surfaces `workflow_id`, `step_id`, - `expected_idempotency_key`, `received_idempotency_key`, and the human-readable `message`. - Exported from `axonflow` top-level. + `mark_step_completed` when the platform returns HTTP 409 with + `error.code == "IDEMPOTENCY_KEY_MISMATCH"`. Surfaces `workflow_id`, `step_id`, + `expected_idempotency_key`, `received_idempotency_key`, and the human-readable `message`. + Exported from `axonflow` top-level. - **`RetryContext`, `PriorCompletionStatus`** — exported pydantic model + enum. ### Deprecated - **`StepGateResponse.cached`** and **`StepGateResponse.decision_source`** — still - populated but deprecated in favor of `retry_context.gate_count > 1` and - `retry_context.prior_completion_status`. Planned for removal in a future major version. + populated but deprecated in favor of `retry_context.gate_count > 1` and + `retry_context.prior_completion_status`. Planned for removal in a future major version. ### Compatibility @@ -362,34 +342,34 @@ callers that never set `idempotency_key` or `include_prior_output` see no behavi ### Added - **Execution boundary semantics** — `RetryPolicy` enum with `IDEMPOTENT` - (default) and `REEVALUATE` values. Step gate requests accept `retry_policy` - to control cached vs fresh evaluation behavior. + (default) and `REEVALUATE` values. Step gate requests accept `retry_policy` + to control cached vs fresh evaluation behavior. - **Step gate response metadata** — `cached` (bool) and `decision_source` - (str) fields on `StepGateResponse` indicate decision provenance. + (str) fields on `StepGateResponse` indicate decision provenance. - **Workflow checkpoints** — `get_checkpoints(workflow_id)` lists step-gate - checkpoints. `resume_from_checkpoint(workflow_id, checkpoint_id)` resumes - from a specific checkpoint with fresh policy evaluation (Enterprise). + checkpoints. `resume_from_checkpoint(workflow_id, checkpoint_id)` resumes + from a specific checkpoint with fresh policy evaluation (Enterprise). - **Checkpoint types** — `Checkpoint`, `CheckpointListResponse`, and - `ResumeFromCheckpointResponse` models. + `ResumeFromCheckpointResponse` models. - **`AxonFlow.explain_decision(decision_id)`** — fetches the full explanation for a - previously-made policy decision via `GET /api/v1/decisions/:id/explain`. - Returns a `DecisionExplanation` with matched policies, risk level, reason, - override availability, existing override ID (if any), and a rolling-24h - session hit count for the matched rule. Shape is frozen; additive-only - fields ensure forward compatibility. + previously-made policy decision via `GET /api/v1/decisions/:id/explain`. + Returns a `DecisionExplanation` with matched policies, risk level, reason, + override availability, existing override ID (if any), and a rolling-24h + session hit count for the matched rule. Shape is frozen; additive-only + fields ensure forward compatibility. - **`DecisionExplanation`, `ExplainPolicy`, `ExplainRule`** — new Pydantic - models exported from `axonflow.decisions`. + models exported from `axonflow.decisions`. - **`AuditSearchRequest.decision_id`, `policy_name`, `override_id`** — three - new optional filter fields on `search_audit_logs`. Use `decision_id` to - gather every record tied to one decision; `policy_name` to find everything - matched by a specific policy; `override_id` to reconstruct an override's - full lifecycle. + new optional filter fields on `search_audit_logs`. Use `decision_id` to + gather every record tied to one decision; `policy_name` to find everything + matched by a specific policy; `override_id` to reconstruct an override's + full lifecycle. ### Fixed - `step_gate()` now correctly passes `retry_policy` in the request body - and populates `cached`/`decision_source` in the response. Previously - these fields were defined on the model but not wired through the client. + and populates `cached`/`decision_source` in the response. Previously + these fields were defined on the model but not wired through the client. ### Compatibility @@ -485,7 +465,7 @@ behavior. - `simulate_policies()` — dry-run all active policies against an input query. Returns allowed/blocked status, applied policies, risk score, and daily usage. Requires Evaluation tier or above. - `get_policy_impact_report()` — test a single policy against multiple inputs and get aggregate match/block statistics. - `detect_policy_conflicts()` — analyze active policies for contradictions, shadows, and redundancies. Optionally filter to conflicts involving a specific policy. -- `AxonFlowLangGraphAdapter.tool_output_wrapper()` — returns an async wrapper for LangGraph `ToolNode(awrap_tool_call=...)` that enforces input and output policy checks on local `@tool` functions. Fixes a gap where locally defined tools bypassed `mcp_tool_interceptor` policy enforcement. +- `AxonFlowLangGraphAdapter.tool_output_wrapper()` — returns an async wrapper for LangGraph `ToolNode(awrap_tool_call=.)` that enforces input and output policy checks on local `@tool` functions. Fixes a gap where locally defined tools bypassed `mcp_tool_interceptor` policy enforcement. - Types: `SimulatePoliciesRequest`, `SimulatePoliciesResponse`, `SimulationDailyUsage`, `ImpactReportInput`, `ImpactReportRequest`, `ImpactReportResult`, `ImpactReportResponse`, `PolicyConflictRef`, `PolicyConflict`, `PolicyConflictResponse` - `wrap_langgraph()` — 1-line wrapper for compiled LangGraph StateGraphs. Transparently enforces AxonFlow governance at every node transition without modifying the graph definition. Uses langchain-core's `AsyncCallbackHandler` to intercept node execution via `metadata["langgraph_node"]`. - `GovernedGraph` class — returned by `wrap_langgraph()`, exposes `ainvoke()`, `invoke()`, `astream()`. Each invocation creates a new AxonFlow workflow. Reusable across multiple invocations. @@ -550,18 +530,18 @@ behavior. ### Breaking Changes - **Removed `total_steps` from `CreateWorkflowRequest`**. Requires Platform v4.5.0+ (recommended v5.0.0+). - Total steps are auto-computed when the workflow reaches a terminal state. + Total steps are auto-computed when the workflow reaches a terminal state. - **`mcp_check_input()` default `operation` changed from `"query"` to `"execute"`**. Callers relying on - the implicit `"query"` default must now pass `operation="query"` explicitly. This better reflects the - default MCP tool call pattern where side effects are unknown. + the implicit `"query"` default must now pass `operation="query"` explicitly. This better reflects the + default MCP tool call pattern where side effects are unknown. ### Added -- **`AxonFlowLangGraphAdapter.mcp_tool_interceptor()`**: Factory method returning an async callable ready for use with `MultiServerMCPClient(tool_interceptors=[...])`. Enforces AxonFlow input and output policies around every MCP tool call: `mcp_check_input → handler() → mcp_check_output`. Handles policy blocks and returns redacted output when `mcp_check_output` applies redaction. - - **`MCPInterceptorOptions`**: Configuration dataclass accepted by `mcp_tool_interceptor()` with two fields: - - `connector_type_fn`: Optional callable to override the default `"{server_name}.{tool_name}"` connector type mapping - - `operation`: Operation type forwarded to `mcp_check_input` (default: `"execute"`; use `"query"` for known read-only tool calls) - - `MCPInterceptorOptions` and `WorkflowApprovalRequiredError` are now exported from `axonflow.adapters` +- **`AxonFlowLangGraphAdapter.mcp_tool_interceptor()`**: Factory method returning an async callable ready for use with `MultiServerMCPClient(tool_interceptors=[.])`. Enforces AxonFlow input and output policies around every MCP tool call: `mcp_check_input → handler() → mcp_check_output`. Handles policy blocks and returns redacted output when `mcp_check_output` applies redaction. + - **`MCPInterceptorOptions`**: Configuration dataclass accepted by `mcp_tool_interceptor()` with two fields: + - `connector_type_fn`: Optional callable to override the default `"{server_name}.{tool_name}"` connector type mapping + - `operation`: Operation type forwarded to `mcp_check_input` (default: `"execute"`; use `"query"` for known read-only tool calls) + - `MCPInterceptorOptions` and `WorkflowApprovalRequiredError` are now exported from `axonflow.adapters` ### Fixed @@ -604,11 +584,11 @@ in v3.5.0. This major version formally acknowledges that breaking change. ### Added - **MCP Policy-Check Endpoints** (Platform v4.6.0+): Standalone policy validation for external orchestrators (LangGraph, CrewAI) to enforce AxonFlow policies without executing connector queries - - `mcp_check_input(connector_type, statement)`: Validate SQL/commands against input policies (SQLi detection, dangerous query blocking, PII in queries, dynamic policies). Returns `allowed=True` or raises with `block_reason` - - `mcp_check_output(connector_type, response_data)`: Validate MCP response data against output policies (PII redaction, exfiltration limits, dynamic policies). Returns original or redacted data with `policy_info` - - New types: `MCPCheckInputRequest`, `MCPCheckInputResponse`, `MCPCheckOutputRequest`, `MCPCheckOutputResponse` - - Async methods with sync wrappers (`mcp_check_input_sync`, `mcp_check_output_sync`) - - Supports both query-style (`response_data`) and execute-style (`message` + `metadata`) output validation + - `mcp_check_input(connector_type, statement)`: Validate SQL/commands against input policies (SQLi detection, dangerous query blocking, PII in queries, dynamic policies). Returns `allowed=True` or raises with `block_reason` + - `mcp_check_output(connector_type, response_data)`: Validate MCP response data against output policies (PII redaction, exfiltration limits, dynamic policies). Returns original or redacted data with `policy_info` + - New types: `MCPCheckInputRequest`, `MCPCheckInputResponse`, `MCPCheckOutputRequest`, `MCPCheckOutputResponse` + - Async methods with sync wrappers (`mcp_check_input_sync`, `mcp_check_output_sync`) + - Supports both query-style (`response_data`) and execute-style (`message` + `metadata`) output validation --- @@ -640,16 +620,16 @@ in v3.5.0. This major version formally acknowledges that breaking change. ### Added -- **fail_workflow()** (#1187): Fail a workflow with optional reason - - `async fail_workflow(workflow_id, reason=None)` + sync wrapper - - Sends `POST /api/v1/workflows/{id}/fail` +- **fail_workflow()**: Fail a workflow with optional reason + - `async fail_workflow(workflow_id, reason=None)` + sync wrapper + - Sends `POST /api/v1/workflows/{id}/fail` - **HITL Queue API** (Enterprise): Human-in-the-loop approval queue management - - `list_hitl_queue(opts)`: list pending approvals with filtering - - `get_hitl_request(request_id)`: get approval details - - `approve_hitl_request(request_id, review)`: approve a request - - `reject_hitl_request(request_id, review)`: reject a request - - `get_hitl_stats()`: dashboard statistics - - New models: `HITLApprovalRequest`, `HITLQueueListOptions`, `HITLQueueListResponse`, `HITLReviewInput`, `HITLStats` + - `list_hitl_queue(opts)`: list pending approvals with filtering + - `get_hitl_request(request_id)`: get approval details + - `approve_hitl_request(request_id, review)`: approve a request + - `reject_hitl_request(request_id, review)`: reject a request + - `get_hitl_stats()`: dashboard statistics + - New models: `HITLApprovalRequest`, `HITLQueueListOptions`, `HITLQueueListResponse`, `HITLReviewInput`, `HITLStats` ### Fixed @@ -665,39 +645,39 @@ in v3.5.0. This major version formally acknowledges that breaking change. ### Added -- **WCP Approval Gates** (Issue #1169): HITL approval and rejection for workflow steps - - `approve_step(workflow_id, step_id)` - Approve a pending workflow step - - `reject_step(workflow_id, step_id, reason=None)` - Reject a step with optional reason - - `get_pending_approvals(limit=20)` - List steps awaiting human approval +- **WCP Approval Gates**: HITL approval and rejection for workflow steps + - `approve_step(workflow_id, step_id)` - Approve a pending workflow step + - `reject_step(workflow_id, step_id, reason=None)` - Reject a step with optional reason + - `get_pending_approvals(limit=20)` - List steps awaiting human approval -- **MAP Plan Cancellation** (Issue #1072): Cancel running multi-agent plans - - `cancel_plan(plan_id, reason=None)` - Cancel a plan with optional reason +- **MAP Plan Cancellation**: Cancel running multi-agent plans + - `cancel_plan(plan_id, reason=None)` - Cancel a plan with optional reason -- **MAP Plan Update** (Issue #1072): Modify plan configuration before or during execution - - `update_plan(plan_id, **kwargs)` - Update execution mode, domain, or version +- **MAP Plan Update**: Modify plan configuration before or during execution + - `update_plan(plan_id, **kwargs)` - Update execution mode, domain, or version -- **MAP Plan Versioning and Rollback** (Issue #1072): Version history and rollback support - - `get_plan_versions(plan_id)` - List plan version history - - `rollback_plan(plan_id, version)` - Rollback to a previous version (raises on 409 conflict) - - New types in response: `RollbackPlanResponse`, `PlanVersion` +- **MAP Plan Versioning and Rollback**: Version history and rollback support + - `get_plan_versions(plan_id)` - List plan version history + - `rollback_plan(plan_id, version)` - Rollback to a previous version (raises on 409 conflict) + - New types in response: `RollbackPlanResponse`, `PlanVersion` -- **Webhook Subscriptions** (Issue #1169): Event notification management - - `create_webhook(url, events, **kwargs)` - Create a webhook subscription - - `list_webhooks()` - List active webhook subscriptions - - `get_webhook(webhook_id)` - Get webhook details - - `update_webhook(webhook_id, *, url=None, events=None, secret=None, active=None, description=None)` - Update webhook with typed parameters - - `delete_webhook(webhook_id)` - Delete a webhook subscription - - Available on both `AxonFlow` (async) and `SyncAxonFlow` (sync) clients +- **Webhook Subscriptions**: Event notification management + - `create_webhook(url, events, **kwargs)` - Create a webhook subscription + - `list_webhooks()` - List active webhook subscriptions + - `get_webhook(webhook_id)` - Get webhook details + - `update_webhook(webhook_id, *, url=None, events=None, secret=None, active=None, description=None)` - Update webhook with typed parameters + - `delete_webhook(webhook_id)` - Delete a webhook subscription + - Available on both `AxonFlow` (async) and `SyncAxonFlow` (sync) clients -- **Unified Execution Cancellation** (EPIC #1074): Cancel running executions across both MAP and WCP subsystems - - `cancel_execution(execution_id, reason=None)` - Cancel a unified execution via `POST /api/v1/unified/executions/{id}/cancel` - - Available on both `AxonFlow` (async) and `SyncAxonFlow` (sync) clients - - Propagates to MAP `cancel_plan()` or WCP `abort_workflow()` based on execution type +- **Unified Execution Cancellation**: Cancel running executions across both MAP and WCP subsystems + - `cancel_execution(execution_id, reason=None)` - Cancel a unified execution via `POST /api/v1/unified/executions/{id}/cancel` + - Available on both `AxonFlow` (async) and `SyncAxonFlow` (sync) clients + - Propagates to MAP `cancel_plan()` or WCP `abort_workflow()` based on execution type ### Fixed - **`execute_plan` status hardcoded**: `execute_plan()` always returned `status="completed"` regardless of actual server response. Now reads status from response (`data.status` > `metadata.status` > default), correctly surfacing `awaiting_approval` for WCP confirm mode. -- **Unified execution API URLs** (EPIC #1074): `get_execution_status()` and `list_unified_executions()` now use correct `/api/v1/unified/executions` path (was incorrectly pointing to `/api/v1/executions` which is the Execution Replay API) +- **Unified execution API URLs**: `get_execution_status()` and `list_unified_executions()` now use correct `/api/v1/unified/executions` path (was incorrectly pointing to `/api/v1/executions` which is the Execution Replay API) - **`update_webhook` typed parameters**: Replaced `**kwargs` with explicit keyword-only arguments for type safety --- @@ -757,42 +737,42 @@ in v3.5.0. This major version formally acknowledges that breaking change. ### Added -- **Unified Execution Tracking** (Issue #1075 - EPIC #1074): Consistent status tracking for MAP plans and WCP workflows - - `get_execution_status(execution_id)` - Get unified execution status by ID - - `list_unified_executions(options)` - List executions with type/status filters - - `ExecutionStatus` Pydantic model with unified fields for both MAP and WCP executions - - `ExecutionType` enum: `MAP_PLAN`, `WCP_WORKFLOW` - - `ExecutionStatusValue` enum: `PENDING`, `RUNNING`, `COMPLETED`, `FAILED`, `CANCELLED`, `ABORTED`, `EXPIRED` - - `StepStatusValue` enum: `PENDING`, `RUNNING`, `COMPLETED`, `FAILED`, `SKIPPED`, `BLOCKED`, `APPROVAL` - - `UnifiedStepType` enum: `LLM_CALL`, `TOOL_CALL`, `CONNECTOR_CALL`, `HUMAN_TASK`, `SYNTHESIS`, `ACTION`, `GATE` - - `UnifiedStepStatus` model with step-level details (duration, cost, policy decisions) - - Helper methods on `ExecutionStatus`: `is_terminal()`, `get_current_step()`, `calculate_total_cost()` - - Consistent response format across MAP Multi-Agent Planning and WCP Workflow Control Plane +- **Unified Execution Tracking**: Consistent status tracking for MAP plans and WCP workflows + - `get_execution_status(execution_id)` - Get unified execution status by ID + - `list_unified_executions(options)` - List executions with type/status filters + - `ExecutionStatus` Pydantic model with unified fields for both MAP and WCP executions + - `ExecutionType` enum: `MAP_PLAN`, `WCP_WORKFLOW` + - `ExecutionStatusValue` enum: `PENDING`, `RUNNING`, `COMPLETED`, `FAILED`, `CANCELLED`, `ABORTED`, `EXPIRED` + - `StepStatusValue` enum: `PENDING`, `RUNNING`, `COMPLETED`, `FAILED`, `SKIPPED`, `BLOCKED`, `APPROVAL` + - `UnifiedStepType` enum: `LLM_CALL`, `TOOL_CALL`, `CONNECTOR_CALL`, `HUMAN_TASK`, `SYNTHESIS`, `ACTION`, `GATE` + - `UnifiedStepStatus` model with step-level details (duration, cost, policy decisions) + - Helper methods on `ExecutionStatus`: `is_terminal()`, `get_current_step()`, `calculate_total_cost()` + - Consistent response format across MAP Multi-Agent Planning and WCP Workflow Control Plane - **MAS FEAT Compliance Module** (Enterprise): Singapore financial services AI governance - - AI System Registry: `masfeat.register_system()`, `masfeat.get_system()`, `masfeat.update_system()`, `masfeat.list_systems()`, `masfeat.activate_system()`, `masfeat.retire_system()`, `masfeat.get_registry_summary()` - - 3-Dimensional Risk Rating: Customer Impact × Model Complexity × Human Reliance - - Materiality Classification: High (sum≥12), Medium (sum≥8), Low (sum<8) - - FEAT Assessments: `masfeat.create_assessment()`, `masfeat.get_assessment()`, `masfeat.update_assessment()`, `masfeat.list_assessments()`, `masfeat.submit_assessment()`, `masfeat.approve_assessment()`, `masfeat.reject_assessment()` - - Assessment Lifecycle: pending → in_progress → completed → approved/rejected - - Kill Switch: `masfeat.get_kill_switch()`, `masfeat.configure_kill_switch()`, `masfeat.check_kill_switch()`, `masfeat.trigger_kill_switch()`, `masfeat.restore_kill_switch()`, `masfeat.enable_kill_switch()`, `masfeat.disable_kill_switch()`, `masfeat.get_kill_switch_history()` - - Automatic model shutdown based on accuracy, bias, and error rate thresholds - - New namespace property: `client.masfeat` (async) and `client.masfeat` (sync via `AxonFlow.sync()`) - - New types: `AISystemRegistry`, `AISystemUseCase`, `MaterialityClassification`, `SystemStatus`, `FEATAssessment`, `FEATAssessmentStatus`, `FEATPillar`, `KillSwitch`, `KillSwitchStatus`, `KillSwitchEvent`, `KillSwitchEventType`, `RegistrySummary` + - AI System Registry: `masfeat.register_system()`, `masfeat.get_system()`, `masfeat.update_system()`, `masfeat.list_systems()`, `masfeat.activate_system()`, `masfeat.retire_system()`, `masfeat.get_registry_summary()` + - 3-Dimensional Risk Rating: Customer Impact × Model Complexity × Human Reliance + - Materiality Classification: High (sum≥12), Medium (sum≥8), Low (sum<8) + - FEAT Assessments: `masfeat.create_assessment()`, `masfeat.get_assessment()`, `masfeat.update_assessment()`, `masfeat.list_assessments()`, `masfeat.submit_assessment()`, `masfeat.approve_assessment()`, `masfeat.reject_assessment()` + - Assessment Lifecycle: pending → in_progress → completed → approved/rejected + - Kill Switch: `masfeat.get_kill_switch()`, `masfeat.configure_kill_switch()`, `masfeat.check_kill_switch()`, `masfeat.trigger_kill_switch()`, `masfeat.restore_kill_switch()`, `masfeat.enable_kill_switch()`, `masfeat.disable_kill_switch()`, `masfeat.get_kill_switch_history()` + - Automatic model shutdown based on accuracy, bias, and error rate thresholds + - New namespace property: `client.masfeat` (async) and `client.masfeat` (sync via `AxonFlow.sync()`) + - New types: `AISystemRegistry`, `AISystemUseCase`, `MaterialityClassification`, `SystemStatus`, `FEATAssessment`, `FEATAssessmentStatus`, `FEATPillar`, `KillSwitch`, `KillSwitchStatus`, `KillSwitchEvent`, `KillSwitchEventType`, `RegistrySummary` - **proxy_llm_call()**: New primary method for Proxy Mode with improved documentation - - Clearly describes Proxy Mode behavior (AxonFlow makes the LLM call on your behalf) - - Documents when to use Proxy Mode vs Gateway Mode - - Same functionality as execute_query, but with clearer naming + - Clearly describes Proxy Mode behavior (AxonFlow makes the LLM call on your behalf) + - Documents when to use Proxy Mode vs Gateway Mode + - Same functionality as execute_query, but with clearer naming - **BudgetInfo**: `QueryResponse.budget_info` for budget enforcement (HTTP 402) ### Deprecated - **execute_query()**: Deprecated in favor of proxy_llm_call() - - Will be removed in v3.0.0 - - Emits deprecation warning in debug mode - - Remains functional as a wrapper around proxy_llm_call() + - Will be removed in v3.0.0 + - Emits deprecation warning in debug mode + - Remains functional as a wrapper around proxy_llm_call() --- @@ -800,11 +780,11 @@ in v3.5.0. This major version formally acknowledges that breaking change. ### Added -- **Workflow Policy Enforcement** (Issues #1019, #1020, #1021): Policy transparency for workflow operations - - `StepGateResponse` now includes `policies_evaluated` and `policies_matched` fields with `PolicyMatch` type - - `PolicyMatch` class with `policy_id`, `policy_name`, `action`, `reason` for policy transparency - - `PolicyEvaluationResult` class for MAP execution with `allowed`, `applied_policies`, `risk_score` - - Workflow operations (`workflow_created`, `workflow_step_gate`, `workflow_completed`) logged to audit trail +- **Workflow Policy Enforcement**: Policy transparency for workflow operations + - `StepGateResponse` now includes `policies_evaluated` and `policies_matched` fields with `PolicyMatch` type + - `PolicyMatch` class with `policy_id`, `policy_name`, `action`, `reason` for policy transparency + - `PolicyEvaluationResult` class for MAP execution with `allowed`, `applied_policies`, `risk_score` + - Workflow operations (`workflow_created`, `workflow_step_gate`, `workflow_completed`) logged to audit trail --- @@ -812,20 +792,20 @@ in v3.5.0. This major version formally acknowledges that breaking change. ### Added -- **Workflow Control Plane** (Issue #834): Governance gates for external orchestrators - - "LangChain runs the workflow. AxonFlow decides when it's allowed to move forward." - - `create_workflow()` - Register workflows from LangChain/LangGraph/CrewAI/external - - `step_gate()` - Check if step is allowed to proceed (allow/block/require_approval) - - `mark_step_completed()` - Mark a step as completed with optional output data - - `get_workflow()` - Get workflow status and step history - - `list_workflows()` - List workflows with filters (status, source, pagination) - - `complete_workflow()` - Mark workflow as completed - - `abort_workflow()` - Abort workflow with reason - - `resume_workflow()` - Resume after approval - - New types: `WorkflowStatus`, `WorkflowSource`, `GateDecision`, `StepType`, `ApprovalStatus`, `MarkStepCompletedRequest` - - Helper methods on `StepGateResponse`: `is_allowed()`, `is_blocked()`, `requires_approval()` - - Helper methods on `WorkflowStatus` and `WorkflowStatusResponse`: `is_terminal()` - - LangGraph adapter: `axonflow.adapters.langgraph.AxonFlowLangGraphAdapter` +- **Workflow Control Plane**: Governance gates for external orchestrators + - "LangChain runs the workflow. AxonFlow decides when it's allowed to move forward." + - `create_workflow()` - Register workflows from LangChain/LangGraph/CrewAI/external + - `step_gate()` - Check if step is allowed to proceed (allow/block/require_approval) + - `mark_step_completed()` - Mark a step as completed with optional output data + - `get_workflow()` - Get workflow status and step history + - `list_workflows()` - List workflows with filters (status, source, pagination) + - `complete_workflow()` - Mark workflow as completed + - `abort_workflow()` - Abort workflow with reason + - `resume_workflow()` - Resume after approval + - New types: `WorkflowStatus`, `WorkflowSource`, `GateDecision`, `StepType`, `ApprovalStatus`, `MarkStepCompletedRequest` + - Helper methods on `StepGateResponse`: `is_allowed()`, `is_blocked()`, `requires_approval()` + - Helper methods on `WorkflowStatus` and `WorkflowStatusResponse`: `is_terminal()` + - LangGraph adapter: `axonflow.adapters.langgraph.AxonFlowLangGraphAdapter` ### Fixed @@ -837,16 +817,16 @@ in v3.5.0. This major version formally acknowledges that breaking change. ### Added -- **MCP Exfiltration Detection** (Issue #966): `ConnectorPolicyInfo` now includes `exfiltration_check` with row/volume limit information - - `ExfiltrationCheckInfo` type with `rows_returned`, `row_limit`, `bytes_returned`, `byte_limit`, `within_limits` fields - - Prevents large-scale data extraction via MCP queries - - Configurable via `MCP_MAX_ROWS_PER_QUERY` and `MCP_MAX_BYTES_PER_QUERY` environment variables +- **MCP Exfiltration Detection**: `ConnectorPolicyInfo` now includes `exfiltration_check` with row/volume limit information + - `ExfiltrationCheckInfo` type with `rows_returned`, `row_limit`, `bytes_returned`, `byte_limit`, `within_limits` fields + - Prevents large-scale data extraction via MCP queries + - Configurable via `MCP_MAX_ROWS_PER_QUERY` and `MCP_MAX_BYTES_PER_QUERY` environment variables -- **MCP Dynamic Policies** (Issue #968): `ConnectorPolicyInfo` now includes `dynamic_policy_info` for Orchestrator-evaluated policies - - `DynamicPolicyInfo` type with `policies_evaluated`, `matched_policies`, `orchestrator_reachable`, `processing_time_ms` - - `DynamicPolicyMatch` type with `policy_id`, `policy_name`, `policy_type`, `action`, `reason` - - Supports rate limiting, budget controls, time-based access, and role-based access policies - - Optional feature - enable via `MCP_DYNAMIC_POLICIES_ENABLED=true` +- **MCP Dynamic Policies**: `ConnectorPolicyInfo` now includes `dynamic_policy_info` for Orchestrator-evaluated policies + - `DynamicPolicyInfo` type with `policies_evaluated`, `matched_policies`, `orchestrator_reachable`, `processing_time_ms` + - `DynamicPolicyMatch` type with `policy_id`, `policy_name`, `policy_type`, `action`, `reason` + - Supports rate limiting, budget controls, time-based access, and role-based access policies + - Optional feature - enable via `MCP_DYNAMIC_POLICIES_ENABLED=true` --- @@ -855,13 +835,13 @@ in v3.5.0. This major version formally acknowledges that breaking change. ### Added - **MCP Policy Enforcement Response Fields**: `mcp_query()` and `mcp_execute()` now return policy enforcement metadata - - `redacted: bool` - Whether any fields were redacted by PII policies - - `redacted_fields: List[str]` - JSON paths of redacted fields (e.g., `rows[0].ssn`) - - `policy_info: ConnectorPolicyInfo` - Full policy evaluation metadata + - `redacted: bool` - Whether any fields were redacted by PII policies + - `redacted_fields: List[str]` - JSON paths of redacted fields (e.g., `rows[0].ssn`) + - `policy_info: ConnectorPolicyInfo` - Full policy evaluation metadata - **PolicyInfo types**: New types for policy enforcement metadata - - `ConnectorPolicyInfo` - Contains `policies_evaluated`, `blocked`, `block_reason`, `redactions_applied`, `processing_time_ms`, `matched_policies` - - `PolicyMatchInfo` - Details of matched policies including `policy_id`, `policy_name`, `category`, `severity`, `action` + - `ConnectorPolicyInfo` - Contains `policies_evaluated`, `blocked`, `block_reason`, `redactions_applied`, `processing_time_ms`, `matched_policies` + - `PolicyMatchInfo` - Details of matched policies including `policy_id`, `policy_name`, `category`, `severity`, `action` --- @@ -870,13 +850,13 @@ in v3.5.0. This major version formally acknowledges that breaking change. ### Added - **OAuth2-style client credentials**: New `client_id` and `client_secret` configuration options following OAuth2 client credentials pattern. - - `client_id` is used for request identification (required for most API calls) - - `client_secret` is optional - community/self-hosted deployments work without it + - `client_id` is used for request identification (required for most API calls) + - `client_secret` is optional - community/self-hosted deployments work without it - **Enterprise: Close PR** (`close_pr`): Close a PR without merging and optionally delete the branch - - Useful for cleaning up test/demo PRs created by code governance examples - - Supports all Git providers: GitHub, GitLab, Bitbucket - - Requires enterprise portal authentication + - Useful for cleaning up test/demo PRs created by code governance examples + - Supports all Git providers: GitHub, GitLab, Bitbucket + - Requires enterprise portal authentication ### Changed @@ -885,8 +865,8 @@ in v3.5.0. This major version formally acknowledges that breaking change. ```python # Community mode - no secret needed client = AxonFlow( - endpoint="http://localhost:8080", - client_id="my-app", # Used for request identification + endpoint="http://localhost:8080", + client_id="my-app", # Used for request identification ) ``` @@ -905,8 +885,8 @@ client = AxonFlow( - **Sensitive Data Category**: Added `SENSITIVE_DATA` to `PolicyCategory` enum for policies that return `sensitive-data` category - **Provider Restrictions for Compliance**: Support for `allowed_providers` in dynamic policy action config - - Specify allowed providers via `DynamicPolicyAction(type="route", config={"allowed_providers": [...]})` - - Enables GDPR, HIPAA, and RBI compliance by restricting LLM routing to specific providers + - Specify allowed providers via `DynamicPolicyAction(type="route", config={"allowed_providers": [.]})` + - Enables GDPR, HIPAA, and RBI compliance by restricting LLM routing to specific providers ### Fixed @@ -926,9 +906,9 @@ client = AxonFlow( - **Audit Log Reading**: Added `search_audit_logs()` for searching audit logs with filters (user email, client ID, time range, request type) - **Tenant Audit Logs**: Added `get_audit_logs_by_tenant()` for retrieving audit logs scoped to a specific tenant - **Audit Types**: Added `AuditLogEntry`, `AuditSearchRequest`, `AuditQueryOptions`, and `AuditSearchResponse` types -- **PII Redaction Support**: Added `requires_redaction` field to `PolicyApprovalResult` (Issue #891) - - When `True`, PII was detected with redact action and response should be processed for redaction - - Supports new detection defaults: PII defaults to redact instead of block +- **PII Redaction Support**: Added `requires_redaction` field to `PolicyApprovalResult` + - When `True`, PII was detected with redact action and response should be processed for redaction + - Supports new detection defaults: PII defaults to redact instead of block ### Changed @@ -941,20 +921,20 @@ client = AxonFlow( **Before (v0.x):** ```python client = AxonFlow( - agent_url="http://localhost:8080", - orchestrator_url="http://localhost:8081", - portal_url="http://localhost:8082", - client_id="my-client", - client_secret="my-secret", + agent_url="http://localhost:8080", + orchestrator_url="http://localhost:8081", + portal_url="http://localhost:8082", + client_id="my-client", + client_secret="my-secret", ) ``` **After (v1.x):** ```python client = AxonFlow( - endpoint="http://localhost:8080", - client_id="my-client", - client_secret="my-secret", + endpoint="http://localhost:8080", + client_id="my-client", + client_secret="my-secret", ) ``` @@ -996,8 +976,8 @@ client = AxonFlow( ### Fixed - **Connector API Endpoints**: Fixed endpoints to use Orchestrator (port 8081) instead of Agent - - `list_connectors()` - Changed from Agent `/api/connectors` to Orchestrator `/api/v1/connectors` - - `install_connector()` - Fixed path to `/api/v1/connectors/{id}/install` + - `list_connectors()` - Changed from Agent `/api/connectors` to Orchestrator `/api/v1/connectors` + - `install_connector()` - Fixed path to `/api/v1/connectors/{id}/install` - **Dynamic Policies Endpoint**: Changed from Agent `/api/v1/policies` to Orchestrator `/api/v1/policies/dynamic` --- @@ -1007,20 +987,20 @@ client = AxonFlow( ### Added - **Execution Replay API**: Debug governed workflows with step-by-step state capture - - `list_executions()` - List executions with filtering (status, time range) - - `get_execution()` - Get execution with all step snapshots - - `get_execution_steps()` - Get individual step snapshots - - `get_execution_timeline()` - Timeline view for visualization - - `export_execution()` - Export for compliance/archival - - `delete_execution()` - Delete execution records + - `list_executions()` - List executions with filtering (status, time range) + - `get_execution()` - Get execution with all step snapshots + - `get_execution_steps()` - Get individual step snapshots + - `get_execution_timeline()` - Timeline view for visualization + - `export_execution()` - Export for compliance/archival + - `delete_execution()` - Delete execution records - **Cost Controls**: Budget management and LLM usage tracking - - `create_budget()` / `get_budget()` / `list_budgets()` - Budget CRUD - - `update_budget()` / `delete_budget()` - Budget management - - `get_budget_status()` - Check current budget usage - - `check_budget()` - Pre-request budget validation - - `record_usage()` - Record LLM token usage - - `get_usage_summary()` - Usage analytics and reporting + - `create_budget()` / `get_budget()` / `list_budgets()` - Budget CRUD + - `update_budget()` / `delete_budget()` - Budget management + - `get_budget_status()` - Check current budget usage + - `check_budget()` - Pre-request budget validation + - `record_usage()` - Record LLM token usage + - `get_usage_summary()` - Usage analytics and reporting --- @@ -1029,14 +1009,14 @@ client = AxonFlow( ### Fixed - **MCP Connector Endpoint**: Fixed `query_connector()` to use `/api/request` endpoint with `request_type="mcp-query"` instead of deprecated `/mcp/resources/query` endpoint - - This aligns Python SDK with Go, TypeScript, and Java SDKs - - Fixes authentication issues in self-hosted mode - - Ensures proper license validation flow + - This aligns Python SDK with Go, TypeScript, and Java SDKs + - Fixes authentication issues in self-hosted mode + - Ensures proper license validation flow - **Nested Event Loop Handling**: Fixed `SyncAxonFlow` wrapper to handle nested event loops - - `execute_query()` and other sync methods now work when called from running event loops - - Fixes "This event loop is already running" error in Jupyter notebooks and async contexts - - Uses `ThreadPoolExecutor` to run coroutines safely when event loop is already running + - `execute_query()` and other sync methods now work when called from running event loops + - Fixes "This event loop is already running" error in Jupyter notebooks and async contexts + - Uses `ThreadPoolExecutor` to run coroutines safely when event loop is already running --- @@ -1045,9 +1025,9 @@ client = AxonFlow( ### Changed - **Community Mode**: Credentials are now optional for self-hosted/community deployments - - SDK can be initialized without `api_key` or `license_key` for community features - - `execute_query()` and `health_check()` work without credentials - - Auth headers are only sent when credentials are configured + - SDK can be initialized without `api_key` or `license_key` for community features + - `execute_query()` and `health_check()` work without credentials + - Auth headers are only sent when credentials are configured ### Added @@ -1079,9 +1059,9 @@ client = AxonFlow( ### Added - **Enterprise Policy Features**: - - `organization_id` field in `CreateStaticPolicyRequest` for organization-tier policies - - `organization_id` field in `ListStaticPoliciesOptions` for filtering by organization - - `list_policy_overrides()` method to list all active policy overrides + - `organization_id` field in `CreateStaticPolicyRequest` for organization-tier policies + - `organization_id` field in `ListStaticPoliciesOptions` for filtering by organization + - `list_policy_overrides()` method to list all active policy overrides --- @@ -1090,8 +1070,8 @@ client = AxonFlow( ### Added - **Code Governance Metrics & Export APIs** (Enterprise): Compliance reporting for AI-generated code - - `get_code_governance_metrics()` / `get_code_governance_metrics_sync()` - Returns aggregated statistics (PR counts, file totals, security findings) - - `export_code_governance_data()` / `export_code_governance_data_sync()` - Exports PR records as JSON for auditors + - `get_code_governance_metrics()` / `get_code_governance_metrics_sync()` - Returns aggregated statistics (PR counts, file totals, security findings) + - `export_code_governance_data()` / `export_code_governance_data_sync()` - Exports PR records as JSON for auditors - **New Types**: `CodeGovernanceMetrics`, `ExportOptions`, `ExportResponse` @@ -1102,21 +1082,21 @@ client = AxonFlow( ### Added - **Code Governance Git Provider APIs** (Enterprise): Create PRs from LLM-generated code - - `validate_git_provider()` - Validate credentials before saving - - `configure_git_provider()` - Configure GitHub, GitLab, or Bitbucket - - `list_git_providers()` - List configured providers - - `delete_git_provider()` - Remove a provider - - `create_pr()` - Create PR from generated code with audit trail - - `list_prs()` - List PRs with filtering - - `get_pr()` - Get PR details - - `sync_pr_status()` - Sync status from Git provider + - `validate_git_provider()` - Validate credentials before saving + - `configure_git_provider()` - Configure GitHub, GitLab, or Bitbucket + - `list_git_providers()` - List configured providers + - `delete_git_provider()` - Remove a provider + - `create_pr()` - Create PR from generated code with audit trail + - `list_prs()` - List PRs with filtering + - `get_pr()` - Get PR details + - `sync_pr_status()` - Sync status from Git provider - **New Types**: `GitProviderType`, `FileAction`, `CodeFile`, `CreatePRRequest`, `CreatePRResponse`, `PRRecord`, `ListPRsOptions`, `ListPRsResponse` - **Supported Git Providers**: - - GitHub (Cloud and Enterprise Server) - - GitLab (Cloud and Self-Managed) - - Bitbucket (Cloud and Server/Data Center) + - GitHub (Cloud and Enterprise Server) + - GitLab (Cloud and Self-Managed) + - Bitbucket (Cloud and Server/Data Center) --- @@ -1125,13 +1105,13 @@ client = AxonFlow( ### Added - **HITL Support**: `PolicyAction.REQUIRE_APPROVAL` for human oversight policies - - Use with `create_static_policy()` to trigger approval workflows - - Enterprise: Full HITL queue integration - - Community: Auto-approves immediately + - Use with `create_static_policy()` to trigger approval workflows + - Enterprise: Full HITL queue integration + - Community: Auto-approves immediately - **Code Governance**: `CodeArtifact` type for LLM-generated code detection - - Language and code type identification - - Potential secrets and unsafe pattern detection + - Language and code type identification + - Potential secrets and unsafe pattern detection --- @@ -1140,14 +1120,14 @@ client = AxonFlow( ### Added - **Policy CRUD Methods**: Full policy management support for Unified Policy Architecture v2.0.0 - - `list_static_policies()` - List policies with filtering - - `get_static_policy()` - Get single policy by ID - - `create_static_policy()` - Create custom policy - - `update_static_policy()` - Update existing policy - - `delete_static_policy()` - Delete policy - - `toggle_static_policy()` - Enable/disable policy - - `get_effective_static_policies()` - Get merged hierarchy - - `test_pattern()` - Test regex pattern + - `list_static_policies()` - List policies with filtering + - `get_static_policy()` - Get single policy by ID + - `create_static_policy()` - Create custom policy + - `update_static_policy()` - Update existing policy + - `delete_static_policy()` - Delete policy + - `toggle_static_policy()` - Enable/disable policy + - `get_effective_static_policies()` - Get merged hierarchy + - `test_pattern()` - Test regex pattern - **Policy Override Methods** (Enterprise) - **Dynamic Policy Methods** @@ -1158,17 +1138,17 @@ client = AxonFlow( ### Added - **MAP Timeout Configuration** - New `map_timeout` parameter (default: 120s) for Multi-Agent Planning operations - - MAP operations involve multiple LLM calls and can take 30-60+ seconds - - Separate `_map_http_client` with longer timeout - - `generate_plan()` and `execute_plan()` now use the longer MAP timeout + - MAP operations involve multiple LLM calls and can take 30-60+ seconds + - Separate `_map_http_client` with longer timeout + - `generate_plan()` and `execute_plan()` now use the longer MAP timeout ## [0.3.0] - 2025-12-19 ### Added -- **Gemini Interceptor** - Support for Google Generative AI models (#8) - - `wrap_gemini_model()` function for intercepting Gemini API calls - - Policy enforcement and audit logging for Gemini +- **Gemini Interceptor** - Support for Google Generative AI models + - `wrap_gemini_model()` function for intercepting Gemini API calls + - Policy enforcement and audit logging for Gemini - Full feature parity with other SDKs for LLM interceptors ## [0.2.0] - 2025-12-15 @@ -1176,23 +1156,23 @@ client = AxonFlow( ### Added - **Contract Testing Suite** - Validates SDK models against real API responses - - 19 contract tests covering all response types - - JSON fixtures for health, query, blocked, plan, and policy responses - - Prevents API/SDK mismatches before release + - 19 contract tests covering all response types + - JSON fixtures for health, query, blocked, plan, and policy responses + - Prevents API/SDK mismatches before release - **Integration Test Workflow** - GitHub Actions CI for live testing - - Contract tests run on every PR - - Integration tests against staging (on merge to main) - - Demo script validation - - Community stack E2E tests (manual trigger) + - Contract tests run on every PR + - Integration tests against staging (on merge to main) + - Demo script validation + - Community stack E2E tests (manual trigger) - **Fixture-Based Test Infrastructure** - - `tests/fixtures/` directory with recorded API responses - - `load_json_fixture()` helper in conftest.py - - Fallback to mock data for backwards compatibility + - `tests/fixtures/` directory with recorded API responses + - `load_json_fixture()` helper in conftest.py + - Fallback to mock data for backwards compatibility - **Fixture Recording Script** - - `scripts/record_fixtures.py` for capturing live API responses + - `scripts/record_fixtures.py` for capturing live API responses ### Changed @@ -1214,18 +1194,18 @@ client = AxonFlow( - Async-first client with sync wrappers - Full type hints with Pydantic v2 models - Gateway Mode support for lowest-latency LLM calls - - `get_policy_approved_context()` for pre-checks - - `audit_llm_call()` for compliance logging + - `get_policy_approved_context()` for pre-checks + - `audit_llm_call()` for compliance logging - OpenAI interceptor for transparent governance - Anthropic interceptor for transparent governance - MCP connector operations - - `list_connectors()` - - `install_connector()` - - `query_connector()` + - `list_connectors()` + - `install_connector()` + - `query_connector()` - Multi-agent planning - - `generate_plan()` - - `execute_plan()` - - `get_plan_status()` + - `generate_plan()` + - `execute_plan()` + - `get_plan_status()` - Comprehensive exception hierarchy - Response caching with TTL - Retry logic with exponential backoff