Skip to content

Commit 45c8954

Browse files
feat: add bindings resource for Execution Intents API
New BindingsResource with create, list, get, update, delete, test, execute, and list_executions methods. Agent types updated. Python SDK v0.2.0 Co-authored-by: Cursor <cursoragent@cursor.com>
1 parent 2b2ec11 commit 45c8954

7 files changed

Lines changed: 255 additions & 4 deletions

File tree

‎README.md‎

Lines changed: 40 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,45 @@ resp = client.agents.sign_transaction(
143143
)
144144
```
145145

146+
### Execution Intents (Bindings)
147+
148+
```python
149+
# Create a binding to an external service
150+
resp = client.bindings.create(
151+
agent_id,
152+
name="OpenAI GPT-4",
153+
binding_type="http_api",
154+
config={"base_url": "https://api.openai.com/v1", "method": "POST"},
155+
guardrails={"max_tokens_per_request": 4096},
156+
)
157+
binding_id = resp.data["id"]
158+
159+
# List bindings
160+
bindings = client.bindings.list(agent_id)
161+
162+
# Test connectivity
163+
result = client.bindings.test(agent_id, binding_id)
164+
print(result.data["latency_ms"])
165+
166+
# Execute an intent
167+
resp = client.bindings.execute(
168+
agent_id,
169+
binding="OpenAI GPT-4",
170+
intent_type="chat_completion",
171+
params={"model": "gpt-4", "messages": [{"role": "user", "content": "Hello"}]},
172+
)
173+
print(resp.data["execution_id"])
174+
175+
# List execution history
176+
events = client.bindings.list_executions(agent_id, limit=20)
177+
178+
# Update guardrails
179+
client.bindings.update(agent_id, binding_id, guardrails={"max_tokens_per_request": 8192})
180+
181+
# Delete a binding
182+
client.bindings.delete(agent_id, binding_id)
183+
```
184+
146185
### Signing Keys
147186

148187
```python
@@ -243,7 +282,7 @@ resp = client.auth.verify_email_otp("user@example.com", "123456")
243282
client.auth.social_login(provider="google", id_token="...")
244283
```
245284

246-
> **Note:** For the full v0.36 API surface (non-EVM transaction signing, spend policies, deposit destinations, fiat ramps, internal accounts, and more), see the [TypeScript SDK](https://www.npmjs.com/package/@1claw/sdk) and the [OpenAPI spec](https://www.npmjs.com/package/@1claw/openapi-spec).
285+
> **Note:** For the full API surface (non-EVM transaction signing, spend policies, deposit destinations, fiat ramps, internal accounts, and more), see the [TypeScript SDK](https://www.npmjs.com/package/@1claw/sdk) and the [OpenAPI spec](https://www.npmjs.com/package/@1claw/openapi-spec).
247286
248287
## Error Handling
249288

‎pyproject.toml‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,15 @@ build-backend = "hatchling.build"
44

55
[project]
66
name = "oneclaw"
7-
version = "0.1.0"
7+
version = "0.2.0"
88
description = "Official Python SDK for the 1Claw secrets management platform"
99
readme = "README.md"
1010
license = "MIT"
1111
requires-python = ">=3.9"
1212
authors = [
1313
{ name = "1Claw", email = "ops@1claw.xyz" },
1414
]
15-
keywords = ["secrets", "vault", "ai", "agents", "crypto", "signing"]
15+
keywords = ["secrets", "vault", "ai", "agents", "crypto", "signing", "bindings", "execution-intents"]
1616
classifiers = [
1717
"Development Status :: 4 - Beta",
1818
"Intended Audience :: Developers",

‎src/oneclaw/__init__.py‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
)
1515
from oneclaw.types import OneclawClientConfig, OneclawResponse
1616

17-
__version__ = "0.1.0"
17+
__version__ = "0.2.0"
1818

1919
__all__ = [
2020
"OneclawClient",

‎src/oneclaw/client.py‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
from oneclaw.resources.agents import AgentsResource
77
from oneclaw.resources.api_keys import ApiKeysResource
88
from oneclaw.resources.approvals import ApprovalsResource
9+
from oneclaw.resources.bindings import BindingsResource
910
from oneclaw.resources.audit import AuditResource
1011
from oneclaw.resources.auth import AuthResource
1112
from oneclaw.resources.billing import BillingResource
@@ -74,6 +75,7 @@ def __init__(self, config: OneclawClientConfig) -> None:
7475
self.approvals = ApprovalsResource(self._http)
7576
self.webhooks = WebhooksResource(self._http)
7677
self.risk = RiskResource(self._http)
78+
self.bindings = BindingsResource(self._http)
7779

7880
def _auto_authenticate_user_key(self, api_key: str) -> None:
7981
"""Exchange a ``1ck_`` user API key for a JWT (fire-and-forget)."""

‎src/oneclaw/resources/__init__.py‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
from oneclaw.resources.agents import AgentsResource
44
from oneclaw.resources.api_keys import ApiKeysResource
55
from oneclaw.resources.approvals import ApprovalsResource
6+
from oneclaw.resources.bindings import BindingsResource
67
from oneclaw.resources.audit import AuditResource
78
from oneclaw.resources.auth import AuthResource
89
from oneclaw.resources.billing import BillingResource
@@ -38,4 +39,5 @@
3839
"ApprovalsResource",
3940
"WebhooksResource",
4041
"RiskResource",
42+
"BindingsResource",
4143
]

‎src/oneclaw/resources/bindings.py‎

Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
"""Execution Intents — bindings, execute, and execution history."""
2+
3+
from __future__ import annotations
4+
5+
from typing import TYPE_CHECKING, Any
6+
7+
if TYPE_CHECKING:
8+
from oneclaw.http_client import HttpClient
9+
from oneclaw.types import OneclawResponse
10+
11+
12+
class BindingsResource:
13+
"""Manage agent bindings and execute intents against external services."""
14+
15+
def __init__(self, http: HttpClient) -> None:
16+
self._http = http
17+
18+
def create(
19+
self,
20+
agent_id: str,
21+
*,
22+
name: str,
23+
binding_type: str,
24+
config: dict[str, Any] | None = None,
25+
guardrails: dict[str, Any] | None = None,
26+
credential: dict[str, Any] | None = None,
27+
) -> OneclawResponse[Any]:
28+
"""Create a binding for an agent."""
29+
body: dict[str, Any] = {"name": name, "binding_type": binding_type}
30+
for key, val in {
31+
"config": config,
32+
"guardrails": guardrails,
33+
"credential": credential,
34+
}.items():
35+
if val is not None:
36+
body[key] = val
37+
return self._http.request("POST", f"/v1/agents/{agent_id}/bindings", body=body)
38+
39+
def list(self, agent_id: str) -> OneclawResponse[Any]:
40+
"""List all bindings for an agent."""
41+
return self._http.request("GET", f"/v1/agents/{agent_id}/bindings")
42+
43+
def get(self, agent_id: str, binding_id: str) -> OneclawResponse[Any]:
44+
"""Get a binding by ID."""
45+
return self._http.request("GET", f"/v1/agents/{agent_id}/bindings/{binding_id}")
46+
47+
def update(
48+
self,
49+
agent_id: str,
50+
binding_id: str,
51+
**kwargs: Any,
52+
) -> OneclawResponse[Any]:
53+
"""Update a binding's configuration, guardrails, or active status."""
54+
body = {k: v for k, v in kwargs.items() if v is not None}
55+
return self._http.request(
56+
"PATCH", f"/v1/agents/{agent_id}/bindings/{binding_id}", body=body,
57+
)
58+
59+
def delete(self, agent_id: str, binding_id: str) -> OneclawResponse[Any]:
60+
"""Delete a binding."""
61+
return self._http.request("DELETE", f"/v1/agents/{agent_id}/bindings/{binding_id}")
62+
63+
def test(
64+
self,
65+
agent_id: str,
66+
binding_id: str,
67+
*,
68+
timeout_ms: int | None = None,
69+
) -> OneclawResponse[Any]:
70+
"""Test connectivity of a binding."""
71+
body: dict[str, Any] = {}
72+
if timeout_ms is not None:
73+
body["timeout_ms"] = timeout_ms
74+
return self._http.request(
75+
"POST", f"/v1/agents/{agent_id}/bindings/{binding_id}/test", body=body,
76+
)
77+
78+
def execute(
79+
self,
80+
agent_id: str,
81+
*,
82+
binding: str,
83+
intent_type: str,
84+
params: dict[str, Any] | None = None,
85+
execution_mode: str | None = None,
86+
) -> OneclawResponse[Any]:
87+
"""Execute an intent against a binding."""
88+
body: dict[str, Any] = {
89+
"binding": binding,
90+
"intent_type": intent_type,
91+
"params": params or {},
92+
}
93+
if execution_mode is not None:
94+
body["execution_mode"] = execution_mode
95+
return self._http.request("POST", f"/v1/agents/{agent_id}/execute", body=body)
96+
97+
def list_executions(
98+
self,
99+
agent_id: str,
100+
*,
101+
limit: int | None = None,
102+
offset: int | None = None,
103+
) -> OneclawResponse[Any]:
104+
"""List execution events for an agent."""
105+
query: dict[str, Any] = {}
106+
if limit is not None:
107+
query["limit"] = limit
108+
if offset is not None:
109+
query["offset"] = offset
110+
return self._http.request(
111+
"GET", f"/v1/agents/{agent_id}/executions", query=query or None,
112+
)

‎src/oneclaw/types.py‎

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -276,6 +276,8 @@ class CreateAgentRequest:
276276
eip712_default_policy: str | None = None
277277
eip712_domain_allowlist: list[dict[str, Any]] | None = None
278278
api_key_expires_at: str | None = None
279+
execution_intents_enabled: bool | None = None
280+
execution_guardrails: dict[str, Any] | None = None
279281

280282

281283
@dataclass
@@ -303,6 +305,8 @@ class UpdateAgentRequest:
303305
eip712_default_policy: str | None = None
304306
eip712_domain_allowlist: list[dict[str, Any]] | None = None
305307
api_key_expires_at: str | None = None
308+
execution_intents_enabled: bool | None = None
309+
execution_guardrails: dict[str, Any] | None = None
306310

307311

308312
@dataclass
@@ -345,6 +349,8 @@ class AgentResponse:
345349
platform_app_id: str | None = None
346350
platform_locked: bool | None = None
347351
treasury_signing_mode: str | None = None
352+
execution_intents_enabled: bool | None = None
353+
execution_guardrails: dict[str, Any] | None = None
348354

349355

350356
@dataclass
@@ -811,3 +817,93 @@ class KnownToken:
811817
@dataclass
812818
class KnownTokenListResponse:
813819
tokens: list[KnownToken] = field(default_factory=list)
820+
821+
822+
# ---------------------------------------------------------------------------
823+
# Execution Intents — Bindings & Execution
824+
# ---------------------------------------------------------------------------
825+
826+
@dataclass
827+
class CreateBindingRequest:
828+
name: str
829+
binding_type: str
830+
config: dict[str, Any] | None = None
831+
guardrails: dict[str, Any] | None = None
832+
credential: dict[str, Any] | None = None
833+
834+
835+
@dataclass
836+
class UpdateBindingRequest:
837+
name: str | None = None
838+
config: dict[str, Any] | None = None
839+
guardrails: dict[str, Any] | None = None
840+
is_active: bool | None = None
841+
credential: dict[str, Any] | None = None
842+
843+
844+
@dataclass
845+
class BindingResponse:
846+
id: str
847+
agent_id: str
848+
binding_type: str
849+
name: str
850+
config: dict[str, Any] = field(default_factory=dict)
851+
guardrails: dict[str, Any] = field(default_factory=dict)
852+
is_active: bool = True
853+
created_at: str | None = None
854+
updated_at: str | None = None
855+
856+
857+
@dataclass
858+
class BindingListResponse:
859+
bindings: list[BindingResponse] = field(default_factory=list)
860+
861+
862+
@dataclass
863+
class ExecuteRequest:
864+
binding: str
865+
intent_type: str
866+
params: dict[str, Any] = field(default_factory=dict)
867+
execution_mode: str | None = None
868+
869+
870+
@dataclass
871+
class ExecuteResponse:
872+
execution_id: str
873+
status: str
874+
result: dict[str, Any] | None = None
875+
error: str | None = None
876+
duration_ms: int | None = None
877+
redactions_applied: int | None = None
878+
879+
880+
@dataclass
881+
class ExecutionEventResponse:
882+
id: str
883+
agent_id: str
884+
binding_id: str
885+
intent_type: str
886+
execution_mode: str
887+
status: str
888+
duration_ms: int | None = None
889+
error: str | None = None
890+
cost_cents: int | None = None
891+
redactions_applied: int | None = None
892+
created_at: str | None = None
893+
894+
895+
@dataclass
896+
class ExecutionEventListResponse:
897+
events: list[ExecutionEventResponse] = field(default_factory=list)
898+
899+
900+
@dataclass
901+
class TestBindingRequest:
902+
timeout_ms: int | None = None
903+
904+
905+
@dataclass
906+
class TestBindingResponse:
907+
success: bool = False
908+
latency_ms: int = 0
909+
error: str | None = None

0 commit comments

Comments
 (0)