onkernel 0.2.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.
@@ -0,0 +1,25 @@
1
+ # ─── Python ──────────────────────────────────────────────────────────────
2
+ __pycache__/
3
+ *.py[cod]
4
+ *.egg-info/
5
+ .eggs/
6
+ build/
7
+ dist/
8
+ .venv/
9
+ venv/
10
+ .pytest_cache/
11
+ .mypy_cache/
12
+ .ruff_cache/
13
+
14
+ # ─── Secrets ─────────────────────────────────────────────────────────────
15
+ *.pem
16
+ !*-pub.pem
17
+ *.key
18
+ .env
19
+ .env.*
20
+ !.env.example
21
+
22
+ # ─── OS / editor ─────────────────────────────────────────────────────────
23
+ .DS_Store
24
+ .idea/
25
+ .vscode/
@@ -0,0 +1,113 @@
1
+ # Changelog
2
+
3
+ All notable changes to the `onkernel` Python SDK are documented here. This
4
+ project follows [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
5
+
6
+ ## 0.2.0 — 2026-06-17
7
+
8
+ First public PyPI release — the Kernel governance SDK with agent-to-agent
9
+ delegation. Designed to be obvious to use from an LLM-tool-dispatch layer
10
+ without a custom wrapper.
11
+
12
+ ### Added
13
+
14
+ - **A2A delegation (`delegate()`)** — record agent-to-agent delegation chains
15
+ in the signed audit log. When one agent's ALLOW decision authorizes another
16
+ agent to act, pass the parent's `decision_id` so Kernel links the child
17
+ decision to its parent and unions their regulatory anchors:
18
+
19
+ ```python
20
+ decision = orchestrator.execute("triage_refund", params=…) # root
21
+ kyc.delegate(decision.decision_id, "verify_identity", params=…) # child
22
+ ```
23
+
24
+ `delegate(parent_decision_id, …)` is sugar over the new
25
+ `execute(…, parent_decision_id=…)` keyword (sends the
26
+ `X-Kernel-Parent-Decision-Id` header). A cross-tenant or unknown parent is
27
+ refused identically to a missing one (no existence fingerprinting).
28
+ - **`ActionResponse.decision_id`** — the ALLOW decision's audit-event id, now
29
+ parsed off the response. Feed it to a downstream agent's `delegate()`. `None`
30
+ on non-ALLOW outcomes and on proxies predating execution verification.
31
+ - **`KernelOutcome` enum** on `ActionResponse.outcome`. Callers branch on
32
+ the outcome via `match/case` instead of catching an exception for the
33
+ "ask a human" path:
34
+
35
+ ```python
36
+ resp = k.execute(action="refund", target="stripe", params={"amount_cents": 12000})
37
+ match resp.outcome:
38
+ case KernelOutcome.ALLOWED: do_thing(resp.result)
39
+ case KernelOutcome.REQUIRES_HUMAN: queue_for_review(resp.approval_id)
40
+ case KernelOutcome.BLOCKED: log_scanner_block(resp.violations)
41
+ case KernelOutcome.DENIED: log_policy_deny(resp.reason)
42
+ ```
43
+
44
+ - **`ScannerBlockedError`** — sibling of `PolicyDeniedError` for 403
45
+ responses with a populated `violations` list (PII / secrets / prompt
46
+ injection detector fired). Exposes `.violation_type` and
47
+ `.violation_details` so callers don't have to fish through
48
+ `e.violations[0]["type"]` themselves.
49
+
50
+ In v0.2 `ScannerBlockedError` inherits from `PolicyDeniedError` so
51
+ existing `except PolicyDeniedError:` blocks keep catching both. In v0.3
52
+ the two will become true siblings under `KernelDeniedError`; migrate by
53
+ catching them separately:
54
+
55
+ ```python
56
+ try:
57
+ k.execute(...)
58
+ except ScannerBlockedError as e:
59
+ # PII / secret / injection — fix the input
60
+ ...
61
+ except PolicyDeniedError as e:
62
+ # Policy rule denied — pick a different tool or escalate
63
+ ...
64
+ ```
65
+
66
+ - **`KernelDeniedError`** — alias for the shared base of the 403-denial
67
+ family. Forward-compatible with the v0.3 split:
68
+
69
+ ```python
70
+ except KernelDeniedError: # catches both scanner blocks and policy denies
71
+ ...
72
+ ```
73
+
74
+ - **`ActionResponse.result_message: str`** — always-non-empty
75
+ human-readable summary alongside `result: dict | None`. Removes the
76
+ LLM-anthropomorphizes-None failure mode ("the result was empty, should I
77
+ retry?"). Populated by the proxy when available; the SDK derives a
78
+ summary from `status` + `result` keys when the proxy is on an older
79
+ build.
80
+
81
+ Note: the Kernel proxy as of this release does NOT emit `result_message`
82
+ on successful action responses (only `message` on error paths). The SDK
83
+ derives a summary client-side and emits a `UserWarning` so operators can
84
+ tell stale proxies apart from current ones. Upgrade the Kernel proxy to
85
+ benefit from richer, server-side summaries.
86
+
87
+ ### Deprecated
88
+
89
+ - **`ApprovalRequiredError`** — the 202-pending-approval path no longer
90
+ raises. The SDK returns `ActionResponse(outcome=REQUIRES_HUMAN,
91
+ approval_id=...)` and callers branch on `outcome`. The class is kept
92
+ importable for v0.2 and removed in v0.3; importing it emits a
93
+ `DeprecationWarning`.
94
+
95
+ - **`ActionResponse.message`** — renamed to `result_message`. Reading
96
+ `.message` emits a `DeprecationWarning`; removed in v0.3.
97
+
98
+ ### Packaging
99
+
100
+ - First PyPI release as `onkernel` (namespace claimed; `kernel-sdk` was
101
+ already taken). `pip install onkernel` replaces the
102
+ `git+https://github.com/onkernel-ai/kernel-python` install string in
103
+ downstream `pyproject.toml`s.
104
+ - Added `LICENSE` (MIT), classifiers, `project.urls`, `[project.authors]`,
105
+ and `[project.optional-dependencies.dev]`.
106
+ - Added a `.github/workflows/test.yml` matrix (3.10–3.13) and a
107
+ `.github/workflows/publish.yml` that builds + publishes on tag push via
108
+ PyPI Trusted Publishing (no API token in secrets). Pre-release tags
109
+ (e.g. `v0.2.0-rc.1`) publish to TestPyPI first.
110
+
111
+ ## 0.1.0
112
+
113
+ Initial baseline.
onkernel-0.2.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Kernel (onkernel.ai)
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,182 @@
1
+ Metadata-Version: 2.4
2
+ Name: onkernel
3
+ Version: 0.2.0
4
+ Summary: Python SDK for Kernel — deterministic governance for AI agents
5
+ Project-URL: Homepage, https://onkernel.ai
6
+ Project-URL: Documentation, https://github.com/onkernel-ai/kernel-python#readme
7
+ Project-URL: Repository, https://github.com/onkernel-ai/kernel-python
8
+ Project-URL: Issues, https://github.com/onkernel-ai/kernel-python/issues
9
+ Project-URL: Changelog, https://github.com/onkernel-ai/kernel-python/blob/main/CHANGELOG.md
10
+ Author-email: Kernel <engineering@onkernel.ai>
11
+ License-Expression: MIT
12
+ License-File: LICENSE
13
+ Keywords: agents,ai,governance,guardrails,llm,policy
14
+ Classifier: Development Status :: 4 - Beta
15
+ Classifier: Intended Audience :: Developers
16
+ Classifier: License :: OSI Approved :: MIT License
17
+ Classifier: Operating System :: OS Independent
18
+ Classifier: Programming Language :: Python :: 3
19
+ Classifier: Programming Language :: Python :: 3.10
20
+ Classifier: Programming Language :: Python :: 3.11
21
+ Classifier: Programming Language :: Python :: 3.12
22
+ Classifier: Programming Language :: Python :: 3.13
23
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
24
+ Classifier: Typing :: Typed
25
+ Requires-Python: >=3.10
26
+ Requires-Dist: httpx>=0.27
27
+ Provides-Extra: crypto
28
+ Requires-Dist: cryptography>=44.0; extra == 'crypto'
29
+ Provides-Extra: dev
30
+ Requires-Dist: cryptography>=44.0; extra == 'dev'
31
+ Requires-Dist: mypy>=1.10; extra == 'dev'
32
+ Requires-Dist: pytest-asyncio>=0.23; extra == 'dev'
33
+ Requires-Dist: pytest>=8.0; extra == 'dev'
34
+ Requires-Dist: respx>=0.21; extra == 'dev'
35
+ Requires-Dist: ruff>=0.6; extra == 'dev'
36
+ Description-Content-Type: text/markdown
37
+
38
+ # onkernel
39
+
40
+ Python SDK for [Kernel](https://onkernel.ai) — deterministic governance for AI agents.
41
+
42
+ ## Install
43
+
44
+ ```bash
45
+ pip install onkernel
46
+ ```
47
+
48
+ For encrypted response decryption:
49
+
50
+ ```bash
51
+ pip install onkernel[crypto]
52
+ ```
53
+
54
+ ## Quick Start
55
+
56
+ ```python
57
+ from kernel import Kernel, KernelOutcome
58
+
59
+ k = Kernel(api_key="ok_live_acme_bot1_abc123")
60
+
61
+ resp = k.execute(action="send_email", target="user@example.com", params={"body": "Hello"})
62
+
63
+ match resp.outcome:
64
+ case KernelOutcome.ALLOWED:
65
+ # resp.result and resp.result_message are populated
66
+ print(resp.result_message)
67
+ case KernelOutcome.REQUIRES_HUMAN:
68
+ # 202 — escalate to your approval queue
69
+ approval_id = resp.approval_id
70
+ case KernelOutcome.BLOCKED:
71
+ # a scanner fired; resp.violations is populated
72
+ ...
73
+ case KernelOutcome.DENIED:
74
+ # a policy rule denied; resp.reason explains why
75
+ ...
76
+ ```
77
+
78
+ `ActionResponse` always carries a non-empty `result_message: str` — a
79
+ human-readable summary that's safe to surface in an LLM tool-call result.
80
+
81
+ ### Handling errors
82
+
83
+ The denial path raises typed exceptions on 403:
84
+
85
+ ```python
86
+ from kernel import (
87
+ Kernel,
88
+ ScannerBlockedError, # scanner fired (PII, secrets, prompt injection, …)
89
+ PolicyDeniedError, # policy rule denied with no scanner involvement
90
+ KernelDeniedError, # base / alias — catch both
91
+ )
92
+
93
+ try:
94
+ k.execute(action="export_pii_report", target="customers")
95
+ except ScannerBlockedError as e:
96
+ # e.violation_type, e.violation_details, e.violations
97
+ pass
98
+ except PolicyDeniedError as e:
99
+ # e.rule_id, e.policy_id, e.reason
100
+ pass
101
+ ```
102
+
103
+ ### 3-step token flow
104
+
105
+ ```python
106
+ session = k.create_session(intent="onboard new customer")
107
+ token = k.request_token(session.id, action="create_account", target="stripe")
108
+ result = k.execute_token(token.token_id, agent_id="bot1")
109
+ ```
110
+
111
+ ## Decrypting Responses
112
+
113
+ When a policy has `encryption_enabled: true`, responses arrive encrypted:
114
+
115
+ ```python
116
+ from kernel import Kernel
117
+ from kernel.crypto import load_private_key
118
+
119
+ k = Kernel(api_key="ok_live_acme_bot1_abc123")
120
+ private_key = load_private_key("path/to/private_key.pem")
121
+
122
+ resp = k.execute(action="fetch_records", target="db")
123
+ if resp.encrypted:
124
+ data = resp.decrypt(private_key)
125
+ ```
126
+
127
+ ## Publishing (maintainers)
128
+
129
+ The SDK is published to PyPI as `onkernel`. Releases go out via tag push;
130
+ the `publish.yml` workflow handles build + Trusted-Publishing OIDC.
131
+
132
+ **Always release to TestPyPI first.** PyPI is fussy about metadata and
133
+ artifact integrity; a TestPyPI dry-run catches mistakes before they're
134
+ permanent (PyPI does not allow overwriting a released version).
135
+
136
+ ### One-time setup
137
+
138
+ 1. Create the project on PyPI: `pip install build twine && python -m build && twine upload --repository testpypi dist/*` (first manual upload claims the namespace).
139
+ 2. On PyPI → project → Publishing, add a pending publisher:
140
+ - Owner: `onkernel-ai`
141
+ - Repository: `kernel-python`
142
+ - Workflow: `publish.yml`
143
+ - Environment: `pypi`
144
+ 3. Repeat the publisher binding on test.pypi.org with environment `testpypi`.
145
+ 4. In the GitHub repo Settings → Environments, create `pypi` and `testpypi`. Add required reviewers on `pypi` if you want a release gate.
146
+
147
+ ### Cutting a release
148
+
149
+ 1. Bump `pyproject.toml` `[project].version` and `src/kernel/__init__.py` `__version__` together.
150
+ 2. Update `CHANGELOG.md`.
151
+ 3. Pre-release dry-run:
152
+ ```bash
153
+ git tag v0.X.Y-rc.1
154
+ git push --tags
155
+ ```
156
+ The `publish.yml` workflow detects the `-rc.N` suffix and routes to TestPyPI. Validate:
157
+ ```bash
158
+ pip install --index-url https://test.pypi.org/simple/ --extra-index-url https://pypi.org/simple/ onkernel==0.X.Y-rc.1
159
+ python -c "from kernel import Kernel, KernelOutcome; print(KernelOutcome.ALLOWED)"
160
+ ```
161
+ 4. Real release:
162
+ ```bash
163
+ git tag v0.X.Y
164
+ git push --tags
165
+ ```
166
+ The same workflow routes the un-suffixed tag to real PyPI.
167
+
168
+ The workflow verifies that the tag matches `pyproject.toml [project].version`
169
+ before building — a mismatched tag fails fast instead of publishing the
170
+ wrong artifact.
171
+
172
+ ### Fallback: manual API token
173
+
174
+ If Trusted Publishing isn't yet bound, set repo secret `PYPI_API_TOKEN`
175
+ and replace the OIDC publish step with:
176
+
177
+ ```yaml
178
+ - run: twine upload -u __token__ -p "$PYPI_API_TOKEN" dist/*
179
+ ```
180
+
181
+ Tokens carry blast-radius risk (leak = arbitrary release). Migrate to OIDC
182
+ as soon as the binding is approved on the PyPI side.
@@ -0,0 +1,145 @@
1
+ # onkernel
2
+
3
+ Python SDK for [Kernel](https://onkernel.ai) — deterministic governance for AI agents.
4
+
5
+ ## Install
6
+
7
+ ```bash
8
+ pip install onkernel
9
+ ```
10
+
11
+ For encrypted response decryption:
12
+
13
+ ```bash
14
+ pip install onkernel[crypto]
15
+ ```
16
+
17
+ ## Quick Start
18
+
19
+ ```python
20
+ from kernel import Kernel, KernelOutcome
21
+
22
+ k = Kernel(api_key="ok_live_acme_bot1_abc123")
23
+
24
+ resp = k.execute(action="send_email", target="user@example.com", params={"body": "Hello"})
25
+
26
+ match resp.outcome:
27
+ case KernelOutcome.ALLOWED:
28
+ # resp.result and resp.result_message are populated
29
+ print(resp.result_message)
30
+ case KernelOutcome.REQUIRES_HUMAN:
31
+ # 202 — escalate to your approval queue
32
+ approval_id = resp.approval_id
33
+ case KernelOutcome.BLOCKED:
34
+ # a scanner fired; resp.violations is populated
35
+ ...
36
+ case KernelOutcome.DENIED:
37
+ # a policy rule denied; resp.reason explains why
38
+ ...
39
+ ```
40
+
41
+ `ActionResponse` always carries a non-empty `result_message: str` — a
42
+ human-readable summary that's safe to surface in an LLM tool-call result.
43
+
44
+ ### Handling errors
45
+
46
+ The denial path raises typed exceptions on 403:
47
+
48
+ ```python
49
+ from kernel import (
50
+ Kernel,
51
+ ScannerBlockedError, # scanner fired (PII, secrets, prompt injection, …)
52
+ PolicyDeniedError, # policy rule denied with no scanner involvement
53
+ KernelDeniedError, # base / alias — catch both
54
+ )
55
+
56
+ try:
57
+ k.execute(action="export_pii_report", target="customers")
58
+ except ScannerBlockedError as e:
59
+ # e.violation_type, e.violation_details, e.violations
60
+ pass
61
+ except PolicyDeniedError as e:
62
+ # e.rule_id, e.policy_id, e.reason
63
+ pass
64
+ ```
65
+
66
+ ### 3-step token flow
67
+
68
+ ```python
69
+ session = k.create_session(intent="onboard new customer")
70
+ token = k.request_token(session.id, action="create_account", target="stripe")
71
+ result = k.execute_token(token.token_id, agent_id="bot1")
72
+ ```
73
+
74
+ ## Decrypting Responses
75
+
76
+ When a policy has `encryption_enabled: true`, responses arrive encrypted:
77
+
78
+ ```python
79
+ from kernel import Kernel
80
+ from kernel.crypto import load_private_key
81
+
82
+ k = Kernel(api_key="ok_live_acme_bot1_abc123")
83
+ private_key = load_private_key("path/to/private_key.pem")
84
+
85
+ resp = k.execute(action="fetch_records", target="db")
86
+ if resp.encrypted:
87
+ data = resp.decrypt(private_key)
88
+ ```
89
+
90
+ ## Publishing (maintainers)
91
+
92
+ The SDK is published to PyPI as `onkernel`. Releases go out via tag push;
93
+ the `publish.yml` workflow handles build + Trusted-Publishing OIDC.
94
+
95
+ **Always release to TestPyPI first.** PyPI is fussy about metadata and
96
+ artifact integrity; a TestPyPI dry-run catches mistakes before they're
97
+ permanent (PyPI does not allow overwriting a released version).
98
+
99
+ ### One-time setup
100
+
101
+ 1. Create the project on PyPI: `pip install build twine && python -m build && twine upload --repository testpypi dist/*` (first manual upload claims the namespace).
102
+ 2. On PyPI → project → Publishing, add a pending publisher:
103
+ - Owner: `onkernel-ai`
104
+ - Repository: `kernel-python`
105
+ - Workflow: `publish.yml`
106
+ - Environment: `pypi`
107
+ 3. Repeat the publisher binding on test.pypi.org with environment `testpypi`.
108
+ 4. In the GitHub repo Settings → Environments, create `pypi` and `testpypi`. Add required reviewers on `pypi` if you want a release gate.
109
+
110
+ ### Cutting a release
111
+
112
+ 1. Bump `pyproject.toml` `[project].version` and `src/kernel/__init__.py` `__version__` together.
113
+ 2. Update `CHANGELOG.md`.
114
+ 3. Pre-release dry-run:
115
+ ```bash
116
+ git tag v0.X.Y-rc.1
117
+ git push --tags
118
+ ```
119
+ The `publish.yml` workflow detects the `-rc.N` suffix and routes to TestPyPI. Validate:
120
+ ```bash
121
+ pip install --index-url https://test.pypi.org/simple/ --extra-index-url https://pypi.org/simple/ onkernel==0.X.Y-rc.1
122
+ python -c "from kernel import Kernel, KernelOutcome; print(KernelOutcome.ALLOWED)"
123
+ ```
124
+ 4. Real release:
125
+ ```bash
126
+ git tag v0.X.Y
127
+ git push --tags
128
+ ```
129
+ The same workflow routes the un-suffixed tag to real PyPI.
130
+
131
+ The workflow verifies that the tag matches `pyproject.toml [project].version`
132
+ before building — a mismatched tag fails fast instead of publishing the
133
+ wrong artifact.
134
+
135
+ ### Fallback: manual API token
136
+
137
+ If Trusted Publishing isn't yet bound, set repo secret `PYPI_API_TOKEN`
138
+ and replace the OIDC publish step with:
139
+
140
+ ```yaml
141
+ - run: twine upload -u __token__ -p "$PYPI_API_TOKEN" dist/*
142
+ ```
143
+
144
+ Tokens carry blast-radius risk (leak = arbitrary release). Migrate to OIDC
145
+ as soon as the binding is approved on the PyPI side.
@@ -0,0 +1,88 @@
1
+ [build-system]
2
+ requires = ["hatchling"]
3
+ build-backend = "hatchling.build"
4
+
5
+ [project]
6
+ name = "onkernel"
7
+ version = "0.2.0"
8
+ description = "Python SDK for Kernel — deterministic governance for AI agents"
9
+ readme = "README.md"
10
+ requires-python = ">=3.10"
11
+ license = "MIT"
12
+ authors = [
13
+ { name = "Kernel", email = "engineering@onkernel.ai" },
14
+ ]
15
+ keywords = ["ai", "agents", "governance", "policy", "llm", "guardrails"]
16
+ classifiers = [
17
+ "Development Status :: 4 - Beta",
18
+ "Intended Audience :: Developers",
19
+ "License :: OSI Approved :: MIT License",
20
+ "Operating System :: OS Independent",
21
+ "Programming Language :: Python :: 3",
22
+ "Programming Language :: Python :: 3.10",
23
+ "Programming Language :: Python :: 3.11",
24
+ "Programming Language :: Python :: 3.12",
25
+ "Programming Language :: Python :: 3.13",
26
+ "Topic :: Software Development :: Libraries :: Python Modules",
27
+ "Typing :: Typed",
28
+ ]
29
+ dependencies = [
30
+ "httpx>=0.27",
31
+ ]
32
+
33
+ [project.optional-dependencies]
34
+ crypto = ["cryptography>=44.0"]
35
+ dev = [
36
+ "pytest>=8.0",
37
+ "pytest-asyncio>=0.23",
38
+ "ruff>=0.6",
39
+ "mypy>=1.10",
40
+ "cryptography>=44.0",
41
+ "respx>=0.21",
42
+ ]
43
+
44
+ [project.urls]
45
+ Homepage = "https://onkernel.ai"
46
+ Documentation = "https://github.com/onkernel-ai/kernel-python#readme"
47
+ Repository = "https://github.com/onkernel-ai/kernel-python"
48
+ Issues = "https://github.com/onkernel-ai/kernel-python/issues"
49
+ Changelog = "https://github.com/onkernel-ai/kernel-python/blob/main/CHANGELOG.md"
50
+
51
+ [tool.hatch.build.targets.wheel]
52
+ packages = ["src/kernel"]
53
+
54
+ [tool.hatch.build.targets.sdist]
55
+ include = [
56
+ "src/kernel",
57
+ "README.md",
58
+ "CHANGELOG.md",
59
+ "LICENSE",
60
+ "pyproject.toml",
61
+ ]
62
+
63
+ [tool.ruff]
64
+ line-length = 100
65
+ target-version = "py310"
66
+
67
+ [tool.ruff.lint]
68
+ select = ["E", "F", "I", "B", "UP", "SIM"]
69
+ ignore = ["E501"]
70
+
71
+ [tool.ruff.lint.per-file-ignores]
72
+ "tests/*" = ["B018"]
73
+
74
+ [tool.mypy]
75
+ python_version = "3.10"
76
+ strict_optional = true
77
+ warn_unused_ignores = true
78
+ ignore_missing_imports = true
79
+ packages = ["kernel"]
80
+
81
+ [tool.pytest.ini_options]
82
+ testpaths = ["tests"]
83
+ filterwarnings = [
84
+ # Tests should fail if they trigger an unexpected DeprecationWarning so
85
+ # callers see clean output. Individual tests assert deprecations with
86
+ # `pytest.warns`.
87
+ "error::DeprecationWarning:kernel.*",
88
+ ]
@@ -0,0 +1,85 @@
1
+ """Kernel SDK — deterministic governance for AI agents."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import warnings as _warnings
6
+ from typing import Any as _Any
7
+
8
+ __version__ = "0.2.0"
9
+
10
+ import contextlib as _contextlib
11
+
12
+ from kernel.client import Kernel
13
+ from kernel.errors import (
14
+ AuthError,
15
+ CircuitOpenError,
16
+ FeatureNotInTier,
17
+ KernelDeniedError,
18
+ KernelError,
19
+ PlanCeilingHit,
20
+ PlanLimitExceeded,
21
+ PlanSoftWarning,
22
+ PolicyDeniedError,
23
+ RateLimitError,
24
+ ScannerBlockedError,
25
+ WebhookError,
26
+ )
27
+ from kernel.types import (
28
+ ActionResponse,
29
+ ApprovalResult,
30
+ ExecuteResponse,
31
+ KernelOutcome,
32
+ Session,
33
+ TokenResponse,
34
+ )
35
+
36
+ with _contextlib.suppress(ImportError):
37
+ from kernel.crypto import decrypt, decrypt_json # noqa: F401 — re-export
38
+
39
+
40
+ def __getattr__(name: str) -> _Any:
41
+ """Lazy deprecation for `kernel.ApprovalRequiredError` import.
42
+
43
+ The SDK no longer raises this exception (the 202 path now returns an
44
+ `ActionResponse` with `outcome == KernelOutcome.REQUIRES_HUMAN`). The
45
+ name is kept exported for one minor version so existing
46
+ `from kernel import ApprovalRequiredError` keeps importing. Reading
47
+ the symbol emits a `DeprecationWarning`; the class is removed in v0.3.
48
+ """
49
+ if name == "ApprovalRequiredError":
50
+ _warnings.warn(
51
+ "ApprovalRequiredError is deprecated and will be removed in v0.3. "
52
+ "The SDK no longer raises on 202 responses; branch on "
53
+ "ActionResponse.outcome (KernelOutcome.REQUIRES_HUMAN) instead. "
54
+ "See CHANGELOG for migration.",
55
+ DeprecationWarning,
56
+ stacklevel=2,
57
+ )
58
+ from kernel.errors import ApprovalRequiredError as _AR
59
+ return _AR
60
+ raise AttributeError(f"module 'kernel' has no attribute {name!r}")
61
+
62
+
63
+ __all__ = [
64
+ "Kernel",
65
+ "KernelError",
66
+ "AuthError",
67
+ "KernelDeniedError",
68
+ "PolicyDeniedError",
69
+ "ScannerBlockedError",
70
+ "RateLimitError",
71
+ "CircuitOpenError",
72
+ "WebhookError",
73
+ "PlanLimitExceeded",
74
+ "PlanCeilingHit",
75
+ "FeatureNotInTier",
76
+ "PlanSoftWarning",
77
+ "ActionResponse",
78
+ "KernelOutcome",
79
+ "Session",
80
+ "TokenResponse",
81
+ "ExecuteResponse",
82
+ "ApprovalResult",
83
+ # Deprecated, retained one minor version:
84
+ "ApprovalRequiredError",
85
+ ]