Metadata-Version: 2.4
Name: haltstate-sdk
Version: 0.8.0.dev0
Summary: HaltState Python SDK - action control and Proof Packs for AI agents
Author-email: Krystal Unity <support@krystalunity.com>
License: MIT License
        
        Copyright (c) 2025 Krystal Unity
        
        Permission is hereby granted, free of charge, to any person obtaining a copy
        of this software and associated documentation files (the "Software"), to deal
        in the Software without restriction, including without limitation the rights
        to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
        copies of the Software, and to permit persons to whom the Software is
        furnished to do so, subject to the following conditions:
        
        The above copyright notice and this permission notice shall be included in all
        copies or substantial portions of the Software.
        
        THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
        IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
        FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
        AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
        LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
        OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
        SOFTWARE.
        
Project-URL: Homepage, https://haltstate.ai
Project-URL: Repository, https://github.com/StartKrystal/krystal-unity-core
Project-URL: Issues, https://github.com/StartKrystal/krystal-unity-core/issues
Project-URL: Documentation, https://haltstate.ai/docs
Keywords: haltstate,ai,policy,sdk,action-control,proof-packs,governance
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Classifier: Topic :: Security
Requires-Python: >=3.9
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: httpx>=0.24
Requires-Dist: cryptography>=41
Dynamic: license-file

# HaltState SDK

[![PyPI](https://img.shields.io/pypi/v/haltstate-sdk.svg)](https://pypi.org/project/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)

```python
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:

```python
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.

```bash
# Published baseline:
pip install haltstate-sdk

# Replay-safe source candidate, from the repository root:
pip install ./packages/haltstate-sdk
```

## Quickstart (sync)
```python
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:

```python
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
  `ActionAlreadyStarted` error, 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:

```python
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
```python
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
```python
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

```python
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](https://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 ...`.

```python
# 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
