getstack 0.18.0__tar.gz → 0.19.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 (72) hide show
  1. {getstack-0.18.0 → getstack-0.19.0}/PKG-INFO +1 -1
  2. {getstack-0.18.0 → getstack-0.19.0}/pyproject.toml +1 -1
  3. {getstack-0.18.0 → getstack-0.19.0}/src/getstack/_async/agents.py +3 -2
  4. getstack-0.19.0/src/getstack/_async/detector_config.py +35 -0
  5. {getstack-0.18.0 → getstack-0.19.0}/src/getstack/_async/reviews.py +5 -1
  6. {getstack-0.18.0 → getstack-0.19.0}/src/getstack/agents.py +6 -2
  7. {getstack-0.18.0 → getstack-0.19.0}/src/getstack/client.py +4 -4
  8. {getstack-0.18.0 → getstack-0.19.0}/src/getstack/detector_config.py +19 -6
  9. {getstack-0.18.0 → getstack-0.19.0}/src/getstack/reviews.py +8 -1
  10. getstack-0.18.0/src/getstack/_async/detector_config.py +0 -27
  11. {getstack-0.18.0 → getstack-0.19.0}/.gitignore +0 -0
  12. {getstack-0.18.0 → getstack-0.19.0}/CLAUDE.md +0 -0
  13. {getstack-0.18.0 → getstack-0.19.0}/LICENSE +0 -0
  14. {getstack-0.18.0 → getstack-0.19.0}/README.md +0 -0
  15. {getstack-0.18.0 → getstack-0.19.0}/scripts/verify_sign_claim_crosslang.py +0 -0
  16. {getstack-0.18.0 → getstack-0.19.0}/src/getstack/__init__.py +0 -0
  17. {getstack-0.18.0 → getstack-0.19.0}/src/getstack/_async/__init__.py +0 -0
  18. {getstack-0.18.0 → getstack-0.19.0}/src/getstack/_async/audit.py +0 -0
  19. {getstack-0.18.0 → getstack-0.19.0}/src/getstack/_async/credentials.py +0 -0
  20. {getstack-0.18.0 → getstack-0.19.0}/src/getstack/_async/delivery_methods.py +0 -0
  21. {getstack-0.18.0 → getstack-0.19.0}/src/getstack/_async/dropoffs.py +0 -0
  22. {getstack-0.18.0 → getstack-0.19.0}/src/getstack/_async/evidence_packs.py +0 -0
  23. {getstack-0.18.0 → getstack-0.19.0}/src/getstack/_async/identity.py +0 -0
  24. {getstack-0.18.0 → getstack-0.19.0}/src/getstack/_async/inbound_webhooks.py +0 -0
  25. {getstack-0.18.0 → getstack-0.19.0}/src/getstack/_async/intents.py +0 -0
  26. {getstack-0.18.0 → getstack-0.19.0}/src/getstack/_async/llm.py +0 -0
  27. {getstack-0.18.0 → getstack-0.19.0}/src/getstack/_async/missions.py +0 -0
  28. {getstack-0.18.0 → getstack-0.19.0}/src/getstack/_async/notifications.py +0 -0
  29. {getstack-0.18.0 → getstack-0.19.0}/src/getstack/_async/partner_connect.py +0 -0
  30. {getstack-0.18.0 → getstack-0.19.0}/src/getstack/_async/partner_tenants.py +0 -0
  31. {getstack-0.18.0 → getstack-0.19.0}/src/getstack/_async/passport_session.py +0 -0
  32. {getstack-0.18.0 → getstack-0.19.0}/src/getstack/_async/passports.py +0 -0
  33. {getstack-0.18.0 → getstack-0.19.0}/src/getstack/_async/proxy.py +0 -0
  34. {getstack-0.18.0 → getstack-0.19.0}/src/getstack/_async/scan.py +0 -0
  35. {getstack-0.18.0 → getstack-0.19.0}/src/getstack/_async/security_events.py +0 -0
  36. {getstack-0.18.0 → getstack-0.19.0}/src/getstack/_async/services.py +0 -0
  37. {getstack-0.18.0 → getstack-0.19.0}/src/getstack/_async/skills.py +0 -0
  38. {getstack-0.18.0 → getstack-0.19.0}/src/getstack/_async/team.py +0 -0
  39. {getstack-0.18.0 → getstack-0.19.0}/src/getstack/_async/usage.py +0 -0
  40. {getstack-0.18.0 → getstack-0.19.0}/src/getstack/agent_auth.py +0 -0
  41. {getstack-0.18.0 → getstack-0.19.0}/src/getstack/audit.py +0 -0
  42. {getstack-0.18.0 → getstack-0.19.0}/src/getstack/auth.py +0 -0
  43. {getstack-0.18.0 → getstack-0.19.0}/src/getstack/browser_bootstrap.py +0 -0
  44. {getstack-0.18.0 → getstack-0.19.0}/src/getstack/credentials.py +0 -0
  45. {getstack-0.18.0 → getstack-0.19.0}/src/getstack/delivery_methods.py +0 -0
  46. {getstack-0.18.0 → getstack-0.19.0}/src/getstack/dropoffs.py +0 -0
  47. {getstack-0.18.0 → getstack-0.19.0}/src/getstack/errors.py +0 -0
  48. {getstack-0.18.0 → getstack-0.19.0}/src/getstack/evidence_packs.py +0 -0
  49. {getstack-0.18.0 → getstack-0.19.0}/src/getstack/identity.py +0 -0
  50. {getstack-0.18.0 → getstack-0.19.0}/src/getstack/inbound_webhooks.py +0 -0
  51. {getstack-0.18.0 → getstack-0.19.0}/src/getstack/intents.py +0 -0
  52. {getstack-0.18.0 → getstack-0.19.0}/src/getstack/llm.py +0 -0
  53. {getstack-0.18.0 → getstack-0.19.0}/src/getstack/missions.py +0 -0
  54. {getstack-0.18.0 → getstack-0.19.0}/src/getstack/notifications.py +0 -0
  55. {getstack-0.18.0 → getstack-0.19.0}/src/getstack/partner_connect.py +0 -0
  56. {getstack-0.18.0 → getstack-0.19.0}/src/getstack/partner_tenants.py +0 -0
  57. {getstack-0.18.0 → getstack-0.19.0}/src/getstack/passport_header_verify.py +0 -0
  58. {getstack-0.18.0 → getstack-0.19.0}/src/getstack/passport_session.py +0 -0
  59. {getstack-0.18.0 → getstack-0.19.0}/src/getstack/passports.py +0 -0
  60. {getstack-0.18.0 → getstack-0.19.0}/src/getstack/proxy.py +0 -0
  61. {getstack-0.18.0 → getstack-0.19.0}/src/getstack/py.typed +0 -0
  62. {getstack-0.18.0 → getstack-0.19.0}/src/getstack/scan.py +0 -0
  63. {getstack-0.18.0 → getstack-0.19.0}/src/getstack/security_events.py +0 -0
  64. {getstack-0.18.0 → getstack-0.19.0}/src/getstack/services.py +0 -0
  65. {getstack-0.18.0 → getstack-0.19.0}/src/getstack/sign_claim.py +0 -0
  66. {getstack-0.18.0 → getstack-0.19.0}/src/getstack/skills.py +0 -0
  67. {getstack-0.18.0 → getstack-0.19.0}/src/getstack/team.py +0 -0
  68. {getstack-0.18.0 → getstack-0.19.0}/src/getstack/types.py +0 -0
  69. {getstack-0.18.0 → getstack-0.19.0}/src/getstack/usage.py +0 -0
  70. {getstack-0.18.0 → getstack-0.19.0}/src/getstack/verify_offline.py +0 -0
  71. {getstack-0.18.0 → getstack-0.19.0}/tests/test_parity.py +0 -0
  72. {getstack-0.18.0 → getstack-0.19.0}/tests/test_sign_claim.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: getstack
3
- Version: 0.18.0
3
+ Version: 0.19.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.18.0"
7
+ version = "0.19.0"
8
8
  description = "Python SDK for STACK — trust infrastructure for AI agents"
9
9
  readme = "README.md"
10
10
  license = "MIT"
@@ -76,5 +76,6 @@ class AsyncAgentService:
76
76
  async def delete(self, agent_id: str) -> dict[str, Any]:
77
77
  return await self._client.delete(f"/v1/agents/{agent_id}")
78
78
 
79
- async def unblock(self, agent_id: str) -> dict[str, Any]:
80
- return await self._client.post(f"/v1/agents/{agent_id}/unblock")
79
+ async def unblock(self, agent_id: str, governance_approval_id: str | None = None) -> dict[str, Any]:
80
+ _gov_headers = {"X-Governance-Approval": governance_approval_id} if governance_approval_id else None
81
+ return await self._client.post(f"/v1/agents/{agent_id}/unblock", extra_headers=_gov_headers)
@@ -0,0 +1,35 @@
1
+ """Per-operator detector customization (async parity with the JS SDK's
2
+ DetectorConfigService).
3
+ """
4
+
5
+ from __future__ import annotations
6
+
7
+ from typing import Any
8
+ from urllib.parse import quote
9
+
10
+ from ..client import AsyncHttpClient
11
+
12
+
13
+ class AsyncDetectorConfigService:
14
+ def __init__(self, client: AsyncHttpClient):
15
+ self._client = client
16
+
17
+ async def list(self) -> dict[str, Any]:
18
+ return await self._client.get("/v1/detectors/config")
19
+
20
+ async def get(self, detector_key: str) -> dict[str, Any]:
21
+ return await self._client.get(f"/v1/detectors/{quote(detector_key)}/config")
22
+
23
+ async def upsert(
24
+ self, detector_key: str, governance_approval_id: str | None = None, **config: Any
25
+ ) -> dict[str, Any]:
26
+ _gov_headers = {"X-Governance-Approval": governance_approval_id} if governance_approval_id else None
27
+ return await self._client.put(
28
+ f"/v1/detectors/{quote(detector_key)}/config", json=config, extra_headers=_gov_headers
29
+ )
30
+
31
+ async def reset(self, detector_key: str, governance_approval_id: str | None = None) -> None:
32
+ _gov_headers = {"X-Governance-Approval": governance_approval_id} if governance_approval_id else None
33
+ await self._client.delete(
34
+ f"/v1/detectors/{quote(detector_key)}/config", extra_headers=_gov_headers
35
+ )
@@ -38,13 +38,17 @@ class AsyncReviewService:
38
38
  decision: str,
39
39
  notes: str | None = None,
40
40
  block_future: bool = False,
41
+ governance_approval_id: str | None = None,
41
42
  ) -> ReviewDecisionResult:
42
43
  body: dict[str, Any] = {"decision": decision}
43
44
  if notes:
44
45
  body["notes"] = notes
45
46
  if block_future:
46
47
  body["block_future"] = True
47
- data = await self._client.post(f"/v1/passports/reviews/{checkout_id}/decide", json=body)
48
+ _gov_headers = {"X-Governance-Approval": governance_approval_id} if governance_approval_id else None
49
+ data = await self._client.post(
50
+ f"/v1/passports/reviews/{checkout_id}/decide", json=body, extra_headers=_gov_headers
51
+ )
48
52
  return ReviewDecisionResult(
49
53
  review_id=data["review_id"],
50
54
  agent_id=data["agent_id"],
@@ -120,5 +120,9 @@ class AgentService:
120
120
  def delete(self, agent_id: str) -> dict[str, Any]:
121
121
  return self._client.delete(f"/v1/agents/{agent_id}")
122
122
 
123
- def unblock(self, agent_id: str) -> dict[str, Any]:
124
- return self._client.post(f"/v1/agents/{agent_id}/unblock")
123
+ def unblock(self, agent_id: str, governance_approval_id: str | None = None) -> dict[str, Any]:
124
+ """GET-230: from API-key sessions this 403s with
125
+ GOVERNANCE_APPROVAL_REQUIRED - retry with governance_approval_id
126
+ after human approval in the dashboard."""
127
+ _gov_headers = {"X-Governance-Approval": governance_approval_id} if governance_approval_id else None
128
+ return self._client.post(f"/v1/agents/{agent_id}/unblock", extra_headers=_gov_headers)
@@ -72,11 +72,11 @@ class HttpClient:
72
72
  def patch(self, path: str, json: Any = None) -> Any:
73
73
  return self.request("PATCH", path, json=json)
74
74
 
75
- def put(self, path: str, json: Any = None) -> Any:
76
- return self.request("PUT", path, json=json)
75
+ def put(self, path: str, json: Any = None, extra_headers: dict[str, str] | None = None) -> Any:
76
+ return self.request("PUT", path, json=json, extra_headers=extra_headers)
77
77
 
78
- def delete(self, path: str) -> Any:
79
- return self.request("DELETE", path)
78
+ def delete(self, path: str, extra_headers: dict[str, str] | None = None) -> Any:
79
+ return self.request("DELETE", path, extra_headers=extra_headers)
80
80
 
81
81
  def close(self) -> None:
82
82
  self._client.close()
@@ -27,13 +27,26 @@ class DetectorConfigService:
27
27
  built-in defaults)."""
28
28
  return self._client.get(f"/v1/detectors/{quote(detector_key)}/config")
29
29
 
30
- def upsert(self, detector_key: str, **config: Any) -> dict[str, Any]:
30
+ def upsert(
31
+ self, detector_key: str, governance_approval_id: str | None = None, **config: Any
32
+ ) -> dict[str, Any]:
31
33
  """Upsert this operator's customization for the named detector.
