agent-runtime-kit 0.5.1__tar.gz → 0.5.2__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (72) hide show
  1. {agent_runtime_kit-0.5.1 → agent_runtime_kit-0.5.2}/CHANGELOG.md +26 -0
  2. {agent_runtime_kit-0.5.1 → agent_runtime_kit-0.5.2}/PKG-INFO +3 -3
  3. {agent_runtime_kit-0.5.1 → agent_runtime_kit-0.5.2}/docs/providers.md +24 -8
  4. {agent_runtime_kit-0.5.1 → agent_runtime_kit-0.5.2}/docs/sdk-evolution-agent.md +32 -2
  5. {agent_runtime_kit-0.5.1 → agent_runtime_kit-0.5.2}/examples/sdk_evolution_agent/behavior.py +108 -18
  6. {agent_runtime_kit-0.5.1 → agent_runtime_kit-0.5.2}/examples/sdk_evolution_agent/cli.py +31 -10
  7. {agent_runtime_kit-0.5.1 → agent_runtime_kit-0.5.2}/examples/sdk_evolution_agent/collectors.py +1 -1
  8. {agent_runtime_kit-0.5.1 → agent_runtime_kit-0.5.2}/examples/sdk_evolution_agent/models.py +5 -0
  9. {agent_runtime_kit-0.5.1 → agent_runtime_kit-0.5.2}/examples/sdk_evolution_agent/release_notes.py +21 -8
  10. {agent_runtime_kit-0.5.1 → agent_runtime_kit-0.5.2}/examples/sdk_evolution_agent/stages.py +23 -7
  11. {agent_runtime_kit-0.5.1 → agent_runtime_kit-0.5.2}/pyproject.toml +3 -3
  12. {agent_runtime_kit-0.5.1 → agent_runtime_kit-0.5.2}/src/agent_runtime_kit/adapters/antigravity.py +65 -13
  13. {agent_runtime_kit-0.5.1 → agent_runtime_kit-0.5.2}/src/agent_runtime_kit/compatibility.py +5 -5
  14. {agent_runtime_kit-0.5.1 → agent_runtime_kit-0.5.2}/tests/test_antigravity_adapter.py +131 -8
  15. {agent_runtime_kit-0.5.1 → agent_runtime_kit-0.5.2}/tests/test_sdk_contract.py +113 -2
  16. {agent_runtime_kit-0.5.1 → agent_runtime_kit-0.5.2}/tests/test_sdk_evolution_agent.py +196 -4
  17. {agent_runtime_kit-0.5.1 → agent_runtime_kit-0.5.2}/.gitignore +0 -0
  18. {agent_runtime_kit-0.5.1 → agent_runtime_kit-0.5.2}/LICENSE +0 -0
  19. {agent_runtime_kit-0.5.1 → agent_runtime_kit-0.5.2}/README.md +0 -0
  20. {agent_runtime_kit-0.5.1 → agent_runtime_kit-0.5.2}/docs/api-stability.md +0 -0
  21. {agent_runtime_kit-0.5.1 → agent_runtime_kit-0.5.2}/docs/capability-matrix.md +0 -0
  22. {agent_runtime_kit-0.5.1 → agent_runtime_kit-0.5.2}/docs/live-smoke.md +0 -0
  23. {agent_runtime_kit-0.5.1 → agent_runtime_kit-0.5.2}/docs/quickstart.md +0 -0
  24. {agent_runtime_kit-0.5.1 → agent_runtime_kit-0.5.2}/docs/sdk-evolution-agent-design.md +0 -0
  25. {agent_runtime_kit-0.5.1 → agent_runtime_kit-0.5.2}/docs/task-control.md +0 -0
  26. {agent_runtime_kit-0.5.1 → agent_runtime_kit-0.5.2}/examples/__init__.py +0 -0
  27. {agent_runtime_kit-0.5.1 → agent_runtime_kit-0.5.2}/examples/run_same_task.py +0 -0
  28. {agent_runtime_kit-0.5.1 → agent_runtime_kit-0.5.2}/examples/sdk_evolution_agent/__init__.py +0 -0
  29. {agent_runtime_kit-0.5.1 → agent_runtime_kit-0.5.2}/examples/sdk_evolution_agent/__main__.py +0 -0
  30. {agent_runtime_kit-0.5.1 → agent_runtime_kit-0.5.2}/examples/sdk_evolution_agent/auth.py +0 -0
  31. {agent_runtime_kit-0.5.1 → agent_runtime_kit-0.5.2}/examples/sdk_evolution_agent/current_state.py +0 -0
  32. {agent_runtime_kit-0.5.1 → agent_runtime_kit-0.5.2}/examples/sdk_evolution_agent/events.py +0 -0
  33. {agent_runtime_kit-0.5.1 → agent_runtime_kit-0.5.2}/examples/sdk_evolution_agent/inspection.py +0 -0
  34. {agent_runtime_kit-0.5.1 → agent_runtime_kit-0.5.2}/examples/sdk_evolution_agent/pr.py +0 -0
  35. {agent_runtime_kit-0.5.1 → agent_runtime_kit-0.5.2}/examples/sdk_evolution_agent/report.py +0 -0
  36. {agent_runtime_kit-0.5.1 → agent_runtime_kit-0.5.2}/examples/sdk_evolution_agent/schemas.py +0 -0
  37. {agent_runtime_kit-0.5.1 → agent_runtime_kit-0.5.2}/examples/sdk_evolution_agent/snapshots.py +0 -0
  38. {agent_runtime_kit-0.5.1 → agent_runtime_kit-0.5.2}/src/agent_runtime_kit/__init__.py +0 -0
  39. {agent_runtime_kit-0.5.1 → agent_runtime_kit-0.5.2}/src/agent_runtime_kit/_control.py +0 -0
  40. {agent_runtime_kit-0.5.1 → agent_runtime_kit-0.5.2}/src/agent_runtime_kit/_errors.py +0 -0
  41. {agent_runtime_kit-0.5.1 → agent_runtime_kit-0.5.2}/src/agent_runtime_kit/_kit.py +0 -0
  42. {agent_runtime_kit-0.5.1 → agent_runtime_kit-0.5.2}/src/agent_runtime_kit/_runtime.py +0 -0
  43. {agent_runtime_kit-0.5.1 → agent_runtime_kit-0.5.2}/src/agent_runtime_kit/_schema.py +0 -0
  44. {agent_runtime_kit-0.5.1 → agent_runtime_kit-0.5.2}/src/agent_runtime_kit/_types.py +0 -0
  45. {agent_runtime_kit-0.5.1 → agent_runtime_kit-0.5.2}/src/agent_runtime_kit/adapters/__init__.py +0 -0
  46. {agent_runtime_kit-0.5.1 → agent_runtime_kit-0.5.2}/src/agent_runtime_kit/adapters/_common.py +0 -0
  47. {agent_runtime_kit-0.5.1 → agent_runtime_kit-0.5.2}/src/agent_runtime_kit/adapters/claude.py +0 -0
  48. {agent_runtime_kit-0.5.1 → agent_runtime_kit-0.5.2}/src/agent_runtime_kit/adapters/codex.py +0 -0
  49. {agent_runtime_kit-0.5.1 → agent_runtime_kit-0.5.2}/src/agent_runtime_kit/adapters/diagnostics.py +0 -0
  50. {agent_runtime_kit-0.5.1 → agent_runtime_kit-0.5.2}/src/agent_runtime_kit/events.py +0 -0
  51. {agent_runtime_kit-0.5.1 → agent_runtime_kit-0.5.2}/src/agent_runtime_kit/py.typed +0 -0
  52. {agent_runtime_kit-0.5.1 → agent_runtime_kit-0.5.2}/src/agent_runtime_kit/readiness.py +0 -0
  53. {agent_runtime_kit-0.5.1 → agent_runtime_kit-0.5.2}/src/agent_runtime_kit/registry.py +0 -0
  54. {agent_runtime_kit-0.5.1 → agent_runtime_kit-0.5.2}/src/agent_runtime_kit/support.py +0 -0
  55. {agent_runtime_kit-0.5.1 → agent_runtime_kit-0.5.2}/src/agent_runtime_kit/testing/__init__.py +0 -0
  56. {agent_runtime_kit-0.5.1 → agent_runtime_kit-0.5.2}/src/agent_runtime_kit/testing/fakes.py +0 -0
  57. {agent_runtime_kit-0.5.1 → agent_runtime_kit-0.5.2}/tests/test_adapter_common.py +0 -0
  58. {agent_runtime_kit-0.5.1 → agent_runtime_kit-0.5.2}/tests/test_adapter_protocol.py +0 -0
  59. {agent_runtime_kit-0.5.1 → agent_runtime_kit-0.5.2}/tests/test_claude_adapter.py +0 -0
  60. {agent_runtime_kit-0.5.1 → agent_runtime_kit-0.5.2}/tests/test_codex_adapter.py +0 -0
  61. {agent_runtime_kit-0.5.1 → agent_runtime_kit-0.5.2}/tests/test_control.py +0 -0
  62. {agent_runtime_kit-0.5.1 → agent_runtime_kit-0.5.2}/tests/test_core.py +0 -0
  63. {agent_runtime_kit-0.5.1 → agent_runtime_kit-0.5.2}/tests/test_events.py +0 -0
  64. {agent_runtime_kit-0.5.1 → agent_runtime_kit-0.5.2}/tests/test_kit.py +0 -0
  65. {agent_runtime_kit-0.5.1 → agent_runtime_kit-0.5.2}/tests/test_live_smoke.py +0 -0
  66. {agent_runtime_kit-0.5.1 → agent_runtime_kit-0.5.2}/tests/test_mestre_compatibility.py +0 -0
  67. {agent_runtime_kit-0.5.1 → agent_runtime_kit-0.5.2}/tests/test_optional_dependencies.py +0 -0
  68. {agent_runtime_kit-0.5.1 → agent_runtime_kit-0.5.2}/tests/test_provider_diagnostics.py +0 -0
  69. {agent_runtime_kit-0.5.1 → agent_runtime_kit-0.5.2}/tests/test_readiness.py +0 -0
  70. {agent_runtime_kit-0.5.1 → agent_runtime_kit-0.5.2}/tests/test_schema.py +0 -0
  71. {agent_runtime_kit-0.5.1 → agent_runtime_kit-0.5.2}/tests/test_sdk_evolution_docs.py +0 -0
  72. {agent_runtime_kit-0.5.1 → agent_runtime_kit-0.5.2}/tests/test_support.py +0 -0
