nighthawk-python 0.13.0__tar.gz → 0.14.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 (184) hide show
  1. {nighthawk_python-0.13.0 → nighthawk_python-0.14.0}/.github/workflows/ci.yml +15 -6
  2. {nighthawk_python-0.13.0 → nighthawk_python-0.14.0}/.github/workflows/docs.yml +8 -2
  3. {nighthawk_python-0.13.0 → nighthawk_python-0.14.0}/.github/workflows/publish.yml +1 -1
  4. {nighthawk_python-0.13.0 → nighthawk_python-0.14.0}/CHANGELOG.md +26 -1
  5. {nighthawk_python-0.13.0 → nighthawk_python-0.14.0}/PKG-INFO +1 -1
  6. {nighthawk_python-0.13.0 → nighthawk_python-0.14.0}/docs/api.md +48 -4
  7. {nighthawk_python-0.13.0 → nighthawk_python-0.14.0}/docs/for-coding-agents.md +4 -4
  8. {nighthawk_python-0.13.0 → nighthawk_python-0.14.0}/docs/patterns.md +4 -4
  9. {nighthawk_python-0.13.0 → nighthawk_python-0.14.0}/docs/runtime-configuration.md +49 -14
  10. {nighthawk_python-0.13.0 → nighthawk_python-0.14.0}/docs/specification.md +74 -34
  11. {nighthawk_python-0.13.0 → nighthawk_python-0.14.0}/docs/verification.md +3 -2
  12. {nighthawk_python-0.13.0 → nighthawk_python-0.14.0}/pyproject.toml +1 -1
  13. {nighthawk_python-0.13.0 → nighthawk_python-0.14.0}/src/nighthawk/__init__.py +8 -5
  14. {nighthawk_python-0.13.0 → nighthawk_python-0.14.0}/src/nighthawk/errors.py +15 -1
  15. nighthawk_python-0.14.0/src/nighthawk/lifecycle.py +129 -0
  16. {nighthawk_python-0.13.0 → nighthawk_python-0.14.0}/src/nighthawk/natural/decorator.py +33 -42
  17. {nighthawk_python-0.13.0 → nighthawk_python-0.14.0}/src/nighthawk/natural/transform.py +20 -5
  18. {nighthawk_python-0.13.0 → nighthawk_python-0.14.0}/src/nighthawk/oversight.py +37 -10
  19. nighthawk_python-0.14.0/src/nighthawk/runtime/execution_reference.py +13 -0
  20. {nighthawk_python-0.13.0 → nighthawk_python-0.14.0}/src/nighthawk/runtime/prompt.py +6 -6
  21. {nighthawk_python-0.13.0 → nighthawk_python-0.14.0}/src/nighthawk/runtime/runner.py +285 -187
  22. {nighthawk_python-0.13.0 → nighthawk_python-0.14.0}/src/nighthawk/runtime/scoping.py +48 -34
  23. {nighthawk_python-0.13.0 → nighthawk_python-0.14.0}/src/nighthawk/runtime/step_context.py +2 -1
  24. {nighthawk_python-0.13.0 → nighthawk_python-0.14.0}/src/nighthawk/testing.py +4 -0
  25. {nighthawk_python-0.13.0 → nighthawk_python-0.14.0}/src/nighthawk/tools/execution.py +6 -11
  26. {nighthawk_python-0.13.0 → nighthawk_python-0.14.0}/tests/backends/test_codex.py +15 -3
  27. {nighthawk_python-0.13.0 → nighthawk_python-0.14.0}/tests/docs/test_docs_architecture.py +32 -0
  28. {nighthawk_python-0.13.0 → nighthawk_python-0.14.0}/tests/docs/test_prompt_examples.py +2 -1
  29. {nighthawk_python-0.13.0 → nighthawk_python-0.14.0}/tests/execution/prompt_test_helpers.py +1 -1
  30. {nighthawk_python-0.13.0 → nighthawk_python-0.14.0}/tests/execution/test_globals_prompt.py +1 -1
  31. {nighthawk_python-0.13.0 → nighthawk_python-0.14.0}/tests/execution/test_managed_model.py +6 -1
  32. {nighthawk_python-0.13.0 → nighthawk_python-0.14.0}/tests/execution/test_natural_block_ordering.py +12 -11
  33. {nighthawk_python-0.13.0 → nighthawk_python-0.14.0}/tests/execution/test_natural_traceback.py +3 -3
  34. {nighthawk_python-0.13.0 → nighthawk_python-0.14.0}/tests/execution/test_runtime.py +14 -14
  35. {nighthawk_python-0.13.0 → nighthawk_python-0.14.0}/tests/governance/test_governance.py +5 -5
  36. nighthawk_python-0.14.0/tests/governance/test_return_expression_policy.py +172 -0
  37. nighthawk_python-0.14.0/tests/governance/test_step_completion.py +630 -0
  38. nighthawk_python-0.14.0/tests/governance/test_step_ledger.py +462 -0
  39. {nighthawk_python-0.13.0 → nighthawk_python-0.14.0}/tests/governance/test_step_results.py +1 -1
  40. {nighthawk_python-0.13.0 → nighthawk_python-0.14.0}/tests/public/test_composition.py +2 -2
  41. {nighthawk_python-0.13.0 → nighthawk_python-0.14.0}/tests/public/test_public_api.py +13 -12
  42. {nighthawk_python-0.13.0 → nighthawk_python-0.14.0}/tests/tools/test_assignment_async.py +7 -6
  43. {nighthawk_python-0.13.0 → nighthawk_python-0.14.0}/tests/tools/test_declarations.py +10 -10
  44. {nighthawk_python-0.13.0 → nighthawk_python-0.14.0}/tests/tools/test_provided_async.py +2 -1
  45. {nighthawk_python-0.13.0 → nighthawk_python-0.14.0}/tests/tools/test_tool_boundary.py +3 -3
  46. {nighthawk_python-0.13.0 → nighthawk_python-0.14.0}/uv.lock +1 -1
  47. {nighthawk_python-0.13.0 → nighthawk_python-0.14.0}/.claude/rules/docs.md +0 -0
  48. {nighthawk_python-0.13.0 → nighthawk_python-0.14.0}/.claude/rules/promptfoo.md +0 -0
  49. {nighthawk_python-0.13.0 → nighthawk_python-0.14.0}/.claude/rules/src.md +0 -0
  50. {nighthawk_python-0.13.0 → nighthawk_python-0.14.0}/.claude/rules/tests.md +0 -0
  51. {nighthawk_python-0.13.0 → nighthawk_python-0.14.0}/.claude/settings.json +0 -0
  52. {nighthawk_python-0.13.0 → nighthawk_python-0.14.0}/.claude/unset_envs.sh +0 -0
  53. {nighthawk_python-0.13.0 → nighthawk_python-0.14.0}/.devcontainer/devcontainer-lock.json +0 -0
  54. {nighthawk_python-0.13.0 → nighthawk_python-0.14.0}/.devcontainer/devcontainer.json +0 -0
  55. {nighthawk_python-0.13.0 → nighthawk_python-0.14.0}/.devcontainer/postCreateCommand.sh +0 -0
  56. {nighthawk_python-0.13.0 → nighthawk_python-0.14.0}/.github/dependabot.yml +0 -0
  57. {nighthawk_python-0.13.0 → nighthawk_python-0.14.0}/.gitignore +0 -0
  58. {nighthawk_python-0.13.0 → nighthawk_python-0.14.0}/.python-version +0 -0
  59. {nighthawk_python-0.13.0 → nighthawk_python-0.14.0}/AGENTS.md +0 -0
  60. {nighthawk_python-0.13.0 → nighthawk_python-0.14.0}/CLAUDE.md +0 -0
  61. {nighthawk_python-0.13.0 → nighthawk_python-0.14.0}/CONTRIBUTING.md +0 -0
  62. {nighthawk_python-0.13.0 → nighthawk_python-0.14.0}/LICENSE +0 -0
  63. {nighthawk_python-0.13.0 → nighthawk_python-0.14.0}/README.md +0 -0
  64. {nighthawk_python-0.13.0 → nighthawk_python-0.14.0}/docs/AGENTS.md +0 -0
  65. {nighthawk_python-0.13.0 → nighthawk_python-0.14.0}/docs/assets/nighthawk_logo-128x128.png +0 -0
  66. {nighthawk_python-0.13.0 → nighthawk_python-0.14.0}/docs/coding-agent-backends.md +0 -0
  67. {nighthawk_python-0.13.0 → nighthawk_python-0.14.0}/docs/executors.md +0 -0
  68. {nighthawk_python-0.13.0 → nighthawk_python-0.14.0}/docs/index.md +0 -0
  69. {nighthawk_python-0.13.0 → nighthawk_python-0.14.0}/docs/natural-blocks.md +0 -0
  70. {nighthawk_python-0.13.0 → nighthawk_python-0.14.0}/docs/philosophy.md +0 -0
  71. {nighthawk_python-0.13.0 → nighthawk_python-0.14.0}/docs/pydantic-ai-providers.md +0 -0
  72. {nighthawk_python-0.13.0 → nighthawk_python-0.14.0}/docs/quickstart.md +0 -0
  73. {nighthawk_python-0.13.0 → nighthawk_python-0.14.0}/docs/roadmap.md +0 -0
  74. {nighthawk_python-0.13.0 → nighthawk_python-0.14.0}/evals/promptfoo/assertions/__init__.py +0 -0
  75. {nighthawk_python-0.13.0 → nighthawk_python-0.14.0}/evals/promptfoo/assertions/binding_value.py +0 -0
  76. {nighthawk_python-0.13.0 → nighthawk_python-0.14.0}/evals/promptfoo/assertions/outcome_kind.py +0 -0
  77. {nighthawk_python-0.13.0 → nighthawk_python-0.14.0}/evals/promptfoo/assertions/raise_message.py +0 -0
  78. {nighthawk_python-0.13.0 → nighthawk_python-0.14.0}/evals/promptfoo/evidence/2026-03-26-baseline-prompt-ab.md +0 -0
  79. {nighthawk_python-0.13.0 → nighthawk_python-0.14.0}/evals/promptfoo/evidence/2026-03-26-baseline-regression.md +0 -0
  80. {nighthawk_python-0.13.0 → nighthawk_python-0.14.0}/evals/promptfoo/evidence/2026-03-26-baseline-suffix-ab.md +0 -0
  81. {nighthawk_python-0.13.0 → nighthawk_python-0.14.0}/evals/promptfoo/evidence/2026-04-27-runtime-prompt-optimization.md +0 -0
  82. {nighthawk_python-0.13.0 → nighthawk_python-0.14.0}/evals/promptfoo/promptfooconfig-agents.yaml +0 -0
  83. {nighthawk_python-0.13.0 → nighthawk_python-0.14.0}/evals/promptfoo/promptfooconfig-luna-sanity.yaml +0 -0
  84. {nighthawk_python-0.13.0 → nighthawk_python-0.14.0}/evals/promptfoo/promptfooconfig-prompt-ab.yaml +0 -0
  85. {nighthawk_python-0.13.0 → nighthawk_python-0.14.0}/evals/promptfoo/promptfooconfig-suffix-ab.yaml +0 -0
  86. {nighthawk_python-0.13.0 → nighthawk_python-0.14.0}/evals/promptfoo/promptfooconfig.yaml +0 -0
  87. {nighthawk_python-0.13.0 → nighthawk_python-0.14.0}/evals/promptfoo/prompts/eval_coding_agent.txt +0 -0
  88. {nighthawk_python-0.13.0 → nighthawk_python-0.14.0}/evals/promptfoo/prompts/eval_default.txt +0 -0
  89. {nighthawk_python-0.13.0 → nighthawk_python-0.14.0}/evals/promptfoo/prompts/eval_legacy_default.txt +0 -0
  90. {nighthawk_python-0.13.0 → nighthawk_python-0.14.0}/evals/promptfoo/prompts/eval_mutation_aware.txt +0 -0
  91. {nighthawk_python-0.13.0 → nighthawk_python-0.14.0}/evals/promptfoo/prompts/eval_sequenced.txt +0 -0
  92. {nighthawk_python-0.13.0 → nighthawk_python-0.14.0}/evals/promptfoo/provider.py +0 -0
  93. {nighthawk_python-0.13.0 → nighthawk_python-0.14.0}/evals/promptfoo/research-result.md +0 -0
  94. {nighthawk_python-0.13.0 → nighthawk_python-0.14.0}/evals/promptfoo/test_cases/binding_operations.yaml +0 -0
  95. {nighthawk_python-0.13.0 → nighthawk_python-0.14.0}/evals/promptfoo/test_cases/edge_cases.yaml +0 -0
  96. {nighthawk_python-0.13.0 → nighthawk_python-0.14.0}/evals/promptfoo/test_cases/loop_outcomes.yaml +0 -0
  97. {nighthawk_python-0.13.0 → nighthawk_python-0.14.0}/evals/promptfoo/test_cases/loop_outcomes_ja.yaml +0 -0
  98. {nighthawk_python-0.13.0 → nighthawk_python-0.14.0}/evals/promptfoo/test_cases/multi_step.yaml +0 -0
  99. {nighthawk_python-0.13.0 → nighthawk_python-0.14.0}/evals/promptfoo/test_cases/null_handling.yaml +0 -0
  100. {nighthawk_python-0.13.0 → nighthawk_python-0.14.0}/evals/promptfoo/test_cases/outcome_kinds.yaml +0 -0
  101. {nighthawk_python-0.13.0 → nighthawk_python-0.14.0}/evals/promptfoo/test_cases/tool_selection.yaml +0 -0
  102. {nighthawk_python-0.13.0 → nighthawk_python-0.14.0}/mkdocs.yml +0 -0
  103. {nighthawk_python-0.13.0 → nighthawk_python-0.14.0}/pyrightconfig.json +0 -0
  104. {nighthawk_python-0.13.0 → nighthawk_python-0.14.0}/src/AGENTS.md +0 -0
  105. {nighthawk_python-0.13.0 → nighthawk_python-0.14.0}/src/nighthawk/backends/__init__.py +0 -0
  106. {nighthawk_python-0.13.0 → nighthawk_python-0.14.0}/src/nighthawk/backends/_text_fallback.py +0 -0
  107. {nighthawk_python-0.13.0 → nighthawk_python-0.14.0}/src/nighthawk/backends/base.py +0 -0
  108. {nighthawk_python-0.13.0 → nighthawk_python-0.14.0}/src/nighthawk/backends/claude_code_cli.py +0 -0
  109. {nighthawk_python-0.13.0 → nighthawk_python-0.14.0}/src/nighthawk/backends/claude_code_sdk.py +0 -0
  110. {nighthawk_python-0.13.0 → nighthawk_python-0.14.0}/src/nighthawk/backends/claude_code_settings.py +0 -0
  111. {nighthawk_python-0.13.0 → nighthawk_python-0.14.0}/src/nighthawk/backends/codex.py +0 -0
  112. {nighthawk_python-0.13.0 → nighthawk_python-0.14.0}/src/nighthawk/backends/mcp_boundary.py +0 -0
  113. {nighthawk_python-0.13.0 → nighthawk_python-0.14.0}/src/nighthawk/backends/mcp_server.py +0 -0
  114. {nighthawk_python-0.13.0 → nighthawk_python-0.14.0}/src/nighthawk/backends/text_projection.py +0 -0
  115. {nighthawk_python-0.13.0 → nighthawk_python-0.14.0}/src/nighthawk/backends/tool_bridge.py +0 -0
  116. {nighthawk_python-0.13.0 → nighthawk_python-0.14.0}/src/nighthawk/composition.py +0 -0
  117. {nighthawk_python-0.13.0 → nighthawk_python-0.14.0}/src/nighthawk/configuration.py +0 -0
  118. {nighthawk_python-0.13.0 → nighthawk_python-0.14.0}/src/nighthawk/identifier_path.py +0 -0
  119. {nighthawk_python-0.13.0 → nighthawk_python-0.14.0}/src/nighthawk/json_renderer.py +0 -0
  120. {nighthawk_python-0.13.0 → nighthawk_python-0.14.0}/src/nighthawk/natural/__init__.py +0 -0
  121. {nighthawk_python-0.13.0 → nighthawk_python-0.14.0}/src/nighthawk/natural/blocks.py +0 -0
  122. {nighthawk_python-0.13.0 → nighthawk_python-0.14.0}/src/nighthawk/resilience/__init__.py +0 -0
  123. {nighthawk_python-0.13.0 → nighthawk_python-0.14.0}/src/nighthawk/resilience/_budget.py +0 -0
  124. {nighthawk_python-0.13.0 → nighthawk_python-0.14.0}/src/nighthawk/resilience/_circuit_breaker.py +0 -0
  125. {nighthawk_python-0.13.0 → nighthawk_python-0.14.0}/src/nighthawk/resilience/_fallback.py +0 -0
  126. {nighthawk_python-0.13.0 → nighthawk_python-0.14.0}/src/nighthawk/resilience/_retry.py +0 -0
  127. {nighthawk_python-0.13.0 → nighthawk_python-0.14.0}/src/nighthawk/resilience/_timeout.py +0 -0
  128. {nighthawk_python-0.13.0 → nighthawk_python-0.14.0}/src/nighthawk/resilience/_vote.py +0 -0
  129. {nighthawk_python-0.13.0 → nighthawk_python-0.14.0}/src/nighthawk/runtime/__init__.py +0 -0
  130. {nighthawk_python-0.13.0 → nighthawk_python-0.14.0}/src/nighthawk/runtime/_user_content.py +0 -0
  131. {nighthawk_python-0.13.0 → nighthawk_python-0.14.0}/src/nighthawk/runtime/async_bridge.py +0 -0
  132. {nighthawk_python-0.13.0 → nighthawk_python-0.14.0}/src/nighthawk/runtime/step_contract.py +0 -0
  133. {nighthawk_python-0.13.0 → nighthawk_python-0.14.0}/src/nighthawk/runtime/step_executor.py +0 -0
  134. {nighthawk_python-0.13.0 → nighthawk_python-0.14.0}/src/nighthawk/runtime/step_result.py +0 -0
  135. {nighthawk_python-0.13.0 → nighthawk_python-0.14.0}/src/nighthawk/runtime/tool_calls.py +0 -0
  136. {nighthawk_python-0.13.0 → nighthawk_python-0.14.0}/src/nighthawk/tools/__init__.py +0 -0
  137. {nighthawk_python-0.13.0 → nighthawk_python-0.14.0}/src/nighthawk/tools/_tool_return_content.py +0 -0
  138. {nighthawk_python-0.13.0 → nighthawk_python-0.14.0}/src/nighthawk/tools/assignment.py +0 -0
  139. {nighthawk_python-0.13.0 → nighthawk_python-0.14.0}/src/nighthawk/tools/contracts.py +0 -0
  140. {nighthawk_python-0.13.0 → nighthawk_python-0.14.0}/src/nighthawk/tools/declarations.py +0 -0
  141. {nighthawk_python-0.13.0 → nighthawk_python-0.14.0}/src/nighthawk/tools/provided.py +0 -0
  142. {nighthawk_python-0.13.0 → nighthawk_python-0.14.0}/src/nighthawk/ulid.py +0 -0
  143. {nighthawk_python-0.13.0 → nighthawk_python-0.14.0}/tests/AGENTS.md +0 -0
  144. {nighthawk_python-0.13.0 → nighthawk_python-0.14.0}/tests/__init__.py +0 -0
  145. {nighthawk_python-0.13.0 → nighthawk_python-0.14.0}/tests/backends/__init__.py +0 -0
  146. {nighthawk_python-0.13.0 → nighthawk_python-0.14.0}/tests/backends/test_claude_code_cli.py +0 -0
  147. {nighthawk_python-0.13.0 → nighthawk_python-0.14.0}/tests/backends/test_claude_code_sdk.py +0 -0
  148. {nighthawk_python-0.13.0 → nighthawk_python-0.14.0}/tests/conftest.py +0 -0
  149. {nighthawk_python-0.13.0 → nighthawk_python-0.14.0}/tests/docs/__init__.py +0 -0
  150. {nighthawk_python-0.13.0 → nighthawk_python-0.14.0}/tests/docs/test_coding_agent_examples.py +0 -0
  151. {nighthawk_python-0.13.0 → nighthawk_python-0.14.0}/tests/execution/__init__.py +0 -0
  152. {nighthawk_python-0.13.0 → nighthawk_python-0.14.0}/tests/execution/stub_executor.py +0 -0
  153. {nighthawk_python-0.13.0 → nighthawk_python-0.14.0}/tests/execution/test_capabilities.py +0 -0
  154. {nighthawk_python-0.13.0 → nighthawk_python-0.14.0}/tests/execution/test_execution_outcome_prompt_fragment.py +0 -0
  155. {nighthawk_python-0.13.0 → nighthawk_python-0.14.0}/tests/execution/test_infer_binding_types.py +0 -0
  156. {nighthawk_python-0.13.0 → nighthawk_python-0.14.0}/tests/execution/test_variables_prompt.py +0 -0
  157. {nighthawk_python-0.13.0 → nighthawk_python-0.14.0}/tests/integration/__init__.py +0 -0
  158. {nighthawk_python-0.13.0 → nighthawk_python-0.14.0}/tests/integration/skip_helpers.py +0 -0
  159. {nighthawk_python-0.13.0 → nighthawk_python-0.14.0}/tests/integration/test_carry_pattern.py +0 -0
  160. {nighthawk_python-0.13.0 → nighthawk_python-0.14.0}/tests/integration/test_claude_code_cli_integration.py +0 -0
  161. {nighthawk_python-0.13.0 → nighthawk_python-0.14.0}/tests/integration/test_claude_code_sdk_integration.py +0 -0
  162. {nighthawk_python-0.13.0 → nighthawk_python-0.14.0}/tests/integration/test_codex_integration.py +0 -0
  163. {nighthawk_python-0.13.0 → nighthawk_python-0.14.0}/tests/integration/test_llm_integration.py +0 -0
  164. {nighthawk_python-0.13.0 → nighthawk_python-0.14.0}/tests/natural/__init__.py +0 -0
  165. {nighthawk_python-0.13.0 → nighthawk_python-0.14.0}/tests/natural/test_blocks.py +0 -0
  166. {nighthawk_python-0.13.0 → nighthawk_python-0.14.0}/tests/natural/test_decorator_globals.py +0 -0
  167. {nighthawk_python-0.13.0 → nighthawk_python-0.14.0}/tests/natural/test_introspection.py +0 -0
  168. {nighthawk_python-0.13.0 → nighthawk_python-0.14.0}/tests/public/__init__.py +0 -0
  169. {nighthawk_python-0.13.0 → nighthawk_python-0.14.0}/tests/public/test_model_configuration.py +0 -0
  170. {nighthawk_python-0.13.0 → nighthawk_python-0.14.0}/tests/public/test_readme_example.py +0 -0
  171. {nighthawk_python-0.13.0 → nighthawk_python-0.14.0}/tests/public/test_usage_meter.py +0 -0
  172. {nighthawk_python-0.13.0 → nighthawk_python-0.14.0}/tests/resilience/__init__.py +0 -0
  173. {nighthawk_python-0.13.0 → nighthawk_python-0.14.0}/tests/resilience/test_budget.py +0 -0
  174. {nighthawk_python-0.13.0 → nighthawk_python-0.14.0}/tests/resilience/test_circuit_breaker.py +0 -0
  175. {nighthawk_python-0.13.0 → nighthawk_python-0.14.0}/tests/resilience/test_composition.py +0 -0
  176. {nighthawk_python-0.13.0 → nighthawk_python-0.14.0}/tests/resilience/test_fallback.py +0 -0
  177. {nighthawk_python-0.13.0 → nighthawk_python-0.14.0}/tests/resilience/test_retry.py +0 -0
  178. {nighthawk_python-0.13.0 → nighthawk_python-0.14.0}/tests/resilience/test_timeout.py +0 -0
  179. {nighthawk_python-0.13.0 → nighthawk_python-0.14.0}/tests/resilience/test_vote.py +0 -0
  180. {nighthawk_python-0.13.0 → nighthawk_python-0.14.0}/tests/test_renderer.py +0 -0
  181. {nighthawk_python-0.13.0 → nighthawk_python-0.14.0}/tests/test_testing.py +0 -0
  182. {nighthawk_python-0.13.0 → nighthawk_python-0.14.0}/tests/tools/__init__.py +0 -0
  183. {nighthawk_python-0.13.0 → nighthawk_python-0.14.0}/tests/tools/test_contracts.py +0 -0
  184. {nighthawk_python-0.13.0 → nighthawk_python-0.14.0}/tests/tools/test_multimodal_helpers.py +0 -0
