getstack 0.19.0__tar.gz → 1.0.0__tar.gz

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.
Files changed (85) hide show
  1. {getstack-0.19.0 → getstack-1.0.0}/.gitignore +15 -0
  2. {getstack-0.19.0 → getstack-1.0.0}/CLAUDE.md +2 -1
  3. {getstack-0.19.0 → getstack-1.0.0}/PKG-INFO +2 -2
  4. {getstack-0.19.0 → getstack-1.0.0}/pyproject.toml +1 -1
  5. {getstack-0.19.0 → getstack-1.0.0}/src/getstack/__init__.py +28 -0
  6. getstack-1.0.0/src/getstack/_async/authority.py +269 -0
  7. getstack-1.0.0/src/getstack/_async/delivery_methods.py +85 -0
  8. {getstack-0.19.0 → getstack-1.0.0}/src/getstack/_async/dropoffs.py +14 -2
  9. {getstack-0.19.0 → getstack-1.0.0}/src/getstack/_async/evidence_packs.py +11 -2
  10. {getstack-0.19.0 → getstack-1.0.0}/src/getstack/_async/identity.py +31 -6
  11. {getstack-0.19.0 → getstack-1.0.0}/src/getstack/_async/llm.py +9 -3
  12. {getstack-0.19.0 → getstack-1.0.0}/src/getstack/_async/missions.py +41 -0
  13. {getstack-0.19.0 → getstack-1.0.0}/src/getstack/_async/partner_tenants.py +1 -1
  14. {getstack-0.19.0 → getstack-1.0.0}/src/getstack/_async/passports.py +26 -0
  15. getstack-1.0.0/src/getstack/_async/pii_config.py +38 -0
  16. {getstack-0.19.0 → getstack-1.0.0}/src/getstack/_async/proxy.py +3 -0
  17. {getstack-0.19.0 → getstack-1.0.0}/src/getstack/_async/services.py +2 -3
  18. {getstack-0.19.0 → getstack-1.0.0}/src/getstack/_async/skills.py +22 -2
  19. {getstack-0.19.0 → getstack-1.0.0}/src/getstack/_async/team.py +2 -4
  20. {getstack-0.19.0 → getstack-1.0.0}/src/getstack/_async/usage.py +5 -1
  21. getstack-1.0.0/src/getstack/_authority_request.py +20 -0
  22. {getstack-0.19.0 → getstack-1.0.0}/src/getstack/agents.py +6 -3
  23. getstack-1.0.0/src/getstack/authority.py +317 -0
  24. {getstack-0.19.0 → getstack-1.0.0}/src/getstack/client.py +24 -0
  25. getstack-1.0.0/src/getstack/delivery_methods.py +85 -0
  26. {getstack-0.19.0 → getstack-1.0.0}/src/getstack/dropoffs.py +14 -2
  27. {getstack-0.19.0 → getstack-1.0.0}/src/getstack/errors.py +28 -5
  28. {getstack-0.19.0 → getstack-1.0.0}/src/getstack/evidence_packs.py +10 -2
  29. {getstack-0.19.0 → getstack-1.0.0}/src/getstack/identity.py +31 -6
  30. {getstack-0.19.0 → getstack-1.0.0}/src/getstack/llm.py +16 -7
  31. {getstack-0.19.0 → getstack-1.0.0}/src/getstack/missions.py +33 -52
  32. {getstack-0.19.0 → getstack-1.0.0}/src/getstack/partner_tenants.py +1 -1
  33. {getstack-0.19.0 → getstack-1.0.0}/src/getstack/passport_header_verify.py +41 -24
  34. {getstack-0.19.0 → getstack-1.0.0}/src/getstack/passports.py +26 -0
  35. getstack-1.0.0/src/getstack/pii_config.py +69 -0
  36. {getstack-0.19.0 → getstack-1.0.0}/src/getstack/proxy.py +5 -0
  37. {getstack-0.19.0 → getstack-1.0.0}/src/getstack/services.py +7 -5
  38. {getstack-0.19.0 → getstack-1.0.0}/src/getstack/skills.py +28 -3
  39. {getstack-0.19.0 → getstack-1.0.0}/src/getstack/team.py +2 -4
  40. {getstack-0.19.0 → getstack-1.0.0}/src/getstack/types.py +91 -1
  41. {getstack-0.19.0 → getstack-1.0.0}/src/getstack/usage.py +10 -4
  42. {getstack-0.19.0 → getstack-1.0.0}/src/getstack/verify_offline.py +6 -2
  43. getstack-1.0.0/tests/test_authority.py +323 -0
  44. getstack-1.0.0/tests/test_delivery_methods.py +77 -0
  45. getstack-1.0.0/tests/test_dropoffs.py +70 -0
  46. getstack-1.0.0/tests/test_errors.py +50 -0
  47. getstack-1.0.0/tests/test_evidence_packs.py +64 -0
  48. getstack-1.0.0/tests/test_identity_requirements.py +68 -0
  49. getstack-1.0.0/tests/test_missions.py +134 -0
  50. {getstack-0.19.0 → getstack-1.0.0}/tests/test_parity.py +115 -0
  51. getstack-0.19.0/src/getstack/_async/delivery_methods.py +0 -35
  52. getstack-0.19.0/src/getstack/delivery_methods.py +0 -35
  53. {getstack-0.19.0 → getstack-1.0.0}/LICENSE +0 -0
  54. {getstack-0.19.0 → getstack-1.0.0}/README.md +0 -0
  55. {getstack-0.19.0 → getstack-1.0.0}/scripts/verify_sign_claim_crosslang.py +0 -0
  56. {getstack-0.19.0 → getstack-1.0.0}/src/getstack/_async/__init__.py +0 -0
  57. {getstack-0.19.0 → getstack-1.0.0}/src/getstack/_async/agents.py +0 -0
  58. {getstack-0.19.0 → getstack-1.0.0}/src/getstack/_async/audit.py +0 -0
  59. {getstack-0.19.0 → getstack-1.0.0}/src/getstack/_async/credentials.py +0 -0
  60. {getstack-0.19.0 → getstack-1.0.0}/src/getstack/_async/detector_config.py +0 -0
  61. {getstack-0.19.0 → getstack-1.0.0}/src/getstack/_async/inbound_webhooks.py +0 -0
  62. {getstack-0.19.0 → getstack-1.0.0}/src/getstack/_async/intents.py +0 -0
  63. {getstack-0.19.0 → getstack-1.0.0}/src/getstack/_async/notifications.py +0 -0
  64. {getstack-0.19.0 → getstack-1.0.0}/src/getstack/_async/partner_connect.py +0 -0
  65. {getstack-0.19.0 → getstack-1.0.0}/src/getstack/_async/passport_session.py +0 -0
  66. {getstack-0.19.0 → getstack-1.0.0}/src/getstack/_async/reviews.py +0 -0
  67. {getstack-0.19.0 → getstack-1.0.0}/src/getstack/_async/scan.py +0 -0
  68. {getstack-0.19.0 → getstack-1.0.0}/src/getstack/_async/security_events.py +0 -0
  69. {getstack-0.19.0 → getstack-1.0.0}/src/getstack/agent_auth.py +0 -0
  70. {getstack-0.19.0 → getstack-1.0.0}/src/getstack/audit.py +0 -0
  71. {getstack-0.19.0 → getstack-1.0.0}/src/getstack/auth.py +0 -0
  72. {getstack-0.19.0 → getstack-1.0.0}/src/getstack/browser_bootstrap.py +0 -0
  73. {getstack-0.19.0 → getstack-1.0.0}/src/getstack/credentials.py +0 -0
  74. {getstack-0.19.0 → getstack-1.0.0}/src/getstack/detector_config.py +0 -0
  75. {getstack-0.19.0 → getstack-1.0.0}/src/getstack/inbound_webhooks.py +0 -0
  76. {getstack-0.19.0 → getstack-1.0.0}/src/getstack/intents.py +0 -0
  77. {getstack-0.19.0 → getstack-1.0.0}/src/getstack/notifications.py +0 -0
  78. {getstack-0.19.0 → getstack-1.0.0}/src/getstack/partner_connect.py +0 -0
  79. {getstack-0.19.0 → getstack-1.0.0}/src/getstack/passport_session.py +0 -0
  80. {getstack-0.19.0 → getstack-1.0.0}/src/getstack/py.typed +0 -0
  81. {getstack-0.19.0 → getstack-1.0.0}/src/getstack/reviews.py +0 -0
  82. {getstack-0.19.0 → getstack-1.0.0}/src/getstack/scan.py +0 -0
  83. {getstack-0.19.0 → getstack-1.0.0}/src/getstack/security_events.py +0 -0
  84. {getstack-0.19.0 → getstack-1.0.0}/src/getstack/sign_claim.py +0 -0
  85. {getstack-0.19.0 → getstack-1.0.0}/tests/test_sign_claim.py +0 -0
