Metadata-Version: 2.4
Name: skillsvault
Version: 0.1.0
Summary: Govern agent skills from any Python framework — detect, install, gate, audit.
Project-URL: Homepage, https://skillsvault.io
Project-URL: Documentation, https://skillsvault.io/docs
Author: skillsvault
License: MIT
License-File: LICENSE
Keywords: agent-skills,agents,governance,langchain,langgraph,llm,openai,skillsvault
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
Classifier: Topic :: Software Development :: Libraries
Requires-Python: >=3.9
Requires-Dist: cryptography>=41
Description-Content-Type: text/markdown

# skillsvault Python SDK

Govern agent skills from **any Python framework** — LangChain, LangGraph, the
OpenAI Agents SDK, LlamaIndex, or plain code. These have no skills directory and
no harness hook, so they connect through this SDK instead.

Same control plane as the CLI and the JS SDK: `sync()` the signed policy bundle
once, then `gate(skill)` locally before each skill/tool use (no model, no
network on the hot path). `governed()` wraps a tool so a banned skill **raises
before it runs** and every use is audited.

```bash
pip install skillsvault
```

Only dependency is `cryptography` (Ed25519 bundle verification); HTTP uses the
standard library.

## Model

```python
from skillsvault import create_client

sv = create_client(
    base_url="https://skillsvault.io",
    org="acme",
    key=os.environ["SKILLSVAULT_AGENT_KEY"],  # Settings → API keys
    harness="langchain",                       # free-form framework id
    machine="prod-agent-7",
)

sv.sync()                                # verify the Ed25519-signed bundle
skills = sv.list_skills()                # org-approved skills
content = sv.get_skill("acme/deploy")    # SKILL.md + files at runtime
decision = sv.gate("acme/deploy")        # Decision(decision="allow"|"warn"|"halt", reason, ...)
```

## Framework adapters

`governed(skill, fn)` returns a wrapped `fn` — drop it in wherever a framework
expects the tool's function. A **halt** raises `SkillsvaultError` before the
tool runs; the model sees the org's reason and stops. Works for sync **and**
async functions.

### LangChain

```python
from langchain_core.tools import StructuredTool

def _deploy(env: str) -> str:
    return deploy_to(env)

deploy = StructuredTool.from_function(
    name="deploy",
    description="Deploy the app",
    func=sv.governed("acme/deploy", _deploy),
)
```

### LangGraph

```python
from langgraph.prebuilt import ToolNode

tools = [StructuredTool.from_function(
    name="deploy", description="Deploy the app",
    func=sv.governed("acme/deploy", _deploy),
)]
tool_node = ToolNode(tools)   # a halted skill raises inside the graph node
```

### OpenAI Agents SDK

```python
from agents import function_tool

@function_tool
def deploy(env: str) -> str:
    ...

# wrap the underlying callable
deploy.on_invoke_tool = sv.governed("acme/deploy", deploy.on_invoke_tool)
```

### Plain code

```python
run = sv.governed("acme/deploy", lambda env: deploy_to(env))
run("prod")   # raises SkillsvaultError if the org halts acme/deploy
```

## API

| Call | Description |
| --- | --- |
| `create_client(base_url, org, key, *, machine, harness, timeout, retries)` | Construct the client. |
| `sync()` | Pull + verify the signed policy bundle. Call at startup and to refresh. |
| `gate(skill) -> Decision` | Local, deterministic allow/warn/halt. |
| `governed(skill, fn)` | Wrap a tool: gate + audit, raise on halt. Sync or async. |
| `list_skills()` | The org's approved skills. |
| `get_skill(name)` | Current-version content (SKILL.md + files), or `None`. |
| `audit(event)` | Append an audit event (best-effort). |
| `evaluate(bundle, skill)` | Pure gate function, if you hold a bundle yourself. |

`Decision` is a frozen dataclass: `decision`, `reason`, `policy_id`, `policy_name`.

## License

MIT
