getstack 3.0.0__tar.gz → 4.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 (98) hide show
  1. {getstack-3.0.0 → getstack-4.0.0}/.gitignore +11 -0
  2. getstack-4.0.0/CLAUDE.md +50 -0
  3. {getstack-3.0.0 → getstack-4.0.0}/PKG-INFO +32 -5
  4. {getstack-3.0.0 → getstack-4.0.0}/README.md +27 -4
  5. {getstack-3.0.0 → getstack-4.0.0}/pyproject.toml +34 -1
  6. {getstack-3.0.0 → getstack-4.0.0}/src/getstack/__init__.py +186 -168
  7. {getstack-3.0.0 → getstack-4.0.0}/src/getstack/_async/__init__.py +1 -1
  8. {getstack-3.0.0 → getstack-4.0.0}/src/getstack/_async/agents.py +13 -11
  9. {getstack-3.0.0 → getstack-4.0.0}/src/getstack/_async/audit.py +4 -4
  10. {getstack-3.0.0 → getstack-4.0.0}/src/getstack/_async/authority.py +94 -109
  11. {getstack-3.0.0 → getstack-4.0.0}/src/getstack/_async/credentials.py +1 -3
  12. getstack-4.0.0/src/getstack/_async/delivery_methods.py +95 -0
  13. {getstack-3.0.0 → getstack-4.0.0}/src/getstack/_async/detector_config.py +9 -8
  14. {getstack-3.0.0 → getstack-4.0.0}/src/getstack/_async/dropoffs.py +22 -13
  15. {getstack-3.0.0 → getstack-4.0.0}/src/getstack/_async/evidence_packs.py +6 -8
  16. getstack-4.0.0/src/getstack/_async/fleets.py +155 -0
  17. {getstack-3.0.0 → getstack-4.0.0}/src/getstack/_async/identity.py +30 -19
  18. getstack-4.0.0/src/getstack/_async/inbound_webhooks.py +60 -0
  19. {getstack-3.0.0 → getstack-4.0.0}/src/getstack/_async/intents.py +34 -24
  20. {getstack-3.0.0 → getstack-4.0.0}/src/getstack/_async/llm.py +35 -19
  21. {getstack-3.0.0 → getstack-4.0.0}/src/getstack/_async/missions.py +27 -19
  22. {getstack-3.0.0 → getstack-4.0.0}/src/getstack/_async/notifications.py +14 -10
  23. {getstack-3.0.0 → getstack-4.0.0}/src/getstack/_async/partner_connect.py +10 -10
  24. {getstack-3.0.0 → getstack-4.0.0}/src/getstack/_async/partner_tenants.py +41 -27
  25. {getstack-3.0.0 → getstack-4.0.0}/src/getstack/_async/passport_session.py +1 -1
  26. {getstack-3.0.0 → getstack-4.0.0}/src/getstack/_async/passports.py +60 -56
  27. getstack-4.0.0/src/getstack/_async/pii_config.py +26 -0
  28. {getstack-3.0.0 → getstack-4.0.0}/src/getstack/_async/proxy.py +14 -10
  29. {getstack-3.0.0 → getstack-4.0.0}/src/getstack/_async/reviews.py +1 -1
  30. {getstack-3.0.0 → getstack-4.0.0}/src/getstack/_async/scan.py +10 -7
  31. {getstack-3.0.0 → getstack-4.0.0}/src/getstack/_async/security_events.py +5 -7
  32. getstack-4.0.0/src/getstack/_async/services.py +78 -0
  33. {getstack-3.0.0 → getstack-4.0.0}/src/getstack/_async/skills.py +29 -25
  34. {getstack-3.0.0 → getstack-4.0.0}/src/getstack/_async/team.py +5 -5
  35. {getstack-3.0.0 → getstack-4.0.0}/src/getstack/_async/usage.py +2 -2
  36. {getstack-3.0.0 → getstack-4.0.0}/src/getstack/_authority_request.py +1 -2
  37. {getstack-3.0.0 → getstack-4.0.0}/src/getstack/_fleet_key.py +6 -3
  38. {getstack-3.0.0 → getstack-4.0.0}/src/getstack/agent_auth.py +42 -33
  39. {getstack-3.0.0 → getstack-4.0.0}/src/getstack/agents.py +6 -6
  40. {getstack-3.0.0 → getstack-4.0.0}/src/getstack/audit.py +4 -4
  41. {getstack-3.0.0 → getstack-4.0.0}/src/getstack/auth.py +6 -10
  42. {getstack-3.0.0 → getstack-4.0.0}/src/getstack/authority.py +57 -82
  43. {getstack-3.0.0 → getstack-4.0.0}/src/getstack/browser_bootstrap.py +38 -28
  44. {getstack-3.0.0 → getstack-4.0.0}/src/getstack/client.py +18 -10
  45. {getstack-3.0.0 → getstack-4.0.0}/src/getstack/credentials.py +0 -1
  46. getstack-4.0.0/src/getstack/delivery_methods.py +92 -0
  47. {getstack-3.0.0 → getstack-4.0.0}/src/getstack/detector_config.py +15 -18
  48. {getstack-3.0.0 → getstack-4.0.0}/src/getstack/dropoffs.py +22 -11
  49. {getstack-3.0.0 → getstack-4.0.0}/src/getstack/errors.py +5 -7
  50. {getstack-3.0.0 → getstack-4.0.0}/src/getstack/evidence_packs.py +5 -8
  51. getstack-4.0.0/src/getstack/fleets.py +149 -0
  52. {getstack-3.0.0 → getstack-4.0.0}/src/getstack/identity.py +28 -20
  53. {getstack-3.0.0 → getstack-4.0.0}/src/getstack/inbound_webhooks.py +26 -13
  54. {getstack-3.0.0 → getstack-4.0.0}/src/getstack/intents.py +48 -37
  55. {getstack-3.0.0 → getstack-4.0.0}/src/getstack/llm.py +35 -19
  56. {getstack-3.0.0 → getstack-4.0.0}/src/getstack/missions.py +23 -26
  57. {getstack-3.0.0 → getstack-4.0.0}/src/getstack/notifications.py +13 -9
  58. {getstack-3.0.0 → getstack-4.0.0}/src/getstack/partner_connect.py +9 -11
  59. {getstack-3.0.0 → getstack-4.0.0}/src/getstack/partner_tenants.py +41 -27
  60. {getstack-3.0.0 → getstack-4.0.0}/src/getstack/passport_header_verify.py +4 -7
  61. {getstack-3.0.0 → getstack-4.0.0}/src/getstack/passport_session.py +1 -1
  62. {getstack-3.0.0 → getstack-4.0.0}/src/getstack/passports.py +72 -54
  63. {getstack-3.0.0 → getstack-4.0.0}/src/getstack/pii_config.py +7 -19
  64. {getstack-3.0.0 → getstack-4.0.0}/src/getstack/proxy.py +20 -19
  65. {getstack-3.0.0 → getstack-4.0.0}/src/getstack/reviews.py +11 -10
  66. {getstack-3.0.0 → getstack-4.0.0}/src/getstack/scan.py +10 -7
  67. {getstack-3.0.0 → getstack-4.0.0}/src/getstack/security_events.py +4 -4
  68. {getstack-3.0.0 → getstack-4.0.0}/src/getstack/services.py +25 -27
  69. {getstack-3.0.0 → getstack-4.0.0}/src/getstack/sign_claim.py +2 -9
  70. {getstack-3.0.0 → getstack-4.0.0}/src/getstack/skills.py +27 -19
  71. {getstack-3.0.0 → getstack-4.0.0}/src/getstack/team.py +5 -5
  72. {getstack-3.0.0 → getstack-4.0.0}/src/getstack/types.py +4 -2
  73. {getstack-3.0.0 → getstack-4.0.0}/src/getstack/usage.py +2 -2
  74. {getstack-3.0.0 → getstack-4.0.0}/src/getstack/verify_offline.py +7 -3
  75. {getstack-3.0.0 → getstack-4.0.0}/tests/test_authority.py +86 -66
  76. {getstack-3.0.0 → getstack-4.0.0}/tests/test_delivery_methods.py +2 -2
  77. {getstack-3.0.0 → getstack-4.0.0}/tests/test_dropoffs.py +11 -9
  78. {getstack-3.0.0 → getstack-4.0.0}/tests/test_errors.py +1 -1
  79. {getstack-3.0.0 → getstack-4.0.0}/tests/test_fleets.py +66 -18
  80. {getstack-3.0.0 → getstack-4.0.0}/tests/test_identity_requirements.py +3 -4
  81. {getstack-3.0.0 → getstack-4.0.0}/tests/test_inbound_webhooks.py +7 -5
  82. {getstack-3.0.0 → getstack-4.0.0}/tests/test_mission_binding.py +87 -48
  83. {getstack-3.0.0 → getstack-4.0.0}/tests/test_missions.py +2 -2
  84. {getstack-3.0.0 → getstack-4.0.0}/tests/test_parity.py +73 -35
  85. {getstack-3.0.0 → getstack-4.0.0}/tests/test_partner_signing.py +13 -5
  86. {getstack-3.0.0 → getstack-4.0.0}/tests/test_sign_claim.py +1 -1
  87. getstack-3.0.0/CLAUDE.md +0 -77
  88. getstack-3.0.0/src/getstack/_async/delivery_methods.py +0 -85
  89. getstack-3.0.0/src/getstack/_async/fleets.py +0 -82
  90. getstack-3.0.0/src/getstack/_async/inbound_webhooks.py +0 -47
  91. getstack-3.0.0/src/getstack/_async/pii_config.py +0 -38
  92. getstack-3.0.0/src/getstack/_async/services.py +0 -80
  93. getstack-3.0.0/src/getstack/delivery_methods.py +0 -85
  94. getstack-3.0.0/src/getstack/fleets.py +0 -82
  95. {getstack-3.0.0 → getstack-4.0.0}/LICENSE +0 -0
  96. {getstack-3.0.0 → getstack-4.0.0}/scripts/verify_sign_claim_crosslang.py +0 -0
  97. {getstack-3.0.0 → getstack-4.0.0}/src/getstack/py.typed +0 -0
  98. {getstack-3.0.0 → getstack-4.0.0}/tests/test_evidence_packs.py +0 -0