@@ -19,13 +19,17 @@ jobs:
19
19
  name: Lint and type check
20
20
  runs-on: ubuntu-latest
21
21
  steps:
22
- - uses: actions/checkout@v6
22
+ - uses: actions/checkout@v7
23
23
  with:
24
24
  persist-credentials: false
25
25
 
26
- - uses: astral-sh/setup-uv@v7
26
+ # setup-uv publishes no floating major tag since v8, so pin an exact release.
27
+ - uses: astral-sh/setup-uv@v10.0.1
27
28
  with:
28
29
  python-version: "3.13"
30
+ # Same dependency set and cache key as "test (3.13)". Only that job
31
+ # saves the entry; concurrent writers race to reserve the same key.
32
+ save-cache: false
29
33
 
30
34
  - name: Install dependencies
31
35
  run: uv sync --all-extras --all-groups
@@ -48,11 +52,12 @@ jobs:
48
52
  python-version: ["3.13", "3.14"]
49
53
  continue-on-error: ${{ matrix.python-version != '3.13' }}
50
54
  steps:
51
- - uses: actions/checkout@v6
55
+ - uses: actions/checkout@v7
52
56
  with:
53
57
  persist-credentials: false
54
58
 
55
- - uses: astral-sh/setup-uv@v7
59
+ # Sole writer of the per-Python-version dependency cache.
60
+ - uses: astral-sh/setup-uv@v10.0.1
56
61
  with:
