nighthawk-python 0.8.0__tar.gz → 0.10.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 (166) hide show
  1. {nighthawk_python-0.8.0 → nighthawk_python-0.10.0}/.github/workflows/docs.yml +1 -1
  2. {nighthawk_python-0.8.0 → nighthawk_python-0.10.0}/CHANGELOG.md +39 -1
  3. {nighthawk_python-0.8.0 → nighthawk_python-0.10.0}/PKG-INFO +1 -1
  4. {nighthawk_python-0.8.0 → nighthawk_python-0.10.0}/docs/api.md +4 -4
  5. {nighthawk_python-0.8.0 → nighthawk_python-0.10.0}/docs/for-coding-agents.md +7 -3
  6. {nighthawk_python-0.8.0 → nighthawk_python-0.10.0}/docs/natural-blocks.md +1 -1
  7. {nighthawk_python-0.8.0 → nighthawk_python-0.10.0}/docs/patterns.md +2 -1
  8. {nighthawk_python-0.8.0 → nighthawk_python-0.10.0}/docs/philosophy.md +1 -1
  9. nighthawk_python-0.10.0/docs/runtime-configuration.md +252 -0
  10. {nighthawk_python-0.8.0 → nighthawk_python-0.10.0}/docs/specification.md +28 -14
  11. {nighthawk_python-0.8.0 → nighthawk_python-0.10.0}/pyproject.toml +1 -1
  12. {nighthawk_python-0.8.0 → nighthawk_python-0.10.0}/src/nighthawk/__init__.py +6 -5
  13. {nighthawk_python-0.8.0 → nighthawk_python-0.10.0}/src/nighthawk/backends/mcp_server.py +2 -1
  14. {nighthawk_python-0.8.0 → nighthawk_python-0.10.0}/src/nighthawk/backends/tool_bridge.py +12 -10
  15. {nighthawk_python-0.8.0 → nighthawk_python-0.10.0}/src/nighthawk/configuration.py +0 -41
  16. nighthawk_python-0.10.0/src/nighthawk/oversight.py +180 -0
  17. {nighthawk_python-0.8.0 → nighthawk_python-0.10.0}/src/nighthawk/resilience/__init__.py +2 -3
  18. {nighthawk_python-0.8.0 → nighthawk_python-0.10.0}/src/nighthawk/resilience/_vote.py +6 -5
  19. {nighthawk_python-0.8.0 → nighthawk_python-0.10.0}/src/nighthawk/runtime/prompt.py +4 -4
  20. {nighthawk_python-0.8.0 → nighthawk_python-0.10.0}/src/nighthawk/runtime/runner.py +209 -78
  21. {nighthawk_python-0.8.0 → nighthawk_python-0.10.0}/src/nighthawk/runtime/scoping.py +104 -75
  22. {nighthawk_python-0.8.0 → nighthawk_python-0.10.0}/src/nighthawk/runtime/step_context.py +8 -1
  23. {nighthawk_python-0.8.0 → nighthawk_python-0.10.0}/src/nighthawk/runtime/step_executor.py +6 -6
  24. {nighthawk_python-0.8.0 → nighthawk_python-0.10.0}/src/nighthawk/testing.py +11 -16
  25. {nighthawk_python-0.8.0 → nighthawk_python-0.10.0}/src/nighthawk/tools/assignment.py +7 -4
  26. {nighthawk_python-0.8.0 → nighthawk_python-0.10.0}/src/nighthawk/tools/contracts.py +7 -8
  27. nighthawk_python-0.10.0/src/nighthawk/tools/execution.py +160 -0
  28. {nighthawk_python-0.8.0 → nighthawk_python-0.10.0}/tests/execution/stub_executor.py +2 -2
  29. {nighthawk_python-0.8.0 → nighthawk_python-0.10.0}/tests/execution/test_natural_block_ordering.py +3 -3
  30. {nighthawk_python-0.8.0 → nighthawk_python-0.10.0}/tests/execution/test_natural_traceback.py +2 -1
  31. {nighthawk_python-0.8.0 → nighthawk_python-0.10.0}/tests/execution/test_runtime.py +201 -8
  32. nighthawk_python-0.10.0/tests/governance/test_governance.py +198 -0
  33. {nighthawk_python-0.8.0 → nighthawk_python-0.10.0}/tests/integration/test_llm_integration.py +2 -2
  34. {nighthawk_python-0.8.0 → nighthawk_python-0.10.0}/tests/public/test_public_api.py +113 -8
  35. {nighthawk_python-0.8.0 → nighthawk_python-0.10.0}/tests/resilience/test_vote.py +8 -0
  36. {nighthawk_python-0.8.0 → nighthawk_python-0.10.0}/tests/tools/test_assignment_async.py +37 -0
  37. nighthawk_python-0.10.0/tests/tools/test_tool_boundary.py +513 -0
  38. {nighthawk_python-0.8.0 → nighthawk_python-0.10.0}/uv.lock +132 -132
  39. nighthawk_python-0.8.0/docs/runtime-configuration.md +0 -138
  40. nighthawk_python-0.8.0/src/nighthawk/tools/execution.py +0 -83
  41. nighthawk_python-0.8.0/tests/tools/test_tool_boundary.py +0 -218
  42. {nighthawk_python-0.8.0 → nighthawk_python-0.10.0}/.claude/rules/docs.md +0 -0
  43. {nighthawk_python-0.8.0 → nighthawk_python-0.10.0}/.claude/rules/promptfoo.md +0 -0
  44. {nighthawk_python-0.8.0 → nighthawk_python-0.10.0}/.claude/rules/src.md +0 -0
  45. {nighthawk_python-0.8.0 → nighthawk_python-0.10.0}/.claude/rules/tests.md +0 -0
  46. {nighthawk_python-0.8.0 → nighthawk_python-0.10.0}/.claude/settings.json +0 -0
  47. {nighthawk_python-0.8.0 → nighthawk_python-0.10.0}/.claude/unset_envs.sh +0 -0
  48. {nighthawk_python-0.8.0 → nighthawk_python-0.10.0}/.devcontainer/Dockerfile.devcontainer +0 -0
  49. {nighthawk_python-0.8.0 → nighthawk_python-0.10.0}/.devcontainer/Dockerfile.litellm +0 -0
  50. {nighthawk_python-0.8.0 → nighthawk_python-0.10.0}/.devcontainer/devcontainer.json +0 -0
  51. {nighthawk_python-0.8.0 → nighthawk_python-0.10.0}/.devcontainer/docker-compose.yaml +0 -0
  52. {nighthawk_python-0.8.0 → nighthawk_python-0.10.0}/.devcontainer/litellm-config.yaml +0 -0
  53. {nighthawk_python-0.8.0 → nighthawk_python-0.10.0}/.github/dependabot.yml +0 -0
  54. {nighthawk_python-0.8.0 → nighthawk_python-0.10.0}/.github/workflows/ci.yml +0 -0
  55. {nighthawk_python-0.8.0 → nighthawk_python-0.10.0}/.github/workflows/publish.yml +0 -0
  56. {nighthawk_python-0.8.0 → nighthawk_python-0.10.0}/.gitignore +0 -0
  57. {nighthawk_python-0.8.0 → nighthawk_python-0.10.0}/.python-version +0 -0
  58. {nighthawk_python-0.8.0 → nighthawk_python-0.10.0}/AGENTS.md +0 -0
  59. {nighthawk_python-0.8.0 → nighthawk_python-0.10.0}/CLAUDE.md +0 -0
  60. {nighthawk_python-0.8.0 → nighthawk_python-0.10.0}/CONTRIBUTING.md +0 -0
  61. {nighthawk_python-0.8.0 → nighthawk_python-0.10.0}/LICENSE +0 -0
  62. {nighthawk_python-0.8.0 → nighthawk_python-0.10.0}/README.md +0 -0
  63. {nighthawk_python-0.8.0 → nighthawk_python-0.10.0}/docs/AGENTS.md +0 -0
  64. {nighthawk_python-0.8.0 → nighthawk_python-0.10.0}/docs/assets/nighthawk_logo-128x128.png +0 -0
  65. {nighthawk_python-0.8.0 → nighthawk_python-0.10.0}/docs/coding-agent-backends.md +0 -0
  66. {nighthawk_python-0.8.0 → nighthawk_python-0.10.0}/docs/executors.md +0 -0
  67. {nighthawk_python-0.8.0 → nighthawk_python-0.10.0}/docs/index.md +0 -0
  68. {nighthawk_python-0.8.0 → nighthawk_python-0.10.0}/docs/pydantic-ai-providers.md +0 -0
  69. {nighthawk_python-0.8.0 → nighthawk_python-0.10.0}/docs/quickstart.md +0 -0
  70. {nighthawk_python-0.8.0 → nighthawk_python-0.10.0}/docs/roadmap.md +0 -0
  71. {nighthawk_python-0.8.0 → nighthawk_python-0.10.0}/docs/verification.md +0 -0
  72. {nighthawk_python-0.8.0 → nighthawk_python-0.10.0}/evals/promptfoo/assertions/__init__.py +0 -0
  73. {nighthawk_python-0.8.0 → nighthawk_python-0.10.0}/evals/promptfoo/assertions/binding_value.py +0 -0
  74. {nighthawk_python-0.8.0 → nighthawk_python-0.10.0}/evals/promptfoo/assertions/outcome_kind.py +0 -0
  75. {nighthawk_python-0.8.0 → nighthawk_python-0.10.0}/evals/promptfoo/assertions/raise_message.py +0 -0
  76. {nighthawk_python-0.8.0 → nighthawk_python-0.10.0}/evals/promptfoo/evidence/2026-03-26-baseline-prompt-ab.md +0 -0
  77. {nighthawk_python-0.8.0 → nighthawk_python-0.10.0}/evals/promptfoo/evidence/2026-03-26-baseline-regression.md +0 -0
  78. {nighthawk_python-0.8.0 → nighthawk_python-0.10.0}/evals/promptfoo/evidence/2026-03-26-baseline-suffix-ab.md +0 -0
  79. {nighthawk_python-0.8.0 → nighthawk_python-0.10.0}/evals/promptfoo/promptfooconfig-agents.yaml +0 -0
  80. {nighthawk_python-0.8.0 → nighthawk_python-0.10.0}/evals/promptfoo/promptfooconfig-prompt-ab.yaml +0 -0
  81. {nighthawk_python-0.8.0 → nighthawk_python-0.10.0}/evals/promptfoo/promptfooconfig-suffix-ab.yaml +0 -0
  82. {nighthawk_python-0.8.0 → nighthawk_python-0.10.0}/evals/promptfoo/promptfooconfig.yaml +0 -0
  83. {nighthawk_python-0.8.0 → nighthawk_python-0.10.0}/evals/promptfoo/prompts/eval_coding_agent.txt +0 -0
  84. {nighthawk_python-0.8.0 → nighthawk_python-0.10.0}/evals/promptfoo/prompts/eval_default.txt +0 -0
  85. {nighthawk_python-0.8.0 → nighthawk_python-0.10.0}/evals/promptfoo/prompts/eval_mutation_aware.txt +0 -0
  86. {nighthawk_python-0.8.0 → nighthawk_python-0.10.0}/evals/promptfoo/prompts/eval_sequenced.txt +0 -0
  87. {nighthawk_python-0.8.0 → nighthawk_python-0.10.0}/evals/promptfoo/provider.py +0 -0
  88. {nighthawk_python-0.8.0 → nighthawk_python-0.10.0}/evals/promptfoo/research-result.md +0 -0
  89. {nighthawk_python-0.8.0 → nighthawk_python-0.10.0}/evals/promptfoo/test_cases/binding_operations.yaml +0 -0
  90. {nighthawk_python-0.8.0 → nighthawk_python-0.10.0}/evals/promptfoo/test_cases/edge_cases.yaml +0 -0
  91. {nighthawk_python-0.8.0 → nighthawk_python-0.10.0}/evals/promptfoo/test_cases/loop_outcomes.yaml +0 -0
  92. {nighthawk_python-0.8.0 → nighthawk_python-0.10.0}/evals/promptfoo/test_cases/multi_step.yaml +0 -0
  93. {nighthawk_python-0.8.0 → nighthawk_python-0.10.0}/evals/promptfoo/test_cases/null_handling.yaml +0 -0
  94. {nighthawk_python-0.8.0 → nighthawk_python-0.10.0}/evals/promptfoo/test_cases/outcome_kinds.yaml +0 -0
  95. {nighthawk_python-0.8.0 → nighthawk_python-0.10.0}/evals/promptfoo/test_cases/tool_selection.yaml +0 -0
  96. {nighthawk_python-0.8.0 → nighthawk_python-0.10.0}/mkdocs.yml +0 -0
  97. {nighthawk_python-0.8.0 → nighthawk_python-0.10.0}/pyrightconfig.json +0 -0
  98. {nighthawk_python-0.8.0 → nighthawk_python-0.10.0}/src/AGENTS.md +0 -0
  99. {nighthawk_python-0.8.0 → nighthawk_python-0.10.0}/src/nighthawk/backends/__init__.py +0 -0
  100. {nighthawk_python-0.8.0 → nighthawk_python-0.10.0}/src/nighthawk/backends/base.py +0 -0
  101. {nighthawk_python-0.8.0 → nighthawk_python-0.10.0}/src/nighthawk/backends/claude_code_cli.py +0 -0
  102. {nighthawk_python-0.8.0 → nighthawk_python-0.10.0}/src/nighthawk/backends/claude_code_sdk.py +0 -0
  103. {nighthawk_python-0.8.0 → nighthawk_python-0.10.0}/src/nighthawk/backends/claude_code_settings.py +0 -0
  104. {nighthawk_python-0.8.0 → nighthawk_python-0.10.0}/src/nighthawk/backends/codex.py +0 -0
  105. {nighthawk_python-0.8.0 → nighthawk_python-0.10.0}/src/nighthawk/backends/mcp_boundary.py +0 -0
  106. {nighthawk_python-0.8.0 → nighthawk_python-0.10.0}/src/nighthawk/errors.py +0 -0
  107. {nighthawk_python-0.8.0 → nighthawk_python-0.10.0}/src/nighthawk/identifier_path.py +0 -0
  108. {nighthawk_python-0.8.0 → nighthawk_python-0.10.0}/src/nighthawk/json_renderer.py +0 -0
  109. {nighthawk_python-0.8.0 → nighthawk_python-0.10.0}/src/nighthawk/natural/__init__.py +0 -0
  110. {nighthawk_python-0.8.0 → nighthawk_python-0.10.0}/src/nighthawk/natural/blocks.py +0 -0
  111. {nighthawk_python-0.8.0 → nighthawk_python-0.10.0}/src/nighthawk/natural/decorator.py +0 -0
  112. {nighthawk_python-0.8.0 → nighthawk_python-0.10.0}/src/nighthawk/natural/transform.py +0 -0
  113. {nighthawk_python-0.8.0 → nighthawk_python-0.10.0}/src/nighthawk/resilience/_budget.py +0 -0
  114. {nighthawk_python-0.8.0 → nighthawk_python-0.10.0}/src/nighthawk/resilience/_circuit_breaker.py +0 -0
  115. {nighthawk_python-0.8.0 → nighthawk_python-0.10.0}/src/nighthawk/resilience/_fallback.py +0 -0
  116. {nighthawk_python-0.8.0 → nighthawk_python-0.10.0}/src/nighthawk/resilience/_retry.py +0 -0
  117. {nighthawk_python-0.8.0 → nighthawk_python-0.10.0}/src/nighthawk/resilience/_timeout.py +0 -0
  118. {nighthawk_python-0.8.0 → nighthawk_python-0.10.0}/src/nighthawk/runtime/__init__.py +0 -0
  119. {nighthawk_python-0.8.0 → nighthawk_python-0.10.0}/src/nighthawk/runtime/async_bridge.py +0 -0
  120. {nighthawk_python-0.8.0 → nighthawk_python-0.10.0}/src/nighthawk/runtime/step_contract.py +0 -0
  121. {nighthawk_python-0.8.0 → nighthawk_python-0.10.0}/src/nighthawk/runtime/tool_calls.py +0 -0
  122. {nighthawk_python-0.8.0 → nighthawk_python-0.10.0}/src/nighthawk/tools/__init__.py +0 -0
  123. {nighthawk_python-0.8.0 → nighthawk_python-0.10.0}/src/nighthawk/tools/provided.py +0 -0
  124. {nighthawk_python-0.8.0 → nighthawk_python-0.10.0}/src/nighthawk/tools/registry.py +0 -0
  125. {nighthawk_python-0.8.0 → nighthawk_python-0.10.0}/src/nighthawk/ulid.py +0 -0
  126. {nighthawk_python-0.8.0 → nighthawk_python-0.10.0}/tests/AGENTS.md +0 -0
  127. {nighthawk_python-0.8.0 → nighthawk_python-0.10.0}/tests/__init__.py +0 -0
  128. {nighthawk_python-0.8.0 → nighthawk_python-0.10.0}/tests/backends/__init__.py +0 -0
  129. {nighthawk_python-0.8.0 → nighthawk_python-0.10.0}/tests/backends/test_claude_code_cli.py +0 -0
  130. {nighthawk_python-0.8.0 → nighthawk_python-0.10.0}/tests/backends/test_claude_code_sdk.py +0 -0
  131. {nighthawk_python-0.8.0 → nighthawk_python-0.10.0}/tests/backends/test_codex.py +0 -0
  132. {nighthawk_python-0.8.0 → nighthawk_python-0.10.0}/tests/conftest.py +0 -0
  133. {nighthawk_python-0.8.0 → nighthawk_python-0.10.0}/tests/docs/__init__.py +0 -0
  134. {nighthawk_python-0.8.0 → nighthawk_python-0.10.0}/tests/docs/test_coding_agent_examples.py +0 -0
  135. {nighthawk_python-0.8.0 → nighthawk_python-0.10.0}/tests/docs/test_docs_architecture.py +0 -0
  136. {nighthawk_python-0.8.0 → nighthawk_python-0.10.0}/tests/docs/test_prompt_examples.py +0 -0
  137. {nighthawk_python-0.8.0 → nighthawk_python-0.10.0}/tests/execution/__init__.py +0 -0
  138. {nighthawk_python-0.8.0 → nighthawk_python-0.10.0}/tests/execution/prompt_test_helpers.py +0 -0
  139. {nighthawk_python-0.8.0 → nighthawk_python-0.10.0}/tests/execution/test_execution_outcome_prompt_fragment.py +0 -0
  140. {nighthawk_python-0.8.0 → nighthawk_python-0.10.0}/tests/execution/test_globals_prompt.py +0 -0
  141. {nighthawk_python-0.8.0 → nighthawk_python-0.10.0}/tests/execution/test_infer_binding_types.py +0 -0
  142. {nighthawk_python-0.8.0 → nighthawk_python-0.10.0}/tests/execution/test_variables_prompt.py +0 -0
  143. {nighthawk_python-0.8.0 → nighthawk_python-0.10.0}/tests/integration/__init__.py +0 -0
  144. {nighthawk_python-0.8.0 → nighthawk_python-0.10.0}/tests/integration/skip_helpers.py +0 -0
  145. {nighthawk_python-0.8.0 → nighthawk_python-0.10.0}/tests/integration/test_carry_pattern.py +0 -0
  146. {nighthawk_python-0.8.0 → nighthawk_python-0.10.0}/tests/integration/test_claude_code_cli_integration.py +0 -0
  147. {nighthawk_python-0.8.0 → nighthawk_python-0.10.0}/tests/integration/test_claude_code_sdk_integration.py +0 -0
  148. {nighthawk_python-0.8.0 → nighthawk_python-0.10.0}/tests/integration/test_codex_integration.py +0 -0
  149. {nighthawk_python-0.8.0 → nighthawk_python-0.10.0}/tests/natural/__init__.py +0 -0
  150. {nighthawk_python-0.8.0 → nighthawk_python-0.10.0}/tests/natural/test_blocks.py +0 -0
  151. {nighthawk_python-0.8.0 → nighthawk_python-0.10.0}/tests/public/__init__.py +0 -0
  152. {nighthawk_python-0.8.0 → nighthawk_python-0.10.0}/tests/public/test_readme_example.py +0 -0
  153. {nighthawk_python-0.8.0 → nighthawk_python-0.10.0}/tests/public/test_usage_meter.py +0 -0
  154. {nighthawk_python-0.8.0 → nighthawk_python-0.10.0}/tests/resilience/__init__.py +0 -0
  155. {nighthawk_python-0.8.0 → nighthawk_python-0.10.0}/tests/resilience/test_budget.py +0 -0
  156. {nighthawk_python-0.8.0 → nighthawk_python-0.10.0}/tests/resilience/test_circuit_breaker.py +0 -0
  157. {nighthawk_python-0.8.0 → nighthawk_python-0.10.0}/tests/resilience/test_composition.py +0 -0
  158. {nighthawk_python-0.8.0 → nighthawk_python-0.10.0}/tests/resilience/test_fallback.py +0 -0
  159. {nighthawk_python-0.8.0 → nighthawk_python-0.10.0}/tests/resilience/test_retry.py +0 -0
  160. {nighthawk_python-0.8.0 → nighthawk_python-0.10.0}/tests/resilience/test_timeout.py +0 -0
  161. {nighthawk_python-0.8.0 → nighthawk_python-0.10.0}/tests/test_renderer.py +0 -0
  162. {nighthawk_python-0.8.0 → nighthawk_python-0.10.0}/tests/test_testing.py +0 -0
  163. {nighthawk_python-0.8.0 → nighthawk_python-0.10.0}/tests/tools/__init__.py +0 -0
  164. {nighthawk_python-0.8.0 → nighthawk_python-0.10.0}/tests/tools/test_contracts.py +0 -0
  165. {nighthawk_python-0.8.0 → nighthawk_python-0.10.0}/tests/tools/test_provided_async.py +0 -0
  166. {nighthawk_python-0.8.0 → nighthawk_python-0.10.0}/tests/tools/test_registry.py +0 -0
