Metadata-Version: 2.4
Name: aiel-sdk
Version: 1.8.0
Summary: AI Execution Layer SDK (contracts + registry + decorators) with curated facades.
Author-email: Aldenir Flauzino <aldenirsrv@gmail.com>
License: ```text
        MIT License
        
        Copyright (c) 2025 Aldenir Flauzino
        
        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.
License-File: LICENSE
Requires-Python: >=3.10
Requires-Dist: email-validator>=2.0
Requires-Dist: keyring<26,>=24
Requires-Dist: pydantic>=2.8
Requires-Dist: typing-extensions>=4.8.0
Provides-Extra: aiel-cli
Requires-Dist: aiel-cli>=1.2.4; extra == 'aiel-cli'
Provides-Extra: all
Requires-Dist: aiel-cli>=1.2.4; extra == 'all'
Requires-Dist: langchain-core>=0.3.0; extra == 'all'
Requires-Dist: langgraph>=0.2.0; extra == 'all'
Requires-Dist: langsmith>=0.1.100; extra == 'all'
Provides-Extra: langchain
Requires-Dist: langchain-core>=0.3.0; extra == 'langchain'
Provides-Extra: langgraph
Requires-Dist: langgraph>=0.2.0; extra == 'langgraph'
Provides-Extra: langsmith
Requires-Dist: langsmith>=0.1.100; extra == 'langsmith'
Description-Content-Type: text/markdown

# AIEL SDK — Contract Layer for Governed AI Execution

[![PyPI](https://img.shields.io/pypi/v/aiel-sdk.svg)](https://pypi.org/project/aiel-sdk/)
[![Python](https://img.shields.io/badge/python-3.10%2B-blue.svg)](https://www.python.org/downloads/)
[![License](https://img.shields.io/badge/license-MIT-green.svg)](LICENSE)

`aiel-sdk` is the local development contract for the AI Execution Layer.

It provides the stable Python interface used to define tools, agents, flows, HTTP handlers, MCP servers, integration calls, and memory operations that can later execute inside the AIEL Execution Plane.

The SDK does not replace the runtime.

The SDK defines what can be executed.

The Execution Plane decides how, where, and under which policies it is executed.

---

## Why the SDK Exists

AI applications are easy to prototype locally.

They become difficult to operate when they need to:
- call external systems safely
- expose tools to agents
- run under policy control
- preserve execution traceability
- execute consistently across environments
- support runtime-managed integrations
- share memory across workflows
- move from local development to production without rewriting code

The AIEL SDK solves this by giving developers a contract-first programming model.

Developers define executable capabilities locally.

The platform packages, validates, registers, and executes those capabilities remotely through `aiel-runtime`.

---

## Position in the Platform

AIEL is composed of multiple layers.

| Component | Responsibility |
|---|---|
| `aiel-sdk` | Defines the local programming contract |
| `aiel-cli` | Packages, validates, versions, and pushes project snapshots |
| `aiel-runtime` | Loads snapshots and executes registered capabilities |
| `Execution Plane` | Provides governed runtime execution |
| `Control Plane` | Manages workspaces, projects, policies, releases, and teams |
| `Data Plane` | Stores snapshots, manifests, traces, memory, and execution state |

The SDK is the developer-facing contract layer.

It is used before runtime execution, but its definitions become the runtime execution surface.

---

## Execution Model

The SDK is designed around explicit exports.

An export is a capability that can be discovered, validated, registered, and executed by the platform.

Supported export types:

| Export Type | Purpose |
|---|---|
| Tool | A discrete callable action |
| Agent | A stateful reasoning or orchestration step |
| Router | A routing decision function |
| Flow | A higher-level workflow entry point |
| HTTP Handler | A runtime-exposed HTTP endpoint |
| MCP Server | A governed tool surface exposed through MCP metadata |

Exports are registered through decorators.

When `entry_point.py` is imported, the SDK registers those exports into an in-memory registry.

The runtime later uses the same registry contract to discover executable capabilities.

---

## Architecture Flow

```text
┌──────────────────────┐
│ Developer Code       │
│ entry_point.py       │
└──────────┬───────────┘
           │
           │ decorators
           ▼