57
62
  python-version: ${{ matrix.python-version }}
58
63
 
@@ -67,11 +72,15 @@ jobs:
67
72
  needs: [lint, test]
68
73
  runs-on: ubuntu-latest
69
74
  steps:
70
- - uses: actions/checkout@v6
75
+ - uses: actions/checkout@v7
71
76
  with:
72
77
  persist-credentials: false
73
78
 
74
- - uses: astral-sh/setup-uv@v7
79
+ # "uv build" only resolves the build backend, so a cache entry would add
80
+ # no measurable benefit while colliding with the docs workflow key.
81
+ - uses: astral-sh/setup-uv@v10.0.1
82
+ with:
83
+ enable-cache: false
75
84
 
76
85
  - name: Build sdist and wheel
77
86
  run: uv build
@@ -24,9 +24,15 @@ jobs:
24
24
  build:
25
25
  runs-on: ubuntu-latest
26
26
  steps:
27
- - uses: actions/checkout@v6
27
+ - uses: actions/checkout@v7
28
28
 
29
- - uses: astral-sh/setup-uv@v7
29
+ # setup-uv publishes no floating major tag since v8, so pin an exact release.
30
+ - uses: astral-sh/setup-uv@v10.0.1
31
+ with:
32
+ python-version: "3.13"
33
+ # The docs group is a subset of the cache "test (3.13)" saves under
34
+ # this key, so restore it here but never overwrite it.
35
+ save-cache: false
30
36
 
