Metadata-Version: 2.4
Name: wormhole-sdk
Version: 0.13.0
Summary: Wormhole SDK library.
Author: Wormhole Team
License-Expression: AGPL-3.0-or-later
Requires-Python: >=3.10
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: anthropic<1.0.0,>=0.40.0
Requires-Dist: wormhole-core<0.14.0,>=0.13.0
Provides-Extra: test
Requires-Dist: pytest>=7.4.0; extra == "test"
Requires-Dist: pytest-asyncio>=0.21.0; extra == "test"
Requires-Dist: pytest-cov>=5.0.0; extra == "test"
Requires-Dist: coverage>=7.4.0; extra == "test"
Provides-Extra: lint
Requires-Dist: ruff>=0.6.4; extra == "lint"
Requires-Dist: mypy>=1.11.0; extra == "lint"
Dynamic: license-file

# Wormhole SDK

Wormhole SDK is the public Python interface for the Wormhole core engine.

## Install (development)

```bash
pip install -e packages/sdk
```

## Basic Usage

```python
import asyncio
from wormhole_sdk import WormholeClient, ModelEventType

async def main():
    client = WormholeClient()
    async for event in client.send_message("Hello"):
        if event.type is ModelEventType.TEXT_DELTA:
            print(event.payload["text"], end="")
    print()

asyncio.run(main())
```

## Tools

```python
from wormhole_sdk import ModelEventType, ToolDefinition, WormholeClient

client = WormholeClient()

tool = ToolDefinition(tool_id="echo", name="Echo", schema={"type": "object"})

async def handler(args: dict) -> str:
    return str(args)

await client.register_tool(tool, handler)

async for event in client.send_message("Echo this", allowlist=["echo"]):
    if event.type is ModelEventType.TOOL_CALL:
        print(event.payload["tool_name"])
```

## Task Delegation (TaskTool)

`TaskTool` delegates a sub-task to a fresh child session and returns a structured
summary (`status`, `result`, `child_session_id`, and child tool-call summary).

```python
from wormhole_sdk import SDKConfig, WormholeClient
from wormhole_sdk.tools.task import (
    TaskToolConfig,
    create_task_handler,
    create_task_tool,
)

client = WormholeClient(SDKConfig(adapter=my_adapter, persist_sessions=False))
task_config = TaskToolConfig(
    max_depth=3,
    default_max_steps=25,
    default_timeout_seconds=None,  # wall-clock timeout disabled unless configured
    default_locked_layers=["identity", "mandates", "safety"],
    allow_recursive_task=False,
)
await client.register_tool(
    create_task_tool(task_config),
    create_task_handler(client=client, task_tool_config=task_config),
)
```

Invocation pattern from the parent model:

```json
{
  "task": "Explore the repo and summarize flaky tests",
  "system_prompt": "Focus on tests only",
  "tools": ["file_read", "bash"],
  "max_steps": 12,
  "timeout_seconds": 300
}
```

Behavior notes:
- Child sessions start with fresh history; parent context is passed via task text.
- Locked layers (`identity`, `mandates`, `safety` by default) remain immutable in child prompt customization.
- Approval-gated child tool calls use the same approval channel/cache as parent flow.
- Timeout grace-mode uses tools-disabled recovery before returning a structured `timeout` result.

## Examples

See `packages/sdk/examples/README.md` for runnable chat and tool demos.

## Configuration

```python
from wormhole_sdk import RetryPolicy, SDKConfig, WormholeClient

config = SDKConfig(
    timeout_seconds=60.0,
    max_consecutive_steps=50,
    continue_on_max_tokens=True,
    retry_policy=RetryPolicy(max_retries=2, base_delay=1.0),
)

client = WormholeClient(config=config)
```

## External Tool Registration

Use three separate concepts:

- `tool_config_paths`: discovery-only list of Python module paths.
- `tools`: registration list of `tool_id` strings for this client.
- `allowlist`/`default_allowlist`: execution-time gate during `send_message`.

```python
from wormhole_sdk import SDKConfig, WormholeClient
from wormhole_sdk.tools import TOOL_BASH

config = SDKConfig(
    adapter=my_adapter,
    tool_config_paths=["my_tools.echo_tool"],  # discover external tools
    tools=[TOOL_BASH, "echo"],  # register built-in + external IDs
)
client = WormholeClient(config)

# Optional execution-time allowlist (separate from registration)
async for event in client.send_message("run echo", allowlist=["echo"]):
    print(event.type)
```

Rules:
- Unknown `tool_id` in `tools` fails fast at initialization.
- `tool_config_paths` import/factory/version failures fail fast at initialization.
- List ordering does not change registration behavior.
- Empty `tools` means no tools are registered.

## Storage Providers and Session Persistence