@@ -31,8 +31,13 @@ Thumbs.db
31
31
 
32
32
  # Next.js
33
33
  .next/
34
+ .next-*/
34
35
  out/
35
36
 
37
+ # Local package-manager cache and temporary preview helpers
38
+ /.pnpm-store/
39
+ /.tmp-*
40
+
36
41
  # MCP publisher
37
42
  mcp-publisher.exe
38
43
  .mcpregistry_*
@@ -74,3 +79,13 @@ docs/yc-q18/
74
79
  docs/yc-application-session-preface.md
75
80
  *-YC.md
76
81
  *-yc.md
82
+
83
+ # Design handoff packages (kept local, never committed)
84
+ STACK website redesign*.zip
85
+
86
+ # Benchmark working files: harvested environments and interim runs. The
87
+ # published artifacts live in docs/benchmarks/.
88
+ scratchpad/
89
+
90
+ # Local venv for the AgentDojo benchmark harness (never commit)
91
+ .venv-agentdojo/
@@ -14,7 +14,7 @@ Published to PyPI as `getstack`. Not a workspace member of the main monorepo
14
14
  credentials, drop-offs (+ expire), skills, identity, audit, notifications
15
15
  (+ update), security-events, proxy, scan, usage, intents, missions,
16
16
  evidence-packs, llm gateway, inbound-webhooks, partner, **team**,