31
37
  - name: Install dependencies
32
38
  run: uv sync --group docs
@@ -24,7 +24,7 @@ jobs:
24
24
  id-token: write
25
25
  steps:
26
26
  - name: Download distribution artifacts
27
- uses: actions/download-artifact@v6
27
+ uses: actions/download-artifact@v7
28
28
  with:
29
29
  name: python-package-distributions
30
30
  path: dist/
@@ -7,6 +7,30 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [0.14.0]
11
+
12
+ ### Added
13
+
14
+ - Scoped `nh.lifecycle.StepLifecycle` delivers typed completion, DSL raise, failure, or interruption records after generated caller assignments. Hosts can record one terminal event and translate failures with explicit exception chaining.
15
+ - `Oversight.inspect_return_expression` approves expressions before core evaluation, await, and return validation.
16
+ - `StepDeliveryError` supports host storage adapters without claiming persistence succeeded.
17
+
18
+ ### Changed
19
+
20
+ - `ExecutionReference` and `get_execution_reference` replace the abbreviated identity API. `step_execution_id` identifies each invocation; `source_location` identifies its source. StepContext, inspection, testing observations, terminal records, and spans share this identity.
21
+ - Runtime internal exceptions, including preparation and await failures, now propagate through `ExecutionError.step_failed` with the original exception as cause. Rejection is a classified failure. DSL exceptions remain distinct.
22
+ - Completion tracing follows actual caller assignments. Cancellation retains priority if terminal delivery fails. Delivery errors never retry execution or undo effects.
23
+ - Natural functions preserve original closure cells for nonlocal assignments and source locations for generated execution.
24
+
25
+ ### Fixed
26
+
27
+ - Lifecycle handlers can rethrow an already-recorded host exception or interruption without replacing its original cause with a self-reference.
28
+ - Host integration guidance distinguishes one delivery attempt from ledger deduplication and explains the validation, await, and rewrite limits of name-only return approval.
29
+
30
+ ### Removed
31
+
32
+ - `ExecutionRef`, `get_execution_ref`, and `step_id`, without compatibility aliases. Span attributes use `step.execution.id` and `step.source_location`.
33
+
10
34
  ## [0.13.0]
