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
33 changes: 33 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,39 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

---

## [3.6.1] - 2026-01-30

### Community

#### Fixed

- **MCP Community Auth** (#1109): MCP query/execute endpoints incorrectly required license validation in community mode, returning HTTP 401
- Replaced raw environment variable check with canonical `isCommunityMode()` helper
- Extracted duplicated license validation into shared `validateServiceLicense()` helper
- **MAP Replay Recording** (#1108): Parallel execution path was missing replay recording — MAP executions left no trace in `execution_snapshots`
- Added `StartExecution`, `recordStepSnapshot`, `CompleteExecution`/`FailExecution` calls to parallel path
- **MAP Parallel Data Race** (#1108): Input map shared across parallel goroutines without protection
- **MAP Silent Error Swallowing** (#1108): `FailExecution` errors silently discarded in 4 call sites
- **EU AI Act Export Data Race** (#1109): `CreateExport` returned shared pointer mutated by async goroutine, causing flaky tests under `-race`
- **Anthropic Default Model** (#1109): Updated default from `claude-3-5-sonnet-20241022` (404) to `claude-sonnet-4-20250514`

#### Added

- **HTTP Examples** (#1109): Added missing HTTP examples for `mcp-connectors` and `map` (completing 30/30 cross-language coverage)

### Enterprise

#### Fixed

- **V1 License Error Messaging** (#1106): Renamed error code to `V1_LICENSE_NOT_SUPPORTED`, removed internal tool paths from user-facing errors
- **DEPLOYMENT_MODE Case Handling** (#1109): Removed unnecessary case normalization in admin auth middleware

#### Security

- **Next.js** (GHSA-h25m-26qc-wcjf): Bumped in customer-portal-ui (16.0.10→16.1.6) and banking-demo (15.5.9→15.5.10)

---

## [3.6.0] - 2026-01-26

### Community
Expand Down
11 changes: 4 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,9 @@ AxonFlow treats agents as long-running, stateful systems that require governance

> **Using AxonFlow in evaluation or production?**
>
> If you're experimenting with AxonFlow internally and would prefer to share feedback privately (no public issues, no attribution), feel free to reach out at **hello@getaxonflow.com**.
> If you are evaluating AxonFlow internally and prefer to share questions or feedback privately, you are welcome to reach out at **hello@getaxonflow.com**. This includes early evaluations, partial integrations, or cases where you are still determining fit.
>
> We're especially interested in learning where agent-based systems hit real production constraints (permissions, compliance, reliability, cost), and we treat all conversations as confidential.
> We are particularly interested in understanding where real systems encounter constraints around permissions, compliance, reliability, cost, or organizational ownership. All conversations are treated as confidential, and there is no expectation of commitment or follow-up unless you want one.

---

Expand Down Expand Up @@ -428,12 +428,9 @@ We welcome contributions. See [CONTRIBUTING.md](CONTRIBUTING.md) for guidelines.

### Public Issues (Technical Questions Welcome)

If you are evaluating AxonFlow and run into unclear behavior, edge cases, or questions about guarantees
(for example policy enforcement, audit semantics, failure modes, or integration boundaries), opening a GitHub issue is welcome.
If you are evaluating AxonFlow and encounter unclear behavior, edge cases, or questions about guarantees such as policy enforcement, audit semantics, or failure modes, opening a GitHub issue or discussion is welcome. This includes situations where you are unsure whether something is expected behavior, a limitation, or a mismatch with your use case.

Public issues help clarify behavior for everyone.

For private questions, you can also reach us at hello@getaxonflow.com
For private or sensitive questions, you can also reach us at hello@getaxonflow.com.

### Evaluating AxonFlow or Exploring Internally?

Expand Down
5 changes: 5 additions & 0 deletions docs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,11 @@ Configuration and how-to guides for common tasks.
| [Proxy Mode](./guides/proxy-mode.md) | Configure Proxy Mode deployment |
| [PII Detection](./guides/pii-detection.md) | Configure PII detection and redaction |
| [Connector Development](./guides/connector-development.md) | Build custom MCP connectors |
| [Workflow Control Plane](./guides/workflow-control-plane.md) | WCP step gates, policy enforcement, SDK integration |
| [Audit Logging](./guides/audit-logging.md) | Audit logging configuration and compliance |
| [MCP Audit Logging](./guides/mcp-audit-logging.md) | Three-phase MCP policy enforcement and audit |
| [Execution Tracking](./guides/execution-tracking.md) | Unified execution history for MAP and WCP |
| [Grafana Dashboard](./guides/grafana-dashboard.md) | Monitoring with Grafana dashboards |

## SDK Documentation

Expand Down
9 changes: 4 additions & 5 deletions docs/reference/license-migration.md
Original file line number Diff line number Diff line change
Expand Up @@ -169,11 +169,10 @@ Contact us for a V2 license:

### Q: Will my V1 license stop working?

**A:** V1 licenses will continue to work but are deprecated. We recommend migrating to V2 for:
- Better security
- Access to new features
- Node limit enforcement
- AWS Marketplace integration
**A:** V1 licenses are no longer accepted. The validation layer rejects V1 format keys and requires V2. Generate a V2 key using the keygen utility:
```bash
./keygen -tier ENT -org YOUR_ORG -service-name platform -service-type backend-service -days 365
```

### Q: Is there a cost to migrate?

Expand Down
2 changes: 1 addition & 1 deletion examples/execution-replay/go/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ func main() {
fmt.Printf(" - %s: %s (%d/%d steps, status=%s)\n",
exec.RequestID, exec.WorkflowName, exec.CompletedSteps, exec.TotalSteps, exec.Status)
assert(exec.RequestID != "", "Execution has valid request_id")
assert(strings.HasPrefix(exec.RequestID, "req_") || strings.HasPrefix(exec.RequestID, "exec_"),
assert(strings.HasPrefix(exec.RequestID, "req_") || strings.HasPrefix(exec.RequestID, "exec_") || strings.HasPrefix(exec.RequestID, "wf_") || strings.HasPrefix(exec.RequestID, "plan_"),
"Execution ID has valid prefix")
}
} else {
Expand Down
168 changes: 168 additions & 0 deletions examples/map/http/map.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,168 @@
#!/bin/bash
# MAP (Multi-Agent Planning) - HTTP API Example
#
# This example demonstrates all MAP API endpoints using raw HTTP calls.
# No SDK required - uses cURL to interact with the Agent API directly.
#
# Usage:
# docker compose up -d # Start AxonFlow
# cd examples/map/http
# ./map.sh
#
# What this demonstrates:
# 1. Generate a multi-agent plan (POST /api/request with request_type=multi-agent-plan)
# 2. Get plan status (GET /api/v1/plan/{id})
# 3. Execute the plan (POST /api/request with request_type=execute-plan)
# 4. Get plan status after execution

set -e

AGENT_URL="${AXONFLOW_AGENT_URL:-http://localhost:8080}"
CLIENT_ID="${AXONFLOW_CLIENT_ID:-map-http-example}"
CLIENT_SECRET="${AXONFLOW_CLIENT_SECRET:-}"

echo "=============================================="
echo "MAP (Multi-Agent Planning) - HTTP API Example"
echo "=============================================="
echo "Agent URL: $AGENT_URL"
echo ""

PASS=0
FAIL=0

check_result() {
local test_name="$1"
local condition="$2"
if [ "$condition" = "true" ]; then
echo " PASS: $test_name"
PASS=$((PASS + 1))
else
echo " FAIL: $test_name"
FAIL=$((FAIL + 1))
fi
}

# 1. Generate Plan
echo "1. GeneratePlan - Creating a multi-agent plan..."
echo "----------------------------------------------"

RESPONSE=$(curl -s -X POST "${AGENT_URL}/api/request" \
-H "Content-Type: application/json" \
-H "X-Client-ID: $CLIENT_ID" \
-H "X-Client-Secret: $CLIENT_SECRET" \
-d '{
"query": "Create a brief plan to greet a new user and ask how to help them",
"user_token": "'"$CLIENT_ID"'",
"client_id": "'"$CLIENT_ID"'",
"request_type": "multi-agent-plan",
"context": {
"domain": "generic"
}
}')

echo "Response (truncated):"
echo "$RESPONSE" | python3 -m json.tool 2>/dev/null | head -30 || echo "$RESPONSE"
echo ""

SUCCESS=$(echo "$RESPONSE" | python3 -c "import sys,json; print('true' if json.load(sys.stdin).get('success', False) else 'false')" 2>/dev/null || echo "false")
check_result "GeneratePlan request succeeded" "$SUCCESS"

PLAN_ID=$(echo "$RESPONSE" | python3 -c "
import sys, json
r = json.load(sys.stdin)
pid = r.get('plan_id', '')
if not pid:
data = r.get('data', {})
if isinstance(data, dict):
pid = data.get('plan_id', '')
print(pid)
" 2>/dev/null || echo "")

HAS_PLAN_ID=$([ -n "$PLAN_ID" ] && echo "true" || echo "false")
check_result "GeneratePlan returned plan_id ($PLAN_ID)" "$HAS_PLAN_ID"
echo ""

if [ -z "$PLAN_ID" ]; then
echo "No plan ID returned. Cannot continue with execute/status tests."
echo "=============================================="
echo "Results: $PASS passed, $FAIL failed"
if [ "$FAIL" -gt 0 ]; then
exit 1
fi
exit 0
fi

# 2. Get Plan Status (before execution)
echo "2. GetPlanStatus - Checking status before execution..."
echo "----------------------------------------------"

RESPONSE=$(curl -s "${AGENT_URL}/api/v1/plan/${PLAN_ID}" \
-H "X-Client-ID: $CLIENT_ID" \
-H "X-Client-Secret: $CLIENT_SECRET")

echo "Response:"
echo "$RESPONSE" | python3 -m json.tool 2>/dev/null || echo "$RESPONSE"
echo ""

STATUS=$(echo "$RESPONSE" | python3 -c "import sys,json; print(json.load(sys.stdin).get('status', ''))" 2>/dev/null || echo "")
IS_PENDING=$([ "$STATUS" = "pending" ] || [ "$STATUS" = "created" ] || [ "$STATUS" = "generated" ] && echo "true" || echo "false")
check_result "Plan status before execution ($STATUS)" "$IS_PENDING"
echo ""

# 3. Execute Plan
echo "3. ExecutePlan - Executing the plan..."
echo "----------------------------------------------"

RESPONSE=$(curl -s -X POST "${AGENT_URL}/api/request" \
-H "Content-Type: application/json" \
-H "X-Client-ID: $CLIENT_ID" \
-H "X-Client-Secret: $CLIENT_SECRET" \
-d '{
"query": "",
"user_token": "'"$CLIENT_ID"'",
"client_id": "'"$CLIENT_ID"'",
"request_type": "execute-plan",
"context": {
"plan_id": "'"$PLAN_ID"'"
}
}')

echo "Response (truncated):"
echo "$RESPONSE" | python3 -m json.tool 2>/dev/null | head -30 || echo "$RESPONSE"
echo ""

EXEC_SUCCESS=$(echo "$RESPONSE" | python3 -c "import sys,json; print('true' if json.load(sys.stdin).get('success', False) else 'false')" 2>/dev/null || echo "false")
check_result "ExecutePlan request succeeded" "$EXEC_SUCCESS"
echo ""

# 4. Get Plan Status (after execution)
echo "4. GetPlanStatus - Checking status after execution..."
echo "----------------------------------------------"

RESPONSE=$(curl -s "${AGENT_URL}/api/v1/plan/${PLAN_ID}" \
-H "X-Client-ID: $CLIENT_ID" \
-H "X-Client-Secret: $CLIENT_SECRET")

echo "Response:"
echo "$RESPONSE" | python3 -m json.tool 2>/dev/null || echo "$RESPONSE"
echo ""

FINAL_STATUS=$(echo "$RESPONSE" | python3 -c "import sys,json; print(json.load(sys.stdin).get('status', ''))" 2>/dev/null || echo "")
IS_DONE=$([ "$FINAL_STATUS" = "completed" ] || [ "$FINAL_STATUS" = "success" ] && echo "true" || echo "false")
check_result "Final status is completed ($FINAL_STATUS)" "$IS_DONE"
echo ""

# Summary
echo "=============================================="
echo "Results: $PASS passed, $FAIL failed"
if [ "$FAIL" -gt 0 ]; then
echo "SOME TESTS FAILED"
exit 1
fi
echo "ALL TESTS PASSED"
echo "=============================================="
echo ""
echo "API Summary:"
echo " POST /api/request (request_type=multi-agent-plan) - Generate a plan"
echo " GET /api/v1/plan/{id} - Get plan status"
echo " POST /api/request (request_type=execute-plan) - Execute a plan"
125 changes: 125 additions & 0 deletions examples/mcp-connectors/http/mcp-connectors.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
#!/bin/bash
# MCP Connectors - HTTP API Example
#
# This example tests the MCP connector flow via the Orchestrator API.
# It sends MCP query requests through the orchestrator, which routes
# them to the agent and its registered connectors.
#
# Usage:
# docker compose up -d # Start AxonFlow
# cd examples/mcp-connectors/http
# ./mcp-connectors.sh
#
# What this demonstrates:
# 1. Query postgres connector via orchestrator routing
# 2. Query with different SQL statements
# 3. Verify orchestrator-to-agent routing works end-to-end

set -e

ORCHESTRATOR_URL="${ORCHESTRATOR_URL:-http://localhost:8081}"

echo "=============================================="
echo "MCP Connectors - HTTP API Example"
echo "=============================================="
echo "Orchestrator URL: $ORCHESTRATOR_URL"
echo ""

PASS=0
FAIL=0

check_result() {
local test_name="$1"
local condition="$2"
if [ "$condition" = "true" ]; then
echo " PASS: $test_name"
PASS=$((PASS + 1))
else
echo " FAIL: $test_name"
FAIL=$((FAIL + 1))
fi
}

# Test 1: Query postgres connector via orchestrator
echo "Test 1: Query postgres connector via orchestrator..."
echo "----------------------------------------------"

RESPONSE=$(curl -s -X POST "${ORCHESTRATOR_URL}/api/v1/process" \
-H "Content-Type: application/json" \
-d '{
"request_id": "mcp-http-test-1",
"query": "SELECT 1 as test_value, '\''hello'\'' as test_message",
"request_type": "mcp-query",
"user": {
"email": "test@example.com",
"role": "user",
"tenant_id": "default"
},
"client": {
"id": "test-client",
"tenant_id": "default"
},
"context": {
"connector": "postgres",
"params": {}
}
}')

echo "Response:"
echo "$RESPONSE" | python3 -m json.tool 2>/dev/null || echo "$RESPONSE"
echo ""

SUCCESS=$(echo "$RESPONSE" | python3 -c "import sys,json; print('true' if json.load(sys.stdin).get('success', False) else 'false')" 2>/dev/null || echo "false")
check_result "Test 1: MCP query through orchestrator" "$SUCCESS"

REQUEST_ID=$(echo "$RESPONSE" | python3 -c "import sys,json; r=json.load(sys.stdin).get('request_id',''); print('true' if r else 'false')" 2>/dev/null || echo "false")
check_result "Test 1: Request ID returned" "$REQUEST_ID"
echo ""

# Test 2: Query current timestamp via orchestrator
echo "Test 2: Query current timestamp..."
echo "----------------------------------------------"

RESPONSE=$(curl -s -X POST "${ORCHESTRATOR_URL}/api/v1/process" \
-H "Content-Type: application/json" \
-d '{
"request_id": "mcp-http-test-2",
"query": "SELECT NOW() as current_time, '\''AxonFlow MCP'\'' as source",
"request_type": "mcp-query",
"user": {
"email": "test@example.com",
"role": "user",
"tenant_id": "default"
},
"client": {
"id": "test-client",
"tenant_id": "default"
},
"context": {
"connector": "postgres",
"params": {}
}
}')

echo "Response:"
echo "$RESPONSE" | python3 -m json.tool 2>/dev/null || echo "$RESPONSE"
echo ""

SUCCESS=$(echo "$RESPONSE" | python3 -c "import sys,json; print('true' if json.load(sys.stdin).get('success', False) else 'false')" 2>/dev/null || echo "false")
check_result "Test 2: Timestamp query succeeded" "$SUCCESS"
echo ""

# Summary
echo "=============================================="
echo "Results: $PASS passed, $FAIL failed"
if [ "$FAIL" -gt 0 ]; then
echo "SOME TESTS FAILED"
exit 1
fi
echo "ALL TESTS PASSED"
echo "=============================================="
echo ""
echo "API Summary:"
echo " POST /api/v1/process - Route MCP query through orchestrator"
echo " request_type: mcp-query"
echo " context.connector: postgres (or other registered connector)"
Loading
Loading