32
34
  studio+ only. Accepts custom_patterns / whitelist_patterns /
33
- severity_overrides / enabled."""
34
- return self._client.put(f"/v1/detectors/{quote(detector_key)}/config", json=config)
35
+ severity_overrides / enabled.
35
36
 
36
- def reset(self, detector_key: str) -> None:
37
+ GET-230: loosening changes 403 with GOVERNANCE_APPROVAL_REQUIRED
38
+ from API-key sessions - retry with governance_approval_id after a
39
+ human approves in the dashboard (/governance/approvals)."""
40
+ _gov_headers = {"X-Governance-Approval": governance_approval_id} if governance_approval_id else None
41
+ return self._client.put(
42
+ f"/v1/detectors/{quote(detector_key)}/config", json=config, extra_headers=_gov_headers
43
+ )
44
+
45
+ def reset(self, detector_key: str, governance_approval_id: str | None = None) -> None:
37
46
  """Reset a detector to built-in defaults (deletes the row). studio+
38
- only."""
39
- self._client.delete(f"/v1/detectors/{quote(detector_key)}/config")
47
+ only. GET-230: always requires a governance approval from API-key
48
+ sessions."""
49
+ _gov_headers = {"X-Governance-Approval": governance_approval_id} if governance_approval_id else None
50
+ self._client.delete(
51
+ f"/v1/detectors/{quote(detector_key)}/config", extra_headers=_gov_headers
52
+ )
@@ -39,13 +39,20 @@ class ReviewService:
39
39
  decision: str,
40
40
  notes: str | None = None,
41
41
  block_future: bool = False,
42
+ governance_approval_id: str | None = None,
42
43
  ) -> ReviewDecisionResult:
44
+ """GET-230: decision='approved' from API-key sessions 403s with
45
+ GOVERNANCE_APPROVAL_REQUIRED - retry with governance_approval_id
46
+ after human approval. 'blocked' needs no approval."""
43
47
  body: dict[str, Any] = {"decision": decision}
44
48
  if notes:
45
49
  body["notes"] = notes
46
50
  if block_future:
47
51
  body["block_future"] = True
48
- data = self._client.post(f"/v1/passports/reviews/{checkout_id}/decide", json=body)
52
+ _gov_headers = {"X-Governance-Approval": governance_approval_id} if governance_approval_id else None
53
+ data = self._client.post(
54
+ f"/v1/passports/reviews/{checkout_id}/decide", json=body, extra_headers=_gov_headers
55
+ )
49
56
  return ReviewDecisionResult(
50
57
  review_id=data["review_id"],
51
58
  agent_id=data["agent_id"],
@@ -1,27 +0,0 @@
1
- """Per-operator detector customization (async parity with the JS SDK's
2
- DetectorConfigService).
3
- """
4
-
5
- from __future__ import annotations
6
-
7
- from typing import Any
8
- from urllib.parse import quote
9
-
10
- from ..client import AsyncHttpClient
11
-
12
-
13
- class AsyncDetectorConfigService:
14
- def __init__(self, client: AsyncHttpClient):
15
- self._client = client
16
-
17
- async def list(self) -> dict[str, Any]:
18
- return await self._client.get("/v1/detectors/config")
19
-
20
- async def get(self, detector_key: str) -> dict[str, Any]:
21
- return await self._client.get(f"/v1/detectors/{quote(detector_key)}/config")
22
-
23
- async def upsert(self, detector_key: str, **config: Any) -> dict[str, Any]:
24
- return await self._client.put(f"/v1/detectors/{quote(detector_key)}/config", json=config)
25
-
26
- async def reset(self, detector_key: str) -> None:
27
- await self._client.delete(f"/v1/detectors/{quote(detector_key)}/config")
File without changes
File without changes
File without changes
File without changes
File without changes