┌──────────────────────┐
│ AIEL SDK Registry    │
│ tools / agents /     │
│ routers / flows /    │
│ http / mcp           │
└──────────┬───────────┘
           │
           │ validate_contracts()
           ▼
┌──────────────────────┐
│ Contract Validation  │
└──────────┬───────────┘
           │
           │ aiel push
           ▼
┌──────────────────────┐
│ Snapshot Packaging   │
└──────────┬───────────┘
           │
           ▼
┌─────────────────────────────┐
│ Execution Plane             │
├─────────────────────────────┤
│ Runtime Hydration           │
│ Registry Loading            │
│ Policy Enforcement          │
│ Integration Resolution      │
│ Memory Access               │
│ Execution Routing           │
│ Trace Collection            │
└──────────┬──────────────────┘
           │
           ▼
┌─────────────────────────────┐
│ Governed Runtime Execution  │
└─────────────────────────────┘
```

---

## Local vs Runtime Responsibilities

| Local SDK Responsibility | Runtime / Platform Responsibility |
|---|---|
| Define exports | Load registered exports |
| Validate function contracts | Enforce runtime policies |
| Provide typed interfaces | Resolve governed integrations |
| Support local testing | Execute inside managed runtime |
| Provide facade imports | Pin approved runtime dependencies |
| Provide local memory helpers | Persist remote memory |
| Build client requests | Enforce workspace/project scope |
| Register MCP metadata | Expose governed MCP capability surfaces |

The SDK keeps local development aligned with production execution.

---

## Installation

### Minimal Installation

Use this for core decorators, registry, contracts, client primitives, and local development.

```bash
pip install aiel-sdk
```

### Full Development Installation

Use this when developing with curated optional integrations.

```bash
pip install "aiel-sdk[all]"
```

The `all` extra includes curated support for:
- LangGraph
- LangChain Core
- LangSmith
- AIEL CLI helper facades

### Local Repository Installation

From a repository checkout:

```bash
pip install -e .
```

With all optional dependencies:

```bash
pip install -e ".[all]"
```

For zsh, always quote extras:

```bash
pip install -e ".[all]"
```

---

## Requirements

- Python 3.10+
- `pip` 21+
- AIEL API token for remote API operations
- Workspace and project scope for governed integrations and memory

---

## Quick Start

Create an `entry_point.py`.

The runtime imports this file to discover executable exports.

```python
from aiel_sdk import tool, agent, router, flow, http, mcp_server
from aiel_sdk.context import Context

@tool("normalize_email")
def normalize_email(ctx: Context, payload: dict) -> dict:
    email = str(payload.get("email") or "").strip().lower()
    ctx.log("normalize_email", email=email)
    return {"email": email}

@agent("collect_personal_data")
def collect_personal_data(ctx: Context, state: dict) -> dict:
    out = normalize_email(ctx, {"email": state.get("email")})
    return {**dict(state), **out}

@router("next_step")
def next_step(ctx: Context, state: dict) -> str:
    return "done" if state.get("email") else "collect_personal_data"

@flow("driver_onboarding")
def driver_onboarding(ctx: Context, input: dict) -> dict:
    state = collect_personal_data(ctx, input)
    return {"status": "ok", "state": state}

@http.post("/driver/onboard")
def http_driver_onboard(ctx: Context, body: dict) -> dict:
    return driver_onboarding(ctx, body)

mcp_server("driver_support", tools=["normalize_email"])
```

---

## Validate Contracts Locally

Importing `entry_point.py` registers all exports.

```python
from aiel_sdk import get_registry, reset_registry, validate_contracts

import entry_point

validate_contracts()

reg = get_registry()

print("tools:", sorted(reg.tools.keys()))
print("agents:", sorted(reg.agents.keys()))
print("routers:", sorted(reg.routers.keys()))
print("flows:", sorted(reg.flows.keys()))
print("http:", [(h.method, h.path) for h in reg.http_handlers])
print("mcp servers:", sorted(reg.mcp_servers.keys()))