@@ -46,4 +46,4 @@ jobs:
46
46
  url: ${{ steps.deployment.outputs.page_url }}
47
47
  steps:
48
48
  - id: deployment
49
- uses: actions/deploy-pages@v4
49
+ uses: actions/deploy-pages@v5
@@ -7,6 +7,42 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [0.10.0]
11
+
12
+ ### Added
13
+ - Scope-level oversight hooks in `nh.scope(oversight=...)` for synchronous tool-call inspection and step-commit inspection.
14
+ - Oversight trace events and backend/wrapper coverage for accept/reject decision paths.
15
+ - Validation for `vote(min_success=...)` bounds (`>= 1` and `<= count`) with matching tests.
16
+ - Tool-boundary regression test ensuring recoverable `ToolBoundaryError` is wrapped into JSON payload.
17
+
18
+ ### Changed
19
+ - Finalized committed binding validation at step boundaries, added write-root dirty tracking for dotted `nh_assign`, and aligned prompt/docs with the new validation contract.
20
+ - Introduced `ExecutionRef` (`run_id`, `scope_id`, optional `step_id`) and renamed runtime accessor to `get_execution_ref`.
21
+ - Added top-level module access for `oversight` and `resilience`, with docs and public API tests aligned.
22
+ - Consolidated tool-call oversight inspection to a single wrapper-side entry point and removed duplicate inspection guard state.
23
+ - Unified tool result handling to TypedDict-based envelope types (`ToolResult`, `ToolError`) instead of Pydantic models.
24
+ - Clarified tool-boundary policy: recoverable tool-call failures are JSON-envelope responses, host invariant violations propagate as Python exceptions.
25
+ - Updated specification and runtime configuration docs to codify envelope-vs-raise boundary semantics for tool-call failures.
26
+ - Updated API and patterns docs for `ExecutionRef`, `get_execution_ref`, and oversight/resilience namespace exposure.
27
+
28
+ ### Fixed
29
+ - Backend handler now preserves wrapped tool error payloads (`{"value": ..., "error": ...}`) without dropping error details.
30
+ - Tool-call context propagation now preserves step identity in execution ref during step execution.
31
+
32
+ ## [0.9.0]
33
+
34
+ ### Changed
35
+
36
+ - Redesigned `nh.scope()` around `mode` semantics (`"inherit"` default, `"replace"` for explicit replacement).
37
+ - Scope prompt suffix arguments now use list-based forms: `system_prompt_suffix_fragments` and `user_prompt_suffix_fragments`.
38
+
39
+ ### Removed
40
+ - Removed `step_executor_configuration_patch` from `nh.scope()`.
41
+ - Removed `StepExecutorConfigurationPatch` from the public API.
42
+
43
+ ### Fixed
44
+ - Clarified and aligned docs/tests for scope merge vs replace behavior.
45
+
10
46
  ## [0.8.0]
11
47
 
12
48
  ### Added
@@ -127,7 +163,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
127
163
  - Step executor abstraction and provider integration foundation.
128
164
  - Core documentation and project scaffolding.
129
165
 
130
- [Unreleased]: https://github.com/kurusugawa-computer/nighthawk-python/compare/v0.8.0...HEAD
166
+ [Unreleased]: https://github.com/kurusugawa-computer/nighthawk-python/compare/v0.10.0...HEAD
167
+ [0.10.0]: https://github.com/kurusugawa-computer/nighthawk-python/compare/v0.9.0...v0.10.0
168
+ [0.9.0]: https://github.com/kurusugawa-computer/nighthawk-python/compare/v0.8.0...v0.9.0
131
169
  [0.8.0]: https://github.com/kurusugawa-computer/nighthawk-python/compare/v0.7.0...v0.8.0
132
170
  [0.7.0]: https://github.com/kurusugawa-computer/nighthawk-python/compare/v0.6.1...v0.7.0
133
171
  [0.6.1]: https://github.com/kurusugawa-computer/nighthawk-python/compare/v0.6.0...v0.6.1
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: nighthawk-python
3
- Version: 0.8.0
3
+ Version: 0.10.0
4
4
  Summary: A Python library where Python controls flow and LLMs or coding agents reason within constrained Natural blocks.
5
5
  Project-URL: Repository, https://github.com/kurusugawa-computer/nighthawk-python
6
6
  Project-URL: Documentation, https://kurusugawa-computer.github.io/nighthawk-python/
@@ -14,17 +14,18 @@
14
14
  - scope
15
15
  - AgentStepExecutor
16
16
  - StepExecutorConfiguration
17
- - StepExecutorConfigurationPatch
18
17
  - StepPromptTemplates
19
18
  - StepContextLimits
20
19
  - JsonableValue
21
20
  - to_jsonable_value
22
- - ExecutionContext
21
+ - ExecutionRef
23
22
  - get_current_step_context
24
23
  - get_current_usage_meter
25
- - get_execution_context
24
+ - get_execution_ref
26
25
  - get_step_executor
26
+ - resilience
27
27
  - UsageMeter
28
+ - oversight
28
29
 
29
30
  ## Errors
30
31
 
@@ -124,4 +125,3 @@
124
125
  ## Testing
125
126
 
126
127
  ::: nighthawk.testing
127
-
@@ -146,8 +146,12 @@ with nh.run(fast_executor):
146
146
  report = write_analysis_report(ticket_text, product_summary)
147
147
  ```
148
148
 
149
- `implicit_references` can inject global helper functions as block capabilities.
150
- Nested scopes still merge additively (set union by key).
149
+ - `implicit_references` inject helper functions as block capabilities.
150
+ - `nh.scope()` must run inside `nh.run()`; it keeps `run_id` and creates a new `scope_id`.
151
+ - `mode` affects only `system_prompt_suffix_fragments`, `user_prompt_suffix_fragments`, and `implicit_references`.
152
+ - `inherit`: suffix lists append; `implicit_references` merge with conflict check (same key + different object => `NighthawkError`).
153
+ - `replace`: explicit list/mapping fully replace; `[]`/`{}` clear; `None` keeps inherited value.
154
+ - `step_executor` / `step_executor_configuration` are resolved independently of `mode`.
151
155
 
152
156
  ## 4. The standard contract shape
153
157
 
@@ -323,7 +327,7 @@ Async rule:
323
327
  Resilience rule:
324
328
 
325
329
  - Keep retry, fallback, timeout, budget, and circuit-breaker policy in Python, not inside Natural text.
326
- - Import from `nighthawk.resilience` (not re-exported from `nighthawk`):
330
+ - Import primitives from `nighthawk.resilience`. After `import nighthawk as nh`, the module namespace is also available as `nh.resilience`:
327
331
 
328
332
  ```py
