agent-runtime-kit 0.4.0__tar.gz → 0.5.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 (75) hide show
  1. {agent_runtime_kit-0.4.0 → agent_runtime_kit-0.5.0}/CHANGELOG.md +76 -0
  2. {agent_runtime_kit-0.4.0 → agent_runtime_kit-0.5.0}/PKG-INFO +43 -17
  3. {agent_runtime_kit-0.4.0 → agent_runtime_kit-0.5.0}/README.md +41 -16
  4. {agent_runtime_kit-0.4.0 → agent_runtime_kit-0.5.0}/docs/api-stability.md +47 -1
  5. {agent_runtime_kit-0.4.0 → agent_runtime_kit-0.5.0}/docs/capability-matrix.md +28 -2
  6. {agent_runtime_kit-0.4.0 → agent_runtime_kit-0.5.0}/docs/providers.md +83 -6
  7. {agent_runtime_kit-0.4.0 → agent_runtime_kit-0.5.0}/docs/quickstart.md +21 -12
  8. agent_runtime_kit-0.5.0/docs/task-control.md +61 -0
  9. {agent_runtime_kit-0.4.0 → agent_runtime_kit-0.5.0}/pyproject.toml +6 -3
  10. {agent_runtime_kit-0.4.0 → agent_runtime_kit-0.5.0}/src/agent_runtime_kit/__init__.py +35 -0
  11. agent_runtime_kit-0.5.0/src/agent_runtime_kit/_control.py +234 -0
  12. {agent_runtime_kit-0.4.0 → agent_runtime_kit-0.5.0}/src/agent_runtime_kit/_errors.py +24 -0
  13. agent_runtime_kit-0.5.0/src/agent_runtime_kit/_kit.py +775 -0
  14. {agent_runtime_kit-0.4.0 → agent_runtime_kit-0.5.0}/src/agent_runtime_kit/_runtime.py +53 -32
  15. {agent_runtime_kit-0.4.0 → agent_runtime_kit-0.5.0}/src/agent_runtime_kit/_schema.py +112 -4
  16. agent_runtime_kit-0.5.0/src/agent_runtime_kit/_types.py +887 -0
  17. agent_runtime_kit-0.5.0/src/agent_runtime_kit/adapters/_common.py +478 -0
  18. {agent_runtime_kit-0.4.0 → agent_runtime_kit-0.5.0}/src/agent_runtime_kit/adapters/antigravity.py +212 -90
  19. {agent_runtime_kit-0.4.0 → agent_runtime_kit-0.5.0}/src/agent_runtime_kit/adapters/claude.py +154 -80
  20. {agent_runtime_kit-0.4.0 → agent_runtime_kit-0.5.0}/src/agent_runtime_kit/adapters/codex.py +175 -77
  21. agent_runtime_kit-0.5.0/src/agent_runtime_kit/adapters/diagnostics.py +42 -0
  22. agent_runtime_kit-0.5.0/src/agent_runtime_kit/compatibility.py +100 -0
  23. agent_runtime_kit-0.5.0/src/agent_runtime_kit/readiness.py +128 -0
  24. {agent_runtime_kit-0.4.0 → agent_runtime_kit-0.5.0}/src/agent_runtime_kit/registry.py +45 -2
  25. agent_runtime_kit-0.5.0/src/agent_runtime_kit/support.py +138 -0
  26. {agent_runtime_kit-0.4.0 → agent_runtime_kit-0.5.0}/src/agent_runtime_kit/testing/fakes.py +54 -1
  27. agent_runtime_kit-0.5.0/tests/test_adapter_common.py +173 -0
  28. {agent_runtime_kit-0.4.0 → agent_runtime_kit-0.5.0}/tests/test_adapter_protocol.py +13 -0
  29. {agent_runtime_kit-0.4.0 → agent_runtime_kit-0.5.0}/tests/test_antigravity_adapter.py +177 -11
  30. {agent_runtime_kit-0.4.0 → agent_runtime_kit-0.5.0}/tests/test_claude_adapter.py +192 -4
  31. {agent_runtime_kit-0.4.0 → agent_runtime_kit-0.5.0}/tests/test_codex_adapter.py +202 -14
  32. agent_runtime_kit-0.5.0/tests/test_control.py +662 -0
  33. {agent_runtime_kit-0.4.0 → agent_runtime_kit-0.5.0}/tests/test_core.py +143 -0
  34. {agent_runtime_kit-0.4.0 → agent_runtime_kit-0.5.0}/tests/test_events.py +40 -0
  35. {agent_runtime_kit-0.4.0 → agent_runtime_kit-0.5.0}/tests/test_kit.py +140 -0
  36. {agent_runtime_kit-0.4.0 → agent_runtime_kit-0.5.0}/tests/test_live_smoke.py +5 -5
  37. {agent_runtime_kit-0.4.0 → agent_runtime_kit-0.5.0}/tests/test_mestre_compatibility.py +4 -1
  38. agent_runtime_kit-0.5.0/tests/test_readiness.py +662 -0
  39. {agent_runtime_kit-0.4.0 → agent_runtime_kit-0.5.0}/tests/test_schema.py +76 -3
  40. agent_runtime_kit-0.5.0/tests/test_support.py +282 -0
  41. agent_runtime_kit-0.4.0/docs/mestre-migration.md +0 -25
  42. agent_runtime_kit-0.4.0/src/agent_runtime_kit/_kit.py +0 -426
  43. agent_runtime_kit-0.4.0/src/agent_runtime_kit/_types.py +0 -462
  44. agent_runtime_kit-0.4.0/src/agent_runtime_kit/adapters/_common.py +0 -334
  45. agent_runtime_kit-0.4.0/src/agent_runtime_kit/adapters/diagnostics.py +0 -18
  46. {agent_runtime_kit-0.4.0 → agent_runtime_kit-0.5.0}/.gitignore +0 -0
  47. {agent_runtime_kit-0.4.0 → agent_runtime_kit-0.5.0}/LICENSE +0 -0
  48. {agent_runtime_kit-0.4.0 → agent_runtime_kit-0.5.0}/docs/live-smoke.md +0 -0
  49. {agent_runtime_kit-0.4.0 → agent_runtime_kit-0.5.0}/docs/sdk-evolution-agent-design.md +0 -0
  50. {agent_runtime_kit-0.4.0 → agent_runtime_kit-0.5.0}/docs/sdk-evolution-agent.md +0 -0
  51. {agent_runtime_kit-0.4.0 → agent_runtime_kit-0.5.0}/examples/__init__.py +0 -0
  52. {agent_runtime_kit-0.4.0 → agent_runtime_kit-0.5.0}/examples/run_same_task.py +0 -0
  53. {agent_runtime_kit-0.4.0 → agent_runtime_kit-0.5.0}/examples/sdk_evolution_agent/__init__.py +0 -0
  54. {agent_runtime_kit-0.4.0 → agent_runtime_kit-0.5.0}/examples/sdk_evolution_agent/__main__.py +0 -0
  55. {agent_runtime_kit-0.4.0 → agent_runtime_kit-0.5.0}/examples/sdk_evolution_agent/auth.py +0 -0
  56. {agent_runtime_kit-0.4.0 → agent_runtime_kit-0.5.0}/examples/sdk_evolution_agent/behavior.py +0 -0
  57. {agent_runtime_kit-0.4.0 → agent_runtime_kit-0.5.0}/examples/sdk_evolution_agent/cli.py +0 -0
  58. {agent_runtime_kit-0.4.0 → agent_runtime_kit-0.5.0}/examples/sdk_evolution_agent/collectors.py +0 -0
  59. {agent_runtime_kit-0.4.0 → agent_runtime_kit-0.5.0}/examples/sdk_evolution_agent/current_state.py +0 -0
  60. {agent_runtime_kit-0.4.0 → agent_runtime_kit-0.5.0}/examples/sdk_evolution_agent/events.py +0 -0
  61. {agent_runtime_kit-0.4.0 → agent_runtime_kit-0.5.0}/examples/sdk_evolution_agent/models.py +0 -0
  62. {agent_runtime_kit-0.4.0 → agent_runtime_kit-0.5.0}/examples/sdk_evolution_agent/pr.py +0 -0
  63. {agent_runtime_kit-0.4.0 → agent_runtime_kit-0.5.0}/examples/sdk_evolution_agent/release_notes.py +0 -0
  64. {agent_runtime_kit-0.4.0 → agent_runtime_kit-0.5.0}/examples/sdk_evolution_agent/report.py +0 -0
  65. {agent_runtime_kit-0.4.0 → agent_runtime_kit-0.5.0}/examples/sdk_evolution_agent/schemas.py +0 -0
  66. {agent_runtime_kit-0.4.0 → agent_runtime_kit-0.5.0}/examples/sdk_evolution_agent/snapshots.py +0 -0
  67. {agent_runtime_kit-0.4.0 → agent_runtime_kit-0.5.0}/examples/sdk_evolution_agent/stages.py +0 -0
  68. {agent_runtime_kit-0.4.0 → agent_runtime_kit-0.5.0}/src/agent_runtime_kit/adapters/__init__.py +0 -0
  69. {agent_runtime_kit-0.4.0 → agent_runtime_kit-0.5.0}/src/agent_runtime_kit/events.py +0 -0
  70. {agent_runtime_kit-0.4.0 → agent_runtime_kit-0.5.0}/src/agent_runtime_kit/py.typed +0 -0
  71. {agent_runtime_kit-0.4.0 → agent_runtime_kit-0.5.0}/src/agent_runtime_kit/testing/__init__.py +0 -0
  72. {agent_runtime_kit-0.4.0 → agent_runtime_kit-0.5.0}/tests/test_optional_dependencies.py +0 -0
  73. {agent_runtime_kit-0.4.0 → agent_runtime_kit-0.5.0}/tests/test_provider_diagnostics.py +0 -0
  74. {agent_runtime_kit-0.4.0 → agent_runtime_kit-0.5.0}/tests/test_sdk_contract.py +0 -0
  75. {agent_runtime_kit-0.4.0 → agent_runtime_kit-0.5.0}/tests/test_sdk_evolution_agent.py +0 -0