reset_registry()
```

Use `reset_registry()` between test runs when multiple import cycles are expected.

---

## Registry Model

The registry is the SDK’s local source of truth for exported capabilities.

When decorators are evaluated, the SDK records metadata about each export.

The registry supports:
- export discovery
- local inspection
- contract validation
- runtime loading
- deployment packaging
- execution surface generation

Supported registry groups:

| Registry Group | Created By |
|---|---|
| `tools` | `@tool(...)` |
| `agents` | `@agent(...)` |
| `routers` | `@router(...)` |
| `flows` | `@flow(...)` |
| `http_handlers` | `@http.get(...)`, `@http.post(...)` |
| `mcp_servers` | `mcp_server(...)` |

---

## Decorator Contracts

### Tool

A tool is a discrete action.

```python
@tool("normalize_email")
def normalize_email(ctx: Context, payload: dict) -> dict:
    return {"email": str(payload["email"]).strip().lower()}
```

Recommended signature:

```python
(ctx: Context, payload: dict) -> dict
```

Use tools for:
- validation
- transformation
- integration calls
- deterministic actions
- reusable execution units

---

### Agent

An agent is a stateful reasoning or orchestration step.

```python
@agent("collect_personal_data")
def collect_personal_data(ctx: Context, state: dict) -> dict:
    out = normalize_email(ctx, {"email": state.get("email")})
    return {**state, **out}
```

Recommended signature:

```python
(ctx: Context, state: dict) -> dict
```

Use agents for:
- state transitions
- multi-step orchestration
- decision support
- interaction with tools

---

### Router

A router returns the next execution path.

```python
@router("next_step")
def next_step(ctx: Context, state: dict) -> str:
    return "done" if state.get("email") else "collect_personal_data"
```

Recommended signature:

```python
(ctx: Context, state: dict) -> str
```

Use routers for:
- branching logic
- workflow transitions
- conditional execution
- agent selection

---

### Flow

A flow is a top-level workflow entry point.

```python
@flow("driver_onboarding")
def driver_onboarding(ctx: Context, input: dict) -> dict:
    state = collect_personal_data(ctx, input)
    return {"status": "ok", "state": state}
```

Recommended signature:

```python
(ctx: Context, input: dict) -> dict
```

Use flows for:
- public workflow entry points
- orchestration units
- runtime-invokable business workflows

---

### HTTP Handler

HTTP handlers expose flows or tools through runtime-managed HTTP routes.

```python
@http.post("/driver/onboard")
def http_driver_onboard(ctx: Context, body: dict) -> dict:
    return driver_onboarding(ctx, body)
```

Supported pattern:

```python
@http.get("/path")
@http.post("/path")
```

Use HTTP handlers when:
- external callers need an HTTP interface
- workflows need to be exposed as managed endpoints
- the Execution Plane should own endpoint routing

---

### MCP Server

MCP server metadata declares which tools should be exposed through an MCP-compatible surface.

```python
mcp_server("driver_support", tools=["normalize_email"])
```

Use MCP metadata when:
- tools should be discoverable by MCP-compatible clients
- tool access must remain governed by platform policies
- tool schemas should be exposed without custom server plumbing

---

## Context Object

Every executable export receives a `Context`.

The context provides the runtime boundary between user code and platform services.

Typical context responsibilities include:
- structured logging
- execution metadata access
- request correlation
- workspace/project awareness
- runtime utilities
- policy-aware service access

Example:

```python
@tool("normalize_email")
def normalize_email(ctx: Context, payload: dict) -> dict:
    email = str(payload.get("email") or "").strip().lower()
    ctx.log("normalize_email", email=email)
    return {"email": email}
```

---

## AielClient

`AielClient` is the SDK client for calling AIEL APIs.

It is used for:
- resolving workspace/project scope
- calling governed integrations
- accessing remote memory
- invoking platform APIs
- using typed wrappers around remote capabilities

### Authentication Resolution

`AielClient` resolves credentials in this order:

1. `api_key=` passed directly
2. `AIEL_TOKEN` environment variable
3. OS keyring entry `("aiel", "default")`, when available

Example:

```python
from aiel_sdk import AielClient