17
- **delivery_methods**, **detector_config**.
17
+ **delivery_methods**, **detector_config**, **authority**.
18
18
  - **Offline / customer-managed crypto** (the JS headline features):
19
19
  `verify_passport_offline` (`sdk-python/src/getstack/verify_offline.py` —
20
20
  pyjwt/EdDSA, jwks_url OR public_jwk), `verify_passport_header`, and
@@ -45,6 +45,7 @@ hit `/v1/services/connected` and `/v1/services/{id}/disconnect`. Notification
45
45
 
46
46
  - Package metadata: `sdk-python/pyproject.toml`
47
47
  - Module entry: `sdk-python/src/getstack/`
48
+ - Authority lifecycle (sync + async): `sdk-python/src/getstack/authority.py`, `sdk-python/src/getstack/_async/authority.py`
48
49
  - User-facing quickstart + examples: `sdk-python/README.md`
49
50
  - License: `sdk-python/LICENSE`
50
51
 
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.4
1
+ Metadata-Version: 2.5
2
2
  Name: getstack
3
- Version: 0.19.0
3
+ Version: 1.0.0
4
4
  Summary: Python SDK for STACK — trust infrastructure for AI agents
5
5
  Project-URL: Homepage, https://getstack.run
6
6
  Project-URL: Documentation, https://getstack.run/docs/sdk
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "getstack"
7
- version = "0.19.0"
7
+ version = "1.0.0"
8
8
  description = "Python SDK for STACK — trust infrastructure for AI agents"
9
9
  readme = "README.md"
10
10
  license = "MIT"
@@ -66,6 +66,9 @@ from .llm import LlmService
66
66
  from .team import TeamService
67
67
  from .delivery_methods import DeliveryMethodService
68
68
  from .detector_config import DetectorConfigService
69
+ from .pii_config import PiiConfigService
70
+ from .authority import AuthorityService
71
+ from ._async.authority import AsyncAuthorityService
69
72
  from .passport_session import PassportSession