```python
from wormhole_sdk import SDKConfig, WormholeClient

# Default: file-based persistence in .wormhole/
client = WormholeClient(SDKConfig(adapter=my_adapter))

# Memory-only mode
client = WormholeClient(SDKConfig(adapter=my_adapter, persist_sessions=False))

# Manual-save mode
client = WormholeClient(
    SDKConfig(adapter=my_adapter, persist_sessions=True, auto_save=False)
)
await client.save_session("session-1")
sessions = await client.list_sessions()
await client.delete_session("session-1")
```

Custom providers can be supplied via `SDKConfig.storage_provider` with the
four storage protocol implementations (session, execution state, event log,
compaction).

## Compaction and Forking

```python
from wormhole_sdk import SDKConfig, WormholeClient

# Auto-compaction is enabled by default when a context limit is provided.
client = WormholeClient(SDKConfig(compaction_context_limit=200_000))

# Manual compaction (summary injected as a SYSTEM message)
result = await client.compact_session("main")
print(result.snapshot_id, result.summary_generated)

# Fork a session (requires persistence enabled)
fork = await client.fork_session(parent_session_id="main", new_session_id="child-1")
print(fork.child_session_id, fork.parent_session_id)

# Query lineage
children = await client.list_sessions(parent_session_id="main")
for child in children:
    print(child.session_id)
```

Notes:
- `compact_session()` works even without persistence (uses an in-memory store).
- `fork_session()` requires a storage provider (persistence enabled).
- Auto-compaction uses reconstructed context and requires `compaction_context_limit` to be set.
- For Anthropic sessions with `persist_sessions=True` and `auto_compaction_enabled=True`, `compaction_context_limit` is mandatory (client initialization raises `ConfigurationError` if missing).

## SemVer and Deprecations

- The SDK follows SemVer for public APIs.
- Deprecated APIs emit `DeprecationWarning` and remain supported for at least
  two minor releases before removal.
- Use the `wormhole_sdk.deprecated` helper for new deprecations.

## Quickstart

See `specs/004-sdk-mvp/quickstart.md` for more examples.

## Version History

`wormhole-sdk` follows pre-1.0 SemVer: in `0.x`, MINOR bumps mark new
release milestones (and breaking changes); PATCH covers fixes within a
milestone. Versions are tracked independently of `wormhole-core`; the
`wormhole-core` dependency range pins to a single core MINOR series.

| Version | Milestone | Notes |
|---|---|---|
| 0.1.0 | Initial scaffold | SDK package layout. |
| 0.2.0 | MVP client | First shippable `WormholeClient`, examples, and CI gates. |
| 0.3.0 | OS tools moved into SDK | bash, file_read, file_edit, file_write live here; tool boundaries codified. |
| 0.4.0 | Centralized approval system | `ApprovalManager`, LLM coding-agent flow, tool safety gates. |
| 0.5.0 | Storage providers | File-based default + sqlite provider example. |
| 0.6.0 | Configurable auto-compaction | Two-stage compaction integration; sqlite observability. |
| 0.7.0 | Tool registration formalization | `tool_config_paths` discovery + `tools` registration + execution allowlist. |
| 0.8.0 | TaskTool delegation MVP | Delegation schema, prompt locks, child-session handler scaffold. |
| 0.9.0 | TaskTool finalization | US2/US3 lock + config + approval coverage; delegation observability + redaction. |
| 0.10.0 | Unified graph loop in `send_message` | Multi-turn conversation runs through one `step_session` call; `stage_message()` for concurrent input. |
| 0.11.0 | **BREAKING**: approval unification | `ApprovalHook` removed; all approval flows go through `ApprovalManager`. |
| 0.12.0 | **BREAKING**: `loopgraph` rename propagation | Core dep is now `wormhole-core>=0.12.0,<0.13.0`; downstream renames (`EventflowError`→`LoopgraphError`, etc.) flow through. |
| 0.13.0 | First public release | License relicensed from proprietary "All Rights Reserved" to **AGPL-3.0-or-later**; `Private :: Do Not Upload` classifier removed. Core dep range bumped to `wormhole-core>=0.13.0,<0.14.0` to track the simultaneous core relicense. No source-API changes from 0.12.0. |

## License

`wormhole-sdk` is distributed under the **GNU Affero General Public License
v3.0 or later** (AGPL-3.0-or-later). See [LICENSE](./LICENSE) for the full
text.

The AGPL extends the GNU GPL by requiring that anyone who runs a modified
version of the software as a network service must make the source code of
that modified version available to the service's users. If you embed
`wormhole-sdk` in a hosted product, that obligation applies to your product
as a whole because of the GPL's combined-work rules. For commercial
licensing terms outside the AGPL, contact the maintainers.