@@ -5,6 +5,32 @@ 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.2 - 2026-09-19
9
+
10
+ ### Changed
11
+
12
+ - Updated tested Claude Agent SDK to `0.2.157`, Google Antigravity SDK to
13
+ `0.1.17`, and the Codex SDK and its exactly coupled CLI to `0.154.0`.
14
+ - SDK evolution supports explicit model, reasoning effort and analysis executable
15
+ options, preserves older locked baselines in implementation history, and
16
+ constructs real Claude options and Codex wire parameters in candidate probes.
17
+ - Upgrade skill invocations now include necessary runtime adaptations,
18
+ verification, and a regular pull request. Explicit report-only and local-only
19
+ requests retain their narrower scope.
20
+
21
+ ### Fixed
22
+
23
+ - Antigravity permissive deny-lists preserve all unrelated tools when the SDK's
24
+ default tool set changes, including the `ask_question` change in 0.1.17.
25
+ - Codex release evidence uses the current official documentation endpoints.
26
+ - Antigravity preserves explicit `vertex=True` with an API key for Vertex
27
+ Express authentication, rejects mixed regional credentials, and refuses older
28
+ SDKs or configurations that would silently drop the requested endpoint.
29
+ - Release-note collection recognizes versions present only in a GitHub
30
+ discussion title and rejects unrelated or longer version-prefix matches.
31
+ - PyPI publication validates and uploads Core Metadata 2.5 distributions with
32
+ matching Twine support.
33
+
8
34
  ## 0.5.1 - 2026-08-29