11
35
 
12
36
  ### Added
@@ -240,7 +264,8 @@ Invalid initial results fail before oversight; use resilience retries to recover
240
264
  - Step executor abstraction and provider integration foundation.
241
265
  - Core documentation and project scaffolding.
242
266
 
243
- [Unreleased]: https://github.com/kurusugawa-computer/nighthawk-python/compare/v0.13.0...HEAD
267
+ [Unreleased]: https://github.com/kurusugawa-computer/nighthawk-python/compare/v0.14.0...HEAD
268
+ [0.14.0]: https://github.com/kurusugawa-computer/nighthawk-python/compare/v0.13.0...v0.14.0
244
269
  [0.13.0]: https://github.com/kurusugawa-computer/nighthawk-python/compare/v0.12.0...v0.13.0
245
270
  [0.12.0]: https://github.com/kurusugawa-computer/nighthawk-python/compare/v0.11.0...v0.12.0
246
271
  [0.11.0]: https://github.com/kurusugawa-computer/nighthawk-python/compare/v0.10.0...v0.11.0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: nighthawk-python
3
- Version: 0.13.0
3
+ Version: 0.14.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/
@@ -23,13 +23,14 @@
23
23
  - StepContextLimits
24
24
  - JsonableValue
25
25
  - to_jsonable_value