70
73
  from .types import (
71
74
  Agent,
@@ -87,6 +90,12 @@ from .types import (
87
90
  IdentityClaim,
88
91
  IdentitySettings,
89
92
  VerificationSession,
93
+ SetServiceRequirementInput,
94
+ ServiceRequirement,
95
+ ReplaceServiceRequirementResult,
96
+ AuthorityReference,
97
+ PassportStackClaims,
98
+ VerifiedPassportClaims,
90
99
  )
91
100
  from .errors import (
92
101
  StackError,
@@ -167,10 +176,19 @@ __all__ = [
167
176
  "IdentityClaim",
168
177
  "IdentitySettings",
169
178
  "VerificationSession",
179
+ "SetServiceRequirementInput",
180
+ "ServiceRequirement",
181
+ "ReplaceServiceRequirementResult",
182
+ "AuthorityReference",
183
+ "PassportStackClaims",
184
+ "VerifiedPassportClaims",
170
185
  # Service classes (for typing / direct construction)
171
186
  "TeamService",
172
187
  "DeliveryMethodService",
173
188
  "DetectorConfigService",
189
+ "PiiConfigService",
190
+ "AuthorityService",
191
+ "AsyncAuthorityService",
174
192
  # Offline / customer-managed crypto (parity with the JS SDK)
175
193
  "verify_passport_offline",
176
194
  "verify_passport_header",
@@ -249,6 +267,8 @@ class Stack:
249
267
  self.team = TeamService(self._client)
250
268
  self.delivery_methods = DeliveryMethodService(self._client)
251
269
  self.detector_config = DetectorConfigService(self._client)
270
+ self.pii_config = PiiConfigService(self._client)
271
+ self.authority = AuthorityService(self._client)
252
272
 
253
273
  def issue_passport(
254
274
  self,
@@ -258,6 +278,7 @@ class Stack:
258
278
  intents: list[dict[str, Any]] | None = None,
259
279
  ttl_seconds: int | None = None,
260
280
  accountability_mode: str | None = None,
281
+ authority_binding_ids: list[str] | None = None,
261
282
  **kwargs: Any,
262
283
  ) -> PassportSession:
263
284
  """Phase 5.2.6 — issue a passport and return a PassportSession
@@ -274,6 +295,7 @@ class Stack:
274
295
  if intents is not None: issue_input["intents"] = intents
275
296
  if ttl_seconds is not None: issue_input["ttl_seconds"] = ttl_seconds
276
297
  if accountability_mode is not None: issue_input["accountability_mode"] = accountability_mode
298
+ if authority_binding_ids is not None: issue_input["authority_binding_ids"] = authority_binding_ids
277
299
  issued = self._client.post("/v1/passports/issue", json=issue_input)
278
300
  return PassportSession(self._client, issued, issue_input)
279
301
 
@@ -396,6 +418,8 @@ class AsyncStack:
396
418
  from ._async.team import AsyncTeamService
397
419
  from ._async.delivery_methods import AsyncDeliveryMethodService
398
420
  from ._async.detector_config import AsyncDetectorConfigService
421
+ from ._async.pii_config import AsyncPiiConfigService
422
+ from ._async.authority import AsyncAuthorityService
399
423
  from ._async.passport_session import AsyncPassportSession
400
424
 
401
425
  self.agents = AsyncAgentService(self._client)
@@ -422,6 +446,8 @@ class AsyncStack:
422
446
  self.team = AsyncTeamService(self._client)
423
447
  self.delivery_methods = AsyncDeliveryMethodService(self._client)
424
448
  self.detector_config = AsyncDetectorConfigService(self._client)
449
+ self.pii_config = AsyncPiiConfigService(self._client)
450
+ self.authority = AsyncAuthorityService(self._client)
425
451
  # Stash for issue_passport() — Python doesn't easily expose the
426
452
  # class binding without re-importing inside the method body.
427
453
  self._AsyncPassportSession = AsyncPassportSession
@@ -434,6 +460,7 @@ class AsyncStack:
434
460
  intents: list[dict[str, Any]] | None = None,
435
461
  ttl_seconds: int | None = None,
436
462
  accountability_mode: str | None = None,
463
+ authority_binding_ids: list[str] | None = None,
437
464
  **kwargs: Any,
438
465
  ):
439
466
  """Phase 5.2.6 — async passport issue + session wrapper."""
@@ -442,6 +469,7 @@ class AsyncStack:
442
469
  if intents is not None: issue_input["intents"] = intents
443
470
  if ttl_seconds is not None: issue_input["ttl_seconds"] = ttl_seconds
444
471
  if accountability_mode is not None: issue_input["accountability_mode"] = accountability_mode
472
+ if authority_binding_ids is not None: issue_input["authority_binding_ids"] = authority_binding_ids
445
473
  issued = await self._client.post("/v1/passports/issue", json=issue_input)
446
474
  return self._AsyncPassportSession(self._client, issued, issue_input)
447
475
 
@@ -0,0 +1,269 @@
1
+ """Async authority principal, proof, binding, receiver, and evidence APIs."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from typing import Any
6
+
7
+ from ..authority import _idempotency_headers, _version_params
8
+ from ..client import AsyncHttpClient
9
+
10
+
11
+ class AsyncAuthorityService:
12
+ """Async mirror of :class:`getstack.authority.AuthorityService`."""
13
+
14
+ def __init__(self, client: AsyncHttpClient):
15
+ self._client = client
16
+
17
+ async def create_enterprise_oidc_request(
18
+ self, *, idempotency_key: str, **input: Any
19
+ ) -> dict[str, Any]:
20
+ """Create a pending request and return the human approval URL."""
21
+ return await self._client.post(
22
+ "/v1/authority/enterprise-oidc/requests", json=input,
23
+ extra_headers=_idempotency_headers(idempotency_key),
24
+ )
25
+
26
+ async def get_enterprise_oidc_request(self, request_id: str) -> dict[str, Any]:
27
+ return await self._client.get(
28
+ f"/v1/authority/enterprise-oidc/requests/{request_id}"
29
+ )
30
+
31
+ async def create_principal(self, *, idempotency_key: str, **input: Any) -> dict[str, Any]:
32
+ data = await self._client.post(
33
+ "/v1/authority/principals", json=input,
34
+ extra_headers=_idempotency_headers(idempotency_key),
35
+ )
36
+ return data["principal"]
37
+
38
+ async def list_principals(self) -> list[dict[str, Any]]:
39
+ return (await self._client.get("/v1/authority/principals"))["principals"]
40
+
41
+ async def get_principal(self, principal_id: str) -> dict[str, Any]:
42
+ return (await self._client.get(f"/v1/authority/principals/{principal_id}"))["principal"]
43
+
44
+ async def archive_principal(
45
+ self, principal_id: str, *, reason_code: str, idempotency_key: str
46
+ ) -> dict[str, Any]:
47
+ data = await self._client.post(
48
+ f"/v1/authority/principals/{principal_id}/archive",
49
+ json={"reason_code": reason_code},
50
+ extra_headers=_idempotency_headers(idempotency_key),
51
+ )
52
+ return data["principal"]
53
+
54
+ async def erase_principal(
55
+ self, principal_id: str, *, idempotency_key: str
56
+ ) -> dict[str, Any]:
57
+ data = await self._client.post(
58
+ f"/v1/authority/principals/{principal_id}/erase", json={},
59
+ extra_headers=_idempotency_headers(idempotency_key),
60
+ )
61
+ return data["tombstone"]
62
+
63
+ async def import_proof(self, *, idempotency_key: str, **input: Any) -> dict[str, Any]:
64
+ data = await self._client.post(
65
+ "/v1/authority/proofs/import", json=input,
66
+ extra_headers=_idempotency_headers(idempotency_key),
67
+ )
68
+ return data["proof"]
69
+
70
+ async def create_proof_collection_session(
71
+ self, *, idempotency_key: str, **input: Any
72
+ ) -> dict[str, Any]:
73
+ data = await self._client.post(
74
+ "/v1/authority/proof-collection-sessions", json=input,
75
+ extra_headers=_idempotency_headers(idempotency_key),
76
+ )
77
+ return data["session"]
78
+
79
+ async def get_proof_collection_session(self, session_id: str) -> dict[str, Any]:
80
+ data = await self._client.get(f"/v1/authority/proof-collection-sessions/{session_id}")
81
+ return data["session"]
82
+
83
+ async def list_proofs(self) -> list[dict[str, Any]]:
84
+ return (await self._client.get("/v1/authority/proofs"))["proofs"]
85
+
86
+ async def get_proof(
87
+ self, proof_id: str, *, version: int | None = None
88
+ ) -> dict[str, Any]:
89
+ data = await self._client.get(
90
+ f"/v1/authority/proofs/{proof_id}", params=_version_params(version)
91
+ )
92
+ return data["proof"]
93
+
94
+ async def revoke_proof(
95
+ self, proof_id: str, *, reason_code: str, idempotency_key: str
96
+ ) -> dict[str, Any]:
97
+ data = await self._client.post(
98
+ f"/v1/authority/proofs/{proof_id}/revoke",
99
+ json={"reason_code": reason_code},
100
+ extra_headers=_idempotency_headers(idempotency_key),
101
+ )
102
+ return data["proof"]
103
+
104
+ async def erase_proof(self, proof_id: str, *, idempotency_key: str) -> dict[str, Any]:
105
+ data = await self._client.post(
106
+ f"/v1/authority/proofs/{proof_id}/erase", json={},
107
+ extra_headers=_idempotency_headers(idempotency_key),
108
+ )
109
+ return data["proof"]
110
+
111
+ async def create_binding(self, *, idempotency_key: str, **input: Any) -> dict[str, Any]:
112
+ data = await self._client.post(
113
+ "/v1/authority/bindings", json=input,
114
+ extra_headers=_idempotency_headers(idempotency_key),
115
+ )
116
+ return data["binding"]
117
+
118
+ async def list_bindings(self) -> list[dict[str, Any]]:
119
+ return (await self._client.get("/v1/authority/bindings"))["bindings"]
120
+
121
+ async def get_binding(
122
+ self, binding_id: str, *, version: int | None = None
123
+ ) -> dict[str, Any]:
124
+ data = await self._client.get(
125
+ f"/v1/authority/bindings/{binding_id}", params=_version_params(version)
126
+ )
127
+ return data["binding"]
128
+
129
+ async def create_approval_requests(
130
+ self, binding_id: str, *, idempotency_key: str, **input: Any
131
+ ) -> list[dict[str, Any]]:
132
+ data = await self._client.post(
133
+ f"/v1/authority/bindings/{binding_id}/approval-requests", json=input,
134
+ extra_headers=_idempotency_headers(idempotency_key),
135
+ )
136
+ return data["approval_requests"]
137
+
138
+ async def list_approval_requests(self, binding_id: str) -> list[dict[str, Any]]:
139
+ data = await self._client.get(f"/v1/authority/bindings/{binding_id}/approval-requests")
140
+ return data["approval_requests"]
141
+
142
+ async def activate_binding(
143
+ self, binding_id: str, *, idempotency_key: str, version: int | None = None
144
+ ) -> dict[str, Any]:
145
+ data = await self._client.request(
146
+ "POST", f"/v1/authority/bindings/{binding_id}/activate", json={},
147
+ params=_version_params(version),
148
+ extra_headers=_idempotency_headers(idempotency_key),
149
+ )
150
+ return data["binding"]
151
+
152
+ async def delegate_binding(
153
+ self, binding_id: str, *, idempotency_key: str, **input: Any
154
+ ) -> dict[str, Any]:
155
+ data = await self._client.post(
156
+ f"/v1/authority/bindings/{binding_id}/delegate", json=input,
157
+ extra_headers=_idempotency_headers(idempotency_key),
158
+ )
159
+ return data["binding"]
160
+
161
+ async def revoke_binding(
162
+ self, binding_id: str, *, reason_code: str, idempotency_key: str
163
+ ) -> dict[str, Any]:
164
+ data = await self._client.post(
165
+ f"/v1/authority/bindings/{binding_id}/revoke",
166
+ json={"reason_code": reason_code},
167
+ extra_headers=_idempotency_headers(idempotency_key),
168
+ )
169
+ return data["binding"]
170
+
171
+ async def erase_binding(
172
+ self, binding_id: str, *, idempotency_key: str
173
+ ) -> dict[str, Any]:
174
+ data = await self._client.post(
175
+ f"/v1/authority/bindings/{binding_id}/erase", json={},
176
+ extra_headers=_idempotency_headers(idempotency_key),
177
+ )
178
+ return data["tombstone"]
179
+
180
+ async def create_receiver(self, *, idempotency_key: str, **input: Any) -> dict[str, Any]:
181
+ data = await self._client.post(
182
+ "/v1/authority/receivers", json=input,
183
+ extra_headers=_idempotency_headers(idempotency_key),
184
+ )
185
+ return data["receiver"]
186
+
187
+ async def list_receivers(self) -> list[dict[str, Any]]:
188
+ return (await self._client.get("/v1/authority/receivers"))["receivers"]
189
+
190
+ async def get_receiver(self, receiver_id: str) -> dict[str, Any]:
191
+ return (await self._client.get(f"/v1/authority/receivers/{receiver_id}"))["receiver"]
192
+
193
+ async def create_receiver_verification_challenge(
194
+ self, receiver_id: str, *, idempotency_key: str
195
+ ) -> dict[str, Any]:
196
+ data = await self._client.post(
197
+ f"/v1/authority/receivers/{receiver_id}/verification-challenges", json={},
198
+ extra_headers=_idempotency_headers(idempotency_key),
199
+ )
200
+ return data["challenge"]
201
+
202
+ async def verify_receiver(
203
+ self, receiver_id: str, *, idempotency_key: str
204
+ ) -> dict[str, Any]:
205
+ data = await self._client.post(
206
+ f"/v1/authority/receivers/{receiver_id}/verify", json={},
207
+ extra_headers=_idempotency_headers(idempotency_key),
208
+ )
209
+ return data["receiver"]
210
+
211
+ async def add_receiver_key(
212
+ self, receiver_id: str, *, idempotency_key: str, **input: Any
213
+ ) -> dict[str, Any]:
214
+ data = await self._client.post(
215
+ f"/v1/authority/receivers/{receiver_id}/keys", json=input,
216
+ extra_headers=_idempotency_headers(idempotency_key),
217
+ )
218
+ return data["key"]
219
+
220
+ async def revoke_receiver_key(
221
+ self, receiver_id: str, key_id: str, *, reason_code: str, idempotency_key: str
222
+ ) -> dict[str, Any]:
223
+ data = await self._client.post(
224
+ f"/v1/authority/receivers/{receiver_id}/keys/{key_id}/revoke",
225
+ json={"reason_code": reason_code},
226
+ extra_headers=_idempotency_headers(idempotency_key),
227
+ )
228
+ return data["key"]
229
+
230
+ async def create_profile_version(
231
+ self, *, idempotency_key: str, **input: Any
232
+ ) -> dict[str, Any]:
233
+ data = await self._client.post(
234
+ "/v1/authority/profile-versions", json=input,
235
+ extra_headers=_idempotency_headers(idempotency_key),
236
+ )
237
+ return data["profile"]
238
+
239
+ async def activate_profile_version(
240
+ self, profile_id: str, *, profile_kind: str, idempotency_key: str
241
+ ) -> dict[str, Any]:
242
+ data = await self._client.post(
243
+ f"/v1/authority/profile-versions/{profile_id}/activate",
244
+ json={"profile_kind": profile_kind},
245
+ extra_headers=_idempotency_headers(idempotency_key),
246
+ )
247
+ return data["profile"]
248
+
249
+ async def list_uses(self) -> list[dict[str, Any]]:
250
+ return (await self._client.get("/v1/authority/uses"))["uses"]
251
+
252
+ async def get_use(self, use_id: str) -> dict[str, Any]:
253
+ return (await self._client.get(f"/v1/authority/uses/{use_id}"))["use"]
254
+
255
+ async def list_presentations(self) -> list[dict[str, Any]]:
256
+ return (await self._client.get("/v1/authority/presentations"))["presentations"]
257
+
258
+ async def get_presentation(self, presentation_id: str) -> dict[str, Any]:
259
+ data = await self._client.get(f"/v1/authority/presentations/{presentation_id}")
260
+ return data["presentation"]
261
+
262
+ async def erase_presentation(
263
+ self, presentation_id: str, *, idempotency_key: str
264
+ ) -> dict[str, Any]:
265
+ data = await self._client.post(
266
+ f"/v1/authority/presentations/{presentation_id}/erase", json={},
267
+ extra_headers=_idempotency_headers(idempotency_key),
268
+ )
269
+ return data["tombstone"]
@@ -0,0 +1,85 @@
1
+ """Notification delivery methods (async parity with the JS SDK's
2
+ DeliveryMethodService).
3
+ """
4
+
5
+ from __future__ import annotations
6
+
7
+ from typing import Any
8
+
9
+ from ..client import AsyncHttpClient
10
+
11
+
12
+ class AsyncDeliveryMethodService:
13
+ def __init__(self, client: AsyncHttpClient):
14
+ self._client = client
15
+
16
+ async def add(
17
+ self,
18
+ kind: str,
19
+ destination: str,
20
+ governance_approval_id: str | None = None,
21
+ **fields: Any,
22
+ ) -> dict[str, Any]:
23
+ body: dict[str, Any] = {
24
+ "channel_type": kind,
25
+ "destination": destination,
26
+ **fields,
27
+ }
28
+ headers = self._approval_headers(governance_approval_id)
29
+ return await self._client.post(
30
+ "/v1/notifications/destinations", json=body, extra_headers=headers
31
+ )
32
+
33
+ async def list(self) -> list[dict[str, Any]]:
34
+ return await self._client.get("/v1/notifications/destinations")
35
+
36
+ async def delete(
37
+ self, destination_id: str, governance_approval_id: str | None = None
38
+ ) -> dict[str, Any]:
39
+ return await self._client.delete(
40
+ f"/v1/notifications/destinations/{destination_id}",
41
+ extra_headers=self._approval_headers(governance_approval_id),
42
+ )
43
+
44
+ async def rotate_secret(
45
+ self, destination_id: str, governance_approval_id: str | None = None
46
+ ) -> dict[str, Any]:
47
+ """Rotate a webhook secret. The replacement is returned once in this response."""
48
+ return await self._client.post(
49
+ f"/v1/notifications/destinations/{destination_id}/rotate-secret",
50
+ extra_headers=self._approval_headers(governance_approval_id),
51
+ )
52
+
53
+ async def send_verification_code(
54
+ self, destination_id: str, governance_approval_id: str | None = None
55
+ ) -> dict[str, Any]:
56
+ return await self._client.post(
57
+ f"/v1/notifications/destinations/{destination_id}/send-code",
58
+ extra_headers=self._approval_headers(governance_approval_id),
59
+ )
60
+
61
+ async def verify(
62
+ self,
63
+ destination_id: str,
64
+ code: str,
65
+ governance_approval_id: str | None = None,
66
+ ) -> dict[str, Any]:
67
+ return await self._client.post(
68
+ f"/v1/notifications/destinations/{destination_id}/verify",
69
+ json={"code": code},
70
+ extra_headers=self._approval_headers(governance_approval_id),
71
+ )
72
+
73
+ async def test(
74
+ self, destination_id: str, governance_approval_id: str | None = None
75
+ ) -> dict[str, Any]:
76
+ return await self._client.post(
77
+ f"/v1/notifications/destinations/{destination_id}/test",
78
+ extra_headers=self._approval_headers(governance_approval_id),
79
+ )
80
+
81
+ @staticmethod
82
+ def _approval_headers(governance_approval_id: str | None) -> dict[str, str] | None:
83
+ if not governance_approval_id:
84
+ return None
85
+ return {"X-Governance-Approval": governance_approval_id}
@@ -30,10 +30,22 @@ class AsyncDropoffService:
30
30
  body["on_expire"] = on_expire
31
31
  return await self._client.post("/v1/dropoffs", json=body)
32
32
 
33
- async def deposit(self, dropoff_id: str, data: dict[str, Any], agent_id: str) -> dict[str, Any]:
33
+ async def deposit(
34
+ self,
35
+ dropoff_id: str,
36
+ data: dict[str, Any],
37
+ agent_id: str,
38
+ passport_jti: str | None = None,
39
+ mission_id: str | None = None,
40
+ ) -> dict[str, Any]:
41
+ body: dict[str, Any] = {"agent_id": agent_id, "payload": data}
42
+ if passport_jti is not None:
43
+ body["passport_jti"] = passport_jti
44
+ if mission_id is not None:
45
+ body["mission_id"] = mission_id
34
46
  return await self._client.post(
35
47
  f"/v1/dropoffs/{dropoff_id}/deposit",
36
- json={"agent_id": agent_id, "payload": data},
48
+ json=body,
37
49
  )
38
50
 
39
51
  async def collect(self, dropoff_id: str, agent_id: str) -> dict[str, Any]:
@@ -1,9 +1,10 @@
1
- """Evidence packs — export, get, list (async mirror of EvidencePacksService)."""
1
+ """Evidence packs — export, get, download, list."""
2
2
 
3
3
  from __future__ import annotations
4
4
 
5
5
  import json
6
6
  from typing import Any
7
+ from urllib.parse import quote
7
8
 
8
9
  from ..client import AsyncHttpClient
9
10
 
@@ -33,7 +34,15 @@ class AsyncEvidencePacksService:
33
34
  return result
34
35
 
35
36
  async def get(self, pack_id: str) -> dict[str, Any]:
36
- return await self._client.request("GET", f"/v1/evidence-packs/{pack_id}")
37
+ return await self._client.request(
38
+ "GET", f"/v1/evidence-packs/{quote(pack_id, safe='')}"
39
+ )
40
+
41
+ async def download(self, pack_id: str) -> bytes:
42
+ """Fetch the exact stored final manifest bytes."""
43
+ return await self._client.request_bytes(
44
+ "GET", f"/v1/evidence-packs/{quote(pack_id, safe='')}/download"
45
+ )
37
46
 
38
47
  async def list(self, *, scope: str | None = None) -> dict[str, Any]:
39
48
  path = "/v1/evidence-packs"
@@ -3,9 +3,18 @@
3
3
  from __future__ import annotations
4
4
 
5
5
  from typing import Any
6
+ from urllib.parse import quote
6
7
 
7
8
  from ..client import AsyncHttpClient
8
- from ..types import IdentityProvider, IdentityClaim, IdentitySettings, VerificationSession
9
+ from ..types import (
10
+ IdentityProvider,
11
+ IdentityClaim,
12
+ IdentitySettings,
13
+ VerificationSession,
14
+ SetServiceRequirementInput,
15
+ ServiceRequirement,
16
+ ReplaceServiceRequirementResult,
17
+ )
9
18
  from ..identity import _to_provider, _to_claim
10
19
 
11
20
 
@@ -85,11 +94,27 @@ class AsyncIdentityService:
85
94
  async def revoke_claim(self, claim_id: str) -> dict[str, Any]:
86
95
  return await self._client.delete(f"/v1/identity/claims/{claim_id}")
87
96
 
88
- async def get_service_requirements(self, service_id: str) -> dict[str, Any]:
89
- return await self._client.get(f"/v1/services/{service_id}/requirements")
97
+ async def get_service_requirements(self, service_id: str) -> list[ServiceRequirement]:
98
+ return await self._client.get(f"/v1/services/{quote(service_id, safe='')}/requirements")
90
99
 
91
- async def set_service_requirement(self, service_id: str, **kwargs: Any) -> dict[str, Any]:
92
- return await self._client.post(f"/v1/services/{service_id}/requirements", json=kwargs)
100
+ async def set_service_requirement(self, service_id: str, **kwargs: Any) -> ServiceRequirement:
101
+ return await self._client.post(
102
+ f"/v1/services/{quote(service_id, safe='')}/requirements",
103
+ json=kwargs,
104
+ )
105
+
106
+ async def replace_service_requirement(
107
+ self,
108
+ service_id: str,
109
+ requirement: SetServiceRequirementInput | None,
110
+ ) -> ReplaceServiceRequirementResult:
111
+ return await self._client.put(
112
+ f"/v1/services/{quote(service_id, safe='')}/requirements",
113
+ json={"requirement": requirement},
114
+ )
93
115
 
94
116
  async def delete_service_requirement(self, service_id: str, requirement_id: str) -> None:
95
- await self._client.delete(f"/v1/services/{service_id}/requirements/{requirement_id}")
117
+ await self._client.delete(
118
+ f"/v1/services/{quote(service_id, safe='')}/requirements/"
119
+ f"{quote(requirement_id, safe='')}"
120
+ )