@@ -5,6 +5,82 @@ All notable changes to this project are documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## 0.5.0 - 2026-07-11
9
+
10
+ ### Added
11
+
12
+ - Core JSON Schema validation via `jsonschema`, including construction-time
13
+ schema checks and `OutputSchemaError` for malformed definitions.
14
+ - `AgentResult.parsed_output_available` distinguishes a valid JSON `null` from
15
+ absent or rejected structured output.
16
+ - All built-in adapters and public fake runtimes now validate returned values
17
+ against `output_schema` locally, including textual JSON fallbacks.
18
+ - `AgentResult.is_success` provides one canonical success predicate.
19
+ - `TaskSupportReport`, `TaskSupportProvider`, and `validate_task()` provide
20
+ side-effect-free task preflight without adding a requirement to the
21
+ `AgentRuntime` protocol. Registry and `AgentKit` helpers preserve custom
22
+ runtime validation and capability-based fallback for older runtimes.
23
+ - `COMPATIBILITY_MANIFEST` records each built-in adapter's install extra,
24
+ import module, accepted SDK range, tested lockfile version, and runtime binary
25
+ versions such as `openai-codex-cli-bin`.
26
+ - `RuntimeReadiness`, `ReadinessStatus`, the optional
27
+ `RuntimeReadinessProvider`, and bounded async `check_readiness()` probes
28
+ distinguish `READY_TO_ATTEMPT`, `NOT_READY`, and `INDETERMINATE` without
29
+ running an agent task. Registry, `AgentKit`, and provider-diagnostics helpers
30
+ expose the same async checks.
31
+ - Absolute `AgentTask.deadline` values, `AgentKit.run(timeout=...)`, typed
32
+ `AgentTaskTimeoutError`, and `FinishReason.TIMED_OUT` provide bounded task
33
+ execution across provider startup and reused-process queueing.
34
+ - Built-in runtimes and `AgentKit.cancel()` now return immutable
35
+ `CancellationReceipt` values with explicit request dispositions; legacy
36
+ third-party `cancel()` methods returning `None` remain supported.
37
+
38
+ ### Changed
39
+
40
+ - BREAKING: built-in adapters no longer pin library-owned default model names.
41
+ Model selection now follows task field > legacy metadata > `default_model=`
42
+ constructor override > provider-native configuration. Provider-native calls
43
+ omit the model kwarg; results expose `model_source` and only report a model
44
+ value when known. Existing constructor overrides remain supported.
45
+ - Pydantic structured-output parsing now requests strict validation, so values
46
+ such as `"42"` no longer coerce into integer fields.
47
+ - BREAKING: task and value-object constructors now reject blank identities,
48
+ non-positive execution hints, invalid budgets, ambiguous session inputs,
49
+ duplicate MCP names, and conflicting tool filters.
50
+ - BREAKING: unknown `Usage` values, including cost, are `None` rather than zero;
51
+ an explicit provider-reported zero remains `0`/`0.0`.
52
+ - Capability declarations now distinguish budget, reasoning-effort, network,
53
+ tool-filter, and per-MCP-server environment support. Built-in `run()` methods
54
+ consume the same support report used by preflight, eliminating divergent
55
+ rejection paths.
56
+ - `availability()` is now strictly package-only and side-effect-free. Credential
57
+ and setup checks moved to async readiness probes: Codex uses the supported
58
+ account API with guaranteed cleanup, and Antigravity runs Google ADC discovery
59
+ off the event loop. Probe failures/timeouts are secret-safe and indeterminate.
60
+
61
+ ### Fixed
62
+
63
+ - `AgentKit.run(task=...)` now rejects every explicitly supplied per-field task
64
+ argument, including default-valued `sdk_executions=1` and empty tool lists,
65
+ instead of silently ignoring it.
66
+ - `ParsedResult[T]` clears an adapter's raw `parsed_output` when the adapter
67
+ reports failure, so the typed result surface never exposes unvalidated data.
68
+ - `AgentKit.aclose()` attempts to close every cached runtime before reraising
69
+ the first close error, preventing later resources from leaking.
70
+ - Permission- and budget-critical vendor options must now be explicit,
71
+ introspectable SDK parameters; opaque `**kwargs`, positional-only options,
72
+ and uninspectable callables fail closed.
73
+ - Configured model allow-lists, Antigravity MCP name syntax, disjoint
74
+ allow/deny lists, and Antigravity's legacy reasoning-effort alias are rejected
75
+ during static preflight instead of surfacing later or being silently ignored.
76
+ - A configured model allow-list now fails closed when model selection is
77
+ provider-native and therefore cannot be verified locally.
78
+ - Cancellation now binds to one active task generation, unregisters without a
79
+ cancellable lock window, emits at most one timeout/cancellation terminal, and
80
+ gives reusable provider-process teardown a bounded grace period under repeated
81
+ cancellation. Runtime instances stay quarantined from new work while detached
82
+ teardown remains pending.
83
+
8
84
  ## 0.4.0 - 2026-07-02