26
- - ExecutionRef
26
+ - ExecutionReference
27
27
  - get_capabilities
28
28
  - get_step_context
29
29
  - get_usage_meter
30
- - get_execution_ref
30
+ - get_execution_reference
31
31
  - get_implicit_references
32
32
  - get_oversight
33
+ - get_lifecycle
33
34
  - get_step_executor
34
35
  - get_system_prompt_suffix_fragments
35
36
  - get_tools
@@ -37,6 +38,7 @@
37
38
  - resilience
38
39
  - UsageMeter
39
40
  - oversight
41
+ - lifecycle
40
42
 
41
43
  ## Errors
42
44
 
@@ -104,15 +106,42 @@
104
106
  - get_step_context
105
107
  - step_context_scope
106
108
 
109
+ ## Step Contracts
110
+
111
+ Import these types from `nighthawk.runtime.step_contract` when implementing a custom
112
+ `StepExecutor` or constructing a `nighthawk.testing.StepResponse`.
113
+ `StepKind` describes the allowed outcome kinds, and `StepOutcome` is the union of
114
+ the five outcome models, discriminated by their `kind` field.
115
+
116
+ These models describe executor results before the runner resolves them for
117
+ oversight. In particular, `ReturnStepOutcome.return_expression` holds a Python
118
+ expression that the runner evaluates, whereas `nighthawk.oversight.Return.value`
119
+ holds the evaluated value. Oversight hooks use `nighthawk.oversight.StepResult`.
120
+
121
+ ::: nighthawk.runtime.step_contract
122
+ options:
123
+ members:
124
+ - StepKind
125
+ - StepOutcome
126
+ - PassStepOutcome
127
+ - ReturnStepOutcome
128
+ - BreakStepOutcome
129
+ - ContinueStepOutcome
130
+ - RaiseStepOutcome
131
+
107
132
  ## Tool Contracts
108
133
 
109
134
  ::: nighthawk.tools.contracts
110
135
  options:
111
136
  members:
112
- - ToolResult
137
+ - ToolOutcome
113
138
  - ToolBoundaryError
114
- - Error
139
+ - ToolError
115
140
  - ErrorKind
141
+
142
+ ::: nighthawk.tools.execution
143
+ options:
144
+ members:
116
145
  - ToolResultWrapperToolset
117
146
 
118
147
  ## Resilience
@@ -147,6 +176,7 @@
147
176
  - Raise
148
177
  - StepResult
149
178
  - StepCommit
179
+ - ReturnExpression
150
180
  - Rewrite
151
181
  - Accept
152
182
  - Reject
@@ -155,3 +185,17 @@
155
185
  - OversightRejectedError
156
186
  - StepCommitDecision
157
187
  - ToolCallDecision
188
+
189
+ ## Lifecycle
190
+
191
+ ::: nighthawk.lifecycle
192
+ options:
193
+ members:
194
+ - StepLifecycle
195
+ - StepFinished
196
+ - StepCompleted
197
+ - StepRaised
198
+ - StepFailed
199
+ - StepInterrupted
200
+ - FailureStage
201
+ - StepDeliveryError
@@ -151,7 +151,7 @@ with nh.run(fast_executor):
151
151
  - `nh.scope()` must run inside `nh.run()`; it keeps `run_id` and creates a new `scope_id`.
152
152
  - Omission or `nh.UNSET` inherits; ordinary values replace and `[]`/`{}` clear collections.
153
153
  - Use `nh.Extend([...])` to append suffix fragments and `nh.Merge({...})` to merge implicit references by identity.
154
- - Only `oversight` accepts `None` to clear hooks; other scope fields reject it.
154
+ - `oversight` and `lifecycle` accept `None` to clear their hooks; other scope fields reject it.
155
155
  - Executor replacement precedes full configuration replacement. Managed Model instances retain identity; external agents own their model selection.
156
156
 
157
157
  ## 4. The standard contract shape
@@ -321,9 +321,9 @@ Error-handling rule:
321
321
 
322
322
  Async rule:
323
323
 
324
- - Async natural functions can use async binding functions.
325
- - If a sync natural function triggers an async binding function and gets an awaitable, Nighthawk raises `ExecutionError`.
326
- - Fix that by making the natural function `async`.
324
+ - Both sync and async Natural functions can call async binding functions through tools; tool expressions await their results.
325
+ - Only an async Natural function may have a final return expression that produces an awaitable. A sync Natural function rejects that final result with `ExecutionError`.
326
+ - Make the Natural function `async` when it needs an awaitable final return expression, not merely because it uses an async binding function.
327
327
 
328
328
  Resilience rule:
329
329
 
@@ -224,12 +224,12 @@ Each concurrent Natural block executes independently -- there is no shared messa
224
224
 
225
225
  Async natural functions can call sync binding functions, and sync natural functions can reference async binding functions. Nighthawk detects awaitable return values and handles them automatically:
226
226
 
227
- - In async natural functions: awaitable results from `nh_eval` and `nh_assign` expressions are awaited before returning to the LLM.
228
- - In sync natural functions: if the resolved return value is awaitable, execution fails (the caller must be async to await).
227
+ - Tool expressions await async binding function results in both sync and async Natural functions before returning them to the LLM.
228
+ - A final return expression may produce an awaitable only in an async Natural function. A sync Natural function rejects an awaitable final return value.
229
229
 
