Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/workflows/check-protected-changes.yml
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ jobs:

- name: Add warning comment
if: steps.check.outputs.threshold_changed == 'true'
uses: actions/github-script@v8
uses: actions/github-script@v9
with:
script: |
const body = `## ⚠️ Coverage Threshold Change Detected
Expand Down Expand Up @@ -80,7 +80,7 @@ jobs:
- name: Check for approval
if: steps.check.outputs.threshold_changed == 'true'
id: approval
uses: actions/github-script@v8
uses: actions/github-script@v9
with:
script: |
const approvedMaintainers = ['saurabhjain1592'];
Expand Down
122 changes: 122 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,128 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

---

## [7.1.0] - 2026-04-18

Combined release: Workflow State Management & HITL Enhancement +
Plugin Batch 1 — Governed Overrides & Explainability.

### Added

#### Workflow State Management & HITL Enhancement

- **Execution boundary semantics** — Step gate decisions are now idempotent
by default. Calling the same `(workflow_id, step_id)` returns the cached
decision without re-running the policy evaluator. Pass
`retry_policy: "reevaluate"` to force a fresh evaluation when external state
has changed. Responses include `cached` (boolean) and `decision_source`
("fresh" or "cached") so callers always know the provenance of the decision.

- **Workflow checkpoints** — Every step gate evaluation automatically creates
a checkpoint capturing the decision, policy context, and full step metadata
(model, provider, tool context, actor identity). Checkpoints are
governance-aware resume boundaries, not arbitrary snapshots.
- Community: list checkpoints via `GET /api/v1/workflows/{id}/checkpoints`
- Evaluation: resume from last checkpoint via `POST /api/v1/workflows/{id}/checkpoints/resume`
- Enterprise: resume from any checkpoint via `POST /api/v1/workflows/{id}/checkpoints/{id}/resume`

- **Risk-tiered approval routing** — HITL approval requests now carry a
severity level (critical, high, medium, low) derived from the triggering
policy's action config or the policy evaluation risk score. When multiple
policies match, the highest severity wins. The HITL queue can be filtered
by severity.
- Enterprise: auto-approve low-risk actions after a configurable delay,
escalate critical-risk actions past SLA threshold. Configure via
`AXONFLOW_RISK_TIER_ENABLED`, `AXONFLOW_RISK_TIER_ORG_ID`,
`AXONFLOW_LOW_AUTO_APPROVE_DELAY_MIN`, `AXONFLOW_CRITICAL_ESCALATION_SLA_MIN`.

- **Deterministic approval deduplication** — WCP approval creation uses a
deterministic UUID derived from `(workflow_id, step_id)` combined with
`ON CONFLICT` to guarantee exactly one approval per execution boundary,
even under concurrent first-time calls.

#### Plugin Batch 1 — Governed Overrides & Explainability

- **Governed session overrides** — users can grant themselves a time-bounded,
audit-logged override on a policy that would otherwise deny, closing the
dev-mode UX gap without weakening governance. TTL is clamped server-side
(default 60 minutes, hard cap 24 hours, zero for critical-risk policies).
A free-text justification is mandatory on every override. Four new audit
event types record the full lifecycle: `override_created`, `override_used`,
`override_expired`, `override_revoked`. New endpoints: `POST /api/v1/overrides`,
`GET /api/v1/overrides`, `GET /api/v1/overrides/{id}`,
`DELETE /api/v1/overrides/{id}`.
- **Policy risk level + override flag** — every policy now carries an explicit
`risk_level` (`low` | `medium` | `high` | `critical`) and an
`allow_override` boolean. The combination is enforced as a contract: a
database trigger forces `allow_override=false` whenever `risk_level=critical`,
and the override creation endpoint rejects with 403 if either condition
forbids the override. Existing policies are migrated with sensible defaults
(dangerous commands, RCE, and privilege-escalation categories set to
`critical`; SQLi, prompt injection, and secret leaks set to `high`).
- **Richer approval context** — `PolicyMatch` now includes `risk_level`,
`allow_override`, `matched_rule`, and `policy_description` fields. Plugins
can surface a structured reason on every block rather than a terse string.
Existing consumers are unaffected — all new fields use `omitempty`.
- **Explain-on-demand endpoint** — `GET /api/v1/decisions/{id}/explain`
returns a stable `DecisionExplanation` payload: matched policies with
descriptions, decision + reason, risk level, override availability and any
existing active override, historical hit count for the same rule in the
caller's rolling 24-hour session window, and a tool signature. Authorization
is scoped to the decision owner or same-tenant callers. Payload shape is
frozen — additive fields only until a major version bump.
- **Audit search filter parity** — `POST /api/v1/audit/search` accepts three
new optional filters: `decision_id` for explain flows, `policy_name` for
"what did this policy block" queries, and `override_id` to reconstruct the
full lifecycle of a single override. Existing filters remain unchanged.
- **MCP tool surface** — `explain_decision`, `create_override`, `delete_override`,
and `list_overrides` are now exposed as MCP tools on the agent's MCP server,
alongside the existing `check_policy` / `check_output` / `audit_tool_call`
/ `list_policies` / `get_policy_stats` / `search_audit_events` tools. Agents
running in the plugin ecosystem can drive the full override lifecycle and
decision explainability without leaving the MCP surface.

### Changed

- **Step gate upsert** — Re-evaluation (`retry_policy: "reevaluate"`) now
updates all step metadata (step_name, step_type, step_input, model,
provider) in the persisted step record, not just the decision columns.

- **Concurrent safety** — After upserting a step decision, the service reads
back the persisted row to ensure the response matches what actually landed
in the database. If a concurrent call won the race with a different
decision, the persisted (winning) decision is returned.

- **Feature matrix** — Updated with checkpoint and risk-tiered approval rows
across Community, Evaluation, and Enterprise tiers.

- `DynamicPolicy` and `StaticPolicy` structs gained `risk_level` and
`allow_override` fields. Policy repositories persist them via migration 070.

### Fixed

- **Severity was hardcoded** — HITL approval severity was always set to
"high" regardless of the triggering policy's risk level. Now derived from
the policy's `require_approval` action config or the evaluation risk score.

### Database

- Migration `069_*` — workflow state management (#1607).
- Migration `070_policy_batch1_risk_and_override_extensions.sql` adds
`risk_level` and `allow_override` columns (with seeded defaults per category)
to `static_policies` and `dynamic_policies`; extends the existing
`policy_overrides` table with `tool_signature`, `revoked_at`, `revoked_by`;
adds `audit_logs` indexes on `decision_id` for the new filters; installs a
trigger that enforces the critical-risk no-override invariant at the
database level.

### Plugin ecosystem

The companion plugin releases for Plugin Batch 1 (OpenClaw v1.3.0,
Claude Code v0.5.0, Cursor v0.5.0, Codex v0.4.0) ship the user-facing
consumption of the new override, explain, and audit-search endpoints.

---

## [7.0.1] - 2026-04-11

### Fixed
Expand Down
12 changes: 10 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -255,8 +255,16 @@ For Go, Java, Python, and TypeScript applications, we recommend using the **[Axo

All features—policy enforcement, audit logging, MCP connectors, WCP workflows—are available via both SDKs and HTTP.

For AI agent runtimes, see:
- [**OpenClaw**](https://docs.getaxonflow.com/docs/integration/openclaw/) — policy enforcement, approval gates, and audit trails for OpenClaw tool execution
AxonFlow ships official plugins for AI agent runtimes, coding assistants, and developer tools. All plugins enforce the same policy surface and share a single audit trail via your self-hosted AxonFlow stack.

| Plugin | Platform | Install | Docs | Repo |
|--------|----------|---------|------|------|
| **OpenClaw** | OpenClaw | `openclaw plugins install @axonflow/openclaw` | [Docs](https://docs.getaxonflow.com/docs/integration/openclaw/) | [GitHub](https://github.com/getaxonflow/axonflow-openclaw-plugin) |
| **Claude Code** | Claude Code CLI | Marketplace or manual hooks | [Docs](https://docs.getaxonflow.com/docs/integration/claude-code/) | [GitHub](https://github.com/getaxonflow/axonflow-claude-plugin) |
| **Cursor** | Cursor IDE | Pre-/post-tool hooks | [Docs](https://docs.getaxonflow.com/docs/integration/cursor/) | [GitHub](https://github.com/getaxonflow/axonflow-cursor-plugin) |
| **Codex** | OpenAI Codex CLI | Bash hooks and advisory skills | [Docs](https://docs.getaxonflow.com/docs/integration/codex/) | [GitHub](https://github.com/getaxonflow/axonflow-codex-plugin) |

For AI agent framework integration, see:
- [**Anthropic Computer Use**](https://docs.getaxonflow.com/docs/integration/computer-use/) — governed desktop and tool actions
- [**Claude Agent SDK**](https://docs.getaxonflow.com/docs/integration/claude-agent-sdk/) — MCP tool governance patterns

Expand Down
4 changes: 2 additions & 2 deletions docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ services:
DEPLOYMENT_MODE: ${DEPLOYMENT_MODE:-community}
AXONFLOW_INTEGRATIONS: ${AXONFLOW_INTEGRATIONS:-}
AXONFLOW_LICENSE_KEY: ${AXONFLOW_LICENSE_KEY:-}
AXONFLOW_VERSION: "${AXONFLOW_VERSION:-7.0.1}"
AXONFLOW_VERSION: "${AXONFLOW_VERSION:-7.1.0}"

# Media governance (v4.5.0+) - set to "true" to enable in Community mode
MEDIA_GOVERNANCE_ENABLED: ${MEDIA_GOVERNANCE_ENABLED:-}
Expand Down Expand Up @@ -223,7 +223,7 @@ services:
PORT: 8081
DEPLOYMENT_MODE: ${DEPLOYMENT_MODE:-community}
AXONFLOW_LICENSE_KEY: ${AXONFLOW_LICENSE_KEY:-}
AXONFLOW_VERSION: "${AXONFLOW_VERSION:-7.0.1}"
AXONFLOW_VERSION: "${AXONFLOW_VERSION:-7.1.0}"

# Media governance (v4.5.0+) - set to "true" to enable in Community mode
MEDIA_GOVERNANCE_ENABLED: ${MEDIA_GOVERNANCE_ENABLED:-}
Expand Down
175 changes: 175 additions & 0 deletions docs/api/orchestrator-api.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -3816,6 +3816,92 @@ paths:
'404':
$ref: '#/components/responses/NotFound'

/api/v1/workflows/{workflow_id}/checkpoints:
get:
tags:
- Workflow Control Plane
summary: List step-gate checkpoints for a workflow
description: |
Returns all checkpoints for a workflow, ordered by step_index.
Checkpoints are created automatically at each step gate evaluation.
Available in all tiers (Community, Evaluation, Enterprise).
operationId: getCheckpoints
parameters:
- name: workflow_id
in: path
required: true
schema:
type: string
responses:
'200':
description: List of checkpoints
content:
application/json:
schema:
$ref: '#/components/schemas/CheckpointListResponse'
'404':
$ref: '#/components/responses/NotFound'

/api/v1/workflows/{workflow_id}/checkpoints/resume:
post:
tags:
- Workflow Control Plane
summary: Resume workflow from last checkpoint (Evaluation+)
description: |
Re-evaluates the step gate at the last resumable checkpoint with
current policies. The step gate uses retry_policy=reevaluate internally.
operationId: resumeFromLastCheckpoint
parameters:
- name: workflow_id
in: path
required: true
schema:
type: string
responses:
'200':
description: Resume result with fresh decision
content:
application/json:
schema:
$ref: '#/components/schemas/ResumeFromCheckpointResponse'
'404':
$ref: '#/components/responses/NotFound'
'409':
description: Workflow not resumable (completed, no checkpoints)

/api/v1/workflows/{workflow_id}/checkpoints/{checkpoint_id}/resume:
post:
tags:
- Workflow Control Plane
summary: Resume workflow from specific checkpoint (Enterprise)
description: |
Re-evaluates the step gate at a specific checkpoint with current policies.
Enterprise only.
operationId: resumeFromCheckpoint
parameters:
- name: workflow_id
in: path
required: true
schema:
type: string
- name: checkpoint_id
in: path
required: true
schema:
type: integer
format: int64
responses:
'200':
description: Resume result
content:
application/json:
schema:
$ref: '#/components/schemas/ResumeFromCheckpointResponse'
'404':
$ref: '#/components/responses/NotFound'
'409':
description: Checkpoint not resumable

/api/v1/workflows/{workflowId}/steps/{stepId}/approve:
post:
tags:
Expand Down Expand Up @@ -7209,6 +7295,15 @@ components:
example: 0.0023
tool_context:
$ref: '#/components/schemas/ToolContext'
retry_policy:
type: string
description: |
Controls behavior on repeated calls for the same (workflow_id, step_id).
Default ("idempotent"): return cached decision from prior evaluation.
"reevaluate": force fresh policy evaluation regardless of prior decision.
enum: [idempotent, reevaluate]
default: idempotent
example: "idempotent"

StepGateResponse:
type: object
Expand Down Expand Up @@ -7245,6 +7340,20 @@ components:
description: Policies that matched and contributed to the decision (Issue #1021)
items:
$ref: '#/components/schemas/PolicyMatch'
cached:
type: boolean
description: |
Whether this response was served from a prior decision rather than
a fresh policy evaluation. True when retry_policy is "idempotent"
(the default) and the step was previously evaluated.
example: false
decision_source:
type: string
description: |
How the decision was produced: "fresh" (policy evaluator ran for
this request) or "cached" (returned from prior evaluation in DB).
enum: [fresh, cached]
example: "fresh"

MarkStepCompletedRequest:
type: object
Expand All @@ -7267,6 +7376,72 @@ components:
description: Actual cost in USD for the step (overrides gate-time estimate)
example: 0.0023

Checkpoint:
type: object
properties:
id:
type: integer
format: int64
description: Database identifier
workflow_id:
type: string
step_id:
type: string
step_index:
type: integer
step_type:
type: string
description: Type of step (llm_call, tool_call, etc.)
checkpoint_type:
type: string
enum: [step_gate, approval_boundary]
description: step_gate for standard gates, approval_boundary for require_approval
gate_decision:
type: string
enum: [allow, block, require_approval]
gate_reason:
type: string
is_resumable:
type: boolean
description: False for blocked steps (no point resuming from a hard block)
resume_count:
type: integer
description: How many times the workflow has been resumed from this checkpoint
created_at:
type: string
format: date-time

CheckpointListResponse:
type: object
properties:
checkpoints:
type: array
items:
$ref: '#/components/schemas/Checkpoint'
workflow_id:
type: string

ResumeFromCheckpointResponse:
type: object
properties:
workflow_id:
type: string
resumed_from_checkpoint:
type: string
description: step_id of the checkpoint
resumed_from_index:
type: integer
new_decision:
type: string
enum: [allow, block, require_approval]
decision_source:
type: string
description: Always "fresh" since resume forces re-evaluation
resume_count:
type: integer
message:
type: string

ListWorkflowsResponse:
type: object
properties:
Expand Down
Loading
Loading