feat(hitl): add createHITLRequest — paired SDK release for ADK plugin v1#185
Merged
Conversation
Enables agent-framework callers (Google ADK, n8n, OpenAI Agents SDK)
to implement the full 4-step HITL approval flow against AxonFlow:
1. Gate evaluates require_approval (via pre_check / checkToolInput)
2. Caller invokes axonflow.createHITLRequest(...) to enqueue the row
3. Caller polls axonflow.getHITLRequest(approvalId) until terminal
state — OR sets notifyUrl so the platform fires a signed
webhook on the transition (n8n Wait-node "On Webhook Call"
pattern, ADK plugin polling-free mode)
4. Caller resumes the agent or denies the call based on the
decision
Prior to this release the SDK exposed getHITLRequest /
approveHITLRequest / rejectHITLRequest (the read + review surface)
but had no method to create a row. The platform's POST
/api/v1/hitl/queue endpoint has existed since v6.x; only the SDK
surface was missing.
Changes:
- src/main/java/com/getaxonflow/sdk/AxonFlow.java —
createHITLRequest(input) -> HITLApprovalRequest sync method +
createHITLRequestAsync(input) -> CompletableFuture overload.
Validates clientId / originalQuery / requestType client-side via
IllegalArgumentException (mirrors the existing approveHITLRequest
/ rejectHITLRequest @nonnull guard pattern). POSTs to
/api/v1/hitl/queue via buildOrchestratorRequest. Parses
APIResponse envelope.
- src/main/java/com/getaxonflow/sdk/types/hitl/HITLTypes.java —
HITLCreateInput POJO with Builder mirroring
platform/agent/hitl/handler.go:86 CreateRequestInput. notifyUrl
field added to both HITLCreateInput and HITLApprovalRequest for
the new outbound-webhook surface introduced in
getaxonflow/axonflow-enterprise#2419.
- src/test/java/com/getaxonflow/sdk/HITLTest.java — 8 new JUnit
cases under @nested CreateHITLRequest covering: full-fields
create, minimal required-fields, bad-notifyUrl-scheme 400
propagation, 401 propagation, connect-failure propagation (via
WireMock CONNECTION_RESET_BY_PEER fault), and the three
required-field validation guards.
- runtime-e2e/create_hitl_request/ — real OkHttp +
com.sun.net.httpserver.HttpServer proof. Asserts the wire body
carries every required field plus notify_url, and the response
envelope parses back into a populated HITLApprovalRequest.
Satisfies the runtime-e2e/ DoD gate.
- CHANGELOG.md + pom.xml — bump 8.1.0 -> 8.2.0 (additive; no
breaking changes).
Cross-SDK parity sweep tracked at
getaxonflow/axonflow-enterprise#2421. Sister Python PR landed at
getaxonflow/axonflow-sdk-python#202; TypeScript PR at
getaxonflow/axonflow-sdk-typescript#235; Go PR at
getaxonflow/axonflow-sdk-go#175. Sister Rust PR follows.
Refs getaxonflow/axonflow-enterprise#2421
Refs getaxonflow/axonflow-enterprise#2419
Refs getaxonflow/axonflow-enterprise#2393
Signed-off-by: Saurabh Jain <saurabh.jain@getaxonflow.com>
4 tasks
saurabhjain1592
added a commit
to getaxonflow/axonflow-sdk-rust
that referenced
this pull request
May 23, 2026
…ats)
Cross-SDK parity bring-up for HITL (Human-in-the-Loop) approval
workflows. Prior to this release the Rust SDK exposed NO HITL
methods at all — the four stable SDKs (Python/TS/Go/Java) all ship
the read + review surface (list_hitl_queue, get_hitl_request,
approve_hitl_request, reject_hitl_request, get_hitl_stats), plus the
new create_hitl_request method introduced in v8.2.0 of each. This
release ports the full surface in one shot so Rust callers can
implement the full 4-step HITL flow against AxonFlow:
1. Gate evaluates require_approval (via pre_check / check_tool_input)
2. Caller invokes client.create_hitl_request(...) to enqueue the row
3. Caller polls client.get_hitl_request(approval_id) until terminal
state — OR sets notify_url so the platform fires a signed
webhook on the transition (n8n Wait-node "On Webhook Call"
pattern, ADK plugin polling-free mode)
4. Caller resumes the agent or denies the call based on the
decision
Changes:
- src/hitl.rs (new) — 6 public methods (list_hitl_queue,
get_hitl_request, create_hitl_request, approve_hitl_request,
reject_hitl_request, get_hitl_stats) using PATH_SEGMENT-encoded
path components + APIResponse{success,data} envelope parsing.
Validates required fields client-side via AxonFlowError::ConfigError
(mirrors the existing decisions::explain_decision empty-id guard
pattern).
- src/types/hitl.rs (new) — HITLApprovalRequest /
HITLCreateInput / HITLReviewInput / HITLQueueListOptions /
HITLQueueListResponse / HITLStats. notify_url field added to
both HITLCreateInput and HITLApprovalRequest for the new
outbound-webhook surface introduced in
getaxonflow/axonflow-enterprise#2419. All fields use
#[serde(skip_serializing_if = "Option::is_none", default)] so
the SDK round-trips cleanly against platforms that don't yet
implement the field.
- src/client.rs — exposes checked_post_json crate-internal helper
for sibling modules that POST a typed payload (symmetric to the
existing checked_get helper).
- src/lib.rs + src/types/mod.rs — wire up the new module + re-export
the public HITL types from the crate root.
- src/hitl.rs::tests — 18 contract tests using wiremock covering:
list happy-path + filter serialization, get happy-path + empty-id
guard + 404 propagation, create happy-path + minimal
required-fields + bad-notify_url-scheme 400 propagation + 401
propagation + connect-failure propagation + the three
required-field validation guards, approve + reject path/body shape
+ empty-id guard, stats envelope parsing, and a unit test for the
build_list_query field-omission contract.
- runtime-e2e/create_hitl_request/ — real reqwest + hyper TCP
socket proof. Asserts the wire body carries every required field
plus notify_url, and the response envelope parses back into a
populated HITLApprovalRequest. Satisfies the runtime-e2e/ DoD
gate that the in-process wiremock-based unit tests do not.
- CHANGELOG.md + Cargo.toml — bump 0.2.0 -> 0.4.0 (skipping 0.3 to
align with the v9.1 telemetry preview train; both ship at v0.4).
Cross-SDK parity sweep tracked at
getaxonflow/axonflow-enterprise#2421. Sister Python PR landed at
getaxonflow/axonflow-sdk-python#202; TypeScript PR at
getaxonflow/axonflow-sdk-typescript#235; Go PR at
getaxonflow/axonflow-sdk-go#175; Java PR at
getaxonflow/axonflow-sdk-java#185.
Refs getaxonflow/axonflow-enterprise#2421
Refs getaxonflow/axonflow-enterprise#2419
Refs getaxonflow/axonflow-enterprise#2393
Signed-off-by: Saurabh Jain <saurabh.jain@getaxonflow.com>
…lass R3 round 1 surfaced two folds against the Java HITL PR: - MED-1: HITLTest.setUp called .endpoint(wmRuntimeInfo.getHttpBaseUrl()) twice on the same builder (copy-paste bug, harmless at runtime but a real code-smell). Collapsed to a single chained call. - MED-5: All three platform-error tests (400 bad-notify_url-scheme, 401 auth, network failure via CONNECTION_RESET_BY_PEER) asserted .isInstanceOf(Exception.class) which would pass even on NullPointerException. Narrowed to AxonFlowException.class to match the @throws AxonFlowException javadoc at AxonFlow.java:6440 and the required-field guard tests at lines 387/397/407 which already narrow to IllegalArgumentException. Tests still green: 25/25 in HITLTest (8 under CreateHITLRequest). Refs getaxonflow/axonflow-enterprise#2421 Signed-off-by: Saurabh Jain <saurabh.jain@getaxonflow.com>
saurabhjain1592
added a commit
to getaxonflow/axonflow-sdk-rust
that referenced
this pull request
May 23, 2026
…n v1 (#49) * feat(hitl): port full HITL surface (list/get/create/approve/reject/stats) Cross-SDK parity bring-up for HITL (Human-in-the-Loop) approval workflows. Prior to this release the Rust SDK exposed NO HITL methods at all — the four stable SDKs (Python/TS/Go/Java) all ship the read + review surface (list_hitl_queue, get_hitl_request, approve_hitl_request, reject_hitl_request, get_hitl_stats), plus the new create_hitl_request method introduced in v8.2.0 of each. This release ports the full surface in one shot so Rust callers can implement the full 4-step HITL flow against AxonFlow: 1. Gate evaluates require_approval (via pre_check / check_tool_input) 2. Caller invokes client.create_hitl_request(...) to enqueue the row 3. Caller polls client.get_hitl_request(approval_id) until terminal state — OR sets notify_url so the platform fires a signed webhook on the transition (n8n Wait-node "On Webhook Call" pattern, ADK plugin polling-free mode) 4. Caller resumes the agent or denies the call based on the decision Changes: - src/hitl.rs (new) — 6 public methods (list_hitl_queue, get_hitl_request, create_hitl_request, approve_hitl_request, reject_hitl_request, get_hitl_stats) using PATH_SEGMENT-encoded path components + APIResponse{success,data} envelope parsing. Validates required fields client-side via AxonFlowError::ConfigError (mirrors the existing decisions::explain_decision empty-id guard pattern). - src/types/hitl.rs (new) — HITLApprovalRequest / HITLCreateInput / HITLReviewInput / HITLQueueListOptions / HITLQueueListResponse / HITLStats. notify_url field added to both HITLCreateInput and HITLApprovalRequest for the new outbound-webhook surface introduced in getaxonflow/axonflow-enterprise#2419. All fields use #[serde(skip_serializing_if = "Option::is_none", default)] so the SDK round-trips cleanly against platforms that don't yet implement the field. - src/client.rs — exposes checked_post_json crate-internal helper for sibling modules that POST a typed payload (symmetric to the existing checked_get helper). - src/lib.rs + src/types/mod.rs — wire up the new module + re-export the public HITL types from the crate root. - src/hitl.rs::tests — 18 contract tests using wiremock covering: list happy-path + filter serialization, get happy-path + empty-id guard + 404 propagation, create happy-path + minimal required-fields + bad-notify_url-scheme 400 propagation + 401 propagation + connect-failure propagation + the three required-field validation guards, approve + reject path/body shape + empty-id guard, stats envelope parsing, and a unit test for the build_list_query field-omission contract. - runtime-e2e/create_hitl_request/ — real reqwest + hyper TCP socket proof. Asserts the wire body carries every required field plus notify_url, and the response envelope parses back into a populated HITLApprovalRequest. Satisfies the runtime-e2e/ DoD gate that the in-process wiremock-based unit tests do not. - CHANGELOG.md + Cargo.toml — bump 0.2.0 -> 0.4.0 (skipping 0.3 to align with the v9.1 telemetry preview train; both ship at v0.4). Cross-SDK parity sweep tracked at getaxonflow/axonflow-enterprise#2421. Sister Python PR landed at getaxonflow/axonflow-sdk-python#202; TypeScript PR at getaxonflow/axonflow-sdk-typescript#235; Go PR at getaxonflow/axonflow-sdk-go#175; Java PR at getaxonflow/axonflow-sdk-java#185. Refs getaxonflow/axonflow-enterprise#2421 Refs getaxonflow/axonflow-enterprise#2419 Refs getaxonflow/axonflow-enterprise#2393 Signed-off-by: Saurabh Jain <saurabh.jain@getaxonflow.com> * fold(hitl): R3 R2 — runtime-e2e shell add -e flag R3 round 2 caught this: `set -uo pipefail` should be `set -euo pipefail` so command failures inside the script abort it. Without `-e` the runtime-e2e gate can green-light a broken SDK. Refs getaxonflow/axonflow-enterprise#2421 Signed-off-by: Saurabh Jain <saurabh.jain@getaxonflow.com> * fix(ci): cargo fmt collapsable Some() / closure blocks in hitl tests Closes the cargo fmt --check gate on PR #49. The R3 round 1 folds (rebase + the bad-scheme test) left a handful of `Some(\n "long-string".into(),\n)` blocks that rustfmt collapses to a single line, plus one `.respond_with(\n ...\n)` chain that should be a single call. Applied `cargo fmt` — mechanical-only diff, no semantic change. Verified locally: $ cargo fmt --check (no output) $ cargo test --lib hitl test result: ok. 18 passed; 0 failed HARD RULE 7 (CI green before SHIP-READY) — captured rule [[feedback_r3_does_not_imply_ci_green]] from master verification. Refs getaxonflow/axonflow-enterprise#2421 Signed-off-by: Saurabh Jain <saurabh.jain@getaxonflow.com> --------- Signed-off-by: Saurabh Jain <saurabh.jain@getaxonflow.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Adds
AxonFlow#createHITLRequest(...)(sync) andcreateHITLRequestAsync(...)(CompletableFuture) for explicitcreation of HITL approval rows via
POST /api/v1/hitl/queue. PairedSDK release that unblocks the Google ADK plugin v1 fixup
(getaxonflow/axonflow-enterprise#2410) and is the Java half of the
cross-SDK parity sweep tracked at
getaxonflow/axonflow-enterprise#2421.
Why now
The platform's
POST /api/v1/hitl/queueendpoint has existed sincev6.x (handler at
platform/agent/hitl/handler.go:177). The Java SDKexposed the read + review surface but had no create counterpart.
Agent-framework callers (future ADK Java plugin) detecting
require_approvalneed to create the row themselves before polling.Changes
src/main/java/com/getaxonflow/sdk/AxonFlow.java—createHITLRequest(HITLCreateInput) -> HITLApprovalRequest+createHITLRequestAsync(HITLCreateInput) -> CompletableFuture<HITLApprovalRequest>.src/main/java/com/getaxonflow/sdk/types/hitl/HITLTypes.java—HITLCreateInputPOJO + Builder mirroringplatform/agent/hitl/handler.go:86 CreateRequestInput. NewnotifyUrlfield on bothHITLCreateInputandHITLApprovalRequest(companion to platform work ingetaxonflow/axonflow-enterprise#2419).
src/test/java/com/getaxonflow/sdk/HITLTest.java— 8 new JUnitcases under
@Nested CreateHITLRequest.runtime-e2e/create_hitl_request/— real OkHttp +com.sun.net.httpserver.HttpServerproof.CHANGELOG.md+pom.xml— bump 8.1.0 → 8.2.0 (additive; nobreaking changes).
Test plan
./mvnw test -Dtest=HITLTestPASS (25 tests, 8 new under@Nested CreateHITLRequest)./mvnw -q -DskipTests compilecleanCross-SDK parity coverage
create_hitl_requestHITLCreateInputcreateHITLRequestHITLCreateInputCreateHITLRequestHITLCreateInputcreateHITLRequest+createHITLRequestAsyncHITLCreateInputcreate_hitl_request(+ full HITL surface port)HITLCreateInputMethod shape (input type, return type, validation guards, error
propagation) intentionally mirrors the existing
approveHITLRequest/
rejectHITLRequestpatterns to keep cross-SDK + intra-SDK coherenceload-bearing for future ADK Java/Go/TS/Kotlin plugins.
Refs getaxonflow/axonflow-enterprise#2421 (umbrella),
getaxonflow/axonflow-enterprise#2419 (notify_url platform companion),
getaxonflow/axonflow-enterprise#2393 (driving epic),
getaxonflow/axonflow-enterprise#2410 (ADK plugin v1 fixup)