@@ -92,3 +92,14 @@ scratchpad/
92
92
 
93
93
  # Isolated test-only LangChain, CrewAI and AutoGen verification environment.
94
94
  .tmp/fleet-framework-venv/
95
+
96
+ # Local verification output (benchmark, fleet and release evidence). Never commit.
97
+ /artifacts/
98
+
99
+ # Cleanup 2026-10: local-only files
100
+ .claude/settings.local.json
101
+ .tmp/
102
+ *.paint
103
+ *.zip
104
+ STATUS.md
105
+ design-qa.md
@@ -0,0 +1,50 @@
1
+ # getstack (Python SDK)
2
+
3
+ The Python SDK, published to PyPI as `getstack` (MIT, Python 3.9+, built with hatchling from `pyproject.toml`). It is not a pnpm workspace member and has its own tooling. Runtime dependencies: `httpx`, `cryptography`, `pyjwt`, `cbor2`. It tracks the TypeScript SDK (`packages/sdk`) service for service.
4
+
5
+ ## Structure
6
+
7
+ - `sdk-python/src/getstack/__init__.py`: the sync `Stack` and async `AsyncStack` clients, `__all__`, auth selection.
8
+ - Sync services, one module per REST domain in `sdk-python/src/getstack/` (`agents`, `passports`, `missions`, `fleets`, `authority`, `evidence_packs`, `llm`, `proxy`, `team`, `delivery_methods`, `detector_config`, `pii_config`, `partner_tenants`, `partner_connect`, and others).
9
+ - Async mirrors of the same services in `sdk-python/src/getstack/_async/`.
10
+ - `sdk-python/src/getstack/client.py`, `auth.py`, `agent_auth.py`, `credentials.py`, `browser_bootstrap.py`: HTTP clients and credential sources.
11
+ - `sdk-python/src/getstack/verify_offline.py`, `passport_header_verify.py`, `sign_claim.py`: offline Passport verification and customer-managed claim signing.
12
+ - `sdk-python/src/getstack/errors.py`: `StackError` with `NotFoundError`, `UnauthorizedError`, `ForbiddenError`, `ValidationError`, `PassportBlockedError`, `MissionEndedError`.
13
+
14
+ ## Sync and async parity
15
+
16
+ Every service and method on `Stack` must also exist on `AsyncStack` (the async modules live under `sdk-python/src/getstack/_async/`). `sdk-python/tests/test_parity.py` is the guard: it asserts both clients expose the services in `EXPECTED_SERVICES` and the integration-critical methods in `EXPECTED_METHODS`, and that signatures such as `agents.register` match. When adding a service or method, add it to both clients and to those lists. Mirror new REST routes in `packages/sdk` too.
17
+
18
+ ## Auth and offline crypto
19
+
20
+ - Credential resolution (`Stack.__init__`): a pre-built `_auth`, then `agent_id` (agent keypair mode, `AgentKeypairAuth`), then `api_key` or `STACK_API_KEY`, then `~/.stack/credentials.json` (OAuth refresh token written by the CLI), then an interactive browser bootstrap when the process has a TTY and is not CI. Otherwise a `ValueError` is raised.
21
+ - `verify_passport_offline` (`verify_offline.py`) mirrors the JS function: EdDSA via PyJWT, with `jwks_url` or `public_jwk`. It cannot see revocation.
22
+ - `sign_claim_envelope` (`sign_claim.py`) produces COSE_Sign1 for `customer_managed` agents and must stay byte-compatible with the TS SDK and the server verifier. `sdk-python/scripts/verify_sign_claim_crosslang.py` proves it by signing in Python and verifying with the JS code; run it from the repo root (needs the JS SDK's `node_modules` for `cbor-x`).
23
+
24
+ ## Mission context manager
25
+
26
+ `stack.passports.mission(agent_id=..., intent=..., services=[...])` (and `async with` on `AsyncStack`) opens a Mission and its Passport in one call, submits checkpoints on a timer, checks out on exit, then completes the Mission. If the block raises, the Mission is revoked instead. Pass `mission_id="ms_..."` to bind an existing Mission, which is then left open on exit. `continuous_mission` rotates Passports under one Mission and raises `MissionEndedError` when the Mission ends. The TypeScript SDK has no equivalent context manager; keep keyword names aligned if one is added.
27
+
28
+ ## Run and test
29
+
30
+ ```bash
31
+ cd sdk-python
32
+ python -m pip install -e ".[dev]" # pytest, ruff, mypy
33
+ ruff check . # lint, import order (rules in pyproject.toml)
34
+ ruff format --check . # formatting, line length 120
35
+ mypy src # strict typing of src/ only; tests are not type-checked
36
+ python -m pytest -q # same tests as `pnpm verify:python`
37
+ ```
38
+
39
+ Run the three checks with a Python 3.10+ interpreter (current mypy no longer runs on 3.9). Fix findings in the code; the only suppressions are narrow `# type: ignore[code]` comments in tests. The HTTP client returns untyped JSON (`Any`), so service methods wrap it in `cast("<return type>", ...)` to satisfy `warn_return_any`. A service class that defines a `list` method must annotate later signatures as `builtins.list[...]`.
40
+
41
+ The integration-guide smoke test (`scripts/smoke-test-integration-guides.py`, with `scripts/smoke-test-integration-guides-setup.mjs`) needs a local API (port 3001); `scripts/release.mjs` runs it during `/publish sdk-python` when the API is reachable and aborts the release on failure.
42
+
43
+ ## Publishing
44
+
45
+ A change to this package must be released in the same session: bump `version` in `pyproject.toml` and run `/publish sdk-python <bump>`. That runs `scripts/release.mjs`, tags `sdk-python-v*`, and `publish-sdk-python.yml` uploads to PyPI. Do not run `twine` by hand. Versions are independent of the TypeScript SDK.
46
+
47
+ ## Gotchas
48
+
49
+ - The grant field on the wire is `service_connection_id`. Notification rules use `/v1/notifications/channels` (`notifications`); delivery methods use `/v1/notifications/destinations` (`delivery_methods`).
50
+ - The monorepo's JS lockfile does not resolve Python dependencies.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: getstack
3
- Version: 3.0.0
3
+ Version: 4.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
@@ -23,6 +23,10 @@ Requires-Dist: cbor2>=5.6.0
23
23
  Requires-Dist: cryptography>=42.0.0
24
24
  Requires-Dist: httpx>=0.25.0
25
25
  Requires-Dist: pyjwt>=2.8.0
26
+ Provides-Extra: dev
27
+ Requires-Dist: mypy>=1.13.0; extra == 'dev'
28
+ Requires-Dist: pytest>=8.0.0; extra == 'dev'
29
+ Requires-Dist: ruff>=0.9.0; extra == 'dev'
26
30
  Description-Content-Type: text/markdown
27
31
 
28
32
  # getstack
@@ -54,13 +58,24 @@ def run_fleet_mission_example(stack, request, provider_url="https://slack.com/ap
54
58
  repeated = stack.missions.start(request)
55
59
  assert repeated["mission_id"] == started["mission_id"]
56
60
  operation_id = hashlib.sha256(json.dumps([started["mission_id"], "support-update"]).encode()).hexdigest()
57
- response = stack.proxy.request("slack", provider_url, "POST",
61
+ response = stack.proxy.request(
62
+ "slack",
63
+ provider_url,
64
+ "POST",
58
65
  body={"channel": "C123", "text": "Support update"},
59
- passport_token=started["passport"]["token"], authority_request_id=operation_id)
66
+ passport_token=started["passport"]["token"],
67
+ authority_request_id=operation_id,
68
+ )
60
69
  assert response.status == 200
61
70
  try:
62
- stack.proxy.request("slack", provider_url, "POST", body={"channel": "OTHER", "text": "Support update"},
63
- passport_token=started["passport"]["token"], authority_request_id=operation_id + "_denied")
71
+ stack.proxy.request(
72
+ "slack",
73
+ provider_url,
74
+ "POST",
75
+ body={"channel": "OTHER", "text": "Support update"},
76
+ passport_token=started["passport"]["token"],
77
+ authority_request_id=operation_id + "_denied",
78
+ )
64
79
  except StackError as error:
65
80
  assert error.status_code == 403
66
81
  else:
@@ -168,6 +183,18 @@ stack.notifications.delete(channel_id)
168
183
  stack.audit.list(page=1, limit=50, action=None, agent_id=None)
169
184
  ```
170
185
 
186
+ ## Development
187
+
188
+ ```bash
189
+ python -m pip install -e ".[dev]"
190
+ ruff check .
191
+ ruff format --check .
192
+ mypy src
193
+ python -m pytest -q
194
+ ```
195
+
196
+ The package ships `py.typed`; `mypy src` runs in strict mode.
197
+
171
198
  ## Links
172
199
 
173
200
  - [Documentation](https://getstack.run/docs/sdk)
@@ -27,13 +27,24 @@ def run_fleet_mission_example(stack, request, provider_url="https://slack.com/ap
27
27
  repeated = stack.missions.start(request)
28
28
  assert repeated["mission_id"] == started["mission_id"]
29
29
  operation_id = hashlib.sha256(json.dumps([started["mission_id"], "support-update"]).encode()).hexdigest()
30
- response = stack.proxy.request("slack", provider_url, "POST",
30
+ response = stack.proxy.request(
31
+ "slack",
32
+ provider_url,
33
+ "POST",
31
34
  body={"channel": "C123", "text": "Support update"},
32
- passport_token=started["passport"]["token"], authority_request_id=operation_id)
35
+ passport_token=started["passport"]["token"],
36
+ authority_request_id=operation_id,
37
+ )
33
38
  assert response.status == 200
34
39
  try:
35
- stack.proxy.request("slack", provider_url, "POST", body={"channel": "OTHER", "text": "Support update"},
36
- passport_token=started["passport"]["token"], authority_request_id=operation_id + "_denied")
40
+ stack.proxy.request(
41
+ "slack",
42
+ provider_url,
43
+ "POST",
44
+ body={"channel": "OTHER", "text": "Support update"},
45
+ passport_token=started["passport"]["token"],
46
+ authority_request_id=operation_id + "_denied",
47
+ )
37
48
  except StackError as error:
38
49
  assert error.status_code == 403
39
50
  else:
@@ -141,6 +152,18 @@ stack.notifications.delete(channel_id)
141
152
  stack.audit.list(page=1, limit=50, action=None, agent_id=None)
142
153
  ```
143
154
 
155
+ ## Development
156
+
157
+ ```bash
158
+ python -m pip install -e ".[dev]"
159
+ ruff check .
160
+ ruff format --check .
161
+ mypy src
162
+ python -m pytest -q
163
+ ```
164
+
165
+ The package ships `py.typed`; `mypy src` runs in strict mode.
166
+
144
167
  ## Links
145
168
 
146
169
  - [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 = "3.0.0"
7
+ version = "4.0.0"
8
8
  description = "Python SDK for STACK — trust infrastructure for AI agents"
9
9
  readme = "README.md"
10
10
  license = "MIT"
@@ -46,3 +46,36 @@ Repository = "https://github.com/getstack-run/sdk-python"
46
46
 
47
47
  [tool.hatch.build.targets.wheel]
48
48
  packages = ["src/getstack"]
49
+
50
+ [project.optional-dependencies]
51
+ dev = [
52
+ "pytest>=8.0.0",
53
+ "ruff>=0.9.0",
54
+ "mypy>=1.13.0",
55
+ ]
56
+
57
+ [tool.ruff]
58
+ line-length = 120
59
+ target-version = "py39"
60
+ src = ["src", "tests"]
61
+
62
+ [tool.ruff.lint]
63
+ select = [
64
+ "E", # pycodestyle errors
65
+ "W", # pycodestyle warnings
66
+ "F", # pyflakes
67
+ "I", # isort import order
68
+ "B", # flake8-bugbear
69
+ "UP", # pyupgrade
70
+ "SIM", # flake8-simplify
71
+ "C4", # flake8-comprehensions
72
+ "RUF", # ruff-specific rules
73
+ ]
74
+
75
+ [tool.ruff.lint.isort]
76
+ known-first-party = ["getstack"]
77
+
78
+ [tool.mypy]
79
+ strict = true
80
+ mypy_path = "src"
81
+ files = ["src"]