230
- This means you can mix sync and async binding functions freely in async natural functions without special handling.
230
+ You can therefore use async binding functions through tools without changing a synchronous Natural function into an async function.
231
231
 
232
- **Failure mode:** if a sync natural function references an async binding function and the LLM calls it, the expression produces an awaitable that cannot be awaited in a sync context. Nighthawk raises an `ExecutionError`. To fix, make the natural function `async`.
232
+ **Failure mode:** if a sync Natural function selects a return expression such as `fetch_result()` and that expression produces an awaitable, Nighthawk raises `ExecutionError`. Make the Natural function `async` to permit an awaitable final return expression. This restriction does not apply when the model calls `fetch_result()` through a tool and writes its resolved result to a binding. See [async execution semantics](specification.md#85-async-execution-model).
233
233
 
234
234
  ## Cross-block composition
235
235
 
@@ -10,7 +10,8 @@ Use `nh.scope()` to override execution settings within an existing run. Each sco
10
10
 
11
11
  ```py
12
12
  with nh.run(step_executor):
13
- # Replace the full executor configuration; omitted fields inherit scope state
13
+ # Replace the full executor configuration, including defaults for its omitted fields.
14
+ # Other omitted scope arguments inherit the parent scope values.
14
15
  with nh.scope(
15
16
  step_executor_configuration=nh.StepExecutorConfiguration(
16
17
  model="openai-responses:gpt-5.6-luna",
@@ -48,7 +49,8 @@ Parameters:
48
49
  - `step_executor_configuration`: replace the entire configuration.
49
50
  - `step_executor`: replace the step executor entirely.
50
51
  - `usage_meter`: replace the `UsageMeter` that steps in this scope record into. See [Usage metering](#usage-metering).
51
- - `oversight`: scope-level synchronous tool-call inspection and step-commit inspection hooks.
52
+ - `oversight`: scope-level synchronous tool-call, return-expression, and resolved step-commit inspection hooks.
53
+ - `lifecycle`: synchronous terminal delivery after caller assignments.
52
54
  - `system_prompt_suffix_fragments`: scope-level system suffix fragments.
53
55
  - `user_prompt_suffix_fragments`: scope-level user suffix fragments.
54
56
  - `implicit_references`: scope-level implicit global references.
@@ -62,7 +64,7 @@ Composition is selected by each supplied value:
62
64
  - `nh.Extend(sequence)` appends prompt fragments, tools, or capabilities.
63
65
  - `nh.Merge(mapping)` merges implicit references, rejecting a same-name value unless it is the identical Python object.
64
66
 
65
- Only `oversight` accepts `None`, which clears the hooks. Other scope fields reject it. `usage_meter` replaces the enclosing meter without forwarding totals. Resolution errors leave the parent context intact.
67
+ `oversight` and `lifecycle` accept `None`, which clears their hooks. Other scope fields reject it. `usage_meter` replaces the enclosing meter without forwarding totals. Resolution errors leave the parent context intact.
66
68
 
67
69
  The context manager yields the resolved `StepExecutor` for the scope.
68
70
 
@@ -118,13 +120,13 @@ from pydantic_ai import RunContext
118
120
  from pydantic_ai.tools import Tool
119
121
 
120
122
 
121
- def read_step_id(run_context: RunContext[nh.StepContext]) -> str:
123
+ def read_step_execution_id(run_context: RunContext[nh.StepContext]) -> str:
122
124
  """Return the current step Id."""
123
- return run_context.deps.step_id
125
+ return run_context.deps.execution_reference.step_execution_id or ""
124
126
 
125
127
 
126
128
  with nh.run(step_executor):
127
- with nh.scope(tools=[read_step_id, Tool(lookup_user, name="find_user")]):
129
+ with nh.scope(tools=[read_step_execution_id, Tool(lookup_user, name="find_user")]):
128
130
  triage_issue(ticket_text)
129
131
  ```
130
132
 
@@ -152,7 +154,7 @@ with nh.run(step_executor):
152
154
  triage_issue(ticket_text)
153
155
  ```
154
156
 
155
- Use capabilities to observe, gate, or instrument model requests with Pydantic AI's own hook vocabulary (`before_model_request`, `after_model_request`, `wrap_model_request`, and the rest of `AbstractCapability`). Nighthawk's `oversight` covers the two boundaries Nighthawk owns, tool calls and step commits; the model request boundary belongs to Pydantic AI. Use `nh.Extend([capability])` to append capabilities; a plain list replaces them. Repeated capability entries are preserved.
157
+ Use capabilities to observe, gate, or instrument model requests with Pydantic AI's own hook vocabulary (`before_model_request`, `after_model_request`, `wrap_model_request`, and the rest of `AbstractCapability`). Nighthawk's `oversight` covers tool calls, return-expression approval, and resolved step commits; the model request boundary belongs to Pydantic AI. Use `nh.Extend([capability])` to append capabilities; a plain list replaces them. Repeated capability entries are preserved.
156
158
 
157
159
  ## Prompt suffix fragments in scopes
158
160
 
@@ -204,6 +206,7 @@ Within an active `nh.run()` context, snapshot getters expose the current scope's
204
206
  - `nh.get_user_prompt_suffix_fragments()` returns the user prompt suffix fragments as a `tuple[str, ...]`.
205
207
  - `nh.get_tools()` returns the scoped native tools as a `tuple[Tool[StepContext], ...]`, excluding built-in tools.
206
208
  - `nh.get_capabilities()` returns the scoped Pydantic AI capabilities as a tuple.
209
+ - `nh.get_lifecycle()` returns the active `StepLifecycle`, or `None`.
207
210
  - `nh.get_oversight()` returns the active `Oversight`, or `None` when none is installed.
208
211
 
209
212
  The prompt suffix getters return only fragments accumulated via `nh.scope(...)`; configuration-level baseline fragments from `StepExecutorConfiguration` are not included.
@@ -219,7 +222,36 @@ with nh.run(step_executor):
219
222
  triage_issue(ticket_text)
220
223
  ```
221
224
 
222
- The getters other than `nh.get_oversight()` require an active run context. Outside `nh.run()` they raise `NighthawkError`, matching `nh.get_step_executor()` semantics. Catch `NighthawkError` if a helper needs to detect the absence of a run.
225
+ All getters listed above, including `nh.get_oversight()`, require an active run context. Outside `nh.run()` they raise `NighthawkError`, matching `nh.get_step_executor()` semantics. Catch `NighthawkError` if a helper needs to detect the absence of a run.
226
+
227
+ ## Record completed executions
228
+
229
+ Install `nh.lifecycle.StepLifecycle` to record actual execution endings, including preparation, validation, and return-await failures. The callback runs after generated assignments and can raise a host exception referring to its stored record. Return-expression approval is available separately through `Oversight.inspect_return_expression`; core retains evaluation and validation.
230
+
231
+ ```python
232
+ import nighthawk as nh
233
+
234
+ execution_id_to_event: dict[str, nh.lifecycle.StepFinished] = {}
235
+
236
+
237
+ def record_finished(event: nh.lifecycle.StepFinished) -> None:
238
+ execution_id = event.execution_reference.step_execution_id
239
+ assert execution_id is not None
240
+ execution_id_to_event[execution_id] = event
241
+
242
+
243
+ # Inside an active nh.run(executor):
244
+ with nh.scope(lifecycle=nh.lifecycle.StepLifecycle(record_finished)):
245
+ result = my_natural_function()
246
+ ```
247
+
248
+ This example is an in-memory history. Durable storage, serialization, and deduplication belong to the host. Callback failures do not roll back assignments or retry execution. See [terminal delivery semantics](specification.md#104-terminal-delivery-and-host-ledgers) for exception chaining, cancellation, and storage failure handling.
249
+
250
+ For a host ledger, use the executor boundary for admission and execution accounting, `inspect_step_commit` for approval before assignment, and StepLifecycle for terminal recording and exception translation. Correlate them with `execution_reference.step_execution_id`, which is unique per invocation; `source_location` only groups executions of the same source block. Preparation failures may have no admission record.
251
+
252
+ If an earlier host boundary already stored a failure and raised its public exception, the lifecycle handler still receives StepFailed. Skip a duplicate append only after verifying that exception refers to the stored event for this same execution, then rethrow the same exception. Core preserves its original cause. Merely returning from the handler results in an ExecutionError wrapper; suppressing all exceptions of the host type would incorrectly omit parent events for failed nested executions.
253
+
254
+ At a host-controlled admission boundary, `nh.get_lifecycle() is expected_lifecycle` can verify that the required callback is configured. If it has been cleared, record the bypass through that still-active boundary: the missing lifecycle cannot record its own absence. Delivery is one in-process attempt with a configured handler, not guaranteed persistence. A callback exception after successful assignment propagates without undoing writes; host delivery-failure and anomaly records are separate from the completed core record. The full rules are in [terminal delivery semantics](specification.md#104-terminal-delivery-and-host-ledgers).
223
255
 
224
256
  ## Synchronous oversight in scopes
225
257
 
@@ -256,7 +288,9 @@ Acceptance preserves validated values without repeating validators. Replacements
256
288
 
257
289
  Inspection mappings are shallow read-only reference views: their entries retain Python types and identity. Trusted hooks must not mutate referenced objects; use `Rewrite`. Copy or serialize explicitly for durable history. Rejection cannot roll back effects of tools, validators, or return expressions. See [Specification](specification.md#102-host-commit-boundary) for the complete contract.
258
290
 
259
- Tool rejections are returned to the model as a recoverable observation. On preview-based paths this appears with `error.kind == "oversight"` in the projected preview; provider-backed paths that use Pydantic AI's standard retry loop may instead surface the same structured details as a retry prompt whose final line is compact JSON. Step rejections raise `nh.oversight.OversightRejectedError` to the host. For the normative boundary rule on which tool-call failures are projected back to the model versus propagated as host exceptions, see [Specification Section 8.3](specification.md#83-tools-available-to-the-llm).
291
+ Tool rejections are returned to the model as a recoverable observation. On preview-based paths this appears with `error.kind == "oversight"` in the projected preview; provider-backed paths that use Pydantic AI's standard retry loop may instead surface the same structured details as a retry prompt whose final line is compact JSON. Step rejections produce a failed terminal record and raise `nh.ExecutionError` chained from `nh.oversight.OversightRejectedError`, unless the lifecycle callback supplies a host exception. For the normative boundary rule on which tool-call failures are projected back to the model versus propagated as host exceptions, see [Specification Section 8.3](specification.md#83-tools-available-to-the-llm).
292
+
293
+ For return approval, a host may parse `ReturnExpression.expression` and permit only a bare name present in `validated_binding_name_to_value`. Declaration alone does not prove that this candidate supplied a validated value. This policy does not skip return validation, prevent awaiting an awaitable binding, or keep the return synchronized with later binding rewrites. Arbitrary constants also bypass write bindings. Keep `deny: [return]` if the host's value policy has not admitted these behaviors; see [return approval semantics](specification.md#103-return-expression-approval).
260
294
 
261
295
  ## Caller-authenticated models
262
296
 
@@ -326,13 +360,14 @@ See [Specification Section 8.2](specification.md#82-prompt-context) for the full
326
360
 
327
361
  ## Runtime execution identity
328
362
 
329
- 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`.
363
+ Each `nh.run()` generates an `ExecutionReference` 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`.
330
364
 
331
365
  ```py
332
- execution_ref = nh.get_execution_ref()
333
- execution_ref.run_id # trace root -- stable across nested scopes
334
- execution_ref.scope_id # current scope -- changes with each nh.scope()
335
- execution_ref.step_id # None outside active step execution
366
+ execution_reference = nh.get_execution_reference()
367
+ execution_reference.run_id # trace root -- stable across nested scopes
368
+ execution_reference.scope_id # current scope -- changes with each nh.scope()
369
+ execution_reference.step_execution_id # Unique per invocation; None outside a step
370
+ execution_reference.source_location # Source module and line; None outside a step
336
371
  ```
337
372
 
338
373
  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.