Skip to content

feat(hitl): add createHITLRequest — paired SDK release for ADK plugin v1#185

Merged
saurabhjain1592 merged 2 commits into
mainfrom
feat/2421-create-hitl-request
May 23, 2026
Merged

feat(hitl): add createHITLRequest — paired SDK release for ADK plugin v1#185
saurabhjain1592 merged 2 commits into
mainfrom
feat/2421-create-hitl-request

Conversation

@saurabhjain1592
Copy link
Copy Markdown
Member

Summary

Adds AxonFlow#createHITLRequest(...) (sync) and
createHITLRequestAsync(...) (CompletableFuture) for explicit
creation of HITL approval rows via POST /api/v1/hitl/queue. Paired
SDK 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/queue endpoint has existed since
v6.x (handler at platform/agent/hitl/handler.go:177). The Java SDK
exposed the read + review surface but had no create counterpart.
Agent-framework callers (future ADK Java plugin) detecting
require_approval need 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
    HITLCreateInput POJO + Builder mirroring
    platform/agent/hitl/handler.go:86 CreateRequestInput. New
    notifyUrl field on both HITLCreateInput and
    HITLApprovalRequest (companion to platform work in
    getaxonflow/axonflow-enterprise#2419).
  • src/test/java/com/getaxonflow/sdk/HITLTest.java — 8 new JUnit
    cases under @Nested CreateHITLRequest.
  • runtime-e2e/create_hitl_request/ — real OkHttp +
    com.sun.net.httpserver.HttpServer proof.
  • CHANGELOG.md + pom.xml — bump 8.1.0 → 8.2.0 (additive; no
    breaking changes).

Test plan

  • ./mvnw test -Dtest=HITLTest PASS (25 tests, 8 new under @Nested CreateHITLRequest)
  • ./mvnw -q -DskipTests compile clean
  • No regression on existing HITL test cases

Cross-SDK parity coverage

SDK Method name Type name Status
Python create_hitl_request HITLCreateInput getaxonflow/axonflow-sdk-python#202 (open)
TypeScript createHITLRequest HITLCreateInput getaxonflow/axonflow-sdk-typescript#235 (open)
Go CreateHITLRequest HITLCreateInput getaxonflow/axonflow-sdk-go#175 (open)
Java createHITLRequest + createHITLRequestAsync HITLCreateInput this PR
Rust create_hitl_request (+ full HITL surface port) HITLCreateInput in-flight

Method shape (input type, return type, validation guards, error
propagation) intentionally mirrors the existing approveHITLRequest
/ rejectHITLRequest patterns to keep cross-SDK + intra-SDK coherence
load-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)

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>
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 saurabhjain1592 merged commit 0300bae into main May 23, 2026
18 checks passed
@saurabhjain1592 saurabhjain1592 deleted the feat/2421-create-hitl-request branch May 23, 2026 10:44
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>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant