getstack 0.6.0__tar.gz → 0.7.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 (45) hide show
  1. {getstack-0.6.0 → getstack-0.7.0}/.gitignore +5 -0
  2. {getstack-0.6.0 → getstack-0.7.0}/PKG-INFO +1 -1
  3. {getstack-0.6.0 → getstack-0.7.0}/pyproject.toml +1 -1
  4. {getstack-0.6.0 → getstack-0.7.0}/src/getstack/__init__.py +4 -0
  5. getstack-0.7.0/src/getstack/_async/missions.py +110 -0
  6. getstack-0.7.0/src/getstack/missions.py +130 -0
  7. {getstack-0.6.0 → getstack-0.7.0}/CLAUDE.md +0 -0
  8. {getstack-0.6.0 → getstack-0.7.0}/LICENSE +0 -0
  9. {getstack-0.6.0 → getstack-0.7.0}/README.md +0 -0
  10. {getstack-0.6.0 → getstack-0.7.0}/src/getstack/_async/__init__.py +0 -0
  11. {getstack-0.6.0 → getstack-0.7.0}/src/getstack/_async/agents.py +0 -0
  12. {getstack-0.6.0 → getstack-0.7.0}/src/getstack/_async/audit.py +0 -0
  13. {getstack-0.6.0 → getstack-0.7.0}/src/getstack/_async/credentials.py +0 -0
  14. {getstack-0.6.0 → getstack-0.7.0}/src/getstack/_async/dropoffs.py +0 -0
  15. {getstack-0.6.0 → getstack-0.7.0}/src/getstack/_async/identity.py +0 -0
  16. {getstack-0.6.0 → getstack-0.7.0}/src/getstack/_async/intents.py +0 -0
  17. {getstack-0.6.0 → getstack-0.7.0}/src/getstack/_async/notifications.py +0 -0
  18. {getstack-0.6.0 → getstack-0.7.0}/src/getstack/_async/passports.py +0 -0
  19. {getstack-0.6.0 → getstack-0.7.0}/src/getstack/_async/proxy.py +0 -0
  20. {getstack-0.6.0 → getstack-0.7.0}/src/getstack/_async/reviews.py +0 -0
  21. {getstack-0.6.0 → getstack-0.7.0}/src/getstack/_async/scan.py +0 -0
  22. {getstack-0.6.0 → getstack-0.7.0}/src/getstack/_async/security_events.py +0 -0
  23. {getstack-0.6.0 → getstack-0.7.0}/src/getstack/_async/services.py +0 -0
  24. {getstack-0.6.0 → getstack-0.7.0}/src/getstack/_async/skills.py +0 -0
  25. {getstack-0.6.0 → getstack-0.7.0}/src/getstack/agent_auth.py +0 -0
  26. {getstack-0.6.0 → getstack-0.7.0}/src/getstack/agents.py +0 -0
  27. {getstack-0.6.0 → getstack-0.7.0}/src/getstack/audit.py +0 -0
  28. {getstack-0.6.0 → getstack-0.7.0}/src/getstack/auth.py +0 -0
  29. {getstack-0.6.0 → getstack-0.7.0}/src/getstack/browser_bootstrap.py +0 -0
  30. {getstack-0.6.0 → getstack-0.7.0}/src/getstack/client.py +0 -0
  31. {getstack-0.6.0 → getstack-0.7.0}/src/getstack/credentials.py +0 -0
  32. {getstack-0.6.0 → getstack-0.7.0}/src/getstack/dropoffs.py +0 -0
  33. {getstack-0.6.0 → getstack-0.7.0}/src/getstack/errors.py +0 -0
  34. {getstack-0.6.0 → getstack-0.7.0}/src/getstack/identity.py +0 -0
  35. {getstack-0.6.0 → getstack-0.7.0}/src/getstack/intents.py +0 -0
  36. {getstack-0.6.0 → getstack-0.7.0}/src/getstack/notifications.py +0 -0
  37. {getstack-0.6.0 → getstack-0.7.0}/src/getstack/passports.py +0 -0
  38. {getstack-0.6.0 → getstack-0.7.0}/src/getstack/proxy.py +0 -0
  39. {getstack-0.6.0 → getstack-0.7.0}/src/getstack/py.typed +0 -0
  40. {getstack-0.6.0 → getstack-0.7.0}/src/getstack/reviews.py +0 -0
  41. {getstack-0.6.0 → getstack-0.7.0}/src/getstack/scan.py +0 -0
  42. {getstack-0.6.0 → getstack-0.7.0}/src/getstack/security_events.py +0 -0
  43. {getstack-0.6.0 → getstack-0.7.0}/src/getstack/services.py +0 -0
  44. {getstack-0.6.0 → getstack-0.7.0}/src/getstack/skills.py +0 -0
  45. {getstack-0.6.0 → getstack-0.7.0}/src/getstack/types.py +0 -0