9
35
 
10
36
  ### Added
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: agent-runtime-kit
3
- Version: 0.5.1
3
+ Version: 0.5.2
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
@@ -22,13 +22,13 @@ Requires-Dist: jsonschema<5,>=4.18
22
22
  Provides-Extra: all
23
23
  Requires-Dist: claude-agent-sdk<0.3,>=0.2.87; extra == 'all'
24
24
  Requires-Dist: google-antigravity<0.2,>=0.1.2; extra == 'all'
25
- Requires-Dist: openai-codex<0.148,>=0.1.0b3; extra == 'all'
25
+ Requires-Dist: openai-codex<0.155,>=0.1.0b3; extra == 'all'
26
26
  Provides-Extra: antigravity
27
27
  Requires-Dist: google-antigravity<0.2,>=0.1.2; extra == 'antigravity'
28
28
  Provides-Extra: claude
29
29
  Requires-Dist: claude-agent-sdk<0.3,>=0.2.87; extra == 'claude'
30
30
  Provides-Extra: codex
31
- Requires-Dist: openai-codex<0.148,>=0.1.0b3; extra == 'codex'
31
+ Requires-Dist: openai-codex<0.155,>=0.1.0b3; extra == 'codex'
32
32
  Description-Content-Type: text/markdown
33
33
 
34
34
  # agent-runtime-kit
@@ -188,16 +188,32 @@ constructor, `GEMINI_API_KEY`, or `GOOGLE_API_KEY`. Without an API key, the
188
188
  adapter uses Vertex AI config with Google ADC when a project can be discovered
189
189
  from ADC, `GOOGLE_CLOUD_PROJECT`, or `GCLOUD_PROJECT`; location defaults to
190
190
  `global` unless `GOOGLE_CLOUD_LOCATION`, `GOOGLE_CLOUD_REGION`, or
191
- `CLOUD_ML_REGION` is set. Precedence: an explicit constructor `api_key` is the
192
- most specific request and wins; otherwise an explicit
191
+ `CLOUD_ML_REGION` is set. An explicit constructor `api_key` takes precedence over
192
+ ambient credentials. With `vertex=True`, that key selects Vertex Express mode
193
+ (supported by released `google-antigravity>=0.1.16` versions): no ADC, project,
194
+ or location is required. Older or unverifiable SDK releases raise
195
+ `UnsupportedTaskInputError` for Express mode; their existing authentication
196
+ modes remain available. Explicit project/location arguments cannot be combined
197
+ with Express mode. For example:
198
+
199
+ ```python
200
+ runtime = AntigravityAgentRuntime(vertex=True, api_key=vertex_express_key)
201
+ ```
202
+
203
+ Without an explicit key, an explicit
193
204
  `AntigravityAgentRuntime(vertex=True, ...)` takes precedence over an ambient
194
205
  `GEMINI_API_KEY`/`GOOGLE_API_KEY`, so a Vertex-configured runtime is never
195
- silently redirected to the Gemini API by an exported environment key. Under
196
- `PERMISSIVE`, `disallowed_tools` maps to `CapabilitiesConfig.disabled_tools`
197
- (the baseline is every tool, so "enable everything else" is exact); under any
198
- other posture the deny-list is folded into an allow-list of the mode's baseline
199
- minus the denied tools, so denying one tool can never re-enable others past the
200
- baseline. An allow-list and a deny-list are mutually exclusive (the SDK forbids
206
+ silently redirected to the Gemini API by an exported environment key. When no
207
+ model is supplied, the adapter preserves the vendor default (`gemini-3.8-flash`
208
+ in Antigravity 0.1.16); supply `default_model` or `AgentTask.model` when a stable
209
+ model selection is required. Under
210
+ `PERMISSIVE`, `disallowed_tools` becomes an explicit `enabled_tools` list of all
211
+ built-in tools minus the denied tools. Antigravity 0.1.17 changed its implicit
212
+ deny-list baseline to exclude `ask_question`; using an explicit list preserves
213
+ the adapter's existing all-tools contract. Under other postures the deny-list
214
+ is subtracted from the mode's narrower baseline, so denying one tool can never
215
+ re-enable others past that baseline. An allow-list and a deny-list are mutually
216
+ exclusive (the SDK forbids
201
217
  combining enabled and disabled tool lists), so supplying both is rejected. Tool
202
218
  names are validated against the
203
219
  `BuiltinTools` enum (`"view_file"`, not `"Read"`). `budget_usd` and
@@ -4,6 +4,15 @@ The SDK evolution agent is a local dogfood workflow for keeping
4
4
  agent-runtime-kit aligned with Claude Agent SDK, OpenAI Codex SDK, and Google
5
5
  Antigravity SDK as those upstream packages evolve.
6
6
 
7
+ For a complete upgrade, invoke the repository's
8
+ [`agent-runtime-kit-upgrade` skill](../.codex/skills/agent-runtime-kit-upgrade/SKILL.md).
9
+ It collects evidence first, maps upstream implementation trends to the current
10
+ integration, implements the necessary adapter/runtime changes with tests and
11
+ docs, applies the compatible dependency update, and creates a verified regular
12
+ pull request. Explicit report-only and local-only requests retain those limits.
13
+ The CLI examples below describe the underlying runner; its report-only default
14
+ and dependency-only implementation lane are stages within that broader skill.
15
+
7
16
  For the intended architecture, evidence contract, behavior probe strategy,
8
17
  changelog strategy, caveats, and alternatives, see
9
18
  [`docs/sdk-evolution-agent-design.md`](sdk-evolution-agent-design.md).
@@ -57,13 +66,26 @@ env -u UV_EXCLUDE_NEWER \
57
66
  uv run --locked --extra codex python -m examples.sdk_evolution_agent.auth ensure-codex
58
67
  ```
59
68
 
60
- Codex-backed SDK evolution runs explicitly choose `gpt-5.5` with
69
+ Codex-backed SDK evolution runs default to `gpt-5.5` with
61
70
  `reasoning_effort=xhigh` for the AI stages that analyze direction, decide the
62
71
  update plan, and review the result. Dependency application and verification are
63
72
  deterministic. This model policy
64
73
  is applied only to `codex-agent-sdk`; Claude and Antigravity runs keep their
65
74
  provider-native model selection because `gpt-5.5` is not a valid model override
66
75
  for those adapters.
76
+ Use `--model` and `--reasoning-effort` to select the requested model explicitly
77
+ for every AI stage. For example, use `--model gpt-6-astra --reasoning-effort xhigh`.
78
+ These options populate the first-class `AgentTask` fields and are recorded in
79
+ `config.json`; unsupported provider values remain provider errors.
80
+
81
+ If the locked Codex CLI cannot run the requested analysis model, the optional
82
+ `--codex-bin /absolute/path/to/codex` selects an installed executable through
83
+ the SDK's supported `CodexConfig.codex_bin`. It applies only to the analysis
84
+ driver and is recorded separately in `config.json`. Exact baseline/candidate
85
+ inspection and the SDK/CLI dependency coupling still use the published package
86
+ versions. Omit this override for the required post-update run against the final
87
+ locked SDK and bundled CLI. The override requires `--runtime codex-agent-sdk`
88
+ and cannot be combined with an injected runtime or registry.
67
89
  Vendor runtimes also enable `reuse_process=True` for the multi-stage run, so
68
90
  compatible SDK subprocesses can stay warm across analysis/review stages and are
69
91
  closed by the CLI when the internally owned runtime exits. Codex reuses its
@@ -148,7 +170,15 @@ failures are retried. Candidate inspection writes the exact compatibility
148
170
  transition to `api_diffs.json` and executes the adapter contract against that
149
171
  same version. Independently, recent-release inspection fingerprints shipped
150
172
  files and Python AST definitions, compares adjacent releases, and writes the
151
- result to `implementation_diffs.json`. Source is never copied into the report;
173
+ result to `implementation_diffs.json`. History also includes the exact locked
174
+ baseline and candidate when they fall outside the latest three releases, so
175
+ the changes between the installed baseline and the recent releases remain
176
+ visible. Claude probes construct permission, budget, session, MCP and schema
177
+ options; Codex probes construct configuration and validated thread/turn wire
178
+ parameters, including permissions, reasoning effort and structured output.
179
+ These checks complement real-adapter regression tests and live runtime checks;
180
+ they do not establish behavior inside opaque executables.
181
+ Source is never copied into the report;
152
182
  only paths, counts, sizes, and hashes are retained. Candidate inspection is
153
183
  opt-in because it executes freshly downloaded upstream code; historical
154
184
  inspection uses the same explicit consent. Without the flag, missing trend
@@ -868,18 +868,26 @@ def _probe_claude(*, version: str | None, scope: str) -> BehaviorProbeResult:
868
868
  "output_format",
869
869
  }
870
870
  missing = sorted(expected - fields)
871
+ cases, failures = _configuration_contract_cases(package)
872
+ failed = bool(missing or failures)
871
873
  return BehaviorProbeResult(
872
874
  package=package,
873
875
  version=version,
874
876
  scope=scope,
875
877
  probe="adapter-contract",
876
- status="fail" if missing else "pass",
878
+ status="fail" if failed else "pass",
877
879
  summary=(
878
- "ClaudeAgentOptions exposes required adapter fields."
879
- if not missing
880
- else "ClaudeAgentOptions is missing required adapter fields."
880
+ "ClaudeAgentOptions exposes and constructs required adapter options."
881
+ if not failed
882
+ else "ClaudeAgentOptions rejected the required adapter contract."
881
883
  ),
882
- details={"fields": sorted(fields), "required_fields": sorted(expected), "missing": missing},
884
+ details={
885
+ "fields": sorted(fields),
886
+ "required_fields": sorted(expected),
887
+ "missing": missing,
888
+ "construction_cases": cases,
889
+ "construction_failures": failures,
890
+ },
883
891
  )
884
892
 
885
893
 
@@ -896,16 +904,18 @@ def _probe_codex(*, version: str | None, scope: str) -> BehaviorProbeResult:
896
904
  missing_run = sorted(expected_run - run_params)
897
905
  missing_start = sorted(expected_start - start_params)
898
906
  missing = missing_run + [f"thread_start.{item}" for item in missing_start]
907
+ cases, failures = _configuration_contract_cases(package)
908
+ failed = bool(missing or failures)
899
909
  return BehaviorProbeResult(
900
910
  package=package,
901
911
  version=version,
902
912
  scope=scope,
903
913
  probe="adapter-contract",
904
- status="fail" if missing else "pass",
914
+ status="fail" if failed else "pass",
905
915
  summary=(
906
- "Codex thread APIs expose required adapter parameters."
907
- if not missing
908
- else "Codex thread APIs are missing required adapter parameters."
916
+ "Codex configuration and wire parameters construct the required adapter contract."
917
+ if not failed
918
+ else "Codex configuration or thread APIs rejected the required adapter contract."
909
919
  ),
910
920
  details={
911
921
  "run_params": sorted(run_params),
@@ -913,10 +923,76 @@ def _probe_codex(*, version: str | None, scope: str) -> BehaviorProbeResult:
913
923
  "required_run_params": sorted(expected_run),
914
924
  "required_start_params": sorted(expected_start),
915
925
  "missing": missing,
926
+ "construction_cases": cases,
927
+ "construction_failures": failures,
916
928
  },
917
929
  )
918
930
 
919
931
 
932
+ def _configuration_contract_cases(package: str) -> tuple[list[str], list[str]]:
933
+ """Construct real provider values without starting clients or reading auth.
934
+
935
+ This self-contained function is also embedded in isolated candidate probes.
936
+ """
937
+ import importlib
938
+
939
+ cases: list[str] = []
940
+ failures: list[str] = []
941
+ try:
942
+ if package == "claude-agent-sdk":
943
+ cases.append("claude-options")
944
+ sdk = importlib.import_module("claude_agent_sdk")
945
+ options = sdk.ClaudeAgentOptions(
946
+ permission_mode="plan",
947
+ allowed_tools=["Read"],
948
+ disallowed_tools=["Bash"],
949
+ system_prompt="Inspect only",
950
+ cwd=".",
951
+ resume="test-session",
952
+ max_budget_usd=1.0,
953
+ env={},
954
+ mcp_servers={"files": {"type": "stdio", "command": "test-mcp", "args": []}},
955
+ output_format={"type": "json_schema", "schema": {"type": "object"}},
956
+ )
957
+ if options.permission_mode != "plan" or options.max_budget_usd != 1.0:
958
+ raise ValueError("permission or budget was not preserved")
959
+ elif package == "openai-codex":
960
+ sdk = importlib.import_module("openai_codex")
961
+ wire = importlib.import_module("openai_codex.generated.v2_all")
962
+ cases.append("codex-config")
963
+ config = sdk.CodexConfig(cwd=".", config_overrides=("features.plugins=false",), env={})
964
+ if config.cwd != ".":
965
+ raise ValueError("working directory was not preserved")
966
+ cases.append("codex-thread-permissions")
967
+ thread = wire.ThreadStartParams(
968
+ cwd=".",
969
+ developer_instructions="Inspect only",
970
+ model="requested-model",
971
+ approval_policy="never",
972
+ sandbox="read-only",
973
+ )
974
+ if thread.model_dump(mode="json", by_alias=True)["sandbox"] != "read-only":
975
+ raise ValueError("thread sandbox was not preserved")
976
+ cases.append("codex-turn-schema-and-effort")
977
+ turn = wire.TurnStartParams(
978
+ thread_id="test-thread",
979
+ input=[],
980
+ cwd=".",
981
+ model="requested-model",
982
+ approval_policy="never",
983
+ sandbox_policy={"type": "readOnly"},
984
+ effort="xhigh",
985
+ output_schema={"type": "object"},
986
+ )
987
+ values = turn.model_dump(mode="json", by_alias=True)
988
+ if values["effort"] != "xhigh" or values["outputSchema"] != {"type": "object"}:
989
+ raise ValueError("reasoning effort or schema was not preserved")
990
+ except Exception as exc:
991
+ # These values are fixed fixtures, but keep diagnostics free of local paths.
992
+ failures.append(f"{cases[-1] if cases else package}:{type(exc).__name__}")
993
+ return cases, failures
994
+
995
+
920
996
  def _probe_codex_cli_bin(*, version: str | None, scope: str) -> BehaviorProbeResult:
921
997
  package = "openai-codex-cli-bin"
922
998
  module_name = DEFAULT_MODULES[package]
@@ -1206,8 +1282,11 @@ def _string_or_none(value: object) -> str | None:
1206
1282
  return text or None
1207
1283
 
1208
1284
 
1209
- _PROBE_SCRIPT = textwrap.dedent(
1210
- r"""
1285
+ _PROBE_SCRIPT = (
1286
+ inspect.getsource(_configuration_contract_cases)
1287
+ + "\n"
1288
+ + textwrap.dedent(
1289
+ r"""
1211
1290
  import importlib
1212
1291
  import importlib.metadata
1213
1292
  import inspect
@@ -1280,15 +1359,20 @@ _PROBE_SCRIPT = textwrap.dedent(
1280
1359
  "max_budget_usd", "output_format",
1281
1360
  }
1282
1361
  missing = sorted(expected - option_fields)
1362
+ cases, failures = _configuration_contract_cases(package)
1363
+ contract_failed = bool(missing or failures)
1283
1364
  payload = [result(
1284
1365
  "adapter-contract",
1285
- "fail" if missing else "pass",
1286
- "ClaudeAgentOptions exposes required adapter fields." if not missing
1287
- else "ClaudeAgentOptions is missing required adapter fields.",
1366
+ "fail" if contract_failed else "pass",
1367
+ "ClaudeAgentOptions exposes and constructs required adapter options."
1368
+ if not contract_failed
1369
+ else "ClaudeAgentOptions rejected the required adapter contract.",
1288
1370
  {
1289
1371
  "fields": sorted(option_fields),
1290
1372
  "required_fields": sorted(expected),
1291
1373
  "missing": missing,
1374
+ "construction_cases": cases,
1375
+ "construction_failures": failures,
1292
1376
  },
1293
1377
  )]
1294
1378
  elif package == "openai-codex":
@@ -1300,17 +1384,22 @@ _PROBE_SCRIPT = textwrap.dedent(
1300
1384
  missing_run = sorted(expected_run - run_params)
1301
1385
  missing_start = sorted(expected_start - start_params)
1302
1386
  missing = missing_run + [f"thread_start.{item}" for item in missing_start]
1387
+ cases, failures = _configuration_contract_cases(package)
1388
+ contract_failed = bool(missing or failures)
1303
1389
  payload = [result(
1304
1390
  "adapter-contract",
1305
- "fail" if missing else "pass",
1306
- "Codex thread APIs expose required adapter parameters." if not missing
1307
- else "Codex thread APIs are missing required adapter parameters.",
1391
+ "fail" if contract_failed else "pass",
1392
+ "Codex configuration and wire parameters construct the required adapter contract."
1393
+ if not contract_failed
1394
+ else "Codex configuration or thread APIs rejected the required adapter contract.",
1308
1395
  {
1309
1396
  "run_params": sorted(run_params),
1310
1397
  "start_params": sorted(start_params),
1311
1398
  "required_run_params": sorted(expected_run),
1312
1399
  "required_start_params": sorted(expected_start),
1313
1400
  "missing": missing,
1401
+ "construction_cases": cases,
1402
+ "construction_failures": failures,
1314
1403
  },
1315
1404
  )]
1316
1405
  elif package == "openai-codex-cli-bin":
@@ -1395,4 +1484,5 @@ _PROBE_SCRIPT = textwrap.dedent(
1395
1484
 
1396
1485
  print(json.dumps(payload, sort_keys=True))
1397
1486
  """
1398
- ).strip()
1487
+ ).strip()
1488
+ )
@@ -86,6 +86,14 @@ def parse_args(argv: list[str] | None = None) -> RunOptions:
86
86
  dest="packages",
87
87
  help="Vendor SDK package to inspect. Repeat to inspect multiple packages.",
88
88
  )
89
+ parser.add_argument("--model", help="Explicit model for every AI analysis and review stage.")
90
+ parser.add_argument(
91
+ "--reasoning-effort", help="Provider-native reasoning effort for AI stages."
92
+ )
93
+ parser.add_argument(
94
+ "--codex-bin",
95
+ help="Codex analysis executable override; does not change inspected SDK/CLI versions.",
96
+ )
89
97
  parser.add_argument(
90
98
  "--report-dir",
91
99
  type=Path,
@@ -128,6 +136,8 @@ def parse_args(argv: list[str] | None = None) -> RunOptions:
128
136
  help="Draft PR title.",
129
137
  )
130
138
  args = parser.parse_args(argv)
139
+ if args.codex_bin and args.runtime != "codex-agent-sdk":
140
+ parser.error("--codex-bin requires --runtime codex-agent-sdk")
131
141
  return RunOptions(
132
142
  workspace=Path.cwd(),
133
143
  runtime=args.runtime,
@@ -142,6 +152,9 @@ def parse_args(argv: list[str] | None = None) -> RunOptions:
142
152
  pr_base=args.pr_base,
143
153
  commit_message=args.commit_message,
144
154
  pr_title=args.pr_title,
155
+ model=args.model,
156
+ reasoning_effort=args.reasoning_effort,
157
+ codex_bin=args.codex_bin,
145
158
  )
146
159
 
147
160
 
@@ -168,8 +181,14 @@ async def run_agent(
168
181
  implementation_enabled=options.implementation_enabled,
169
182
  draft_pr=options.draft_pr,
170
183
  event_sink=event_sink,
184
+ model=options.model,
185
+ reasoning_effort=options.reasoning_effort,
186
+ )
187
+ if options.codex_bin is not None and (runtime is not None or registry is not None):
188
+ raise ValueError("--codex-bin cannot be combined with an injected runtime or registry")
189
+ selected_runtime = runtime or resolve_runtime(
190
+ options.runtime, registry=registry, codex_bin=options.codex_bin
171
191
  )
172
- selected_runtime = runtime or resolve_runtime(options.runtime, registry=registry)
173
192
  close_owned_runtime = runtime is None
174
193
  try:
175
194
  pre_run_results: list[dict[str, Any]] = []
@@ -245,6 +264,8 @@ async def run_agent(
245
264
  implementation_enabled=context.implementation_enabled,
246
265
  draft_pr=context.draft_pr,
247
266
  event_sink=event_sink,
267
+ model=options.model,
268
+ reasoning_effort=options.reasoning_effort,
248
269
  ),
249
270
  )
250
271
  implementation = await maybe_run_implementation(
@@ -728,15 +749,15 @@ def _collect_implementation_snapshots(
728
749
  ]
729
750
  history.extend(matching[:1])
730
751
  continue
731
- versions = _ordered_versions(recent)
732
- if len(versions) < 2:
733
- versions = _ordered_versions(
734
- [
735
- *recent,
736
- str(package.get("locked_version") or ""),
737
- str(package.get("candidate_version") or ""),
738
- ]
739
- )
752
+ # The locked baseline can fall outside the latest three releases. Always
753
+ # include it so direction analysis covers the actual upgrade interval.
754
+ versions = _ordered_versions(
755
+ [
756
+ *recent,
757
+ str(package.get("locked_version") or ""),
758
+ str(package.get("candidate_version") or ""),
759
+ ]
760
+ )
740
761
  for version in versions:
741
762
  snapshot = by_exact_version.get((name, version))
742
763
  if snapshot is None:
@@ -66,7 +66,7 @@ PACKAGE_SOURCE_HINTS: dict[str, tuple[SourceRef, ...]] = {
66
66
  SourceRef(
67
67
  kind="docs",
68
68
  label="OpenAI Codex SDK docs",
69
- url="https://developers.openai.com/codex/sdk",
69
+ url="https://learn.chatgpt.com/docs/codex-sdk",
70
70
  ),
71
71
  ),
72
72
  "openai-codex-cli-bin": (
@@ -212,6 +212,9 @@ class RunOptions:
212
212
  pr_base: str | None = None
213
213
  commit_message: str = "Run SDK evolution update"
214
214
  pr_title: str = "Adapt agent-runtime-kit to upstream SDK evolution"
215
+ model: str | None = None
216
+ reasoning_effort: str | None = None
217
+ codex_bin: str | None = None
215
218
 
216
219
 
217
220
  @dataclass(frozen=True)
@@ -226,6 +229,8 @@ class RunContext:
226
229
  implementation_enabled: bool
227
230
  draft_pr: bool
228
231
  event_sink: Any | None = None
232
+ model: str | None = None
233
+ reasoning_effort: str | None = None
229
234
 
230
235
 
231
236
  @dataclass(frozen=True)
@@ -35,12 +35,12 @@ RELEASE_NOTE_SOURCES: dict[str, tuple[SourceRef, ...]] = {
35
35
  SourceRef(
36
36
  kind="docs",
37
37
  label="Codex SDK docs",
38
- url="https://developers.openai.com/codex/sdk",
38
+ url="https://learn.chatgpt.com/docs/codex-sdk",
39
39
  ),
40
40
  SourceRef(
41
41
  kind="changelog",
42
42
  label="Codex changelog",
43
- url="https://developers.openai.com/codex/changelog",
43
+ url="https://learn.chatgpt.com/docs/changelog",
44
44
  ),
45
45
  SourceRef(
46
46
  kind="release",
@@ -349,14 +349,21 @@ def _summaries_for_interval(
349
349
  ) -> list[str]:
350
350
  text = _prefer_github_discussion_body(text)
351
351
  lines = [line.strip() for line in text.splitlines()]
352
- version_patterns = [to_version]
352
+ versions = [to_version]
353
353
  if from_version:
354
- version_patterns.append(from_version)
354
+ versions.append(from_version)
355
+ version_patterns = [
356
+ re.compile(rf"(?<![\w.+-])v?{re.escape(version)}(?![\w.+-])") for version in versions
357
+ ]
358
+ # A baseline mention or a longer version sharing the candidate's prefix is
359
+ # not release evidence for the candidate itself.
360
+ if not version_patterns[0].search(text):
361
+ return []
355
362
  matches: list[str] = []
356
363
  for index, line in enumerate(lines):
357
364
  if not line:
358
365
  continue
359
- if any(pattern and pattern in line for pattern in version_patterns):
366
+ if any(pattern.search(line) for pattern in version_patterns):
360
367
  matches.append(_clean_summary(line))
361
368
  for nearby in lines[index + 1 : index + 9]:
362
369
  cleaned = _clean_summary(nearby)
@@ -364,8 +371,9 @@ def _summaries_for_interval(
364
371
  matches.append(cleaned)
365
372
  if not matches and to_version:
366
373
  compact = re.sub(r"\s+", " ", text)
367
- version_index = compact.find(to_version)
368
- if version_index >= 0:
374
+ version_match = version_patterns[0].search(compact)
375
+ if version_match:
376
+ version_index = version_match.start()
369
377
  start = max(0, version_index - 160)
370
378
  end = min(len(compact), version_index + 320)
371
379
  matches.append(_clean_summary(compact[start:end]))
@@ -380,7 +388,12 @@ def _prefer_github_discussion_body(text: str) -> str:
380
388
  )
381
389
  if not match:
382
390
  return text
383
- return match.group("body")
391
+ # GitHub release discussions may identify the version only in the page
392
+ # title, while the comment body describes the changes without repeating it.
393
+ # Keep that title attached to its body, excluding navigation and replies.
394
+ title = re.search(r"<title\b[^>]*>(.*?)</title>", text, flags=re.DOTALL | re.IGNORECASE)
395
+ body = match.group("body")
396
+ return f"{title.group(1)}\n{body}" if title else body
384
397
 
385
398
 
386
399
  def _release_note_links_for_version(source_url: str, text: str, to_version: str) -> tuple[str, ...]:
@@ -5,6 +5,7 @@ from __future__ import annotations
5
5
  import json
6
6
  import re
7
7
  from collections.abc import Mapping, Sequence
8
+ from functools import partial
8
9
  from pathlib import Path
9
10
  from typing import Any
10
11
 
@@ -109,7 +110,7 @@ class FixtureEvolutionRuntime:
109
110
  await self.aclose()
110
111
 
111
112
 
112
- def build_registry() -> RuntimeRegistry:
113
+ def build_registry(*, codex_bin: str | None = None) -> RuntimeRegistry:
113
114
  """Build the runtime registry used by the SDK evolution agent."""
114
115
 
115
116
  registry = create_default_registry(include_fake=False)
@@ -117,7 +118,7 @@ def build_registry() -> RuntimeRegistry:
117
118
  register_adapters(registry)
118
119
  registry.register(
119
120
  AgentRuntimeKind.CODEX_AGENT_SDK,
120
- _codex_evolution_runtime,
121
+ partial(_codex_evolution_runtime, codex_bin=codex_bin),
121
122
  replace=True,
122
123
  )
123
124
  registry.register(
@@ -133,12 +134,16 @@ def build_registry() -> RuntimeRegistry:
133
134
  return registry
134
135
 
135
136
 
136
- def _codex_evolution_runtime(**kwargs: Any) -> CodexAgentRuntime:
137
+ def _codex_evolution_runtime(*, codex_bin: str | None = None, **kwargs: Any) -> CodexAgentRuntime:
137
138
  codex_home = prepare_isolated_codex_home(codex_home=SDK_EVOLUTION_CODEX_HOME)
138
139
  env = dict(kwargs.pop("env", {}) or {})
139
140
  env.setdefault("CODEX_HOME", str(codex_home))
140
141
  kwargs.setdefault("default_model", SDK_EVOLUTION_CODEX_MODEL)
141
142
  kwargs.setdefault("reuse_process", True)
143
+ if codex_bin is not None:
144
+ from openai_codex import CodexConfig
145
+
146
+ kwargs.setdefault("config_cls", partial(CodexConfig, codex_bin=codex_bin))
142
147
  return CodexAgentRuntime(env=env, **kwargs)
143
148
 
144
149
 
@@ -152,10 +157,17 @@ def _antigravity_evolution_runtime(**kwargs: Any) -> AntigravityAgentRuntime:
152
157
  return AntigravityAgentRuntime(**kwargs)
153
158
 
154
159
 
155
- def resolve_runtime(kind: str, *, registry: RuntimeRegistry | None = None) -> AgentRuntime:
160
+ def resolve_runtime(
161
+ kind: str, *, registry: RuntimeRegistry | None = None, codex_bin: str | None = None
162
+ ) -> AgentRuntime:
156
163
  """Resolve a runtime through agent-runtime-kit and verify availability."""
157
164
 
158
- registry = registry or build_registry()
165
+ if codex_bin is not None:
166
+ if kind != "codex-agent-sdk":
167
+ raise ValueError("codex_bin requires the codex-agent-sdk runtime")
168
+ if registry is not None:
169
+ raise ValueError("codex_bin cannot be combined with an injected registry")
170
+ registry = registry or build_registry(codex_bin=codex_bin)
159
171
  runtime = registry.resolve(kind)
160
172
  availability = runtime.availability()
161
173
  if not availability.available:
@@ -188,6 +200,8 @@ async def run_stage(
188
200
  permissions=permissions,
189
201
  event_sink=context.event_sink,
190
202
  output_schema=schema,
203
+ model=context.model,
204
+ reasoning_effort=context.reasoning_effort,
191
205
  metadata=_stage_metadata(runtime, stage=stage, context=context),
192
206
  )
193
207
  try:
@@ -990,8 +1004,10 @@ def _stage_metadata(
990
1004
  ) -> dict[str, Any]:
991
1005
  metadata: dict[str, Any] = {"stage": stage, "run_id": context.run_id}
992
1006
  if runtime.kind is AgentRuntimeKind.CODEX_AGENT_SDK:
993
- metadata["model"] = SDK_EVOLUTION_CODEX_MODEL
994
- metadata["reasoning_effort"] = SDK_EVOLUTION_CODEX_REASONING_EFFORT
1007
+ metadata["model"] = context.model or SDK_EVOLUTION_CODEX_MODEL
1008
+ metadata["reasoning_effort"] = (
1009
+ context.reasoning_effort or SDK_EVOLUTION_CODEX_REASONING_EFFORT
1010
+ )
995
1011
  return metadata
996
1012
 
997
1013
 
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "agent-runtime-kit"
7
- version = "0.5.1"
7
+ version = "0.5.2"
8
8
  description = "One typed runtime API for Claude, Codex, and Antigravity agent SDKs."
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.10"
@@ -48,12 +48,12 @@ dependencies = ["jsonschema>=4.18,<5"]
48
48
  claude = ["claude-agent-sdk>=0.2.87,<0.3"]
49
49
  # 0.1.0b3 floor: earlier betas pin a codex binary with no manylinux wheels,
50
50
  # making the extra uninstallable on glibc Linux.
51
- codex = ["openai-codex>=0.1.0b3,<0.148"]
51
+ codex = ["openai-codex>=0.1.0b3,<0.155"]
52
52
  antigravity = ["google-antigravity>=0.1.2,<0.2"]
53
53
  all = [
54
54
  "claude-agent-sdk>=0.2.87,<0.3",
55
55
  "google-antigravity>=0.1.2,<0.2",
56
- "openai-codex>=0.1.0b3,<0.148",
56
+ "openai-codex>=0.1.0b3,<0.155",
57
57
  ]
58
58
 
59
59
  [project.urls]