Metadata-Version: 2.5
Name: aegis-smart-accounts
Version: 0.4.0
Summary: Python SDK and resumable onboarding CLI for Aegis Smart Accounts
License: MIT
Requires-Python: >=3.10
Requires-Dist: eth-abi>=5.1
Requires-Dist: eth-account>=0.13
Requires-Dist: requests>=2.31
Requires-Dist: web3>=7.0
Description-Content-Type: text/markdown

# Aegis Python SDK

The managed-bundler handoff documented here requires SDK `0.4.0` or newer.
The runtime check, typed lifecycle, and local explanation APIs require `0.2.0`
or newer. Verify the installed package with
`python -c "import aegis_sdk; print(aegis_sdk.__version__)"`.

The released SDK is published as [`aegis-smart-accounts`](https://pypi.org/project/aegis-smart-accounts/).
It requires Python 3.10 or newer. Install it in an isolated environment:

```bash
python3 -m venv .venv
source .venv/bin/activate
python -m pip install --upgrade pip
python -m pip install aegis-smart-accounts
```

For repository development instead, use `python -m pip install -e sdk/python`.

`aegis init` records only public identifiers and addresses in a mode-0600 state
file so an interrupted setup can resume. Before its first write it records an
operation marker, which lets a retry rediscover an agent or policy whose HTTP
response was lost. Account deployment, policy activation, and permission mint
also reconcile their durable transaction records instead of broadcasting a
second nondeterministic resource. Exit code `2` means an on-chain operation is
still pending and the same command/state file should be resumed. `PrivateKeySigner` is a development
adapter that holds its key in the running process; production agents should
use `CallbackSigner` with a wallet, HSM, secret manager, or other signing
service.

```bash
export AEGIS_API_KEY=aegissa_...
aegis init --name my-agent --owner 0x... --signer 0x... \
  --chain-id 11155111 --policy-file /path/to/policy.json
aegis doctor AGENT_UUID --permission-id PERMISSION_UUID --asset native
# Equivalent flag form:
aegis doctor --agent-id AGENT_UUID --permission-id PERMISSION_UUID --asset native
```

The shell command `aegis doctor` is distinct from the MCP tool
`aegis_doctor`; use the latter only through the configured MCP server.
Doctor may use an expiring `AEGIS_RUNTIME_API_KEY` with `agent:read` scope.
Issued runtime credentials use the `aegisrt_` prefix; the SDK rejects a
management-format key placed in that variable, because renaming a key does not
narrow its authority.
Provisioning commands still require the wallet-scoped management key and must
run in a trusted control-plane environment. Legacy `erc8004_` management keys
remain accepted during the migration.

For a bounded, API-key-free policy explanation, use
`explain_action_locally(request, rpc_url=..., capability=...)`. The capability
must explicitly identify the account generation, policy-only mode, bound
EntryPoint/enforcer/resolver addresses, all dependency runtime hashes, and the
normalized smart-account executable profile. Use
`load_local_capability(chain_id, generation=...)` to load this data from the
same generated registry consumed by the backend and MCP package. The SDK
re-reads and pins that tuple at one block before evaluating resolver-derived
calldata. Exact verdicts also require a capability promoted by the repository's
versioned conformance-evidence input; the packaged registry keeps untested
tuples unpromoted. Missing, unpromoted, or mismatched
capability data, resolver/read failures, and unknown generations remain
`policyDecision: "unknown"`; a policy pass never means that funding, target
execution, or the submission route is ready. The local lane never signs or
submits a UserOperation.

The API key is sent in the `X-API-Key` header. `Authorization: Bearer` is for
JWTs, not Aegis API keys. Keep the key in secure runtime storage and do not put
it in an agent prompt or checked-in configuration.

A minimal Sepolia policy example is available in the repository at
[`examples/minimal-sepolia-policy.json`](https://github.com/aegisinfra/aegis/blob/develop/sdk/python/examples/minimal-sepolia-policy.json).
Use an explicit policy file that matches the account and chain you intend to
operate on.

Use `permission_calldata(permission_id, calldata)` to create the exact Aegis
permission envelope. Never select a permission by “newest”; pass the permission
UUID or on-chain bytes32 ID explicitly and run doctor immediately before use.

The authenticated client intentionally exposes only routes registered by the
API. It does not invent `/limits`, `/quota`, or `/usage` endpoints. Use
`RuntimeChecker` and `ChainReader` for authoritative typed on-chain reads of
the account binding, permission, per-asset limits, shared usage, quota, reset
time, EntryPoint state, and bundler capabilities. A configured zero daily or
transaction-count limit is unbounded even though the corresponding on-chain
quota return is zero. Usage is shared across permissions for the same on-chain
agent and asset and resets at the fixed UTC-day boundary anchored by the first
recorded spend.

`build_execute` and `build_execute_batch` encode policy-only account calls.
`sign_userop` uses the EIP-191 encoding expected by the account. The preferred
`SafeUserOperation`/`UserOperationLifecycle` path signs for estimation, forces
a fresh signature after gas fields are returned, and defaults to no submission.
The legacy `estimate_userop` and `submit_userop` helpers remain for compatible
low-level integrations, but emit deprecation warnings because they cannot
prove signature freshness. Both reject an unsigned operation before RPC.
Use `wait_for_userop_receipt` after submission. It reads the mined transaction
hash from the nested `receipt.transactionHash` field and exposes it as
`tx_hash`; the top-level `userOpHash` is not an EVM transaction hash.

The SDK is the execution helper, not a hosted bundler. The safe SDK lane uses a
bundler route; self-submit is not silently inferred from a missing URL. A
dashboard handoff may point `AEGIS_BUNDLER_URL` at Aegis's managed runtime
gateway. In that case the SDK sends the bound `aegisrt_` credential only to the
exact `AEGIS_API_URL` origin and `/api/v1/runtime/bundler/<chain-id>` path. It
never forwards that credential to the read RPC, a custom bundler, a lookalike
host, or a URL with query parameters. For the
full operator handoff, see the [Aegis Quick Start](../../docs/AGENT_SMART_ACCOUNT_QUICKSTART.md)
and [Agent Golden Path](https://app.projectaegis.ai/docs/agent-golden-path).

## Handoff bundles

The dashboard can package the agent handoff as a directory or `.zip` using the
`aegis.handoff.v1` contract. The bundle is explicit and portable: the SDK never
searches the bundle directory for likely environment files.

```text
handoff.json
<agent-slug>.runtime.env
secrets/runtime-secrets.env
secrets/<agent>.signer.env       # generated local signer only
first-action.example.json        # optional
START-HERE.md
```

`handoff.json` contains `schemaVersion`, `createdAt`, the public `agent`
bindings (`uuid`, `name`, `chainId`, `smartAccount`, `onchainAgentId`,
`permissionId`, `signerAddress`, and `signerType`), `files`, `runbookUrl`, and
an `integrity` map. The integrity map must cover each referenced file exactly
once as `relative/path: "sha256:<64 hex characters>"`; it intentionally does
not self-reference `handoff.json`, because a manifest cannot contain its own
hash. File paths are relative, and the runtime descriptor must explicitly
contain:

```dotenv
AEGIS_RUNTIME_SECRET_FILE=./secrets/runtime-secrets.env
AEGIS_SIGNER_SECRET_FILE=./secrets/my-agent.signer.env
```

The signer reference is required for `signerType: generated` and is omitted
for wallet/external signer handoffs. The runtime-secrets file accepts only
`AEGIS_BUNDLER_URL`, `AEGIS_RPC_URL`, `AEGIS_RUNTIME_API_KEY`, and
`AEGIS_RUNTIME_CREDENTIAL_EXPIRES_AT`. The runtime key must be an issued
`aegisrt_` credential. The expiry may instead live in the public descriptor,
as dashboard ZIPs do. Management keys, signer private keys, and unrelated
environment variables are rejected.

The guided dashboard starts with a credential-free public read RPC. Public
RPCs are convenient but may be rate-limited; replace only `AEGIS_RPC_URL` with
an owner-approved, agent-specific endpoint when the workload needs more
capacity. Base, Base Sepolia, and Sepolia handoffs can use Aegis-managed
submission by default. Custom ERC-4337 bundlers remain supported, but their
provider credentials are visible to the agent and should be separately scoped.

Validate a bundle before using it. By default this also runs the existing
read-only runtime check and an optional first-action dry run; `--offline`
performs only local schema, binding, and integrity validation:

```bash
aegis handoff check /path/to/agent-handoff.zip
aegis handoff check /path/to/agent-handoff --offline --json
```

Install creates a new owner-only tree. It rejects zip-slip paths, archive
symlinks, directory symlinks in referenced paths, duplicate/extra zip files,
manifest binding mismatches, and integrity failures. The installed directories
are mode `0700` and all installed files are mode `0600`:

```bash
aegis handoff install /path/to/agent-handoff.zip \
  --destination ~/.aegis/handoffs/my-agent
```

The command prints the exact runtime descriptor and optional first-action paths
to use with the existing commands. Relative secret references are resolved
relative to the descriptor, and `aegis runtime check --env-file ...` plus
`aegis action run --env-file ... --request-file ...` automatically consume the
explicit `AEGIS_RUNTIME_SECRET_FILE`. Existing standalone `--env-file` flows
remain unchanged; no process-wide management key is imported by the handoff
commands. The default install directory is `~/.aegis/handoffs/<safe agent slug>`;
the dashboard uses the same slug when writing its agent instructions. The
frontend may also include a public `policy` metadata object in the manifest;
the SDK validates its shape for compatibility but never treats it as runtime
authorization—the live permission and on-chain checks remain authoritative.

The required UserOperation sequence is build → sign → estimate → re-sign with
the returned gas fields → submit. Any changed hashed field requires a fresh
signature.

The CLI exposes that same lifecycle for `aegis.action-request.v1`. Prefer the
normal calls form: provide the exact target, value, target calldata, and
permission ID. The CLI reads the EntryPoint nonce and route fees, wraps the
call exactly once, signs for estimation, and replaces the estimated gas fields
before requiring a fresh submission signature. Advanced callers can still
provide a complete raw UserOperation. The command dry-runs by default and
prints a redacted, correlated result; submission is a separate explicit
command:

```json
{
  "schemaVersion": "aegis.action-request.v1",
  "chainId": 8453,
  "account": "0x1111111111111111111111111111111111111111",
  "permissionId": "0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
  "calls": [{
    "target": "0x2222222222222222222222222222222222222222",
    "value": "0",
    "data": "0x"
  }],
  "route": { "mode": "bundler", "bundlerUrlRef": "selected-runtime-route" }
}
```

```bash
aegis action run \
  --env-file /absolute/path/agent.runtime.env \
  --request-file /absolute/path/first-action.json \
  --readiness-report-file /absolute/path/first-action.readiness.json

# Only after reviewing the exact explanation and successful estimate:
aegis action run \
  --env-file /absolute/path/agent.runtime.env \
  --request-file /absolute/path/first-action.json \
  --execute
```

The optional readiness file is written atomically with owner-only permissions.
It contains no private key or management credential. Its EIP-191 proof binds
the local report to `account.signer()` so the dashboard can detect tampering;
the dashboard still verifies the live signer, current account/permission
binding, freshness, and owner recovery before showing the handoff as ready.

A calls-form request needs no custom UserOperation construction on supported
routes. Routes whose gas estimator merely echoes supplied limits remain
fail-closed and require promoted measured limits before this automatic path is
enabled. An exact denial always blocks. Unknown or unpromoted capability
support also blocks before signing; the deliberately named
`--unsafe-allow-unknown` option is for expert beta diagnostics and cannot
override a denial. No capability should be promoted until its checked-in live
conformance evidence passes the repository release gate.

Advanced programmatic dry-run-first execution shape:

```python
from aegis_sdk import PrivateKeySigner, SafeUserOperation, build_execute

call_data = build_execute(TARGET, 0, TARGET_CALLDATA, PERMISSION_ID)
userop = {
    "sender": SMART_ACCOUNT,
    "nonce": "0x0",                 # resolve the real nonce first
    "initCode": "0x",
    "callData": call_data,
    "callGasLimit": "0x0",
    "verificationGasLimit": "0x0",
    "preVerificationGas": "0x0",
    "maxFeePerGas": MAX_FEE,
    "maxPriorityFeePerGas": MAX_PRIORITY_FEE,
    "paymasterAndData": "0x",
}

# dry_run signs only for estimation and never submits.
operation = SafeUserOperation.from_userop(userop, ENTRYPOINT, CHAIN_ID)
operation.dry_run(PrivateKeySigner(BOT_PRIVATE_KEY), BUNDLER_URL)

# Submission is a separate, explicit decision after reviewing the dry run.
userop_hash = operation.submit(PrivateKeySigner(BOT_PRIVATE_KEY), BUNDLER_URL, execute=True)
receipt = operation.lifecycle.wait_for_receipt(BUNDLER_URL, timeout=180)
```

`CallbackSigner(address, callback)` passes the 32-byte ERC-4337
UserOperation hash to `callback`; the callback must return the account's
EIP-191/personal-sign signature. Its `user_presence_required` metadata defaults
to `True`. Set it to `False` only for a non-interactive signer such as an HSM.
`PrivateKeySigner.user_presence_required` is `False` because it signs locally.

## Legacy standalone runtime descriptors

The verified handoff bundle above is the preferred dashboard workflow. Older
dashboard releases downloaded a standalone public runtime descriptor and a
separate signer backup. That compatibility flow remains supported, but it does
not have the bundle manifest, integrity verification, safe installer, or
included first-action request.

A legacy descriptor contains an explicit `AEGIS_SIGNER_SECRET_FILE` reference
but no private key or management API key. Store it and the separate signer
backup outside the repository, replace the placeholder with the absolute
signer-backup path, and restrict both files before checking them:

```bash
chmod 600 /absolute/path/agent.runtime.env /absolute/path/signer-backup.env
# Edit agent.runtime.env so AEGIS_SIGNER_SECRET_FILE points to signer-backup.env.
AEGIS_BUNDLER_URL='https://custom-bundler.example/agent-route' \
  aegis runtime check --env-file /absolute/path/agent.runtime.env --json
```

The selected `--env-file` remains authoritative for chain, agent, account,
permission, and route identity. The explicit signer file contributes only a
supported signer key or signer endpoint; public address lines are ignored and
backend credential fields are rejected. The checker never scans a directory or
loads another env file, never prints the signer path or secret, and performs
read-only RPC/bundler probes only. Account, EntryPoint, enforcer, and resolver
addresses and runtime code hashes from the dashboard descriptor are compared
with live reads pinned to one block; a mismatch fails readiness rather than
silently substituting the current deployment. A relative signer path is resolved only
relative to the selected runtime descriptor; use an absolute path for clarity.

`build_execute` only wraps already-encoded target calldata with the Aegis
policy envelope. It does not encode a Uniswap, Aerodrome, or other DEX call;
construct that calldata from the exact router ABI for the target chain, then
run Aegis preflight and target-level gas estimation before submission.

When checking an intended token, pass `--asset <address>` (or `native`) to
Doctor. Doctor's normal asset rows describe the permission's configured asset
list; an account holding a token does not by itself mean that token is allowed
by the permission.

Robinhood Chain Testnet (`46630`) metadata is available through
`get_chain_profile(46630)` and its SDK profile is marked `pending`. The v15
contracts are deployed and verified, but Aegis API/provisioning support for this
chain is not enabled for normal onboarding yet, so do not target it through the
API. The SDK deliberately does not publish placeholder contract or test-token
addresses.