@@ -60,6 +60,11 @@ scripts/.openapi-cache/
60
60
  # Local dev screenshots
61
61
  *-stripe.png
62
62
 
63
+ # Root-level scratch images (founder workspace — never commit)
64
+ /*.png
65
+ /*.jpg
66
+ /*.jpeg
67
+
63
68
  # YC application session exports (founder voice + redacted creds — never commit)
64
69
  docs/yc-q18/
65
70
  docs/yc-application-session-preface.md
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: getstack
3
- Version: 0.6.0
3
+ Version: 0.7.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.6.0"
7
+ version = "0.7.0"
8
8
  description = "Python SDK for STACK — trust infrastructure for AI agents"
9
9
  readme = "README.md"
10
10
  license = "MIT"
@@ -52,6 +52,7 @@ from .security_events import SecurityEventService as SecurityEventSvc
52
52
  from .skills import SkillService
53
53
  from .identity import IdentityService
54
54
  from .intents import IntentsService
55
+ from .missions import MissionsService
55
56
  from .types import (
56
57
  Agent,
57
58
  Passport,
@@ -216,6 +217,7 @@ class Stack:
216
217
  self.skills = SkillService(self._client)
217
218
  self.identity = IdentityService(self._client)
218
219
  self.intents = IntentsService(self._client)
220
+ self.missions = MissionsService(self._client)
219
221
 
220
222
  @classmethod
221
223
  def from_session(cls, session_token: str, **kwargs) -> Stack:
@@ -326,6 +328,7 @@ class AsyncStack:
326
328
  from ._async.skills import AsyncSkillService
327
329
  from ._async.identity import AsyncIdentityService
328
330
  from ._async.intents import AsyncIntentsService
331
+ from ._async.missions import AsyncMissionsService
329
332
 
330
333
  self.agents = AsyncAgentService(self._client)
331
334
  self.passports = AsyncPassportService(self._client)
@@ -341,6 +344,7 @@ class AsyncStack:
341
344
  self.skills = AsyncSkillService(self._client)
342
345
  self.identity = AsyncIdentityService(self._client)
343
346
  self.intents = AsyncIntentsService(self._client)
347
+ self.missions = AsyncMissionsService(self._client)
344
348
 
345
349
  @classmethod
346
350
  def from_session(cls, session_token: str, **kwargs) -> AsyncStack:
@@ -0,0 +1,110 @@
1
+ """Async mirror of MissionsService. Same surface, async/await."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from typing import Any
6
+
7
+ from ..client import AsyncHttpClient
8
+
9
+
10
+ class AsyncMissionsService:
11
+ def __init__(self, client: AsyncHttpClient):
12
+ self._client = client
13
+
14
+ async def open(
15
+ self,
16
+ intent_summary: str,
17
+ *,
18
+ intent_services: list[str] | None = None,
19
+ expected_counterparties: list[str] | None = None,
20
+ baseline_profile: dict[str, Any] | None = None,
21
+ expected_output_shape: dict[str, Any] | None = None,
22
+ max_actions: int | None = None,
23
+ max_dropoff_bytes: int | None = None,
24
+ coverage_cap_cents: int | None = None,
25
+ max_duration_seconds: int | None = None,
26
+ max_concurrent_intents: int | None = None,
27
+ max_actions_per_service: dict[str, int] | None = None,
28
+ cap_behavior: dict[str, str] | None = None,
29
+ expires_in_seconds: int | None = None,
30
+ parent_passport_services: list[str] | None = None,
31
+ ) -> dict[str, Any]:
32
+ body: dict[str, Any] = {"intent_summary": intent_summary}
33
+ if intent_services is not None:
34
+ body["intent_services"] = intent_services
35
+ if expected_counterparties is not None:
36
+ body["expected_counterparties"] = expected_counterparties
37
+ if baseline_profile is not None:
38
+ body["baseline_profile"] = baseline_profile
39
+ if expected_output_shape is not None:
40
+ body["expected_output_shape"] = expected_output_shape
41
+ if max_actions is not None:
42
+ body["max_actions"] = max_actions
43
+ if max_dropoff_bytes is not None:
44
+ body["max_dropoff_bytes"] = max_dropoff_bytes
45
+ if coverage_cap_cents is not None:
46
+ body["coverage_cap_cents"] = coverage_cap_cents
47
+ if max_duration_seconds is not None:
48
+ body["max_duration_seconds"] = max_duration_seconds
49
+ if max_concurrent_intents is not None:
50
+ body["max_concurrent_intents"] = max_concurrent_intents
51
+ if max_actions_per_service is not None:
52
+ body["max_actions_per_service"] = max_actions_per_service
53
+ if cap_behavior is not None:
54
+ body["cap_behavior"] = cap_behavior
55
+ if expires_in_seconds is not None:
56
+ body["expires_in_seconds"] = expires_in_seconds
57
+ if parent_passport_services is not None:
58
+ body["parent_passport_services"] = parent_passport_services
59
+ return await self._client.request("POST", "/v1/missions", json=body)
60
+
61
+ async def complete(
62
+ self,
63
+ mission_id: str,
64
+ *,
65
+ output: dict[str, Any] | None = None,
66
+ ) -> dict[str, Any]:
67
+ body: dict[str, Any] = {}
68
+ if output is not None:
69
+ body["output"] = output
70
+ return await self._client.request(
71
+ "POST", f"/v1/missions/{mission_id}/complete", json=body
72
+ )
73
+
74
+ async def revoke(
75
+ self,
76
+ mission_id: str,
77
+ *,
78
+ reason: str,
79
+ notes: str | None = None,
80
+ ) -> dict[str, Any]:
81
+ body: dict[str, Any] = {"reason": reason}
82
+ if notes is not None:
83
+ body["notes"] = notes
84
+ return await self._client.request(
85
+ "POST", f"/v1/missions/{mission_id}/revoke", json=body
86
+ )
87
+
88
+ async def get(self, mission_id: str) -> dict[str, Any]:
89
+ return await self._client.request("GET", f"/v1/missions/{mission_id}")
90
+
91
+ async def list(
92
+ self,
93
+ *,
94
+ status: str | None = None,
95
+ page: int | None = None,
96
+ limit: int | None = None,
97
+ ) -> dict[str, Any]:
98
+ params: dict[str, Any] = {}
99
+ if status is not None:
100
+ params["status"] = status
101
+ if page is not None:
102
+ params["page"] = page
103
+ if limit is not None:
104
+ params["limit"] = limit
105
+ path = "/v1/missions"
106
+ if params:
107
+ from urllib.parse import urlencode
108
+
109
+ path = f"{path}?{urlencode(params)}"
110
+ return await self._client.request("GET", path)
@@ -0,0 +1,130 @@
1
+ """Mission lifecycle — open / complete / revoke / get / list.
2
+
3
+ Phase 5.0 sub-chunk 5.0.8.
4
+
5
+ open() Open a new mission. Returns mission_id (ms_*) + the
6
+ signed mission_open claim_id.
7
+ complete() Mark a mission complete (terminal_state='completed_successful').
8
+ Idempotent; second call returns the prior terminal state.
9
+ revoke() Revoke a mission. reason='adversarial' triggers forward
10
+ cascade (passports bound to the mission also revoke);
11
+ reason='operator_initiated_clean' leaves passports alive.
12
+ get() Fetch full mission view by id.
13
+ list() List missions for the calling operator.
14
+ """
15
+
16
+ from __future__ import annotations
17
+
18
+ from typing import Any
19
+
20
+ from .client import HttpClient
21
+
22
+
23
+ class MissionsService:
24
+ def __init__(self, client: HttpClient):
25
+ self._client = client
26
+
27
+ def open(
28
+ self,
29
+ intent_summary: str,
30
+ *,
31
+ intent_services: list[str] | None = None,
32
+ expected_counterparties: list[str] | None = None,
33
+ baseline_profile: dict[str, Any] | None = None,
34
+ expected_output_shape: dict[str, Any] | None = None,
35
+ max_actions: int | None = None,
36
+ max_dropoff_bytes: int | None = None,
37
+ coverage_cap_cents: int | None = None,
38
+ max_duration_seconds: int | None = None,
39
+ max_concurrent_intents: int | None = None,
40
+ max_actions_per_service: dict[str, int] | None = None,
41
+ cap_behavior: dict[str, str] | None = None,
42
+ expires_in_seconds: int | None = None,
43
+ parent_passport_services: list[str] | None = None,
44
+ ) -> dict[str, Any]:
45
+ """Open a new mission. Returns dict with mission_id, claim_id,
46
+ expires_at."""
47
+ body: dict[str, Any] = {"intent_summary": intent_summary}
48
+ if intent_services is not None:
49
+ body["intent_services"] = intent_services
50
+ if expected_counterparties is not None:
51
+ body["expected_counterparties"] = expected_counterparties
52
+ if baseline_profile is not None:
53
+ body["baseline_profile"] = baseline_profile
54
+ if expected_output_shape is not None:
55
+ body["expected_output_shape"] = expected_output_shape
56
+ if max_actions is not None:
57
+ body["max_actions"] = max_actions
58
+ if max_dropoff_bytes is not None:
59
+ body["max_dropoff_bytes"] = max_dropoff_bytes
60
+ if coverage_cap_cents is not None:
61
+ body["coverage_cap_cents"] = coverage_cap_cents
62
+ if max_duration_seconds is not None:
63
+ body["max_duration_seconds"] = max_duration_seconds
64
+ if max_concurrent_intents is not None:
65
+ body["max_concurrent_intents"] = max_concurrent_intents
66
+ if max_actions_per_service is not None:
67
+ body["max_actions_per_service"] = max_actions_per_service
68
+ if cap_behavior is not None:
69
+ body["cap_behavior"] = cap_behavior
70
+ if expires_in_seconds is not None:
71
+ body["expires_in_seconds"] = expires_in_seconds
72
+ if parent_passport_services is not None:
73
+ body["parent_passport_services"] = parent_passport_services
74
+ return self._client.request("POST", "/v1/missions", json=body)
75
+
76
+ def complete(
77
+ self,
78
+ mission_id: str,
79
+ *,
80
+ output: dict[str, Any] | None = None,
81
+ ) -> dict[str, Any]:
82
+ """Mark a mission complete. Returns dict with mission_id, claim_id,
83
+ terminal_state, completed_at, transitioned."""
84
+ body: dict[str, Any] = {}
85
+ if output is not None:
86
+ body["output"] = output
87
+ return self._client.request(
88
+ "POST", f"/v1/missions/{mission_id}/complete", json=body
89
+ )
90
+
91
+ def revoke(
92
+ self,
93
+ mission_id: str,
94
+ *,
95
+ reason: str,
96
+ notes: str | None = None,
97
+ ) -> dict[str, Any]:
98
+ """Revoke a mission. reason in {'adversarial', 'operator_initiated_clean'}."""
99
+ body: dict[str, Any] = {"reason": reason}
100
+ if notes is not None:
101
+ body["notes"] = notes
102
+ return self._client.request(
103
+ "POST", f"/v1/missions/{mission_id}/revoke", json=body
104
+ )
105
+
106
+ def get(self, mission_id: str) -> dict[str, Any]:
107
+ """Fetch a mission by id. 404 for unknown or cross-tenant."""
108
+ return self._client.request("GET", f"/v1/missions/{mission_id}")
109
+
110
+ def list(
111
+ self,
112
+ *,
113
+ status: str | None = None,
114
+ page: int | None = None,
115
+ limit: int | None = None,
116
+ ) -> dict[str, Any]:
117
+ """List missions for the calling operator. Returns dict with `items`."""
118
+ params: dict[str, Any] = {}
119
+ if status is not None:
120
+ params["status"] = status
121
+ if page is not None:
122
+ params["page"] = page
123
+ if limit is not None:
124
+ params["limit"] = limit
125
+ path = "/v1/missions"
126
+ if params:
127
+ from urllib.parse import urlencode
128
+
129
+ path = f"{path}?{urlencode(params)}"
130
+ return self._client.request("GET", path)
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes