Skip to content

Commit 5337d73

Browse files
feat: add WCP approval, plan rollback, and webhook CRUD methods
Add approveStep, rejectStep, getPendingApprovals for WCP step gating. Add rollbackPlan for plan version rollback. Add webhook subscription CRUD (createWebhook, getWebhook, updateWebhook, deleteWebhook, listWebhooks). New types: RollbackPlanResponse, VersionConflictException, webhook types. All with comprehensive tests.
1 parent b0ce97f commit 5337d73

19 files changed

Lines changed: 3619 additions & 2 deletions

src/main/java/com/getaxonflow/sdk/AxonFlow.java

Lines changed: 487 additions & 1 deletion
Large diffs are not rendered by default.
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
/*
2+
* Copyright 2025 AxonFlow
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package com.getaxonflow.sdk.exceptions;
17+
18+
/**
19+
* Thrown when a plan update fails due to a version conflict (HTTP 409).
20+
*
21+
* <p>This indicates that the plan was modified by another client between
22+
* the time it was read and the time the update was attempted. The caller
23+
* should re-read the plan, resolve any conflicts, and retry with the
24+
* updated version number.
25+
*
26+
* <p>Example usage:
27+
* <pre>{@code
28+
* try {
29+
* axonflow.updatePlan(planId, request);
30+
* } catch (VersionConflictException e) {
31+
* System.out.println("Conflict on plan: " + e.getPlanId());
32+
* System.out.println("Expected version: " + e.getExpectedVersion());
33+
* System.out.println("Current version: " + e.getCurrentVersion());
34+
* // Re-read and retry
35+
* }
36+
* }</pre>
37+
*/
38+
public class VersionConflictException extends AxonFlowException {
39+
40+
private static final long serialVersionUID = 1L;
41+
42+
private final String planId;
43+
private final int expectedVersion;
44+
private final Integer currentVersion;
45+
46+
/**
47+
* Creates a new VersionConflictException.
48+
*
49+
* @param message the error message
50+
* @param planId the plan that had the conflict
51+
* @param expectedVersion the version the client expected
52+
* @param currentVersion the actual current version on the server, or null if unknown
53+
*/
54+
public VersionConflictException(String message, String planId, int expectedVersion, Integer currentVersion) {
55+
super(message, 409, "VERSION_CONFLICT");
56+
this.planId = planId;
57+
this.expectedVersion = expectedVersion;
58+
this.currentVersion = currentVersion;
59+
}
60+
61+
/**
62+
* Returns the plan ID that had the version conflict.
63+
*
64+
* @return the plan ID
65+
*/
66+
public String getPlanId() {
67+
return planId;
68+
}
69+
70+
/**
71+
* Returns the version the client expected.
72+
*
73+
* @return the expected version number
74+
*/
75+
public int getExpectedVersion() {
76+
return expectedVersion;
77+
}
78+
79+
/**
80+
* Returns the actual current version on the server.
81+
*
82+
* @return the current version, or null if unknown
83+
*/
84+
public Integer getCurrentVersion() {
85+
return currentVersion;
86+
}
87+
}

src/main/java/com/getaxonflow/sdk/exceptions/package-info.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,8 @@
3030
* ├── ConnectionException - Network/connection errors
3131
* ├── ConfigurationException - Invalid configuration
3232
* ├── ConnectorException - MCP connector errors
33-
* └── PlanExecutionException - Plan generation/execution errors
33+
* ├── PlanExecutionException - Plan generation/execution errors
34+
* └── VersionConflictException - Optimistic concurrency version conflict
3435
* </pre>
3536
*
3637
* <h2>Usage Example</h2>
Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
/*
2+
* Copyright 2025 AxonFlow
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package com.getaxonflow.sdk.types;
17+
18+
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
19+
import com.fasterxml.jackson.annotation.JsonProperty;
20+
21+
import java.util.Objects;
22+
23+
/**
24+
* Response from cancelling a multi-agent plan.
25+
*/
26+
@JsonIgnoreProperties(ignoreUnknown = true)
27+
public final class CancelPlanResponse {
28+
29+
@JsonProperty("plan_id")
30+
private final String planId;
31+
32+
@JsonProperty("status")
33+
private final String status;
34+
35+
@JsonProperty("message")
36+
private final String message;
37+
38+
public CancelPlanResponse(
39+
@JsonProperty("plan_id") String planId,
40+
@JsonProperty("status") String status,
41+
@JsonProperty("message") String message) {
42+
this.planId = planId;
43+
this.status = status;
44+
this.message = message;
45+
}
46+
47+
/**
48+
* Returns the ID of the cancelled plan.
49+
*
50+
* @return the plan ID
51+
*/
52+
public String getPlanId() {
53+
return planId;
54+
}
55+
56+
/**
57+
* Returns the status after cancellation.
58+
*
59+
* @return the status (e.g., "cancelled")
60+
*/
61+
public String getStatus() {
62+
return status;
63+
}
64+
65+
/**
66+
* Returns a human-readable message about the cancellation.
67+
*
68+
* @return the cancellation message
69+
*/
70+
public String getMessage() {
71+
return message;
72+
}
73+
74+
@Override
75+
public boolean equals(Object o) {
76+
if (this == o) return true;
77+
if (o == null || getClass() != o.getClass()) return false;
78+
CancelPlanResponse that = (CancelPlanResponse) o;
79+
return Objects.equals(planId, that.planId) &&
80+
Objects.equals(status, that.status) &&
81+
Objects.equals(message, that.message);
82+
}
83+
84+
@Override
85+
public int hashCode() {
86+
return Objects.hash(planId, status, message);
87+
}
88+
89+
@Override
90+
public String toString() {
91+
return "CancelPlanResponse{" +
92+
"planId='" + planId + '\'' +
93+
", status='" + status + '\'' +
94+
", message='" + message + '\'' +
95+
'}';
96+
}
97+
}
Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
/*
2+
* Copyright 2025 AxonFlow
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package com.getaxonflow.sdk.types;
17+
18+
import com.fasterxml.jackson.annotation.JsonValue;
19+
20+
/**
21+
* Execution mode for multi-agent plan execution.
22+
*
23+
* <p>Controls how plan steps are scheduled and executed:
24+
* <ul>
25+
* <li>{@link #AUTO} - Let the engine determine optimal execution order</li>
26+
* <li>{@link #SEQUENTIAL} - Execute steps strictly in order</li>
27+
* <li>{@link #PARALLEL} - Execute independent steps concurrently</li>
28+
* <li>{@link #BALANCED} - Balance between parallelism and resource usage</li>
29+
* <li>{@link #CONFIRM} - Pause before each step for user confirmation</li>
30+
* <li>{@link #STEP} - Execute one step at a time with manual advancement</li>
31+
* </ul>
32+
*/
33+
public enum ExecutionMode {
34+
35+
/**
36+
* Let the engine determine optimal execution order.
37+
*/
38+
AUTO("auto"),
39+
40+
/**
41+
* Execute steps strictly in order.
42+
*/
43+
SEQUENTIAL("sequential"),
44+
45+
/**
46+
* Execute independent steps concurrently.
47+
*/
48+
PARALLEL("parallel"),
49+
50+
/**
51+
* Balance between parallelism and resource usage.
52+
*/
53+
BALANCED("balanced"),
54+
55+
/**
56+
* Pause before each step for user confirmation.
57+
*/
58+
CONFIRM("confirm"),
59+
60+
/**
61+
* Execute one step at a time with manual advancement.
62+
*/
63+
STEP("step");
64+
65+
private final String value;
66+
67+
ExecutionMode(String value) {
68+
this.value = value;
69+
}
70+
71+
/**
72+
* Returns the string value used in API requests.
73+
*
74+
* @return the execution mode value as a string
75+
*/
76+
@JsonValue
77+
public String getValue() {
78+
return value;
79+
}
80+
81+
/**
82+
* Parses a string value to an ExecutionMode enum.
83+
*
84+
* @param value the string value to parse
85+
* @return the corresponding ExecutionMode
86+
* @throws IllegalArgumentException if the value is not recognized
87+
*/
88+
public static ExecutionMode fromValue(String value) {
89+
if (value == null) {
90+
throw new IllegalArgumentException("Execution mode cannot be null");
91+
}
92+
for (ExecutionMode mode : values()) {
93+
if (mode.value.equalsIgnoreCase(value)) {
94+
return mode;
95+
}
96+
}
97+
throw new IllegalArgumentException("Unknown execution mode: " + value);
98+
}
99+
}

0 commit comments

Comments
 (0)