329
333
  from nighthawk.resilience import retrying
@@ -129,7 +129,7 @@ def detect_language(text: str):
129
129
  return language
130
130
  ```
131
131
 
132
- Type annotations on write bindings enable validation and coercion at commit time.
132
+ Type annotations on write bindings enable validation and coercion at step finalization for values that are actually committed.
133
133
 
134
134
  ### Pydantic model write bindings
135
135
 
@@ -326,7 +326,8 @@ Natural blocks are non-deterministic by nature. Production deployments need stra
326
326
  from nighthawk.resilience import retrying, fallback, vote, timeout, budget, circuit_breaker
327
327
  ```
328
328
 
329
- Import directly from `nighthawk.resilience`. Resilience primitives are not re-exported from the top-level `nighthawk` namespace.
329
+ Import directly from `nighthawk.resilience`.
330
+ After `import nighthawk as nh`, the module namespace is also available as `nh.resilience`.
330
331
 
331
332
  ### Retrying
332
333
 
@@ -23,7 +23,7 @@ calculate_average([1, "2", "three", "cuatro", "五"]) # 3.0
23
23
 
24
24
  Binding functions like `<python_average>` appear in the prompt as a compact signature line. The LLM's pre-trained Python knowledge lets it reason about types, return values, and composition from the signature alone, without JSON Schema or protocol overhead. See [Tool exposure efficiency](#tool-exposure-efficiency) for the quantitative comparison with MCP and CLI tool exposure.
25
25
 
26
- With provider-backed executors, each Natural block is a single LLM call. A sentiment classifier whose write binding is typed as `Literal["positive", "negative", "neutral"]` rejects any output outside the declared set -- Pydantic validates the type annotation at runtime, not as a hint. The same mechanism applies to numeric extraction (`int`, `float`), structured parsing (Pydantic models), and any task where the judgment space is bounded. Because the host program owns the loop, a misclassified result can be retried, logged, or routed to a fallback -- all in ordinary Python.
26
+ With provider-backed executors, each Natural block is a single LLM call. A sentiment classifier whose write binding is typed as `Literal["positive", "negative", "neutral"]` rejects any output outside the declared set -- Pydantic validates committed write bindings at step finalization, not as a hint. The same mechanism applies to numeric extraction (`int`, `float`), structured parsing (Pydantic models), and any task where the judgment space is bounded. Because the host program owns the loop, a misclassified result can be retried, logged, or routed to a fallback -- all in ordinary Python.
27
27
 
28
28
  With [coding agent backends](coding-agent-backends.md), the same boundary contract applies, but each Natural block becomes an autonomous agent execution. The agent can read files, run commands, and invoke skills -- while typed bindings enforce what crosses the boundary back to Python. The same `scope()` and `run()` context managers that structure human-written workflows are equally legible to a coding agent constructing workflows programmatically. When a coding agent operates inside a Natural block, binding functions appear as Python signatures in the prompt:
29
29
 
