This release is a pre-release and may not be stable for production use.
HaltState SDK
Action control and Proof Packs for autonomous AI agents. Perform pre-action checks, approvals, and post-action reporting with minimal code.
Semiotic Probe (source candidate, not activated)
probe = client.connect_semiotic_probe("openai", "gpt-4.1", lambda raw, limit: isolated_model(raw, max_tokens=limit))
# Or use probe.poll_once() and probe.close() in an application-owned lifecycle.
The hook must be fresh and isolated: no history, memory, retrieval, tools, or side effects.
Canonical public base URL: https://haltstate.ai
Canonical governance namespace: /api/haltstate/sentinel/*
Current SDKs default to https://haltstate.ai. Guard endpoints use the branded /api/haltstate/sentinel/* namespace when available. The released check(), report(), and approval-polling paths still use supported legacy /api/sentinel/* compatibility aliases until matching branded backend routes are registered.
Replay safety status
Replay-ledger hardening is implemented and locally verified in this source tree. Production activation remains pending the caller-first quiesce/drain window, migration 106, matching API/SDK deployment with no mixed workers, and the delayed/replay route smokes; this is not a production-live claim.
On the hardened path, each tenant and idempotency key binds to one exact operation: agent, action, resource, normalized parameters, and risk class. The stored decision carries the evaluated policy version; a retry with changed operation data is rejected. Approvals expire, and an eligible operation receives at most one permit. Exact outcome-report retries receive the original receipt.
The durable lifecycle is recorded in an append-only, hash-chained event log. That log is not externally anchored or immune to a database superuser. Destination idempotency remains the customer's responsibility: HaltState cannot guarantee exactly-once execution inside an external payment, email, or infrastructure system. Hosted LLM inference is likewise not claimed to be bit-for-bit deterministic.
Post-cutover idempotency epoch
During the controlled replay-ledger cutover, configure the UUID epoch issued for that activation:
client = HaltStateClient(
tenant_id="acme",
api_key="hs_xxx",
idempotency_epoch="11111111-2222-4333-8444-555555555555",
)
After configuration, guard() calls that omit idempotency_key generate a
qualified key in the form hsr1:<epoch>:<uuid>. The SDK never prefixes,
rewrites, or upgrades an explicit caller-supplied key. Consequently, explicit
keys sent after cutover must already be fully qualified for the active epoch.
Keep idempotency_epoch unset only for preactivation compatibility, where
omitted keys retain the legacy plain-UUID behavior.
Keep this HaltState guard key separate from the destination's stable business
idempotency key; the destination key must not rotate with the guard epoch.
Configure the epoch only after guarded callers and schedulers are quiesced, guard ingress is closed, legacy API workers are stopped, and old work is drained or reconciled. Reopen with no mixed workers. Once epoch-qualified work is admitted, do not roll back to a pre-replay binary under open ingress; remain fail closed and roll forward.
Installation
PyPI currently serves 0.7.0. The replay-safe contract in this tree is the
unpublished 0.8.0.dev0 candidate and must be installed from the reviewed
source checkout until it is released.
# Published baseline:
pip install haltstate-sdk
# Replay-safe source candidate, from the repository root:
pip install ./packages/haltstate-sdk
Quickstart (sync)
from haltstate import HaltStateClient
client = HaltStateClient(
tenant_id="your_tenant_id",
api_key="hs_xyz",
base_url="https://haltstate.ai",
fail_open=False,
)
decision = client.check(
action="payment.process",
params={"amount": 5000, "currency": "USD"},
agent_id="payment-bot-01",
)
if decision.allowed:
process_payment(...)
client.report(decision, status="success", result={"transaction_id": "tx_123"}, action="payment.process", agent_id="payment-bot-01")
elif decision.requires_approval:
print(f"Approval required: {decision.reason}")
else:
print(f"Action denied: {decision.reason}")
Guard Pattern
For actions requiring human approval, use the idempotent guard pattern:
from haltstate import HaltStateClient, ApprovalPending, ActionDenied
def process_high_value_payment(invoice_id: str, amount: float):
operator_epoch = "11111111-2222-4333-8444-555555555555"
destination_key = f"payment-{invoice_id}"
guard_key = f"hsr1:{operator_epoch}:{destination_key}"
report_id = "40000000-0000-4000-8000-000000000501"
client = HaltStateClient(
tenant_id="acme",
api_key="hs_xxx",
base_url="https://haltstate.ai",
)
try:
with client.guard(
action="payment.process",
agent_id="payment-bot",
params={"invoice_id": invoice_id, "amount": amount},
idempotency_key=guard_key,
resource=f"invoice/{invoice_id}",
risk_class="high",
report_id=report_id,
) as permit:
permit.validate_for_execution()
result = execute_payment_once(
invoice_id=invoice_id,
amount=amount,
idempotency_key=destination_key,
)
print(f"Approved by {permit.approver} at {permit.approved_at}")
return result
except ApprovalPending:
print("Awaiting human approval...")
return {"status": "pending"}
except ActionDenied as e:
print(f"Denied: {e.reason}")
raise
Key features:
- Exact operation binding: The same key and same proposal replay the stored decision; changed agent, action, resource, risk, or parameters conflict.
- Expiring approval authority: Approvals expire, and the SDK refuses an expired executable response before entering the action.
- One-permit rule: A retry after permit issuance receives an
ActionAlreadyStartederror, not replacement authority. A lost permit or missing outcome report requires destination reconciliation. - Durable outcome reporting: Exact report retries reuse their receipt;
exhausted reporting retries raise
OutcomeReportError. - Replay evidence: Policy-versioned decision, approval, permit, and outcome events form an append-only hash chain.
Retries while an approval is still pending can safely poll the same exact operation. A restart after permit issuance must not blindly execute the action again; reconcile against an idempotent destination first.
Persist the permit, report_id, outcome, and exact payload before an outcome
retry. If the context manager raises OutcomeReportError after the side effect
returns, resume only the receipt call:
receipt = client.report_guard_outcome(
permit,
report_id=report_id,
outcome="success",
result={"destination_key": destination_key},
)
Do not rerun the payment to obtain that receipt. Error outcomes use the same
public method with outcome="error" and the same caller-stable UUID.
Decorators
from haltstate import HaltStateClient, haltstate_guard
client = HaltStateClient(tenant_id="acme", api_key="hs_xxx", base_url="https://haltstate.ai")
@haltstate_guard(client, action="email.send", agent_id="email-bot")
def send_email(to, subject, body):
return mailer.send(to, subject, body)
Async
from haltstate import AsyncHaltStateClient
async def main():
async with AsyncHaltStateClient(tenant_id="acme", api_key="hs_xxx", base_url="https://haltstate.ai") as client:
res = await client.check("database.drop", params={"table": "users"})
if res.allowed:
await client.report(res, status="success", action="database.drop", agent_id="ops-bot")
Exceptions
from haltstate import (
HaltStateError, # Base error
HaltStateAuthError, # Invalid API key
HaltStateConnectionError, # Network/timeout
ApprovalPending, # Awaiting approval (guard pattern)
ActionDenied, # Human rejected (guard pattern)
ActionExpired, # Approval expired (guard pattern)
ActionAlreadyStarted, # Permit already issued; never rerun the action
OutcomeReportError, # Durable outcome receipt was not confirmed
)
Docs
Full documentation at haltstate.ai/docs:
- Quickstart - 5-minute setup
- Guard Pattern - HITL approval flow
- API Reference - All methods
- Error Handling - Exception handling
- Governance alignment - Operational evidence mapping
Legacy route aliases remain supported for existing installations, but new integrations should target the HaltState-branded public contract.
Legacy migration note
If upgrading from the legacy package/import namespace:
Existing code using the legacy import namespace continues to work, but new integrations should use from haltstate import ....
# Legacy (still works)
from janus import JanusClient, janus_guard
# New
from haltstate import HaltStateClient, haltstate_guard
Both import styles work - no code changes required for existing users.
License
MIT
Release files for haltstate-sdk 0.8.0.dev0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| haltstate_sdk-0.8.0.dev0.tar.gz | 35.7 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| haltstate_sdk-0.8.0.dev0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 66.3 kB
Release files / haltstate_sdk-0.8.0.dev0.tar.gz
| Download URL | haltstate_sdk-0.8.0.dev0.tar.gz |
|---|---|
| Size | 35.7 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
168757bbcf20a71d423a6b487c37a1a02af3f8590f2890e0e9ba026962d2c74f
|
|
BLAKE2b-256 checksum How to use checksums |
0ea6615c238e2c632eb0037c5b7d5bd3876ebbcc98f171b6d5f9c5c274ac5cd2
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.10.9 {"installer":{"name":"uv","version":"0.10.9","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
|
Release files / haltstate_sdk-0.8.0.dev0-py3-none-any.whl
| Download URL | haltstate_sdk-0.8.0.dev0-py3-none-any.whl |
|---|---|
| Size | 30.7 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
e3c480de54327e9ef95dbbd664f83166582d2d3f00f5e81906b763e074913cf8
|
|
BLAKE2b-256 checksum How to use checksums |
fe3d328275cb7e746173a1431fc55a9b1712f704187b88d5064f42d8a77eade5
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.10.9 {"installer":{"name":"uv","version":"0.10.9","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
|