9
85
 
10
86
  ### Added
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: agent-runtime-kit
3
- Version: 0.4.0
3
+ Version: 0.5.0
4
4
  Summary: One typed runtime API for Claude, Codex, and Antigravity agent SDKs.
5
5
  Project-URL: Homepage, https://github.com/ebarti/agent-runtime-kit
6
6
  Project-URL: Repository, https://github.com/ebarti/agent-runtime-kit
@@ -18,6 +18,7 @@ Classifier: Programming Language :: Python :: 3.12
18
18
  Classifier: Programming Language :: Python :: 3.13
19
19
  Classifier: Typing :: Typed
20
20
  Requires-Python: >=3.10
21
+ Requires-Dist: jsonschema<5,>=4.18
21
22
  Provides-Extra: all
22
23
  Requires-Dist: claude-agent-sdk<0.3,>=0.2.87; extra == 'all'
23
24
  Requires-Dist: google-antigravity<0.2,>=0.1.2; extra == 'all'
@@ -46,7 +47,7 @@ capabilities visible.
46
47
  `agent-runtime-kit` is for Python developers who want to run coding-agent tasks
47
48
  through vendor runtimes without rewriting their application around each SDK. It
48
49
  normalizes the runtime boundary: task inputs, capability checks, event streams,
49
- tool audits, availability diagnostics, and typed results.
50
+ tool audits, package/readiness diagnostics, and typed results.
50
51
 
51
52
  The library keeps vendor differences visible. Claude, Codex, and Antigravity
52
53
  still expose different capabilities, permission models, setup requirements, and
@@ -55,7 +56,7 @@ shape instead of hiding them behind a lowest-common-denominator wrapper.
55
56
 
56
57
  The package is intentionally not a router, benchmark harness, queue, hosted
57
58
  service, or full agent framework. It is the reusable layer underneath those
58
- systems: task models, runtime capabilities, event sinks, availability
59
+ systems: task models, runtime capabilities, event sinks, package/readiness
59
60
  diagnostics, and adapters.
60
61
 
61
62
  ## Install
@@ -66,7 +67,7 @@ If you want all first-party runtimes available, install the `all` extra:
66
67
  pip install "agent-runtime-kit[all]"
67
68
  ```
68
69
 
69
- Install the dependency-free core when you only need the public models, fake
70
+ Install the vendor-SDK-free core when you only need the public models, fake
70
71
  runtime, registry, diagnostics types, or you plan to add provider SDKs later:
71
72
 
72
73
  ```bash
@@ -115,7 +116,7 @@ caches them per kind, and turns Python types into structured output.
115
116
  import asyncio
116
117
  from dataclasses import dataclass
117
118
 
118
- from agent_runtime_kit import AgentKit
119
+ from agent_runtime_kit import AgentKit, ReadinessStatus
119
120
 
120
121
 
121
122
  @dataclass
@@ -126,9 +127,9 @@ class RepoSummary:
126
127
 
127
128
  async def main() -> None:
128
129
  async with AgentKit() as kit:
129
- diagnostic = kit.availability_for("claude")
130
- if not diagnostic.available:
131
- raise RuntimeError(diagnostic.message)
130
+ readiness = await kit.readiness_for("claude")
131
+ if readiness.status is ReadinessStatus.NOT_READY:
132
+ raise RuntimeError(readiness.message)
132
133
  result = await kit.run(
133
134
  "claude",
134
135
  goal="Summarize this repository",
@@ -146,15 +147,15 @@ Adapters also work standalone when you need vendor-specific configuration:
146
147
  ```python
147
148
  import asyncio
148
149
 
149
- from agent_runtime_kit import AgentTask
150
+ from agent_runtime_kit import AgentTask, ReadinessStatus, check_readiness
150
151
  from agent_runtime_kit.adapters import ClaudeAgentRuntime
151
152
 
152
153
 
153
154
  async def main() -> None:
154
155
  runtime = ClaudeAgentRuntime(default_model="claude-sonnet-4-6")
155
- diagnostic = runtime.availability()
156
- if not diagnostic.available:
157
- raise RuntimeError(diagnostic.message)
156
+ readiness = await check_readiness(runtime)
157
+ if readiness.status is ReadinessStatus.NOT_READY:
158
+ raise RuntimeError(readiness.message)
158
159
  result = await runtime.run(AgentTask(goal="Summarize this repository"))
159
160
  print(result.output)
160
161
 
@@ -173,10 +174,35 @@ a field (for example only Claude maps `budget_usd`; Codex and Antigravity reject
173
174
  it with a typed `UnsupportedTaskInputError`) the adapter raises rather than
174
175
  silently dropping it.
175
176
 
176
- `AgentResult` returns output, finish reason (see `FinishReason`), parsed
177
- structured output, usage, cost, session id, tool-call audits, and provider
178
- metadata. `artifacts` is a reserved field: no built-in runtime populates it yet,
179
- so it is always an empty tuple today.
177
+ Model selection follows one explicit precedence chain: `AgentTask.model`, then
178
+ legacy `metadata["model"]`, then an adapter's `default_model=` constructor
179
+ override, then the provider's native configuration/default. Adapters omit the
180
+ vendor model option at the final step rather than pinning a library-owned model.
181
+ Results record `metadata["model_source"]`; `metadata["model"]` appears only when
182
+ the kit knows the selected value.
183
+
184
+ Call `validate_task(runtime, task)` (or `kit.validate_task("codex", task)`) to
185
+ inspect every statically detectable incompatibility before dispatch. The
186
+ returned `TaskSupportReport` is side-effect-free and lists source fields such as
187
+ `budget_usd` and `permissions.network`; `run()` still fails closed on the first
188
+ issue. Third-party runtimes can opt into provider-specific checks with the
189
+ `TaskSupportProvider` protocol, while older `AgentRuntime` implementations
190
+ continue to work through capability-based fallback checks.
191
+
192
+ `availability()` is deliberately synchronous, side-effect-free, and package-only.
193
+ Use `await check_readiness(runtime)` (or `kit.readiness_for(...)`) for an explicit,
194
+ bounded credential/setup probe. `READY_TO_ATTEMPT` means setup was positively
195
+ detected, not that a future provider call is guaranteed to succeed;
196
+ `INDETERMINATE` lets callers decide whether to attempt provider-chain or local
197
+ login authentication. Probe failures and timeouts never include credential values.
198
+
199
+ `AgentResult` returns output, finish reason (see `FinishReason`), locally
200
+ validated structured output, usage, cost, session id, tool-call audits, and
201
+ provider metadata. `is_success` is true only for a natural, error-free
202
+ completion. Unknown usage and cost fields are `None`; reported zero remains
203
+ distinct. `parsed_output_available` distinguishes an absent payload from valid
204
+ JSON `null` (both use `parsed_output=None`). `artifacts` is a reserved field: no
205
+ built-in runtime populates it yet, so it is always an empty tuple today.
180
206
 
181
207
  ## Docs
182
208
 
@@ -185,5 +211,5 @@ so it is always an empty tuple today.
185
211
  - [Capability matrix](https://github.com/ebarti/agent-runtime-kit/blob/main/docs/capability-matrix.md)
186
212
  - [API stability](https://github.com/ebarti/agent-runtime-kit/blob/main/docs/api-stability.md)
187
213
  - [Live smoke tests](https://github.com/ebarti/agent-runtime-kit/blob/main/docs/live-smoke.md)
188
- - [Mestre migration notes](https://github.com/ebarti/agent-runtime-kit/blob/main/docs/mestre-migration.md)
214
+ - [Deadlines and cancellation](https://github.com/ebarti/agent-runtime-kit/blob/main/docs/task-control.md)
189
215
  - [SDK evolution agent](https://github.com/ebarti/agent-runtime-kit/blob/main/docs/sdk-evolution-agent.md)
@@ -14,7 +14,7 @@ capabilities visible.
14
14
  `agent-runtime-kit` is for Python developers who want to run coding-agent tasks
15
15
  through vendor runtimes without rewriting their application around each SDK. It
16
16
  normalizes the runtime boundary: task inputs, capability checks, event streams,
17
- tool audits, availability diagnostics, and typed results.
17
+ tool audits, package/readiness diagnostics, and typed results.
18
18
 
19
19
  The library keeps vendor differences visible. Claude, Codex, and Antigravity
20
20
  still expose different capabilities, permission models, setup requirements, and
@@ -23,7 +23,7 @@ shape instead of hiding them behind a lowest-common-denominator wrapper.
23
23
 
24
24
  The package is intentionally not a router, benchmark harness, queue, hosted
25
25
  service, or full agent framework. It is the reusable layer underneath those
26
- systems: task models, runtime capabilities, event sinks, availability
26
+ systems: task models, runtime capabilities, event sinks, package/readiness
27
27
  diagnostics, and adapters.
28
28
 
29
29
  ## Install
@@ -34,7 +34,7 @@ If you want all first-party runtimes available, install the `all` extra:
34
34
  pip install "agent-runtime-kit[all]"
35
35
  ```
36
36
 
37
- Install the dependency-free core when you only need the public models, fake
37
+ Install the vendor-SDK-free core when you only need the public models, fake
38
38
  runtime, registry, diagnostics types, or you plan to add provider SDKs later:
39
39
 
40
40
  ```bash
@@ -83,7 +83,7 @@ caches them per kind, and turns Python types into structured output.
83
83
  import asyncio
84
84
  from dataclasses import dataclass
85
85
 
86
- from agent_runtime_kit import AgentKit
86
+ from agent_runtime_kit import AgentKit, ReadinessStatus
87
87
 
88
88
 
89
89
  @dataclass
@@ -94,9 +94,9 @@ class RepoSummary:
94
94
 
95
95
  async def main() -> None:
96
96
  async with AgentKit() as kit:
97
- diagnostic = kit.availability_for("claude")
98
- if not diagnostic.available:
99
- raise RuntimeError(diagnostic.message)
97
+ readiness = await kit.readiness_for("claude")
98
+ if readiness.status is ReadinessStatus.NOT_READY:
99
+ raise RuntimeError(readiness.message)
100
100
  result = await kit.run(
101
101
  "claude",
102
102
  goal="Summarize this repository",
@@ -114,15 +114,15 @@ Adapters also work standalone when you need vendor-specific configuration:
114
114
  ```python
115
115
  import asyncio
116
116
 
117
- from agent_runtime_kit import AgentTask
117
+ from agent_runtime_kit import AgentTask, ReadinessStatus, check_readiness
118
118
  from agent_runtime_kit.adapters import ClaudeAgentRuntime
119
119
 
120
120
 
121
121
  async def main() -> None:
122
122
  runtime = ClaudeAgentRuntime(default_model="claude-sonnet-4-6")
123
- diagnostic = runtime.availability()
124
- if not diagnostic.available:
125
- raise RuntimeError(diagnostic.message)
123
+ readiness = await check_readiness(runtime)
124
+ if readiness.status is ReadinessStatus.NOT_READY:
125
+ raise RuntimeError(readiness.message)
126
126
  result = await runtime.run(AgentTask(goal="Summarize this repository"))
127
127
  print(result.output)
128
128
 
@@ -141,10 +141,35 @@ a field (for example only Claude maps `budget_usd`; Codex and Antigravity reject
141
141
  it with a typed `UnsupportedTaskInputError`) the adapter raises rather than
142
142
  silently dropping it.
143
143
 
144
- `AgentResult` returns output, finish reason (see `FinishReason`), parsed
145
- structured output, usage, cost, session id, tool-call audits, and provider
146
- metadata. `artifacts` is a reserved field: no built-in runtime populates it yet,
147
- so it is always an empty tuple today.
144
+ Model selection follows one explicit precedence chain: `AgentTask.model`, then
145
+ legacy `metadata["model"]`, then an adapter's `default_model=` constructor
146
+ override, then the provider's native configuration/default. Adapters omit the
147
+ vendor model option at the final step rather than pinning a library-owned model.
148
+ Results record `metadata["model_source"]`; `metadata["model"]` appears only when
149
+ the kit knows the selected value.
150
+
151
+ Call `validate_task(runtime, task)` (or `kit.validate_task("codex", task)`) to
152
+ inspect every statically detectable incompatibility before dispatch. The
153
+ returned `TaskSupportReport` is side-effect-free and lists source fields such as
154
+ `budget_usd` and `permissions.network`; `run()` still fails closed on the first
155
+ issue. Third-party runtimes can opt into provider-specific checks with the
156
+ `TaskSupportProvider` protocol, while older `AgentRuntime` implementations
157
+ continue to work through capability-based fallback checks.
158
+
159
+ `availability()` is deliberately synchronous, side-effect-free, and package-only.
160
+ Use `await check_readiness(runtime)` (or `kit.readiness_for(...)`) for an explicit,
161
+ bounded credential/setup probe. `READY_TO_ATTEMPT` means setup was positively
162
+ detected, not that a future provider call is guaranteed to succeed;
163
+ `INDETERMINATE` lets callers decide whether to attempt provider-chain or local
164
+ login authentication. Probe failures and timeouts never include credential values.
165
+
166
+ `AgentResult` returns output, finish reason (see `FinishReason`), locally
167
+ validated structured output, usage, cost, session id, tool-call audits, and
168
+ provider metadata. `is_success` is true only for a natural, error-free
169
+ completion. Unknown usage and cost fields are `None`; reported zero remains
170
+ distinct. `parsed_output_available` distinguishes an absent payload from valid
171
+ JSON `null` (both use `parsed_output=None`). `artifacts` is a reserved field: no
172
+ built-in runtime populates it yet, so it is always an empty tuple today.
148
173
 
149
174
  ## Docs
150
175
 
@@ -153,5 +178,5 @@ so it is always an empty tuple today.
153
178
  - [Capability matrix](https://github.com/ebarti/agent-runtime-kit/blob/main/docs/capability-matrix.md)
154
179
  - [API stability](https://github.com/ebarti/agent-runtime-kit/blob/main/docs/api-stability.md)
155
180
  - [Live smoke tests](https://github.com/ebarti/agent-runtime-kit/blob/main/docs/live-smoke.md)
156
- - [Mestre migration notes](https://github.com/ebarti/agent-runtime-kit/blob/main/docs/mestre-migration.md)
181
+ - [Deadlines and cancellation](https://github.com/ebarti/agent-runtime-kit/blob/main/docs/task-control.md)
157
182
  - [SDK evolution agent](https://github.com/ebarti/agent-runtime-kit/blob/main/docs/sdk-evolution-agent.md)
@@ -23,6 +23,11 @@ import the names from the top-level package instead.
23
23
  party can ship an adapter for a new runtime without forking the enum.
24
24
  - **Every runtime exposes the async lifecycle** (`aclose`, `async with`) declared
25
25
  by the `AgentRuntime` protocol. Stateless runtimes implement it as a no-op.
26
+ - **Cancellation receipts describe requests, not rollback.** Built-in runtimes
27
+ return an immutable `CancellationReceipt`; the protocol still permits `None`
28
+ for existing third-party runtimes, which `AgentKit` reports as
29
+ `LEGACY_UNCONFIRMED`. `AgentTask.deadline` is keyword-only and an expired task
30
+ raises `AgentTaskTimeoutError` with `FinishReason.TIMED_OUT` event semantics.
26
31
  - **`finish_reason` values** come from `FinishReason`. The field is typed `str`
27
32
  for forward-compatibility, so new reasons can be added without a type break;
28
33
  compare against `FinishReason` members rather than bare literals.
@@ -33,10 +38,37 @@ import the names from the top-level package instead.
33
38
  convention and compare by value. The wrappers remain plain `dict` subclasses,
34
39
  so `dataclasses.asdict`, `pickle`, `copy.deepcopy`, and JSON serialization
35
40
  keep working.
41
+ - **Constructors enforce domain invariants.** Goals and identifiers are
42
+ non-blank; execution hints are positive; counts and monetary values are
43
+ finite and non-negative; session start and resume inputs are mutually
44
+ exclusive; MCP server names and tool filters are unambiguous.
45
+ - **Unknown telemetry is not zero.** Every `Usage` field is nullable. `None`
46
+ means the provider did not report a value; `0` and `0.0` mean it explicitly
47
+ reported zero.
36
48
  - **Unsupported inputs raise, they are not dropped.** An adapter that cannot honor
37
49
  a task field raises `UnsupportedTaskInputError`; the one exception is
38
50
  vendor-option drift, which is recorded in
39
51
  `AgentResult.metadata["dropped_options"]` instead.
52
+ - **Task support can be inspected without dispatch.** `validate_task(runtime,
53
+ task)`, `RuntimeRegistry.validate_task_for(...)`, and `AgentKit.validate_task(...)`
54
+ return every statically detectable incompatibility as a `TaskSupportReport`.
55
+ `TaskSupportProvider` is an optional extension, not a new requirement on the
56
+ `AgentRuntime` protocol, so existing third-party runtimes retain structural
57
+ compatibility and use capability-based fallback checks.
58
+ - **Package availability and execution readiness are separate.** Synchronous
59
+ `availability()` is package-only and side-effect-free. The bounded async
60
+ `check_readiness()` helper uses the optional `RuntimeReadinessProvider`
61
+ extension without changing the `AgentRuntime` protocol. A third-party runtime
62
+ without the extension maps a missing package to `NOT_READY` and present
63
+ package to `INDETERMINATE`. `READY_TO_ATTEMPT` establishes only that known
64
+ setup signals are present; it is not a guarantee of future execution.
65
+ - **Providers own the default model.** With no task, legacy metadata, or
66
+ `default_model=` override, built-in adapters omit the SDK model option and let
67
+ the provider's supported configuration select it. The effective precedence is
68
+ task field, metadata alias, constructor override, provider-native. Result
69
+ metadata always records `model_source` and records `model` only when known.
70
+ A configured `supported_models` allow-list requires an explicit/verifiable
71
+ selection and fails closed on provider-native selection.
40
72
  - **`AgentKit` is sugar, not a second API.** The hub assembles the same frozen
41
73
  `AgentTask` and returns the same `AgentResult` the runtimes produce
42
74
  (`ParsedResult` is a runtime-identical subclass adding only the typed
@@ -44,7 +76,14 @@ import the names from the top-level package instead.
44
76
  `KIND_ALIASES` mapping; exact kind strings always work. `output_type=`
45
77
  supports a bounded, documented subset of the typing system and raises
46
78
  `OutputTypeError` on anything outside it (Pydantic-style models are used
47
- through their own `model_json_schema`/`model_validate` when present).
79
+ through their own `model_json_schema`/`model_validate(strict=True)` when
80
+ present). Raw schemas are checked at construction and again at dispatch
81
+ because their nested values are only shallow-frozen. Every built-in adapter
82
+ validates returned structured values locally.
83
+ - **Structured-output presence is explicit.** `parsed_output_available` is true
84
+ for a validated payload, including JSON `null`. Existing non-null custom
85
+ runtime results opt in automatically; a custom runtime returning valid null
86
+ must set the flag explicitly.
48
87
 
49
88
  ## Vendor SDK version policy
50
89
 
@@ -58,6 +97,13 @@ it reaches installed users; a release above a cap is by design invisible to that
58
97
  lane until the cap is raised. A separate lane installs every direct dependency at
59
98
  its declared floor so a stale minimum cannot sit undetected in the metadata.
60
99
 
100
+ `COMPATIBILITY_MANIFEST` is the machine-readable form of that policy. Each entry
101
+ records the install extra, import module, accepted SDK range, exact lockfile
102
+ version exercised by the repository, and any separately versioned runtime binary
103
+ (currently `openai-codex-cli-bin`). The manifest is tested against both
104
+ `pyproject.toml` and `uv.lock`; it is evidence of the committed test baseline,
105
+ not a claim that every version in the accepted range was exhaustively tested.
106
+
61
107
  ## Deprecation
62
108
 
63
109
  When a public name is slated for removal it will be kept working for at least one
@@ -4,6 +4,7 @@
4
4
  |------------|------------------|------------------|------------------------|
5
5
  | Optional extra | `claude` | `codex` | `antigravity` |
6
6
  | Core import without extra | Yes | Yes | Yes |
7
+ | Model selection | Provider-native unless task/metadata/constructor overrides it | Provider-native unless task/metadata/constructor overrides it | Provider-native unless task/metadata/constructor overrides it |
7
8
  | Working directory | Yes | Yes | Yes |
8
9
  | Session resume | Yes | Yes | Yes |
9
10
  | Structured output | Native `output_format` when available | Native output schema / JSON parse fallback | Native response schema / JSON parse fallback |
@@ -11,15 +12,33 @@
11
12
  | Permission mapping | `permission_mode` | approval mode + sandbox | capabilities + policies |
12
13
  | Streaming output events | Yes — incremental `output.delta` while the SDK runs | No — a single `output.delta` at the end (non-streaming SDK) | Yes — from response chunks |
13
14
  | Tool audit events | Yes — streamed from message blocks | Yes — emitted from parsed `TurnResult` items after the turn | Yes — from tool chunks |
15
+ | Deadlines and cancellation | Absolute deadline + coroutine cancellation | Absolute deadline + coroutine cancellation | Absolute deadline + coroutine cancellation |
14
16
  | `vendor.turn` events | No | No | Yes — from thought/unknown chunks |
15
- | Missing package diagnostics | Yes (`AgentRuntimeUnavailableError`) | Yes (`AgentRuntimeUnavailableError`) | Yes (`AgentRuntimeUnavailableError`) |
16
- | Missing credential diagnostics | No — `availability()` reports available with an `auth_source` label; auth failures surface at `run()` | No — same as Claude (`auth_source` label; deferred) | Yes — `availability()` returns `MISSING_CREDENTIALS` when no API key / ADC-Vertex project is configured |
17
+ | Package availability | Sync, side-effect-free, package-only | Sync, side-effect-free, package-only | Sync, side-effect-free, package-only; never calls ADC |
18
+ | Async readiness | Direct API key/OAuth/Bedrock bearer signal → `READY_TO_ATTEMPT`; provider/local chains → `INDETERMINATE` | Supported `AsyncCodex.account(refresh_token=False)` probe; absent account → `NOT_READY` | API key or off-loop ADC/project probe; missing → `NOT_READY` |
19
+ | Probe failure/timeout | `INDETERMINATE` | `INDETERMINATE`, app-server always closed | `INDETERMINATE` |
17
20
  | Live smoke test | Opt-in | Opt-in | Opt-in |
18
21
 
19
22
  The matrix is intentionally not a lowest-common-denominator contract. Adapters
20
23
  reject unsupported inputs (see below) when silently dropping them would be
21
24
  misleading.
22
25
 
26
+ The public `AgentCapabilities` value also advertises the granular task controls
27
+ used by preflight: `budget`, `reasoning_effort`, `network_control`,
28
+ `tool_filters`, and `mcp_server_env`. Use `validate_task(runtime, task)` when a
29
+ concrete task is available; unlike a raw capability flag, its
30
+ `TaskSupportReport` preserves instance-specific rules such as a configured model
31
+ allow-list and reports all detectable issues at once.
32
+
33
+ For every provider, malformed schemas are rejected locally before dispatch and
34
+ returned structured values are validated locally after the SDK completes.
35
+ `parsed_output_available` distinguishes valid JSON `null` from no parsed value.
36
+
37
+ Deadlines cover provider startup and waits for a reused process. Cancellation
38
+ receipts acknowledge a request at the coroutine boundary; they do not imply
39
+ rollback of tool side effects that already completed. See
40
+ [Deadlines and cancellation](task-control.md).
41
+
23
42
  ## Permission mapping
24
43
 
25
44
  A single portable `PermissionMode` maps to each vendor's native controls. The
@@ -59,6 +78,13 @@ tool, uses the SDK's `disabled_tools` route).
59
78
  Each adapter raises `UnsupportedTaskInputError` for task fields it has no SDK
60
79
  surface to honor, rather than dropping them silently.
61
80
 
81
+ The same fields can be checked before dispatch through `validate_task`,
82
+ `RuntimeRegistry.validate_task_for`, or `AgentKit.validate_task`. Built-in
83
+ adapters additionally preflight configured model allow-lists. Antigravity also
84
+ checks its MCP server-name syntax and the SDK's prohibition on combining an
85
+ allow-list with a deny-list. SDK-version-dependent Antigravity tool vocabulary
86
+ is still validated at dispatch, after the installed SDK supplies its enum.
87
+
62
88
  | Field | Claude | Codex | Antigravity |
63
89
  |-------|--------|-------|-------------|
64
90
  | `budget_usd` | Mapped (`max_budget_usd`, fails closed under SDK drift) | Rejected | Rejected |
@@ -1,7 +1,8 @@
1
1
  # Provider Diagnostics
2
2
 
3
- `agent-runtime-kit` keeps provider setup checks explicit. Each runtime exposes
4
- `availability()` and returns a `RuntimeAvailability` value with:
3
+ `agent-runtime-kit` separates cheap package discovery from provider setup
4
+ probes. Each runtime's synchronous `availability()` is side-effect-free and
5
+ package-only. It returns a `RuntimeAvailability` value with:
5
6
 
6
7
  - runtime kind
7
8
  - availability flag
@@ -10,9 +11,68 @@
10
11
  - package name
11
12
  - installed version when discoverable
12
13
 
14
+ Availability never imports a vendor SDK, starts a subprocess, reads a
15
+ credential store, calls Google ADC, or contacts a provider. Package presence is
16
+ not a claim that execution will work.
17
+
18
+ Use the public `await check_readiness(runtime, timeout=5.0)` for an explicit
19
+ credential/setup probe. Registry and hub forms are also available:
20
+
21
+ ```python
22
+ readiness = await registry.readiness_for("codex")
23
+ readiness = await kit.readiness_for("codex")
24
+ all_readiness = await kit.readiness()
25
+ ```
26
+
27
+ `RuntimeReadiness.status` has three outcomes:
28
+
29
+ - `READY_TO_ATTEMPT`: a supported setup or credential signal was positively
30
+ established. It does not promise that a later model/network request succeeds.
31
+ - `NOT_READY`: a concrete problem such as a missing package, account, API key,
32
+ or ADC project was established.
33
+ - `INDETERMINATE`: the runtime uses a provider/local credential chain that
34
+ cannot be verified safely, lacks the optional readiness extension, or the
35
+ bounded probe failed/timed out. The caller chooses whether to attempt work.
36
+
37
+ The built-in probes never return credential values or raw exception messages.
38
+ They report only safe categories such as auth source and exception type. The
39
+ optional `RuntimeReadinessProvider` protocol does not change the required
40
+ `AgentRuntime` protocol. For older third-party runtimes,
41
+ `check_readiness()` maps negative availability to `NOT_READY` and positive
42
+ package presence conservatively to `INDETERMINATE`.
43
+
44
+ Provider behavior is deliberately specific:
45
+
46
+ - Claude treats direct API key, OAuth-token, and Bedrock bearer-token signals
47
+ as `READY_TO_ATTEMPT`. Provider chains and provider-owned local login remain
48
+ `INDETERMINATE`; probing them would require starting a real task or scraping
49
+ unsupported credential stores.
50
+ - Codex starts the supported `AsyncCodex` client, calls
51
+ `account(refresh_token=False)`, and always closes the app-server context. An
52
+ account is `READY_TO_ATTEMPT`, no account is `NOT_READY`, and startup/API/
53
+ cleanup failures are `INDETERMINATE`.
54
+ - Antigravity accepts an explicit or ambient Gemini API key, otherwise probes
55
+ Google Application Default Credentials and project setup in a worker thread
56
+ so synchronous Google discovery cannot block the event loop. Missing setup
57
+ is `NOT_READY`; errors and timeouts are `INDETERMINATE`.
58
+
59
+ For all built-ins at once, the existing sync collector stays package-only while
60
+ the async collector performs bounded readiness checks:
61
+
62
+ ```python
63
+ from agent_runtime_kit.adapters.diagnostics import (
64
+ collect_provider_diagnostics,
65
+ collect_provider_readiness,
66
+ )
67
+
68
+ packages = collect_provider_diagnostics()
69
+ readiness = await collect_provider_readiness(timeout=5.0)
70
+ ```
71
+
13
72
  ## Install Model
14
73
 
15
- The plain `agent-runtime-kit` package is the dependency-free core. Use
74
+ The plain `agent-runtime-kit` package is the vendor-SDK-free core and includes
75
+ lightweight JSON Schema validation. Use
16
76
  `agent-runtime-kit[all]` when you want Claude, Codex, and Antigravity adapters
17
77
  available in one install, or use a single extra such as
18
78
  `agent-runtime-kit[codex]` when your application only needs one runtime.
@@ -27,16 +87,33 @@ to the exact missing extra.
27
87
 
28
88
  All three adapters map the task's system prompt (Claude `system_prompt`, Codex
29
89
  `developer_instructions`, Antigravity `system_instructions`) and the `model`
30
- field (falling back to the `metadata` aliases of the same names).
90
+ field. Model precedence is `AgentTask.model` > legacy `metadata["model"]` > the
91
+ adapter's `default_model=` constructor override > provider-native selection. At
92
+ the final step the adapter omits the SDK model option; it does not impose a
93
+ library-owned model that can go stale. Results always include
94
+ `metadata["model_source"]` (`task`, `metadata`, `constructor`, or
95
+ `provider-native`) and include `metadata["model"]` only when the selected value
96
+ is known. When `supported_models=` is configured, provider-native selection is
97
+ rejected as unverifiable until the caller chooses an explicit model.
31
98
  `reasoning_effort` maps to the Claude and Codex `effort` options; Antigravity
32
99
  has no reasoning-effort control and rejects the first-class field with a typed
33
- error (its legacy `metadata["reasoning_effort"]` alias stays ignored, as it
34
- always has been). Effort values are passed through to the vendor SDK rather
100
+ error. Its legacy `metadata["reasoning_effort"]` alias is rejected too, rather
101
+ than being silently ignored. Effort values are passed through to the vendor SDK rather
35
102
  than validated by this library — each vendor defines its own accepted
36
103
  vocabulary (for example `claude-agent-sdk` 0.2.x accepts
37
104
  `low`/`medium`/`high`/`xhigh`/`max`), and an SDK too old to accept `effort` at
38
105
  all records the drop in `AgentResult.metadata["dropped_options"]`.
39
106
 
107
+ Usage fields are `None` when a provider omits its usage breakdown or cost.
108
+ Provider-reported zeros remain numeric zero, so callers can distinguish a free
109
+ or zero-token run from missing telemetry.
110
+
111
+ Every built-in adapter exposes a pure `validate_task()` extension. The public
112
+ `validate_task(runtime, task)`, registry, and `AgentKit` helpers call that richer
113
+ validator when present and otherwise fall back to declared capabilities for
114
+ older third-party runtimes. A report is a static preflight, not an availability
115
+ or credential probe; installed-SDK drift can still make dispatch fail closed.
116
+
40
117
  Claude uses the `claude-agent-sdk` package and maps working directory,
41
118
  permissions, filesystem access (a `READ_ONLY` filesystem forces `plan` mode),
42
119
  MCP servers, sessions, structured output, tool allow/deny lists, runtime
@@ -6,7 +6,7 @@ Install all first-party runtime adapters:
6
6
  pip install "agent-runtime-kit[all]"
7
7
  ```
8
8
 
9
- Or install only the dependency-free core:
9
+ Or install only the vendor-SDK-free core:
10
10
 
11
11
  ```bash
12
12
  pip install agent-runtime-kit
@@ -51,9 +51,9 @@ asyncio.run(main())
51
51
 
52
52
  ## Typed structured output
53
53
 
54
- Pass a type instead of hand-writing JSON schema. Dataclasses and `TypedDict`s
55
- work out of the box (no dependency); Pydantic v2 models are used through their
56
- own `model_json_schema`/`model_validate` when you have Pydantic installed.
54
+ Pass a type instead of hand-writing JSON Schema. Dataclasses and `TypedDict`s
55
+ work without Pydantic; Pydantic v2 models are used through their own
56
+ `model_json_schema`/`model_validate` methods and validated with `strict=True`.
57
57
 
58
58
  ```python
59
59
  from dataclasses import dataclass
@@ -75,17 +75,21 @@ async def main() -> None:
75
75
  goal="Summarize this repository",
76
76
  output_type=RepoSummary,
77
77
  )
78
- if result.parsed is not None:
78
+ if result.parsed_output_available:
79
79
  print(result.parsed.languages) # typed: list[str]
80
80
  else:
81
81
  print(result.error)
82
82
  ```
83
83
 
84
- A payload that does not conform yields `finish_reason="failed"` with the
85
- mismatch in `result.error` — the same convention the adapters use for
86
- unsatisfied structured output. Unsupported annotations (sets, plain unions,
87
- ...) raise `OutputTypeError` at call time rather than sending a half-true
88
- schema.
84
+ A malformed raw `output_schema` raises `OutputSchemaError` before execution.
85
+ `AgentKit` strictly validates values requested with `output_type`; a mismatch
86
+ yields `finish_reason="failed"` with the details in `result.error`.
87
+ All built-in adapters also validate results requested with a raw
88
+ `output_schema` and include the failing JSON path in `result.error`.
89
+ Unsupported Python annotations (sets, plain unions, ...) raise
90
+ `OutputTypeError` at call time. For nullable schemas,
91
+ `parsed_output_available=True` with `parsed_output=None` represents valid JSON
92
+ `null`.
89
93
 
90
94
  ## Events and custom runtimes
91
95
 
@@ -118,5 +122,10 @@ runtime = ClaudeAgentRuntime(default_model="claude-sonnet-4-6", reuse_process=Tr
118
122
  result = await runtime.run(AgentTask(goal="Summarize this repository"))
119
123
  ```
120
124
 
121
- Use `kit.availability()` (or `runtime.availability()`) before dispatching work
122
- in applications that need clear setup diagnostics.
125
+ Use synchronous `kit.availability()` (or `runtime.availability()`) for a
126
+ side-effect-free package check. Use `await kit.readiness()` or
127
+ `await check_readiness(runtime)` when an application also needs a bounded
128
+ credential/setup probe. Treat `ReadinessStatus.READY_TO_ATTEMPT` as permission
129
+ to try — not a guarantee that the provider, model, or network will accept the
130
+ next request. `NOT_READY` is a confirmed setup problem; `INDETERMINATE` leaves
131
+ the attempt policy to the caller.