client = AielClient(
    base_url="https://api.example.com",
    api_key="REPLACE_ME",
    workspace_id="ws_123",
    project_id="proj_456",
)

workspace_id, project_id = client.resolve_scope()
```

Workspace and project can also be resolved from:

```text
AIEL_WORKSPACE_ID
AIEL_PROJECT_ID
```

---

## Integration Access

Integrations are accessed through governed platform connections.

Application code should not embed raw third-party credentials.

Instead, it references platform-managed connection IDs.

This allows the platform to enforce:
- workspace scope
- project scope
- provider policy
- allowed actions
- audit logging
- credential isolation

---

## Generic Integration API

```python
from aiel_sdk import AielClient

client = AielClient(
    base_url="https://api.example.com",
    api_key="REPLACE_ME",
    workspace_id="ws_123",
    project_id="proj_456",
)

connections = client.integrations.list("ws_123", "proj_456")

connection = connections[0]

detail = client.integrations.get(
    connection.workspace_id,
    connection.project_id,
    connection.connection_id,
)

result = client.integrations.invoke_action(
    connection.workspace_id,
    connection.project_id,
    connection.connection_id,
    action="slack.post_message",
    payload={
        "params": {
            "channel": "#general",
            "text": "Hello from AIEL SDK",
        }
    },
)
```

---

## Typed Integration Wrappers

Typed wrappers provide provider-specific request and response models on top of governed platform connections.

### Slack

```python
from aiel_sdk import AielClient
from aiel_sdk.integrations.slack import RemoteSlack, SlackPostMessageRequest

client = AielClient(
    base_url="https://api.example.com",
    api_key="REPLACE_ME",
    workspace_id="ws_123",
    project_id="proj_456",
)

slack = RemoteSlack(client, connection_id="conn_slack_123")

slack.post_message(
    SlackPostMessageRequest(
        channel="#general",
        text="Hello from AIEL SDK",
    )
)
```

### OpenAI

```python
from aiel_sdk import AielClient
from aiel_sdk.integrations.openai import RemoteOpenAI, OpenAIResponsesCreateRequest

client = AielClient(
    base_url="https://api.example.com",
    api_key="REPLACE_ME",
    workspace_id="ws_123",
    project_id="proj_456",
)

oai = RemoteOpenAI(client, connection_id="conn_openai_123")

result = oai.responses_create(
    OpenAIResponsesCreateRequest(
        input="Say hello",
        model="gpt-4o-mini",
    )
)

print(result.raw)
```

---

## Memory

The SDK supports both local and remote memory implementations.

Use local memory for development and tests.

Use remote memory for production workflows running through AIEL-managed workspace/project scope.

---

### Local Memory

```python
from aiel_sdk.memory import InMemorySaver, ThreadMessage

saver = InMemorySaver()

saver.thread_write(
    thread_key="t1",
    messages=[
        ThreadMessage(role="human", content="hi"),
    ],
)

thread = saver.thread_read(thread_key="t1")

print(thread.messages[-1].content)
```

Local memory:
- does not require network access
- is useful for tests
- is not durable
- should not be used as production state

---

### Remote Memory

```python
from aiel_sdk import AielClient
from aiel_sdk.memory import RemoteSaver, ThreadMessage, RecallQuery

client = AielClient(
    base_url="https://api.example.com",
    api_key="REPLACE_ME",
    workspace_id="ws_123",
    project_id="proj_456",
)

remote = RemoteSaver(
    client,
    workspace_id="ws_123",
    project_id="proj_456",
)

remote.thread_write(
    thread_key="t1",
    messages=[
        ThreadMessage(role="human", content="hi"),
    ],
    ttl_seconds=3600,
)

state = remote.state_patch(
    thread_key="t1",
    patch={"step": "triage"},
    ttl_seconds=3600,
)

checkpoint = remote.checkpoint_write(
    thread_key="t1",
    state=state.state,
    metadata={"node": "triage_agent"},
    ttl_seconds=86400,
)

