agentdraft 0.1.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
agentdraft/__init__.py ADDED
@@ -0,0 +1,42 @@
1
+ """AgentDraft — Python SDK.
2
+
3
+ The scheduling source of truth for AI agents. See https://agentdraft.io/docs.
4
+
5
+ Quickstart::
6
+
7
+ from agentdraft import Client, Conflict
8
+ from datetime import datetime, timedelta, timezone
9
+
10
+ client = Client(api_key="avs_live_...")
11
+
12
+ start = datetime.now(timezone.utc) + timedelta(hours=4)
13
+ end = start + timedelta(minutes=30)
14
+
15
+ try:
16
+ booking = client.bookings.commit(
17
+ start=start, end=end,
18
+ idempotency_key="ik_call_42",
19
+ metadata={"title": "Discovery call"},
20
+ )
21
+ print("booked:", booking.booking_id)
22
+ except Conflict as e:
23
+ print(f"outranked by {e.winning_agent_id} (rank {e.winning_agent_priority})")
24
+ """
25
+
26
+ from .client import Client
27
+ from .errors import AgentDraftError, AuthError, Conflict, RateLimited, RuleViolation
28
+ from .models import Booking, Hold, Slot
29
+
30
+ __all__ = [
31
+ "Client",
32
+ "AgentDraftError",
33
+ "AuthError",
34
+ "Conflict",
35
+ "RateLimited",
36
+ "RuleViolation",
37
+ "Booking",
38
+ "Hold",
39
+ "Slot",
40
+ ]
41
+
42
+ __version__ = "0.1.0"
agentdraft/client.py ADDED
@@ -0,0 +1,312 @@
1
+ """The Client class — the SDK's only entrypoint."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import os
6
+ from datetime import datetime
7
+ from typing import Any, Optional
8
+
9
+ import httpx
10
+
11
+ from .errors import AgentDraftError, AuthError, Conflict, RateLimited, RuleViolation
12
+ from .models import AgentIdentity, Booking, Slot
13
+
14
+ DEFAULT_BASE_URL = "https://api.agentdraft.io"
15
+
16
+
17
+ class Client:
18
+ """Top-level SDK client.
19
+
20
+ Parameters
21
+ ----------
22
+ api_key:
23
+ An ``avs_live_…`` key issued from your AgentDraft dashboard. If omitted,
24
+ reads ``AGENTDRAFT_API_KEY`` from the environment.
25
+ base_url:
26
+ Override the API base. Defaults to ``https://api.agentdraft.io``; for
27
+ local development point at ``http://localhost:8080``.
28
+ timeout:
29
+ Per-request timeout in seconds (default 10).
30
+ user_agent:
31
+ Override the User-Agent string sent on every request.
32
+ """
33
+
34
+ def __init__(
35
+ self,
36
+ api_key: Optional[str] = None,
37
+ *,
38
+ base_url: Optional[str] = None,
39
+ timeout: float = 10.0,
40
+ user_agent: str = "agentdraft-python/0.1.0",
41
+ ):
42
+ api_key = api_key or os.environ.get("AGENTDRAFT_API_KEY")
43
+ if not api_key:
44
+ raise ValueError("api_key is required (pass it or set AGENTDRAFT_API_KEY)")
45
+ if not api_key.startswith("avs_live_"):
46
+ raise ValueError("api_key must start with 'avs_live_'")
47
+ self._api_key = api_key
48
+ self._base = (base_url or os.environ.get("AGENTDRAFT_BASE_URL") or DEFAULT_BASE_URL).rstrip("/")
49
+ self._http = httpx.Client(
50
+ timeout=timeout,
51
+ headers={"Authorization": f"Bearer {api_key}", "User-Agent": user_agent},
52
+ )
53
+
54
+ self.availability = _Availability(self)
55
+ self.bookings = _Bookings(self)
56
+ self.agents = _Agents(self)
57
+ self.mailbox = _Mailbox(self)
58
+
59
+ def close(self) -> None:
60
+ self._http.close()
61
+
62
+ def __enter__(self) -> "Client":
63
+ return self
64
+
65
+ def __exit__(self, *_exc) -> None:
66
+ self.close()
67
+
68
+ # -- low-level ---------------------------------------------------------
69
+
70
+ def _request(
71
+ self,
72
+ method: str,
73
+ path: str,
74
+ *,
75
+ json: Any = None,
76
+ params: dict | None = None,
77
+ headers: dict | None = None,
78
+ ) -> tuple[int, dict, dict]:
79
+ resp = self._http.request(method, f"{self._base}{path}", json=json, params=params, headers=headers)
80
+ try:
81
+ body = resp.json() if resp.content else {}
82
+ except Exception:
83
+ body = {"raw": resp.text}
84
+
85
+ if resp.status_code >= 400:
86
+ self._raise_for(resp.status_code, body, resp.headers)
87
+
88
+ return resp.status_code, body, dict(resp.headers)
89
+
90
+ def _raise_for(self, status: int, body: dict, headers: dict) -> None:
91
+ if status == 401 or status == 403:
92
+ raise AuthError(body.get("detail") or str(body), status=status, body=body)
93
+ if status == 409:
94
+ raise Conflict(
95
+ body.get("error", "outranked"),
96
+ body=body,
97
+ winning_booking_id=body.get("winning_booking_id"),
98
+ winning_agent_id=body.get("winning_agent_id"),
99
+ winning_agent_priority=body.get("winning_agent_priority"),
100
+ your_priority=body.get("your_priority"),
101
+ reason=body.get("error", "outranked"),
102
+ )
103
+ if status == 422:
104
+ raise RuleViolation(body.get("detail") or "rule violation", status=status, body=body)
105
+ if status == 429:
106
+ retry = headers.get("retry-after") or headers.get("Retry-After")
107
+ raise RateLimited(
108
+ "rate limited",
109
+ body=body,
110
+ retry_after=int(retry) if retry else None,
111
+ )
112
+ raise AgentDraftError(f"HTTP {status}: {body}", status=status, body=body)
113
+
114
+
115
+ # -- resources -----------------------------------------------------------------
116
+
117
+
118
+ class _Resource:
119
+ def __init__(self, client: Client):
120
+ self._c = client
121
+
122
+
123
+ class _Availability(_Resource):
124
+ def list(
125
+ self,
126
+ *,
127
+ start: datetime,
128
+ end: datetime,
129
+ duration_minutes: int = 30,
130
+ granularity_minutes: int = 15,
131
+ include_holds: bool = True,
132
+ user_id: Optional[str] = None,
133
+ ) -> list[Slot]:
134
+ params: dict[str, Any] = {
135
+ "range_start": start.isoformat(),
136
+ "range_end": end.isoformat(),
137
+ "duration_minutes": duration_minutes,
138
+ "granularity_minutes": granularity_minutes,
139
+ "include_holds": str(include_holds).lower(),
140
+ }
141
+ if user_id:
142
+ params["user_id"] = user_id
143
+ else:
144
+ # The endpoint requires user_id today; resolve it via /v1/agents/me
145
+ me = self._c.agents.me()
146
+ params["user_id"] = me.user_id
147
+
148
+ _, body, _ = self._c._request("GET", "/v1/availability", params=params)
149
+ return [Slot.from_json(s) for s in body.get("slots", [])]
150
+
151
+
152
+ class _Bookings(_Resource):
153
+ def commit(
154
+ self,
155
+ *,
156
+ start: datetime,
157
+ end: datetime,
158
+ idempotency_key: Optional[str] = None,
159
+ buffer_before_min: int = 0,
160
+ buffer_after_min: int = 0,
161
+ bump_window_seconds: Optional[int] = None,
162
+ metadata: Optional[dict] = None,
163
+ ) -> Booking:
164
+ return self._post(
165
+ start=start,
166
+ end=end,
167
+ mode="commit",
168
+ idempotency_key=idempotency_key,
169
+ buffer_before_min=buffer_before_min,
170
+ buffer_after_min=buffer_after_min,
171
+ bump_window_seconds=bump_window_seconds,
172
+ metadata=metadata,
173
+ )
174
+
175
+ def hold(
176
+ self,
177
+ *,
178
+ start: datetime,
179
+ end: datetime,
180
+ idempotency_key: Optional[str] = None,
181
+ buffer_before_min: int = 0,
182
+ buffer_after_min: int = 0,
183
+ metadata: Optional[dict] = None,
184
+ ) -> Booking:
185
+ return self._post(
186
+ start=start,
187
+ end=end,
188
+ mode="hold",
189
+ idempotency_key=idempotency_key,
190
+ buffer_before_min=buffer_before_min,
191
+ buffer_after_min=buffer_after_min,
192
+ metadata=metadata,
193
+ )
194
+
195
+ def release(self, hold_id: str) -> None:
196
+ self._c._request("POST", f"/v1/holds/{hold_id}/release")
197
+
198
+ def cancel(self, booking_id: str) -> None:
199
+ """Cancel a previously-committed booking owned by this agent."""
200
+ self._c._request("DELETE", f"/v1/bookings/{booking_id}")
201
+
202
+ def _post(self, **kwargs: Any) -> Booking:
203
+ idempotency_key = kwargs.pop("idempotency_key", None)
204
+ body = {
205
+ "start": kwargs["start"].isoformat(),
206
+ "end": kwargs["end"].isoformat(),
207
+ "mode": kwargs["mode"],
208
+ "buffer_before_min": kwargs.get("buffer_before_min", 0),
209
+ "buffer_after_min": kwargs.get("buffer_after_min", 0),
210
+ }
211
+ if kwargs.get("bump_window_seconds") is not None:
212
+ body["bump_window_seconds"] = kwargs["bump_window_seconds"]
213
+ if kwargs.get("metadata"):
214
+ body["metadata"] = kwargs["metadata"]
215
+
216
+ headers = {"Idempotency-Key": idempotency_key} if idempotency_key else None
217
+ _, j, _ = self._c._request("POST", "/v1/bookings", json=body, headers=headers)
218
+ return Booking.from_json(j)
219
+
220
+
221
+ class _Agents(_Resource):
222
+ def me(self) -> AgentIdentity:
223
+ _, j, _ = self._c._request("GET", "/v1/agents/me")
224
+ return AgentIdentity.from_json(j)
225
+
226
+
227
+ class _Mailbox(_Resource):
228
+ """Inbound + outbound mail for the calling agent.
229
+
230
+ Threading + recipient on ``reply`` are derived server-side from the
231
+ original message — the SDK only needs the body. The address returned
232
+ by ``info()`` is the agent's inbox, suitable for surfacing to humans.
233
+ """
234
+
235
+ def info(self) -> dict:
236
+ _, j, _ = self._c._request("GET", "/v1/mailbox/me")
237
+ return j
238
+
239
+ def quota(self) -> dict:
240
+ """Convenience: just the quota sub-object of ``info()``."""
241
+ return self.info().get("quota", {})
242
+
243
+ def list_messages(
244
+ self,
245
+ *,
246
+ limit: int = 25,
247
+ cursor: Optional[str] = None,
248
+ direction: Optional[str] = None,
249
+ booking_id: Optional[str] = None,
250
+ since: Optional[datetime] = None,
251
+ ) -> dict:
252
+ params: dict[str, Any] = {"limit": limit}
253
+ if cursor:
254
+ params["cursor"] = cursor
255
+ if direction:
256
+ params["direction"] = direction
257
+ if booking_id:
258
+ params["booking_id"] = booking_id
259
+ if since:
260
+ params["since"] = since.isoformat()
261
+ _, j, _ = self._c._request("GET", "/v1/mailbox/messages", params=params)
262
+ return j
263
+
264
+ def get_message(self, message_id: str) -> dict:
265
+ _, j, _ = self._c._request("GET", f"/v1/mailbox/messages/{message_id}")
266
+ return j
267
+
268
+ def send(
269
+ self,
270
+ *,
271
+ to: str,
272
+ subject: str,
273
+ body_text: str,
274
+ booking_id: Optional[str] = None,
275
+ in_reply_to: Optional[str] = None,
276
+ reply_to: Optional[str] = None,
277
+ ) -> dict:
278
+ body: dict[str, Any] = {"to": to, "subject": subject, "body_text": body_text}
279
+ if booking_id is not None:
280
+ body["booking_id"] = booking_id
281
+ if in_reply_to is not None:
282
+ body["in_reply_to"] = in_reply_to
283
+ if reply_to is not None:
284
+ body["reply_to"] = reply_to
285
+ _, j, _ = self._c._request("POST", "/v1/mailbox/send", json=body)
286
+ return j
287
+
288
+ def reply(
289
+ self,
290
+ message_id: str,
291
+ *,
292
+ body_text: str,
293
+ subject: Optional[str] = None,
294
+ reply_to: Optional[str] = None,
295
+ ) -> dict:
296
+ body: dict[str, Any] = {"body_text": body_text}
297
+ if subject is not None:
298
+ body["subject"] = subject
299
+ if reply_to is not None:
300
+ body["reply_to"] = reply_to
301
+ _, j, _ = self._c._request(
302
+ "POST", f"/v1/mailbox/messages/{message_id}/reply", json=body
303
+ )
304
+ return j
305
+
306
+ def check_suppression(self, address: str) -> dict:
307
+ from urllib.parse import quote
308
+
309
+ _, j, _ = self._c._request(
310
+ "GET", f"/v1/mailbox/suppressions/{quote(address, safe='@+.')}"
311
+ )
312
+ return j
agentdraft/errors.py ADDED
@@ -0,0 +1,58 @@
1
+ """SDK exception hierarchy.
2
+
3
+ All AgentDraft errors inherit from ``AgentDraftError``. ``Conflict`` is the
4
+ domain-meaningful one — your booking lost to a higher-priority agent — and
5
+ carries the winner's identity for graceful fallback.
6
+ """
7
+
8
+ from __future__ import annotations
9
+
10
+ from typing import Optional
11
+
12
+
13
+ class AgentDraftError(Exception):
14
+ """Base for all SDK errors."""
15
+
16
+ def __init__(self, message: str, *, status: int | None = None, body: dict | None = None):
17
+ super().__init__(message)
18
+ self.status = status
19
+ self.body = body or {}
20
+
21
+
22
+ class AuthError(AgentDraftError):
23
+ """401 / 403 — your API key is missing, invalid, or lacks the required scope."""
24
+
25
+
26
+ class Conflict(AgentDraftError):
27
+ """409 — your booking lost. Inspect ``winning_*`` to decide what to do next."""
28
+
29
+ def __init__(
30
+ self,
31
+ message: str,
32
+ *,
33
+ status: int = 409,
34
+ body: dict,
35
+ winning_booking_id: Optional[str] = None,
36
+ winning_agent_id: Optional[str] = None,
37
+ winning_agent_priority: Optional[int] = None,
38
+ your_priority: Optional[int] = None,
39
+ reason: str = "outranked",
40
+ ):
41
+ super().__init__(message, status=status, body=body)
42
+ self.winning_booking_id = winning_booking_id
43
+ self.winning_agent_id = winning_agent_id
44
+ self.winning_agent_priority = winning_agent_priority
45
+ self.your_priority = your_priority
46
+ self.reason = reason
47
+
48
+
49
+ class RuleViolation(AgentDraftError):
50
+ """422 — the proposed booking violates a rule (working hours, focus block, etc.)."""
51
+
52
+
53
+ class RateLimited(AgentDraftError):
54
+ """429 — you've exceeded your rate limit. ``retry_after`` is in seconds."""
55
+
56
+ def __init__(self, message: str, *, status: int = 429, body: dict, retry_after: int | None = None):
57
+ super().__init__(message, status=status, body=body)
58
+ self.retry_after = retry_after
agentdraft/models.py ADDED
@@ -0,0 +1,64 @@
1
+ """Lightweight value objects. No Pydantic dep — keep the SDK small."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from dataclasses import dataclass
6
+ from datetime import datetime
7
+ from typing import Optional
8
+
9
+
10
+ def _parse_dt(s: Optional[str]) -> Optional[datetime]:
11
+ if not s:
12
+ return None
13
+ return datetime.fromisoformat(s.replace("Z", "+00:00"))
14
+
15
+
16
+ @dataclass
17
+ class Booking:
18
+ booking_id: str
19
+ status: str # "COMMITTED" | "HOLD"
20
+ expires_at: Optional[datetime] = None
21
+ audit_event_id: Optional[str] = None
22
+
23
+ @classmethod
24
+ def from_json(cls, j: dict) -> "Booking":
25
+ return cls(
26
+ booking_id=j["booking_id"],
27
+ status=j["status"],
28
+ expires_at=_parse_dt(j.get("expires_at")),
29
+ audit_event_id=j.get("audit_event_id"),
30
+ )
31
+
32
+
33
+ @dataclass
34
+ class Hold(Booking):
35
+ """A Booking with status=HOLD. Same shape; semantic alias for clarity."""
36
+
37
+ pass
38
+
39
+
40
+ @dataclass
41
+ class Slot:
42
+ start: datetime
43
+ end: datetime
44
+
45
+ @classmethod
46
+ def from_json(cls, j: dict) -> "Slot":
47
+ return cls(start=_parse_dt(j["start"]), end=_parse_dt(j["end"]))
48
+
49
+
50
+ @dataclass
51
+ class AgentIdentity:
52
+ agent_id: str
53
+ user_id: str
54
+ priority: int
55
+ scopes: list[str]
56
+
57
+ @classmethod
58
+ def from_json(cls, j: dict) -> "AgentIdentity":
59
+ return cls(
60
+ agent_id=j["agent_id"],
61
+ user_id=j["user_id"],
62
+ priority=j["priority"],
63
+ scopes=j.get("scopes", []),
64
+ )
agentdraft/py.typed ADDED
File without changes
@@ -0,0 +1,147 @@
1
+ Metadata-Version: 2.4
2
+ Name: agentdraft
3
+ Version: 0.1.0
4
+ Summary: Python SDK for AgentDraft — the scheduling source of truth for AI agents
5
+ Author-email: AgentDraft Labs <hello@agentdraft.io>
6
+ License: MIT
7
+ Project-URL: Homepage, https://agentdraft.io
8
+ Project-URL: Documentation, https://agentdraft.io/docs
9
+ Project-URL: Repository, https://github.com/GipsyChef/agentdraft
10
+ Project-URL: Issues, https://github.com/GipsyChef/agentdraft/issues
11
+ Project-URL: Changelog, https://github.com/GipsyChef/agentdraft/blob/main/sdks/python/CHANGELOG.md
12
+ Keywords: calendar,scheduling,ai-agents,agentdraft
13
+ Classifier: Development Status :: 4 - Beta
14
+ Classifier: Intended Audience :: Developers
15
+ Classifier: License :: OSI Approved :: MIT License
16
+ Classifier: Programming Language :: Python :: 3
17
+ Classifier: Programming Language :: Python :: 3.9
18
+ Classifier: Programming Language :: Python :: 3.10
19
+ Classifier: Programming Language :: Python :: 3.11
20
+ Classifier: Programming Language :: Python :: 3.12
21
+ Classifier: Topic :: Office/Business :: Scheduling
22
+ Requires-Python: >=3.9
23
+ Description-Content-Type: text/markdown
24
+ License-File: LICENSE
25
+ Requires-Dist: httpx>=0.27.0
26
+ Dynamic: license-file
27
+
28
+ # agentdraft — Python SDK
29
+
30
+ [![PyPI](https://img.shields.io/pypi/v/agentdraft.svg)](https://pypi.org/project/agentdraft/)
31
+ [![Python](https://img.shields.io/pypi/pyversions/agentdraft.svg)](https://pypi.org/project/agentdraft/)
32
+ [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE)
33
+
34
+ The official Python SDK for [AgentDraft](https://agentdraft.io) — the
35
+ scheduling source of truth for AI agents.
36
+
37
+ AgentDraft is the coordination layer that prevents AI scheduling agents
38
+ from colliding on the same calendar. Every agent calls one API before
39
+ booking; every commit goes through one deterministic engine; every
40
+ action is recorded in one tamper-evident audit log.
41
+
42
+ This SDK gives Python agents a typed, one-line surface to participate.
43
+
44
+ ## Install
45
+
46
+ ```bash
47
+ pip install agentdraft
48
+ ```
49
+
50
+ Requires Python 3.9+.
51
+
52
+ ## Quickstart
53
+
54
+ ```python
55
+ from datetime import datetime, timedelta, timezone
56
+ from agentdraft import Client, Conflict
57
+
58
+ client = Client(api_key="avs_live_...") # or set AGENTDRAFT_API_KEY
59
+
60
+ start = datetime.now(timezone.utc) + timedelta(hours=4)
61
+ end = start + timedelta(minutes=30)
62
+
63
+ try:
64
+ booking = client.bookings.commit(
65
+ start=start, end=end,
66
+ idempotency_key="ik_call_42",
67
+ metadata={"title": "Discovery call"},
68
+ )
69
+ print("booked:", booking.booking_id)
70
+ except Conflict as e:
71
+ print(f"outranked by {e.winning_agent_id} (rank {e.winning_agent_priority})")
72
+ ```
73
+
74
+ A losing agent gets a typed `Conflict` exception, not a timeout — it
75
+ knows who won, by what priority, and where the audit row lives, so
76
+ fallback behavior (propose an alternate, escalate, defer) is a clean
77
+ `except` clause away.
78
+
79
+ ## Authentication
80
+
81
+ API keys are issued from the AgentDraft dashboard and start with
82
+ `avs_live_`. Pass it explicitly or let the client read it from the
83
+ environment:
84
+
85
+ ```python
86
+ Client(api_key="avs_live_...")
87
+ # or
88
+ import os; os.environ["AGENTDRAFT_API_KEY"] = "avs_live_..."
89
+ Client()
90
+ ```
91
+
92
+ For local development against a dev backend, point at it via
93
+ `AGENTDRAFT_BASE_URL` or the `base_url=` kwarg.
94
+
95
+ ## Surface
96
+
97
+ | Attribute | Purpose |
98
+ |---|---|
99
+ | `client.availability` | Read merged availability across all agents writing to the calendar |
100
+ | `client.bookings` | `hold`, `release`, `commit`, `cancel` — the four state transitions |
101
+ | `client.agents` | `me()` — confirm key + current priority + scopes |
102
+ | `client.mailbox` | Inbound/outbound mail surface for agents that book via email |
103
+
104
+ All blocking I/O. An async client is on the roadmap; for now wrap with
105
+ `asyncio.to_thread` if you need concurrency.
106
+
107
+ ## Error types
108
+
109
+ Every failure is a typed exception so callers can branch precisely:
110
+
111
+ - `Conflict` — your write was outranked. Carries `winning_agent_id`,
112
+ `winning_agent_priority`, `winning_booking_id`, `audit_event_id`.
113
+ - `AuthError` — bad/missing/expired API key.
114
+ - `RateLimited` — token bucket exhausted. Has `retry_after_seconds`.
115
+ - `RuleViolation` — request was syntactically valid but violated a
116
+ rule (focus block, daily cap, business hours).
117
+ - `AgentDraftError` — base class; catch this if you only need a
118
+ catch-all.
119
+
120
+ ## Idempotency
121
+
122
+ Pass `idempotency_key=` to `bookings.commit(...)`. The server caches the
123
+ result by `(agent_id, key)` for 24 hours, so a retry over a flaky network
124
+ returns the original booking, not a duplicate.
125
+
126
+ ## Links
127
+
128
+ - Protocol spec: <https://agentdraft.io/spec>
129
+ - API docs: <https://agentdraft.io/docs>
130
+ - Changelog: [CHANGELOG.md](CHANGELOG.md)
131
+ - Issues / source: <https://github.com/GipsyChef/agentdraft>
132
+ - TypeScript SDK: [`@agentdraft/sdk`](https://www.npmjs.com/package/@agentdraft/sdk)
133
+
134
+ ## Security
135
+
136
+ Found a vulnerability? Please follow [SECURITY.md](SECURITY.md) — **do
137
+ not** open a public issue for a security report.
138
+
139
+ ## Contributing
140
+
141
+ See [CONTRIBUTING.md](CONTRIBUTING.md). This SDK lives inside the
142
+ [`GipsyChef/agentdraft`](https://github.com/GipsyChef/agentdraft)
143
+ monorepo under `sdks/python/`.
144
+
145
+ ## License
146
+
147
+ MIT — see [LICENSE](LICENSE).
@@ -0,0 +1,10 @@
1
+ agentdraft/__init__.py,sha256=QxWb3NorY8l9eFT9NhoTBM8ltTmId1CSNjLcCGUfBzs,1058
2
+ agentdraft/client.py,sha256=vbXYcFjKPdLV-QoZ9V_8H1pzTf2kPATJeTPWugtMv08,10305
3
+ agentdraft/errors.py,sha256=qq5S4-BfQ83Jugv8o81Q4BLhOKamDMJjxiuXa7QHDLo,1912
4
+ agentdraft/models.py,sha256=BalmulhbZymr4yRKkG57RSIEFeHmts9lIje-8EbmvW0,1471
5
+ agentdraft/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
6
+ agentdraft-0.1.0.dist-info/licenses/LICENSE,sha256=M1YQ3J9kW25EJEAYueRGXCMrj8wR-quyUrZzRomQXwg,1073
7
+ agentdraft-0.1.0.dist-info/METADATA,sha256=Biu0WBPbuCsW7na2ODV3uZ4Fm9bN9rzvXbkMvh7caB8,5134
8
+ agentdraft-0.1.0.dist-info/WHEEL,sha256=aeYiig01lYGDzBgS8HxWXOg3uV61G9ijOsup-k9o1sk,91
9
+ agentdraft-0.1.0.dist-info/top_level.txt,sha256=T5v__C9StkgOlkNaj_iQZs_KXGj8yu2k5N3UDuLBUY4,11
10
+ agentdraft-0.1.0.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (82.0.1)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 AgentDraft Labs
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT, OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1 @@
1
+ agentdraft