6.12. HITL Client¶
The HITL Client talks to the sw4rm.hitl.HitlService service. Use it to:
- Escalate decisions to human operators when policy or risk requires it.
- Provide structured context and candidate actions for review.
- Capture the human decision payload and rationale.
6.12.1. Service Overview¶
The service exposes one RPC:
Decide(HitlInvocation) -> HitlDecision
Invocations are often generated by the Scheduler and carried in an Envelope payload. The HITL service provides the decision side of that workflow.
HitlReasonType values¶
| Value | Description |
|---|---|
HITL_REASON_UNSPECIFIED | Default/unknown reason |
CONFLICT | Agent or policy conflict |
SECURITY_APPROVAL | Security-sensitive approval required |
TASK_ESCALATION | Task needs human escalation |
MANUAL_OVERRIDE | Manual override requested |
WORKTREE_OVERRIDE | Worktree switch requires approval |
DEBATE_DEADLOCK | Debate or negotiation is deadlocked |
TOOL_PRIVILEGE_ESCALATION | Tool privilege escalation |
CONNECTOR_APPROVAL | Connector action requires approval |
HitlInvocation fields¶
| Field | Type | Description |
|---|---|---|
reason_type | HitlReasonType | Escalation reason |
context | bytes | Serialized context (often JSON) |
proposed_actions | list[string] | Candidate actions for the operator |
priority | int | Priority hint for operator queues |
HitlDecision fields¶
| Field | Type | Description |
|---|---|---|
action | string | Selected action |
decision_payload | bytes | Optional serialized payload |
rationale | string | Human rationale or notes |
6.12.2. Constructors¶
Python¶
HitlClient(channel: grpc.Channel)
channel: A gRPC channel connected to the HitlService endpoint.
JavaScript/TypeScript¶
new HITLClient(options: ClientOptions)
options.address:host:portfor the HitlService endpoint.- Optional:
deadlineMs,retry,userAgent,interceptors,errorMapper.
Rust¶
HitlClient::new(endpoint: &str) -> Result<HitlClient>
endpoint: Full gRPC URL (for example,http://host:port).
6.12.3. Key Methods¶
decide¶
Python decide(invocation: dict) -> HitlDecision
JavaScript/TypeScript decide(inv: HitlInvocation): Promise<HitlDecision>
Rust decide(&mut self, reason_type: i32, context: Vec<u8>, proposed_actions: Vec<String>, priority: i32) -> Result<HitlDecisionResponse>
Response fields
action(string): Selected action.decision_payload(bytes): Optional decision payload.rationale(string): Human rationale or notes.
6.12.4. Usage Examples¶
import grpc
import json
from sw4rm.clients import HitlClient
channel = grpc.insecure_channel("HITL_HOST:HITL_PORT")
client = HitlClient(channel)
invocation = {
"reason_type": "SECURITY_APPROVAL",
"context": json.dumps({
"operation": "deploy",
"environment": "prod",
"change_id": "chg-42",
}).encode("utf-8"),
"proposed_actions": ["approve", "reject"],
"priority": 5,
}
decision = client.decide(invocation)
print(decision.action, decision.rationale)
import { HITLClient } from '@sw4rm/js-sdk';
const client = new HITLClient({
address: 'HITL_HOST:HITL_PORT',
deadlineMs: 20000,
});
const encoder = new TextEncoder();
const decision = await client.decide({
reason_type: 'SECURITY_APPROVAL',
context: encoder.encode(JSON.stringify({
operation: 'deploy',
environment: 'prod',
change_id: 'chg-42',
})),
proposed_actions: ['approve', 'reject'],
priority: 5,
});
console.log(decision.action, decision.rationale);
use sw4rm_sdk::clients::HitlClient;
use sw4rm_sdk::proto::sw4rm::common::HitlReasonType;
#[tokio::main]
async fn main() -> sw4rm_sdk::Result<()> {
let mut client = HitlClient::new("http://HITL_HOST:HITL_PORT").await?;
let context = serde_json::json!({
"operation": "deploy",
"environment": "prod",
"change_id": "chg-42",
});
let decision = client
.decide(
HitlReasonType::SecurityApproval as i32,
serde_json::to_vec(&context)?,
vec!["approve".to_string(), "reject".to_string()],
5,
)
.await?;
println!("action={} rationale={}", decision.action, decision.rationale);
Ok(())
}
6.12.5. HITL Absence Policy¶
Deployments without a HITL component MUST define a policy for how to proceed when human decisions are required. Per spec §15.3:
-
Deny-by-default (RECOMMENDED for security-sensitive deployments): Reject the operation that triggered the escalation.
-
Threshold-based auto-decision: Apply automatic decisions based on configurable score thresholds when confidence is sufficient.
The Scheduler MUST document and log which fallback was applied. Components MUST NOT block indefinitely waiting for human input when no HITL component is available.
6.12.6. HITL Unavailability During Negotiation Timeout¶
When a negotiation timeout fires and requires HITL escalation (e.g., DEBATE_DEADLOCK), but the HITL component is unavailable, the Scheduler MUST handle the situation per spec §15.4:
-
Detect HITL unavailability: The Scheduler MUST detect unavailability within a bounded time (RECOMMENDED: 5 seconds) through health checks, connection failures, or response timeouts.
-
Apply fallback policy: The Scheduler MUST apply the configured
hitl_unavailable_policy. Valid policy values:
| Policy | Behavior |
|---|---|
DENY_BY_DEFAULT | Abort the negotiation with error_code=hitl_unavailable. RECOMMENDED default for security-sensitive deployments. |
AUTO_DECIDE_THRESHOLD | If the highest-scoring proposal exceeds a configured auto-approve threshold, accept it automatically; otherwise abort. |
EXTEND_TIMEOUT | Extend the negotiation timeout by a configured duration (RECOMMENDED: 1x original) and retry HITL escalation. Maximum retry count RECOMMENDED: 3. |
-
Log and notify: The Scheduler MUST log the HITL unavailability event with negotiation context and the fallback action taken. Implementations SHOULD emit an alert to operators.
-
Preserve audit trail: The decision record MUST indicate that the decision was made via fallback policy due to HITL unavailability, including the policy applied and timestamp.
Working Examples¶
For complete runnable examples demonstrating HITL usage:
6.12.7. Error Handling¶
- Python raises
RuntimeErrorif protobuf stubs are missing. Runmake protos. - JavaScript/TypeScript methods reject with gRPC errors (wrapped as
Sw4rmError). - Rust returns
Result<T>; handle transport errors and status codes via?.