items = remote.recall_search(
    query=RecallQuery(
        namespace=["users", "u1"],
        limit=10,
    )
)
```

Remote memory supports:
- thread history
- state patches
- checkpoints
- recall search
- TTL-based lifecycle control
- workspace/project isolation

---

## Facade Modules

The SDK provides curated facade modules for approved runtime dependencies.

Facade imports are used to keep local development aligned with managed runtime environments.

| Capability | Import Path | Purpose |
|---|---|---|
| Pydantic | `aiel_core.pydantic` | Data validation and serialization |
| LangGraph | `aiel_core.langgraph.graph` | State graph orchestration |
| LangChain Core | `aiel_core.langchain.core` | Prompt templates and runnables |
| LangChain Messages | `aiel_core.langchain.messages` | AI/Human message types |
| LangSmith | `aiel_core.langsmith.client` | Observability and tracing |
| AIEL CLI Context | `aiel_core.aiel_cli.context.user_context` | CLI context helpers |

Example:

```python
from aiel_core.pydantic import BaseModel, Field

class NormalizeEmailPayload(BaseModel):
    email: str = Field(..., description="Raw email address")
```

LangGraph example:

```python
from aiel_core.langgraph.graph import StateGraph, START, END, MessagesState

graph = StateGraph(MessagesState)
graph.add_node("process", lambda state: state)
graph.add_edge(START, "process")
graph.add_edge("process", END)

app = graph.compile()
result = app.invoke({"messages": []})
```

Missing optional dependencies fail with actionable errors that identify the required installation extra.

---

## Runtime Execution

In production, application code is executed by `aiel-runtime` inside the Execution Plane.

The runtime performs the following sequence:

1. Fetches the project snapshot from the Data Plane
2. Hydrates the approved runtime environment
3. Imports `entry_point.py`
4. Loads the SDK registry
5. Validates registered contracts
6. Resolves workspace/project scope
7. Applies integration and policy controls
8. Invokes the requested export
9. Captures logs, traces, and execution metadata
10. Returns a normalized runtime response

Runtime invocation is handled by the platform.

Local code should be written against the SDK contract, not against runtime internals.

---

## SDK to Execution Plane Flow

```text
Developer
   │
   │ writes entry_point.py
   ▼
AIEL SDK
   │
   │ decorators register exports
   ▼
Local Registry
   │
   │ validate_contracts()
   ▼
AIEL CLI
   │
   │ aiel push
   ▼
Snapshot
   │
   │ versioned + immutable
   ▼
Execution Plane
   │
   │ aiel-runtime loads registry
   ▼
Governed Execution
   │
   ├── policies
   ├── integrations
   ├── memory
   ├── traces
   └── audit logs
```

---

## Runtime Bundles

The Execution Plane may provide curated runtime bundles with approved dependency sets.

### Minimal Runtime

```text
aiel-runtime==X.Y.Z
aiel-sdk==X.Y.Z
```

Used for:
- core tools
- flows
- routers
- HTTP handlers
- basic integrations

### AI Runtime

```text
aiel-runtime[ai]==X.Y.Z
```

May include curated AI orchestration dependencies such as:
- LangGraph
- LangChain Core
- LangSmith

Runtime identifiers should be allowlisted by the platform.

Package versions should be pinned by runtime bundle.

---

## Data Plane Manifest

Project snapshots should include manifest metadata such as:

| Field | Purpose |
|---|---|
| `runtime` | Runtime bundle or runtime image identifier |
| `sdk_version` | SDK contract version |
| `entry_point` | Python module loaded by runtime |
| `exports` | Registered tools, agents, flows, HTTP handlers, MCP metadata |
| `requirements` | Dependency metadata |
| `snapshot_id` | Immutable deployment identifier |
| `created_at` | Snapshot creation timestamp |

The manifest is used by the Execution Plane to determine how the snapshot should be loaded and executed.

---

## Recommended Project Structure

```text
my-aiel-project/
├── entry_point.py
├── tools/
│   ├── __init__.py
│   └── validation.py
├── agents/
│   ├── __init__.py
│   └── onboarding.py
├── flows/
│   ├── __init__.py
│   └── driver_onboarding.py
├── models/
│   ├── __init__.py
│   └── schemas.py
├── tests/
│   └── test_contracts.py
├── requirements.txt
└── README.md
```

---

## Type Safety

Use Pydantic models for validation at the boundary of tools, agents, and flows.

```python
from aiel_core.pydantic import BaseModel, Field
from aiel_sdk import tool
from aiel_sdk.context import Context

class NormalizeEmailPayload(BaseModel):
    email: str = Field(..., description="Raw email address")

class NormalizeEmailResult(BaseModel):
    email: str

@tool("normalize_email")
def normalize_email(ctx: Context, payload: dict) -> dict:
    data = NormalizeEmailPayload.model_validate(payload)
    email = data.email.strip().lower()
    return NormalizeEmailResult(email=email).model_dump()
```

Recommended practice:
- validate external input
- return JSON-serializable dictionaries
- keep tool outputs explicit
- use typed request models for integrations
- avoid hidden global state

---

## Logging

Use `ctx.log(...)` for structured runtime logs.

```python
@tool("normalize_email")
def normalize_email(ctx: Context, payload: dict) -> dict:
    email = str(payload.get("email") or "").strip().lower()
    ctx.log("normalize_email.completed", email=email)
    return {"email": email}
```

Logs should be:
- structured
- safe to persist
- free of raw secrets
- useful for trace correlation

---

## Error Handling

Prefer explicit errors and structured return values.

```python
@tool("safe_normalize_email")
def safe_normalize_email(ctx: Context, payload: dict) -> dict:
    try:
        email = str(payload.get("email") or "").strip().lower()

        if not email:
            return {
                "status": "invalid",
                "reason": "email_required",
            }

        return {
            "status": "ok",
            "email": email,
        }

    except Exception as exc:
        ctx.log("safe_normalize_email.failed", error=str(exc))
        return {
            "status": "failed",
            "reason": "unexpected_error",
        }
```

Avoid leaking:
- access tokens
- provider credentials
- customer secrets
- raw personal data
- internal stack traces in user-facing outputs

---

## Testing

### Run Unit Tests

```bash
python -m unittest discover -s tests -v
```

### Run SDK Smoke Example

From a repository checkout:

```bash
PYTHONPATH=src python -m aiel_sdk.examples.smoke
```

### Example Contract Test

```python
def test_contracts_are_valid():
    import entry_point

    from aiel_sdk import get_registry, validate_contracts

    validate_contracts()

    reg = get_registry()

    assert "normalize_email" in reg.tools
    assert "driver_onboarding" in reg.flows
```

---

## Best Practices

### Keep `entry_point.py` Small

Use `entry_point.py` as the registration surface.

Move business logic into modules.

Recommended:

```python
# entry_point.py
from aiel_sdk import tool
from tools.email import normalize_email_impl

@tool("normalize_email")
def normalize_email(ctx, payload):
    return normalize_email_impl(ctx, payload)