@@ -0,0 +1,252 @@
1
+ # Runtime configuration
2
+
3
+ > This page assumes you have completed [Executors](executors.md).
4
+
5
+ This page covers how to configure execution at runtime: scoping modes, prompt suffix fragments, context limits, JSON rendering, and execution identity. These settings are independent of executor choice and apply equally to Pydantic AI providers and coding agent backends.
6
+
7
+ ## Scoped overrides with `nh.scope()`
8
+
9
+ Use `nh.scope()` to override execution settings within an existing run. Each scope generates a new `scope_id` while keeping the current `run_id`.
10
+
11
+ ```py
12
+ with nh.run(step_executor):
13
+
14
+ # Inherit mode (default): merge/append into current scope state
15
+ with nh.scope(
16
+ step_executor_configuration=nh.StepExecutorConfiguration(
17
+ model="openai-responses:gpt-5.4-mini",
18
+ ),
19
+ ) as scoped_executor:
20
+ expensive_analysis(data)
21
+
22
+ # Replace mode: replace only explicitly provided values
23
+ # None means "no change". [] / {} means "clear".
24
+ with nh.scope(
25
+ mode="replace",
26
+ system_prompt_suffix_fragments=["Always respond in formal English."],
27
+ implicit_references={},
28
+ ):
29
+ formal_summary(text)
30
+
31
+ # Replace the step executor entirely for a section
32
+ with nh.scope(step_executor=another_executor):
33
+ specialized_step(data)
34
+
35
+ # Add implicit global references for this scope
36
+ with nh.scope(implicit_references={"search_repository": search_repository}):
37
+ typed_labeling_step(ticket_text)
38
+
39
+ # Add synchronous oversight for this scope
40
+ with nh.scope(
41
+ oversight=nh.oversight.Oversight(
42
+ inspect_tool_call=inspect_tool_call,
43
+ inspect_step_commit=inspect_step_commit,
44
+ )
45
+ ):
46
+ inspected_step(ticket_text)
47
+ ```
48
+
49
+ Parameters:
50
+
51
+ - `mode`: scope composition mode. Default: `"inherit"`.
52
+ - `step_executor_configuration`: replace the entire configuration.
53
+ - `step_executor`: replace the step executor entirely.
54
+ - `oversight`: scope-level synchronous tool-call inspection and step-commit inspection hooks.
55
+ - `system_prompt_suffix_fragments`: scope-level system suffix fragments.
56
+ - `user_prompt_suffix_fragments`: scope-level user suffix fragments.
57
+ - `implicit_references`: scope-level implicit global references.
58
+
59
+ Mode semantics:
60
+
61
+ - `mode="inherit"` (default):
62
+ - `system_prompt_suffix_fragments` and `user_prompt_suffix_fragments` are appended.
63
+ - `implicit_references` are merged additively with conflict checks.
64
+ - `mode="replace"`:
65
+ - `None` means no change.
66
+ - Explicit `[]` or `{}` clears inherited list/dict values.
67
+ - Explicit list/dict values (for example `[e1, e2]` or `{k1: v1, k2: v2}`) fully replace inherited values.
68
+
69
+ For `oversight`, omitted means inherit the current hooks, while explicit `None` clears them for the nested scope.
70
+
71
+ The context manager yields the resolved `StepExecutor` for the scope.
72
+
73
+ `StepExecutorConfiguration` also accepts `system_prompt_suffix_fragments` and `user_prompt_suffix_fragments` (tuples of strings) as baseline suffix fragments for the whole run. In `mode="inherit"`, scope-level fragments are appended after configuration-level fragments.
74
+
75
+ ## Scoped implicit references
76
+
77
+ `implicit_references` can inject global helper functions as step capabilities:
78
+
79
+ ```py
80
+ def search_repository(query: str) -> list[str]: ...
81
+
82
+ with nh.run(step_executor):
83
+ with nh.scope(implicit_references={"search_repository": search_repository}):
84
+ triage_issue(ticket_text)
85
+ ```
86
+
87
+ In `mode="inherit"` (default), nested scopes merge additively with conflict checks.
88
+ In `mode="replace"`, explicit mappings fully replace inherited mappings.
89
+
90
+ ```py
91
+ with nh.run(step_executor), nh.scope(implicit_references={"parent": search_repository}):
92
+ with nh.scope(mode="replace", implicit_references={}):
93
+ triage_issue(ticket_text)
94
+ ```
95
+
96
+ The inner scope above clears inherited implicit references.
97
+
98
+ ```py
99
+ with nh.scope(mode="replace", implicit_references={"child": search_repository}):
100
+ triage_issue(ticket_text)
101
+ ```
102
+
103
+ This fully replaces inherited references with the provided mapping.
104
+
105
+ ```py
106
+ with nh.scope(mode="replace", implicit_references=None):
107
+ triage_issue(ticket_text)
108
+ ```
109
+
110
+ This keeps inherited references unchanged.
111
+
112
+ ## Prompt suffix fragments in scopes
113
+
114
+ Use list values when setting scope-level suffix fragments:
115
+
116
+ ```py
117
+ with nh.run(step_executor):
118
+ with nh.scope(system_prompt_suffix_fragments=["Use concise answers."]):
119
+ summarize_ticket(ticket_text)
120
+ ```
121
+
122
+ `user_prompt_suffix_fragments` follows the same rules.
123
+
124
+ ```py
125
+ with nh.scope(user_prompt_suffix_fragments=["Focus on actionable output."]):
126
+ summarize_ticket(ticket_text)
127
+ ```
128
+
129
+ In `mode="inherit"`, provided lists are appended.
130
+ In `mode="replace"`, provided lists fully replace inherited lists.
131
+
132
+ ```py
133
+ with nh.run(step_executor), nh.scope(system_prompt_suffix_fragments=["parent"]):
134
+ with nh.scope(mode="replace", system_prompt_suffix_fragments=["child_1", "child_2"]):
135
+ summarize_ticket(ticket_text)
136
+ ```
137
+
138
+ Pass `[]` to clear inherited fragments.
139
+
140
+ ```py
141
+ with nh.scope(mode="replace", system_prompt_suffix_fragments=[]):
142
+ summarize_ticket(ticket_text)
143
+ ```
144
+
145
+ Pass `None` to keep inherited fragments unchanged.
146
+
147
+ ```py
148
+ with nh.scope(mode="replace", system_prompt_suffix_fragments=None):
149
+ summarize_ticket(ticket_text)
150
+ ```
151
+
152
+ The same `replace` semantics apply to `user_prompt_suffix_fragments`.
153
+
154
+ ## Synchronous oversight in scopes
155
+
156
+ Use `nh.scope(oversight=...)` when the host needs synchronous inspection around tool calls or a final rewrite/reject checkpoint before Nighthawk commits a step result.
157
+
158
+ ```py
159
+ def inspect_tool_call(tool_call: nh.oversight.ToolCall) -> nh.oversight.ToolCallDecision:
160
+ if tool_call.tool_name == "delete_file":
161
+ return nh.oversight.Reject("Deletion must be approved by a human.")
162
+ return nh.oversight.Accept()
163
+
164
+
165
+ def inspect_step_commit(proposal: nh.oversight.StepCommitProposal) -> nh.oversight.StepCommitDecision:
166
+ if "result" in proposal.proposed_binding_name_to_value:
167
+ return nh.oversight.Rewrite(rewritten_binding_name_to_value={"result": "reviewed"})
168
+ return nh.oversight.Accept()
169
+
170
+
171
+ with nh.run(step_executor):
172
+ with nh.scope(
173
+ oversight=nh.oversight.Oversight(
174
+ inspect_tool_call=inspect_tool_call,
175
+ inspect_step_commit=inspect_step_commit,
176
+ )
177
+ ):
178
+ inspected_step(ticket_text)
179
+ ```
180
+
181
+ Tool rejections are returned to the model as a normal tool result envelope with `error.kind == "oversight"`. Step rejections raise `nh.oversight.OversightRejectedError` to the host. Rewrite values still flow through the normal step finalization path. For the normative boundary rule on which tool-call failures are envelope-wrapped versus propagated as host exceptions, see [Specification Section 8.3](specification.md#83-tool-boundary-contract-built-in-tooling).
182
+
183
+ ## Mixing executors
184
+
185
+ Use `nh.scope(step_executor=...)` to switch executors within a single run. This is the standard pattern for mixing a cheap classifier with a deep autonomous step:
186
+
187
+ ```py
188
+ fast_executor = nh.AgentStepExecutor.from_configuration(
189
+ configuration=nh.StepExecutorConfiguration(model="openai-responses:gpt-5.4-mini"),
190
+ )
191
+
192
+ deep_executor = nh.AgentStepExecutor.from_configuration(
193
+ configuration=nh.StepExecutorConfiguration(model="codex:default"),
194
+ )
195
+
196
+ with nh.run(fast_executor):
197
+ label = classify_ticket(text) # fast, cheap
198
+ with nh.scope(step_executor=deep_executor):
199
+ diagnosis = inspect_repository(text) # deep, autonomous
200
+ ```
201
+
202
+ See [Executors](executors.md#decision-tree) for when to choose a coding agent backend over a provider-backed executor.
203
+
204
+ ## Context limits
205
+
206
+ The LOCALS and GLOBALS sections are bounded by token and item limits configured via `StepContextLimits`. When a limit is reached, remaining entries are omitted and a `<snipped>` marker is appended. The underlying data remains in Python memory and is accessible through binding functions at runtime -- truncation affects prompt coherence, not data availability.
207
+
208
+ ```py
209
+ configuration = nh.StepExecutorConfiguration(
210
+ model="openai-responses:gpt-5.4-mini",
211
+ context_limits=nh.StepContextLimits(
212
+ locals_max_tokens=4096,
213
+ locals_max_items=50,
214
+ ),
215
+ )
216
+ ```
217
+
218
+ See [Specification Section 8.2](specification.md#82-prompt-context) for the full specification.
219
+
220
+ ## JSON rendering style
221
+
222
+ `StepExecutorConfiguration` also accepts `json_renderer_style`, which controls how values are rendered in prompt context and tool results (e.g., strict JSON vs annotated pseudo-JSON with omission markers). See [Specification Section 5.2](specification.md#52-configuration) for available styles.
223
+
224
+ ## Runtime execution identity
225
+
226
+ Each `nh.run()` generates an `ExecutionRef` with a unique `run_id` (trace root) and `scope_id`. Nested `nh.scope()` calls generate new `scope_id` values while keeping the same `run_id`.
227
+
228
+ ```py
229
+ execution_ref = nh.get_execution_ref()
230
+ execution_ref.run_id # trace root -- stable across nested scopes
231
+ execution_ref.scope_id # current scope -- changes with each nh.scope()
232
+ execution_ref.step_id # None outside active step execution
233
+ ```
234
+
235
+ Use `run_id` to correlate distributed agent processes in logs and traces. Use `scope_id` to identify the current logical execution context. See [Specification Section 10](specification.md#10-runtime-scoping) for the full specification and [Verification: observability](verification.md#observability) for tracing integration.
236
+
237
+ ## Usage metering
238
+
239
+ Each `nh.run()` creates a `UsageMeter` that accumulates LLM token usage across all Natural block executions in the run. The meter is thread-safe and updated automatically after each step.
240
+
241
+ ```py
242
+ meter = nh.get_current_usage_meter() # None outside nh.run()
243
+ if meter is not None:
244
+ meter.total_tokens # cumulative input + output tokens
245
+ meter.snapshot() # independent RunUsage copy of current totals
246
+ ```
247
+
248
+ `get_current_usage_meter()` returns `None` outside an active `nh.run()` context. Use the meter to inspect cumulative cost at decision points -- for example, to choose a cheaper model mid-pipeline when spend is high. For automatic budget enforcement, see [Patterns: Budget](patterns.md#budget).
249
+
250
+ ## Next steps
251
+
252
+ Continue to **[Patterns](patterns.md)** for outcomes, error handling, async, cross-block composition, resilience, and common mistakes.
@@ -91,9 +91,6 @@ The condensed coding agent guide (`for-coding-agents.md`) is a derivative docume
91
91
  - `json_renderer_style`: [headson](https://github.com/kantord/headson) rendering style used in prompt context and tool result envelopes. Default: `"default"`. Available values: `"strict"` (valid JSON, no annotations), `"default"` (pseudo-JSON with omission markers like `…`), `"detailed"` (JS-like with inline comments such as `// N more`).
92
92
  - `system_prompt_suffix_fragments`: optional baseline system prompt suffix fragments for this executor configuration.
93
93
  - `user_prompt_suffix_fragments`: optional baseline user prompt suffix fragments for this executor configuration.
94
- - `StepExecutorConfigurationPatch`
95
- - Partial override object for scoped configuration updates.
96
- - Supports patching model, model settings, templates, limits, renderer style, tokenizer encoding, and prompt suffix fragment tuples.
97
94
 
98
95
  ### 5.3. Supporting types
99
96
 
@@ -106,17 +103,18 @@ The condensed coding agent guide (`for-coding-agents.md`) is a derivative docume
106
103
  - Fields: `locals_max_tokens`, `locals_max_items`, `globals_max_tokens`, `globals_max_items`, `value_max_tokens`, `object_max_methods`, `object_max_fields`, `object_field_value_max_tokens`, `tool_result_max_tokens`.
107
104
  - `JsonableValue`
108
105
  - Type alias for JSON-serializable Python values (`dict | list | str | int | float | bool | None`).
109
- - `ExecutionContext`
106
+ - `ExecutionRef`
110
107
  - Frozen dataclass representing runtime execution identity.
111
108
  - `run_id`: the Id of the outermost run (trace root).
112
109
  - `scope_id`: the Id of the current scope.
110
+ - `step_id`: the Id of the current step when available, otherwise `None`.
113
111
 
114
112
  ### 5.4. Runtime accessors
115
113
 
116
114
  - `nighthawk.get_current_step_context() -> StepContext`
117
115
  - Get the `StepContext` for the currently executing Natural block. Raises if no step is active.
118
116
 
119
- See [Section 10](#10-runtime-scoping) for additional runtime accessors (`get_step_executor`, `get_execution_context`).
117
+ See [Section 10](#10-runtime-scoping) for additional runtime accessors (`get_step_executor`, `get_execution_ref`).
120
118
 
121
119
  ## 6. Natural block detection
122
120
 
@@ -395,6 +393,7 @@ Eval tool:
395
393
 
396
394
  - `nh_eval(expression: str) -> object`
397
395
  - Evaluate a Python expression and return the result. Use to inspect values, call functions, and mutate objects in-place.
396
+ - In-place mutations performed via `nh_eval` are not runtime-validated.
398
397
  - If the evaluated expression is awaitable, it is awaited before returning.
399
398
 
400
399
  Binding tool:
@@ -424,13 +423,16 @@ Semantics of `nh_assign`:
424
423
  - Traverse attributes for each intermediate segment.
425
424
  - Assign using attribute assignment on the final segment.
426
425
  - Validation:
427
- - Validate only when runtime type metadata is available; otherwise assign without validation.
426
+ - Validate only the final assigned field when runtime type metadata is available; otherwise assign without validation.
428
427
 
429
428
  Commit and mutation notes:
430
429
 
431
430
  - Commit selection is controlled only by `<:name>` bindings.
432
431
  - `<:name>` selects which top-level names are committed from `step_locals` into Python locals at Natural block boundaries.
433
- - Dotted mutation is independent of `<:name>`.
432
+ - Dotted `nh_assign` on a write binding root marks that root as dirty for commit selection.
433
+ - Dotted `nh_assign` on a read binding root does not participate in commit selection.
434
+ - Final validation is applied only to committed write bindings at step finalization.
435
+ - Dotted mutation on read bindings and in-place mutation via `nh_eval` are outside the final validation guarantee.
434
436
 
435
437
  Write tool return value:
436
438
 
@@ -449,6 +451,11 @@ All tool results are wrapped in a JSON envelope with the following structure:
449
451
 
450
452
  Error kind categories: `invalid_input`, `resolution`, `execution`, `transient`, `internal`.
451
453
 
454
+ Boundary rule for tool-call failures:
455
+
456
+ - If the model can recover by changing tool arguments, choosing another tool, or continuing without the tool, the failure MUST be returned in the JSON envelope (`{"value": null, "error": ...}`).
457
+ - Host invariant violations (for example, broken runtime contract or invalid host-side oversight hook contract) MAY propagate as Python exceptions to the host instead of being envelope-wrapped.
458
+
452
459
  The `value` field is bounded by `context_limits.tool_result_max_tokens` and may be summarized using [headson](https://github.com/kantord/headson) truncation when the full rendering exceeds the token budget. Headson is a structure-aware JSON summarizer: it parses the full JSON tree, then selects representative nodes to produce a compact preview that preserves the shape and key values of the data within a strict byte budget (analogous to `head`/`tail` but for structured data).
453
460
 
454
461
  Atomicity requirement:
@@ -573,10 +580,11 @@ The required runtime object for step execution is:
573
580
 
574
581
  - `step_executor` (required): a strategy object responsible for executing steps (Natural blocks).
575
582
 
576
- Runtime execution identity is modeled separately in `ExecutionContext`:
583
+ Runtime execution identity is modeled separately in `ExecutionRef`:
577
584
 
578
585
  - `run_id`: the Id of the outermost run (trace root). This serves as the golden thread that connects distributed agent processes (e.g. parent, child, grandchild) across process boundaries in observability tools.
579
586
  - `scope_id`: the Id of the current (possibly nested) run scope. This serves as the identity of the current logical execution context.
587
+ - `step_id`: the Id of the current step when available. Outside active step execution it is `None`.
580
588
 
581
589
  Nighthawk does not own workspace filesystem concerns (such as include resolution or host file operations). Those concerns belong to the host application layer that embeds Nighthawk.
582
590
 
@@ -585,21 +593,27 @@ API:
585
593
 
586
594
  - `nighthawk.run(step_executor: StepExecutor, *, run_id: str | None = None)`
587
595
  - Replaces the current context step executor with the provided step executor.
588
- - Generates a new `ExecutionContext` for the duration of the `with`.
596
+ - Generates a new `ExecutionRef` for the duration of the `with`.
589
597
  - Uses provided `run_id` when given; otherwise generates a new `run_id` (trace root).
590
598
  - Always generates a fresh `scope_id`.
591
599
  - Can be used even when no step executor is currently set.
592
- - `nighthawk.scope(*, step_executor_configuration: StepExecutorConfiguration | None = None, step_executor_configuration_patch: StepExecutorConfigurationPatch | None = None, step_executor: StepExecutor | None = None, system_prompt_suffix_fragment: str | None = None, user_prompt_suffix_fragment: str | None = None) -> Iterator[StepExecutor]`
600
+ - `nighthawk.scope(*, mode: Literal["inherit", "replace"] = "inherit", step_executor_configuration: StepExecutorConfiguration | None = None, step_executor: StepExecutor | None = None, oversight: Oversight | None = None, system_prompt_suffix_fragments: Sequence[str] | None = None, user_prompt_suffix_fragments: Sequence[str] | None = None, implicit_references: Mapping[str, object] | None = None) -> Iterator[StepExecutor]`
593
601
  - Enter a nested scope within the current run.
594
602
  - Requires an existing step executor.
595
603
  - Generates a new `scope_id` (keeps the current `run_id`).
596
- - Only specified fields are overridden for the duration of the `with`.
597
- - `system_prompt_suffix_fragment`: optional string appended to the system prompt for the duration of the scope.
598
- - `user_prompt_suffix_fragment`: optional string appended to the user prompt for the duration of the scope.
604
+ - `oversight` omitted means inherit the current hooks; explicit `None` clears them for the nested scope.
605
+ - `mode="inherit"` (default):
606
+ - Appends prompt suffix fragment lists.
607
+ - Merges `implicit_references` additively with conflict checks.
608
+ - `mode="replace"`:
609
+ - Replaces provided list/mapping values.
610
+ - `None` means no change.
611
+ - Explicit `[]` / `{}` clears inherited list/mapping values.
612
+ - Explicit list/mapping values (for example `[e1, e2]` or `{k1: v1, k2: v2}`) fully replace inherited values.
599
613
  - Yields the resolved `StepExecutor` for the scope.
600
614
  - `nighthawk.get_step_executor() -> StepExecutor`
601
615
  - Get the current step executor. Raises if unset.
602
- - `nighthawk.get_execution_context() -> ExecutionContext`
616
+ - `nighthawk.get_execution_ref() -> ExecutionRef`
603
617
  - Get the current runtime execution identity. Raises if unset.
604
618
 
605
619
  ### 10.1. Observability contract (OpenTelemetry span/event)
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "nighthawk-python"
3
- version = "0.8.0"
3
+ version = "0.10.0"
4
4
  description = "A Python library where Python controls flow and LLMs or coding agents reason within constrained Natural blocks."
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.13"
@@ -1,9 +1,9 @@
1
1
  from __future__ import annotations
2
2
 
3
+ from . import oversight, resilience
3
4
  from .configuration import (
4
5
  StepContextLimits,
5
6
  StepExecutorConfiguration,
6
- StepExecutorConfigurationPatch,
7
7
  StepPromptTemplates,
8
8
  )
9
9
  from .errors import (
@@ -16,15 +16,15 @@ from .errors import (
16
16
  )
17
17
  from .json_renderer import JsonableValue, to_jsonable_value
18
18
  from .natural.decorator import natural_function
19
- from .runtime.scoping import ExecutionContext, UsageMeter, get_current_usage_meter, get_execution_context, get_step_executor, run, scope
19
+ from .runtime.scoping import ExecutionRef, UsageMeter, get_current_usage_meter, get_execution_ref, get_step_executor, run, scope
20
20
  from .runtime.step_context import StepContext, get_current_step_context
21
21
  from .runtime.step_executor import AgentStepExecutor, StepExecutor
22
22
  from .tools.registry import tool
23
23
 
24
24
  __all__ = [
25
25
  "AgentStepExecutor",
26
- "ExecutionContext",
27
26
  "ExecutionError",
27
+ "ExecutionRef",
28
28
  "JsonableValue",
29
29
  "NaturalParseError",
30
30
  "NighthawkError",
@@ -32,7 +32,6 @@ __all__ = [
32
32
  "StepContextLimits",
33
33
  "StepExecutor",
34
34
  "StepExecutorConfiguration",
35
- "StepExecutorConfigurationPatch",
36
35
  "StepPromptTemplates",
37
36
  "ToolEvaluationError",
38
37
  "ToolRegistrationError",
@@ -40,9 +39,11 @@ __all__ = [
40
39
  "UsageMeter",
41
40
  "get_current_step_context",
42
41
  "get_current_usage_meter",
43
- "get_execution_context",
42
+ "get_execution_ref",
44
43
  "get_step_executor",
44
+ "oversight",
45
45
  "natural_function",
46
+ "resilience",
46
47
  "run",
47
48
  "scope",
48
49
  "to_jsonable_value",
@@ -23,6 +23,7 @@ from pydantic_ai import RunContext
23
23
  from pydantic_ai.exceptions import UnexpectedModelBehavior
24
24
  from pydantic_ai.tools import ToolDefinition
25
25
 
26
+ from ..errors import NighthawkError
26
27
  from ..runtime.step_context import (
27
28
  DEFAULT_TOOL_RESULT_RENDERING_POLICY,
28
29
  StepContext,
@@ -203,7 +204,7 @@ async def mcp_server_if_needed(
203
204
  parent_otel_context = otel_context.get_current()
204
205
  parent_run_context = get_current_run_context()
205
206
  if parent_run_context is None:
206
- raise RuntimeError("Codex MCP tool server requires an active RunContext")
207
+ raise NighthawkError("Codex MCP tool server requires an active RunContext")
207
208
  typed_parent_run_context = cast(RunContext[StepContext], parent_run_context)
208
209
  tool_result_rendering_policy = typed_parent_run_context.deps.tool_result_rendering_policy
209
210
  if tool_result_rendering_policy is None: