Metadata-Version: 2.5
Name: isle-sdk
Version: 0.2.0
Summary: Python SDK for Isle — application environments for AI agents
Project-URL: Homepage, https://www.tryisle.com
Project-URL: Documentation, https://www.tryisle.com/docs
License-Expression: MIT
License-File: LICENSE
Classifier: Development Status :: 3 - Alpha
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3 :: Only
Requires-Python: >=3.9
Requires-Dist: httpx>=0.25.0
Description-Content-Type: text/markdown

# Isle Python SDK

Application environments for AI agents.

## Install

Requires Python 3.9 or later.

```bash
python -m pip install isle-sdk
```

The package is named `isle-sdk` on PyPI and imported as `isle`. Get an API key
from the [Isle dashboard](https://www.tryisle.com/api-keys).

Version 0.2.0 adds persistent execution, capability discovery, and structured
observations. Use a newly created environment and check its capabilities first;
existing environments retain their original image.

The SDK automatically generates an idempotency key and reuses it for one
transport retry. Pass `idempotency_key=` when the same create operation may be
retried by a different process.

## Quickstart

```python
from isle import Client, IsleAPIError

isle = Client(api_key="isle_...")

# Create a KiCad sandbox
sandbox = isle.sandboxes.create(
    "kicad",
    name="Power supply board",
    idempotency_key="power-supply-board-v1",
)
sandbox.wait_until_ready()

# Upload a file
sandbox.files.upload("schematic.kicad_sch")

# Take a screenshot
image = sandbox.screen.screenshot()

# Control the mouse and keyboard
sandbox.mouse.click(500, 300)
sandbox.keyboard.type("100nF")
sandbox.keyboard.keypress(["CTRL", "S"])

# Download results
sandbox.files.download("/home/user/work/board.kicad_pcb")

# Stop (archives the sandbox, can be resumed later)
sandbox.stop()
sandbox.wait_until_stopped()

# Resume a stopped sandbox
sandbox.resume()

# Lifecycle failures are returned by the API and retained on the environment
sandbox.refresh()
if sandbox.last_error:
    print(sandbox.last_error, sandbox.last_error_at)

# Retrieve timestamped error history (newest first)
for error in sandbox.errors():
    print(error["source"], error["message"], error["created_at"])

# Recording metadata distinguishes a complete recording from the bounded
# 512 MiB / six-hour / low-disk partial recording policy.
recording = sandbox.recording_info()
print(recording["url"], recording["truncated"], recording["truncation_reason"])

# Permanently delete a stopped sandbox and its retained data
sandbox.stop()
sandbox.wait_until_stopped()
sandbox.destroy(timeout=120)
```

API failures raise `IsleAPIError`. Its `status_code`, `error`, and `metadata`
attributes can be used to handle failures without parsing an exception string.
For example, a provisioning failure includes its recoverable
`metadata["environment_id"]` when the API created a record before the remote
environment failed to start.

## Environments

- `kicad` - EDA environment with KiCad
- `freecad` - CAD environment with FreeCAD

## Persistent execution

Use Isle SDK 0.2.0 or later with a newly created KiCad or FreeCAD environment.
Existing environments keep their original image. The API is model-neutral
and works with any agent framework or HTTP client.

```bash
python -m pip install --upgrade 'isle-sdk>=0.2.0'
```

Connect to an existing environment running the updated image:

```python
import uuid
from isle import Client, ExecutionSubmissionUnknown

client = Client()  # ISLE_API_KEY stays in the client
sandbox = client.sandboxes.get("YOUR_SANDBOX_ID")
capabilities = sandbox.capabilities()
assert capabilities["execution"]["available"]
generation = capabilities["generation"]
execution_id = str(uuid.uuid4())  # preserve this and generation before POST

try:
    result = sandbox.execution.submit(
        "counter = 41\nlog(counter)\ndisplay(pyautogui.screenshot())",
        execution_id=execution_id,
        expected_generation=generation,
        timeout_seconds=30,
    )
except ExecutionSubmissionUnknown as error:
    result = sandbox.execution.get(error.execution_id, generation=error.generation)

if result["status"] == "running":
    result = sandbox.execution.wait(execution_id, generation=generation)
print(result["status"], result["stdout"], result["error"])
observation = sandbox.screen.observe()
```

`execution.run()` combines submit and wait. `execution.cancel(execution_id,
expected_generation=generation)` cancels a running cell. `ExecutionWaitTimeout`
preserves the ID and generation and does not cancel remote work. After any
unknown outcome, query the original execution; do not replay under a new ID.
A missing or expired record requires inspecting the desktop and saved files.

The `default` namespace includes `pyautogui`, `time`, `log()`, and `display()`.
Combine Python imports, short GUI actions, installed CLI tools, and project
files beneath `/home/user/work`; keep PyAutoGUI's fail-safe enabled. Variables
persist between cells, while Python descendants pause between them. One
execution or other desktop operation runs at a time; an explicit
`desktop_busy` rejection can be retried with bounded backoff and the same
request identity when the API confirms it was not started or was rejected.

Cancellation and server timeouts verify worker and held-key/button cleanup,
then reset the namespace. Completed edits remain. Results retain their
original generation and expose `namespace_reset`; refresh capabilities before
new work. A Python exception also returns a result, so inspect `status` and
`error` rather than treating HTTP success as successful code execution.

Current cells allow 64 KiB of UTF-8 source and 1–120 seconds. stdout and stderr
are bounded to 64 KiB each. At most eight emitted outputs share a 1 MiB budget
for PNG bytes and text; execution JSON is limited to 2 MiB. Read
`capabilities["limits"]` and `output_truncated`. PNGs from `display()` are
base64 image outputs in bounded runtime records, without durable image URLs.
Download them while available or save them to the workspace. Observations
carry a PNG, timestamp, generation, dimensions, scale factor, and
`active_document=None`; retained observations use screenshot storage.

See the [execution reference](https://www.tryisle.com/docs/rest#execution) for
the HTTP contract. Keep model-provider credentials in your client process.

### Shell bridge for an existing agent session

From an authorized repository checkout, Codex or any agent with shell access
can use `examples/codex/desktop.py` with Isle SDK 0.2.0 or later and an existing
execution-enabled environment:

```bash
python examples/codex/desktop.py --sandbox-id SANDBOX_ID capabilities
python examples/codex/desktop.py --sandbox-id SANDBOX_ID observe
python examples/codex/desktop.py --sandbox-id SANDBOX_ID run action.py --generation GENERATION
```

Set `ISLE_API_KEY` in the host environment. The bridge makes no model API calls
and requires no model-provider SDK or separate model API key. It records request
IDs before writes and returns local PNG, text, and result paths for the agent
to inspect. See `examples/codex/README.md` in that checkout for polling,
cancellation, downloads, and independent application verification. The bridge
is a repository example, not an installed `isle-sdk` command.

Bounded KiCad and FreeCAD mechanical tasks have been validated through Codex
using the deployed API, independent saved-file checks, and visual review.

## Astra example

After installing the SDK above, add the optional client dependencies:

```bash
python -m pip install -r examples/astra/requirements.txt
python examples/astra/run.py YOUR_SANDBOX_ID
```

`examples/astra/run.py` connects the OpenAI Responses API from your client
process. Keep `OPENAI_API_KEY` there, outside desktop scripts and files, and
pass emitted PNGs to Astra as image inputs. `examples/astra/validate.py` checks
saved KiCad copies with CLI ERC, DRC, and schematic parity. New KiCad IPC images
expose the official `kipy` client through execution cells; check for `kicad_ipc`
in capabilities and see `examples/kicad/README.md` in the source checkout. An Isle MCP connector is not included. This optional Responses API adapter has
offline coverage; its own live API run remains pending. The Codex-driven
application checks above do not establish that adapter's live behavior. See the
[integration guide](https://www.tryisle.com/docs#astra) and
[execution reference](https://www.tryisle.com/docs/rest#execution).

## Development

From an authorized Isle repository checkout:

```bash
python -m pip install -e ./sdk/python
python -m unittest discover -s sdk/python/tests
```