```

---

### Use Platform-Managed Integrations

Do not store provider credentials in application code.

Use AIEL connection IDs and governed integration wrappers.

Recommended:

```python
slack = RemoteSlack(client, connection_id="conn_slack_123")
```

Avoid:

```python
# Do not do this
slack_token = "xoxb-..."
```

---

### Separate Local and Remote State

Use `InMemorySaver` for tests.

Use `RemoteSaver` for production.

Do not assume local memory behavior matches durable remote state semantics.

---

### Keep Export Names Stable

Export names become part of the execution contract.

Changing names can affect:
- invocation references
- runtime routing
- MCP exposure
- deployment compatibility
- external callers

Recommended:

```python
@flow("driver_onboarding")
```

Avoid frequently renaming exports after they are used by production callers.

---

### Make Outputs Explicit

Return structured dictionaries with clear status fields.

Recommended:

```python
return {
    "status": "ok",
    "state": state,
}
```

Avoid ambiguous responses:

```python
return state
```

---

## Security Model

The SDK supports the platform security model by avoiding direct credential ownership in application code.

Security boundaries are enforced by the platform, including:
- workspace scope
- project scope
- connection scope
- action-level permissions
- policy evaluation
- audit logging
- runtime isolation

The SDK should be treated as the local contract layer, not the policy enforcement engine.

Policy enforcement occurs in the managed platform and runtime environment.

---

## Operational Guarantees

When used with AIEL CLI and AIEL Runtime, the SDK enables:

- explicit execution contracts
- discoverable capabilities
- predictable runtime loading
- typed local development
- governed integration access
- workspace/project-scoped memory
- runtime traceability
- version-aware deployment
- consistent local-to-production imports

---

## Troubleshooting

### Missing Optional Dependency

Example error:

```text
ImportError: aiel_core.langgraph is not available
```

Install the required extra:

```bash
pip install "aiel-sdk[langgraph]"
```

Or install all curated integrations:

```bash
pip install "aiel-sdk[all]"
```

---

### Contract Validation Failure

Typical cause:
- invalid function signature
- missing context argument
- unsupported return shape
- decorator registered against the wrong callable

Check signatures:

| Export Type | Expected Pattern |
|---|---|
| Tool | `(ctx, payload: dict) -> dict` |
| Agent | `(ctx, state: dict) -> dict` |
| Router | `(ctx, state: dict) -> str` |
| Flow | `(ctx, input: dict) -> dict` |
| HTTP POST | `(ctx, body: dict) -> dict` |
| HTTP GET | `(ctx, query: dict) -> dict` |

---

### Registry Is Empty

Possible causes:
- `entry_point.py` was not imported
- decorators are inside a function that was not called
- exports are defined behind conditional logic
- registry was reset before inspection

Fix:

```python
import entry_point

from aiel_sdk import get_registry

reg = get_registry()
print(reg.tools.keys())
```

---

### Authentication Failure

`AielClient` could not resolve credentials.

Check one of the following:
- pass `api_key=...`
- set `AIEL_TOKEN`
- authenticate through AIEL CLI if keyring integration is available

---

### Workspace or Project Not Resolved

Set scope explicitly:

```python
client = AielClient(
    base_url="https://api.example.com",
    api_key="REPLACE_ME",
    workspace_id="ws_123",
    project_id="proj_456",
)
```

Or use environment variables:

```bash
export AIEL_WORKSPACE_ID=ws_123
export AIEL_PROJECT_ID=proj_456
```

---

## Migration Guidance

When moving existing AI application code to AIEL:

1. Identify reusable actions and define them as tools.
2. Identify stateful reasoning steps and define them as agents.
3. Identify top-level workflows and define them as flows.
4. Expose external endpoints through `@http`.
5. Move provider credentials into platform-managed integrations.
6. Replace direct third-party calls with governed integration wrappers.
7. Add local contract validation tests.
8. Package and deploy through `aiel-cli`.
9. Execute through the AIEL Execution Plane.

---

## Relationship to the CLI

The SDK defines the contract.

The CLI deploys the contract.

Typical workflow:

```bash
pip install aiel-sdk
pip install aiel-cli

aiel auth login
aiel config set workspace <workspace-slug>
aiel config set project <project-slug>
aiel init
aiel add .
aiel commit -m "Add driver onboarding flow"
aiel push
```

---

## Relationship to the Runtime

The runtime executes the contract.

At runtime:
- `entry_point.py` is imported
- decorators register exports
- contracts are validated
- requested exports are invoked
- integrations and memory are resolved through platform services
- logs and traces are captured

Developers should not depend on runtime internals directly.

Use SDK interfaces for local development and runtime compatibility.

---

## Changelog

Version history is managed through Git tags and PyPI releases.

---

## License

See `LICENSE`.

---

## Summary

`aiel-sdk` is the contract layer for governed AI execution.

It lets teams define tools, agents, flows, HTTP handlers, MCP surfaces, integration calls, and memory operations locally while keeping production execution under the control of the AIEL Execution Plane.

The SDK gives developers a stable programming model.

The platform provides the runtime, governance, isolation, and operational controls required for production AI systems.
