nighthawk-python 0.11.0__tar.gz → 0.12.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 (176) hide show
  1. {nighthawk_python-0.11.0 → nighthawk_python-0.12.0}/.claude/rules/docs.md +2 -2
  2. nighthawk_python-0.12.0/.devcontainer/devcontainer-lock.json +14 -0
  3. nighthawk_python-0.12.0/.devcontainer/devcontainer.json +16 -0
  4. nighthawk_python-0.12.0/.devcontainer/postCreateCommand.sh +16 -0
  5. {nighthawk_python-0.11.0 → nighthawk_python-0.12.0}/.github/dependabot.yml +6 -0
  6. {nighthawk_python-0.11.0 → nighthawk_python-0.12.0}/.github/workflows/docs.yml +1 -1
  7. {nighthawk_python-0.11.0 → nighthawk_python-0.12.0}/CHANGELOG.md +11 -1
  8. {nighthawk_python-0.11.0 → nighthawk_python-0.12.0}/CONTRIBUTING.md +10 -7
  9. {nighthawk_python-0.11.0 → nighthawk_python-0.12.0}/PKG-INFO +9 -9
  10. {nighthawk_python-0.11.0 → nighthawk_python-0.12.0}/README.md +3 -3
  11. {nighthawk_python-0.11.0 → nighthawk_python-0.12.0}/docs/coding-agent-backends.md +2 -1
  12. {nighthawk_python-0.11.0 → nighthawk_python-0.12.0}/docs/index.md +4 -4
  13. {nighthawk_python-0.11.0 → nighthawk_python-0.12.0}/docs/natural-blocks.md +26 -1
  14. {nighthawk_python-0.11.0 → nighthawk_python-0.12.0}/docs/patterns.md +21 -9
  15. nighthawk_python-0.12.0/docs/philosophy.md +196 -0
  16. {nighthawk_python-0.11.0 → nighthawk_python-0.12.0}/docs/quickstart.md +1 -3
  17. {nighthawk_python-0.11.0 → nighthawk_python-0.12.0}/docs/roadmap.md +8 -0
  18. {nighthawk_python-0.11.0 → nighthawk_python-0.12.0}/docs/runtime-configuration.md +7 -7
  19. {nighthawk_python-0.11.0 → nighthawk_python-0.12.0}/docs/specification.md +4 -3
  20. {nighthawk_python-0.11.0 → nighthawk_python-0.12.0}/docs/verification.md +21 -12
  21. {nighthawk_python-0.11.0 → nighthawk_python-0.12.0}/evals/promptfoo/research-result.md +1 -0
  22. {nighthawk_python-0.11.0 → nighthawk_python-0.12.0}/pyproject.toml +8 -7
  23. {nighthawk_python-0.11.0 → nighthawk_python-0.12.0}/src/nighthawk/backends/base.py +29 -3
  24. {nighthawk_python-0.11.0 → nighthawk_python-0.12.0}/src/nighthawk/backends/claude_code_cli.py +1 -2
  25. {nighthawk_python-0.11.0 → nighthawk_python-0.12.0}/src/nighthawk/backends/claude_code_sdk.py +1 -2
  26. {nighthawk_python-0.11.0 → nighthawk_python-0.12.0}/src/nighthawk/backends/codex.py +1 -2
  27. {nighthawk_python-0.11.0 → nighthawk_python-0.12.0}/src/nighthawk/backends/mcp_boundary.py +2 -2
  28. {nighthawk_python-0.11.0 → nighthawk_python-0.12.0}/src/nighthawk/backends/mcp_server.py +28 -16
  29. {nighthawk_python-0.11.0 → nighthawk_python-0.12.0}/src/nighthawk/runtime/step_executor.py +1 -1
  30. {nighthawk_python-0.11.0 → nighthawk_python-0.12.0}/tests/backends/test_codex.py +1 -1
  31. {nighthawk_python-0.11.0 → nighthawk_python-0.12.0}/tests/docs/test_docs_architecture.py +10 -1
  32. {nighthawk_python-0.11.0 → nighthawk_python-0.12.0}/tests/execution/test_runtime.py +28 -0
  33. {nighthawk_python-0.11.0 → nighthawk_python-0.12.0}/tests/governance/test_governance.py +1 -1
  34. {nighthawk_python-0.11.0 → nighthawk_python-0.12.0}/tests/public/test_public_api.py +1 -1
  35. {nighthawk_python-0.11.0 → nighthawk_python-0.12.0}/tests/resilience/test_budget.py +1 -1
  36. {nighthawk_python-0.11.0 → nighthawk_python-0.12.0}/tests/resilience/test_circuit_breaker.py +1 -1
  37. {nighthawk_python-0.11.0 → nighthawk_python-0.12.0}/tests/resilience/test_retry.py +1 -1
  38. {nighthawk_python-0.11.0 → nighthawk_python-0.12.0}/tests/resilience/test_timeout.py +1 -1
  39. {nighthawk_python-0.11.0 → nighthawk_python-0.12.0}/tests/tools/test_tool_boundary.py +8 -8
  40. {nighthawk_python-0.11.0 → nighthawk_python-0.12.0}/uv.lock +263 -275
  41. nighthawk_python-0.11.0/.devcontainer/Dockerfile.devcontainer +0 -3
  42. nighthawk_python-0.11.0/.devcontainer/Dockerfile.litellm +0 -11
  43. nighthawk_python-0.11.0/.devcontainer/devcontainer.json +0 -24
  44. nighthawk_python-0.11.0/.devcontainer/docker-compose.yaml +0 -19
  45. nighthawk_python-0.11.0/.devcontainer/litellm-config.yaml +0 -37
  46. nighthawk_python-0.11.0/docs/philosophy.md +0 -178
  47. {nighthawk_python-0.11.0 → nighthawk_python-0.12.0}/.claude/rules/promptfoo.md +0 -0
  48. {nighthawk_python-0.11.0 → nighthawk_python-0.12.0}/.claude/rules/src.md +0 -0
  49. {nighthawk_python-0.11.0 → nighthawk_python-0.12.0}/.claude/rules/tests.md +0 -0
  50. {nighthawk_python-0.11.0 → nighthawk_python-0.12.0}/.claude/settings.json +0 -0
  51. {nighthawk_python-0.11.0 → nighthawk_python-0.12.0}/.claude/unset_envs.sh +0 -0
  52. {nighthawk_python-0.11.0 → nighthawk_python-0.12.0}/.github/workflows/ci.yml +0 -0
  53. {nighthawk_python-0.11.0 → nighthawk_python-0.12.0}/.github/workflows/publish.yml +0 -0
  54. {nighthawk_python-0.11.0 → nighthawk_python-0.12.0}/.gitignore +0 -0
  55. {nighthawk_python-0.11.0 → nighthawk_python-0.12.0}/.python-version +0 -0
  56. {nighthawk_python-0.11.0 → nighthawk_python-0.12.0}/AGENTS.md +0 -0
  57. {nighthawk_python-0.11.0 → nighthawk_python-0.12.0}/CLAUDE.md +0 -0
  58. {nighthawk_python-0.11.0 → nighthawk_python-0.12.0}/LICENSE +0 -0
  59. {nighthawk_python-0.11.0 → nighthawk_python-0.12.0}/docs/AGENTS.md +0 -0
  60. {nighthawk_python-0.11.0 → nighthawk_python-0.12.0}/docs/api.md +0 -0
  61. {nighthawk_python-0.11.0 → nighthawk_python-0.12.0}/docs/assets/nighthawk_logo-128x128.png +0 -0
  62. {nighthawk_python-0.11.0 → nighthawk_python-0.12.0}/docs/executors.md +0 -0
  63. {nighthawk_python-0.11.0 → nighthawk_python-0.12.0}/docs/for-coding-agents.md +0 -0
  64. {nighthawk_python-0.11.0 → nighthawk_python-0.12.0}/docs/pydantic-ai-providers.md +0 -0
  65. {nighthawk_python-0.11.0 → nighthawk_python-0.12.0}/evals/promptfoo/assertions/__init__.py +0 -0
  66. {nighthawk_python-0.11.0 → nighthawk_python-0.12.0}/evals/promptfoo/assertions/binding_value.py +0 -0
  67. {nighthawk_python-0.11.0 → nighthawk_python-0.12.0}/evals/promptfoo/assertions/outcome_kind.py +0 -0
  68. {nighthawk_python-0.11.0 → nighthawk_python-0.12.0}/evals/promptfoo/assertions/raise_message.py +0 -0
  69. {nighthawk_python-0.11.0 → nighthawk_python-0.12.0}/evals/promptfoo/evidence/2026-03-26-baseline-prompt-ab.md +0 -0
  70. {nighthawk_python-0.11.0 → nighthawk_python-0.12.0}/evals/promptfoo/evidence/2026-03-26-baseline-regression.md +0 -0
  71. {nighthawk_python-0.11.0 → nighthawk_python-0.12.0}/evals/promptfoo/evidence/2026-03-26-baseline-suffix-ab.md +0 -0
  72. {nighthawk_python-0.11.0 → nighthawk_python-0.12.0}/evals/promptfoo/evidence/2026-04-27-runtime-prompt-optimization.md +0 -0
  73. {nighthawk_python-0.11.0 → nighthawk_python-0.12.0}/evals/promptfoo/promptfooconfig-agents.yaml +0 -0
  74. {nighthawk_python-0.11.0 → nighthawk_python-0.12.0}/evals/promptfoo/promptfooconfig-nano-sanity.yaml +0 -0
  75. {nighthawk_python-0.11.0 → nighthawk_python-0.12.0}/evals/promptfoo/promptfooconfig-prompt-ab.yaml +0 -0
  76. {nighthawk_python-0.11.0 → nighthawk_python-0.12.0}/evals/promptfoo/promptfooconfig-suffix-ab.yaml +0 -0
  77. {nighthawk_python-0.11.0 → nighthawk_python-0.12.0}/evals/promptfoo/promptfooconfig.yaml +0 -0
  78. {nighthawk_python-0.11.0 → nighthawk_python-0.12.0}/evals/promptfoo/prompts/eval_coding_agent.txt +0 -0
  79. {nighthawk_python-0.11.0 → nighthawk_python-0.12.0}/evals/promptfoo/prompts/eval_default.txt +0 -0
  80. {nighthawk_python-0.11.0 → nighthawk_python-0.12.0}/evals/promptfoo/prompts/eval_legacy_default.txt +0 -0
  81. {nighthawk_python-0.11.0 → nighthawk_python-0.12.0}/evals/promptfoo/prompts/eval_mutation_aware.txt +0 -0
  82. {nighthawk_python-0.11.0 → nighthawk_python-0.12.0}/evals/promptfoo/prompts/eval_sequenced.txt +0 -0
  83. {nighthawk_python-0.11.0 → nighthawk_python-0.12.0}/evals/promptfoo/provider.py +0 -0
  84. {nighthawk_python-0.11.0 → nighthawk_python-0.12.0}/evals/promptfoo/test_cases/binding_operations.yaml +0 -0
  85. {nighthawk_python-0.11.0 → nighthawk_python-0.12.0}/evals/promptfoo/test_cases/edge_cases.yaml +0 -0
  86. {nighthawk_python-0.11.0 → nighthawk_python-0.12.0}/evals/promptfoo/test_cases/loop_outcomes.yaml +0 -0
  87. {nighthawk_python-0.11.0 → nighthawk_python-0.12.0}/evals/promptfoo/test_cases/loop_outcomes_ja.yaml +0 -0
  88. {nighthawk_python-0.11.0 → nighthawk_python-0.12.0}/evals/promptfoo/test_cases/multi_step.yaml +0 -0
  89. {nighthawk_python-0.11.0 → nighthawk_python-0.12.0}/evals/promptfoo/test_cases/null_handling.yaml +0 -0
  90. {nighthawk_python-0.11.0 → nighthawk_python-0.12.0}/evals/promptfoo/test_cases/outcome_kinds.yaml +0 -0
  91. {nighthawk_python-0.11.0 → nighthawk_python-0.12.0}/evals/promptfoo/test_cases/tool_selection.yaml +0 -0
  92. {nighthawk_python-0.11.0 → nighthawk_python-0.12.0}/mkdocs.yml +0 -0
  93. {nighthawk_python-0.11.0 → nighthawk_python-0.12.0}/pyrightconfig.json +0 -0
  94. {nighthawk_python-0.11.0 → nighthawk_python-0.12.0}/src/AGENTS.md +0 -0
  95. {nighthawk_python-0.11.0 → nighthawk_python-0.12.0}/src/nighthawk/__init__.py +0 -0
  96. {nighthawk_python-0.11.0 → nighthawk_python-0.12.0}/src/nighthawk/backends/__init__.py +0 -0
  97. {nighthawk_python-0.11.0 → nighthawk_python-0.12.0}/src/nighthawk/backends/_text_fallback.py +0 -0
  98. {nighthawk_python-0.11.0 → nighthawk_python-0.12.0}/src/nighthawk/backends/claude_code_settings.py +0 -0
  99. {nighthawk_python-0.11.0 → nighthawk_python-0.12.0}/src/nighthawk/backends/text_projection.py +0 -0
  100. {nighthawk_python-0.11.0 → nighthawk_python-0.12.0}/src/nighthawk/backends/tool_bridge.py +0 -0
  101. {nighthawk_python-0.11.0 → nighthawk_python-0.12.0}/src/nighthawk/configuration.py +0 -0
  102. {nighthawk_python-0.11.0 → nighthawk_python-0.12.0}/src/nighthawk/errors.py +0 -0
  103. {nighthawk_python-0.11.0 → nighthawk_python-0.12.0}/src/nighthawk/identifier_path.py +0 -0
  104. {nighthawk_python-0.11.0 → nighthawk_python-0.12.0}/src/nighthawk/json_renderer.py +0 -0
  105. {nighthawk_python-0.11.0 → nighthawk_python-0.12.0}/src/nighthawk/natural/__init__.py +0 -0
  106. {nighthawk_python-0.11.0 → nighthawk_python-0.12.0}/src/nighthawk/natural/blocks.py +0 -0
  107. {nighthawk_python-0.11.0 → nighthawk_python-0.12.0}/src/nighthawk/natural/decorator.py +0 -0
  108. {nighthawk_python-0.11.0 → nighthawk_python-0.12.0}/src/nighthawk/natural/transform.py +0 -0
  109. {nighthawk_python-0.11.0 → nighthawk_python-0.12.0}/src/nighthawk/oversight.py +0 -0
  110. {nighthawk_python-0.11.0 → nighthawk_python-0.12.0}/src/nighthawk/resilience/__init__.py +0 -0
  111. {nighthawk_python-0.11.0 → nighthawk_python-0.12.0}/src/nighthawk/resilience/_budget.py +0 -0
  112. {nighthawk_python-0.11.0 → nighthawk_python-0.12.0}/src/nighthawk/resilience/_circuit_breaker.py +0 -0
  113. {nighthawk_python-0.11.0 → nighthawk_python-0.12.0}/src/nighthawk/resilience/_fallback.py +0 -0
  114. {nighthawk_python-0.11.0 → nighthawk_python-0.12.0}/src/nighthawk/resilience/_retry.py +0 -0
  115. {nighthawk_python-0.11.0 → nighthawk_python-0.12.0}/src/nighthawk/resilience/_timeout.py +0 -0
  116. {nighthawk_python-0.11.0 → nighthawk_python-0.12.0}/src/nighthawk/resilience/_vote.py +0 -0
  117. {nighthawk_python-0.11.0 → nighthawk_python-0.12.0}/src/nighthawk/runtime/__init__.py +0 -0
  118. {nighthawk_python-0.11.0 → nighthawk_python-0.12.0}/src/nighthawk/runtime/_user_content.py +0 -0
  119. {nighthawk_python-0.11.0 → nighthawk_python-0.12.0}/src/nighthawk/runtime/async_bridge.py +0 -0
  120. {nighthawk_python-0.11.0 → nighthawk_python-0.12.0}/src/nighthawk/runtime/prompt.py +0 -0
  121. {nighthawk_python-0.11.0 → nighthawk_python-0.12.0}/src/nighthawk/runtime/runner.py +0 -0
  122. {nighthawk_python-0.11.0 → nighthawk_python-0.12.0}/src/nighthawk/runtime/scoping.py +0 -0
  123. {nighthawk_python-0.11.0 → nighthawk_python-0.12.0}/src/nighthawk/runtime/step_context.py +0 -0
  124. {nighthawk_python-0.11.0 → nighthawk_python-0.12.0}/src/nighthawk/runtime/step_contract.py +0 -0
  125. {nighthawk_python-0.11.0 → nighthawk_python-0.12.0}/src/nighthawk/runtime/tool_calls.py +0 -0
  126. {nighthawk_python-0.11.0 → nighthawk_python-0.12.0}/src/nighthawk/testing.py +0 -0
  127. {nighthawk_python-0.11.0 → nighthawk_python-0.12.0}/src/nighthawk/tools/__init__.py +0 -0
  128. {nighthawk_python-0.11.0 → nighthawk_python-0.12.0}/src/nighthawk/tools/_tool_return_content.py +0 -0
  129. {nighthawk_python-0.11.0 → nighthawk_python-0.12.0}/src/nighthawk/tools/assignment.py +0 -0
  130. {nighthawk_python-0.11.0 → nighthawk_python-0.12.0}/src/nighthawk/tools/contracts.py +0 -0
  131. {nighthawk_python-0.11.0 → nighthawk_python-0.12.0}/src/nighthawk/tools/execution.py +0 -0
  132. {nighthawk_python-0.11.0 → nighthawk_python-0.12.0}/src/nighthawk/tools/provided.py +0 -0
  133. {nighthawk_python-0.11.0 → nighthawk_python-0.12.0}/src/nighthawk/tools/registry.py +0 -0
  134. {nighthawk_python-0.11.0 → nighthawk_python-0.12.0}/src/nighthawk/ulid.py +0 -0
  135. {nighthawk_python-0.11.0 → nighthawk_python-0.12.0}/tests/AGENTS.md +0 -0
  136. {nighthawk_python-0.11.0 → nighthawk_python-0.12.0}/tests/__init__.py +0 -0
  137. {nighthawk_python-0.11.0 → nighthawk_python-0.12.0}/tests/backends/__init__.py +0 -0
  138. {nighthawk_python-0.11.0 → nighthawk_python-0.12.0}/tests/backends/test_claude_code_cli.py +0 -0
  139. {nighthawk_python-0.11.0 → nighthawk_python-0.12.0}/tests/backends/test_claude_code_sdk.py +0 -0
  140. {nighthawk_python-0.11.0 → nighthawk_python-0.12.0}/tests/conftest.py +0 -0
  141. {nighthawk_python-0.11.0 → nighthawk_python-0.12.0}/tests/docs/__init__.py +0 -0
  142. {nighthawk_python-0.11.0 → nighthawk_python-0.12.0}/tests/docs/test_coding_agent_examples.py +0 -0
  143. {nighthawk_python-0.11.0 → nighthawk_python-0.12.0}/tests/docs/test_prompt_examples.py +0 -0
  144. {nighthawk_python-0.11.0 → nighthawk_python-0.12.0}/tests/execution/__init__.py +0 -0
  145. {nighthawk_python-0.11.0 → nighthawk_python-0.12.0}/tests/execution/prompt_test_helpers.py +0 -0
  146. {nighthawk_python-0.11.0 → nighthawk_python-0.12.0}/tests/execution/stub_executor.py +0 -0
  147. {nighthawk_python-0.11.0 → nighthawk_python-0.12.0}/tests/execution/test_execution_outcome_prompt_fragment.py +0 -0
  148. {nighthawk_python-0.11.0 → nighthawk_python-0.12.0}/tests/execution/test_globals_prompt.py +0 -0
  149. {nighthawk_python-0.11.0 → nighthawk_python-0.12.0}/tests/execution/test_infer_binding_types.py +0 -0
  150. {nighthawk_python-0.11.0 → nighthawk_python-0.12.0}/tests/execution/test_natural_block_ordering.py +0 -0
  151. {nighthawk_python-0.11.0 → nighthawk_python-0.12.0}/tests/execution/test_natural_traceback.py +0 -0
  152. {nighthawk_python-0.11.0 → nighthawk_python-0.12.0}/tests/execution/test_variables_prompt.py +0 -0
  153. {nighthawk_python-0.11.0 → nighthawk_python-0.12.0}/tests/integration/__init__.py +0 -0
  154. {nighthawk_python-0.11.0 → nighthawk_python-0.12.0}/tests/integration/skip_helpers.py +0 -0
  155. {nighthawk_python-0.11.0 → nighthawk_python-0.12.0}/tests/integration/test_carry_pattern.py +0 -0
  156. {nighthawk_python-0.11.0 → nighthawk_python-0.12.0}/tests/integration/test_claude_code_cli_integration.py +0 -0
  157. {nighthawk_python-0.11.0 → nighthawk_python-0.12.0}/tests/integration/test_claude_code_sdk_integration.py +0 -0
  158. {nighthawk_python-0.11.0 → nighthawk_python-0.12.0}/tests/integration/test_codex_integration.py +0 -0
  159. {nighthawk_python-0.11.0 → nighthawk_python-0.12.0}/tests/integration/test_llm_integration.py +0 -0
  160. {nighthawk_python-0.11.0 → nighthawk_python-0.12.0}/tests/natural/__init__.py +0 -0
  161. {nighthawk_python-0.11.0 → nighthawk_python-0.12.0}/tests/natural/test_blocks.py +0 -0
  162. {nighthawk_python-0.11.0 → nighthawk_python-0.12.0}/tests/public/__init__.py +0 -0
  163. {nighthawk_python-0.11.0 → nighthawk_python-0.12.0}/tests/public/test_readme_example.py +0 -0
  164. {nighthawk_python-0.11.0 → nighthawk_python-0.12.0}/tests/public/test_usage_meter.py +0 -0
  165. {nighthawk_python-0.11.0 → nighthawk_python-0.12.0}/tests/resilience/__init__.py +0 -0
  166. {nighthawk_python-0.11.0 → nighthawk_python-0.12.0}/tests/resilience/test_composition.py +0 -0
  167. {nighthawk_python-0.11.0 → nighthawk_python-0.12.0}/tests/resilience/test_fallback.py +0 -0
  168. {nighthawk_python-0.11.0 → nighthawk_python-0.12.0}/tests/resilience/test_vote.py +0 -0
  169. {nighthawk_python-0.11.0 → nighthawk_python-0.12.0}/tests/test_renderer.py +0 -0
  170. {nighthawk_python-0.11.0 → nighthawk_python-0.12.0}/tests/test_testing.py +0 -0
  171. {nighthawk_python-0.11.0 → nighthawk_python-0.12.0}/tests/tools/__init__.py +0 -0
  172. {nighthawk_python-0.11.0 → nighthawk_python-0.12.0}/tests/tools/test_assignment_async.py +0 -0
  173. {nighthawk_python-0.11.0 → nighthawk_python-0.12.0}/tests/tools/test_contracts.py +0 -0
  174. {nighthawk_python-0.11.0 → nighthawk_python-0.12.0}/tests/tools/test_multimodal_helpers.py +0 -0
  175. {nighthawk_python-0.11.0 → nighthawk_python-0.12.0}/tests/tools/test_provided_async.py +0 -0
  176. {nighthawk_python-0.11.0 → nighthawk_python-0.12.0}/tests/tools/test_registry.py +0 -0
@@ -39,7 +39,7 @@ Public API documentation layering:
39
39
  | `coding-agent-backends.md` | Backend users | Backend reference | Backend-specific settings, shared capabilities, skills, MCP tool exposure, working directory, troubleshooting. |
40
40
  | `for-coding-agents.md` | Coding agents (LLMs) | Operational guide | Condensed, decision-oriented rules derived from human-oriented docs. Self-contained with absolute URLs. |
41
41
  | `specification.md` | Implementors | Canonical spec | Syntax, state layers, tools, outcomes, frontmatter, runtime semantics, observability contract, and custom backend capability/protocol semantics. Numbered section headings. |
42
- | `philosophy.md` | Evaluators | Design rationale | Execution model, harness evidence, design consequences (resilience, scoped execution contexts, tool exposure, multi-agent coordination, tradeoffs), runtime evaluation rationale, design landscape. |
42
+ | `philosophy.md` | Evaluators | Design rationale | Execution model, design landscape, boundary-first consequences, trust and safety boundaries, tradeoffs, and runtime evaluation rationale. |
43
43
  | `api.md` | Developers | API reference | Auto-generated from docstrings, including protocol and extension-hook symbols. |
44
44
  | `roadmap.md` | Contributors | Future directions | Ideas only. Remove when implemented. |
45
45
  | `docs/AGENTS.md` | Coding agents editing docs | Documentation governance | Canonical ownership, page roles, routing rules, and docs test invariants. Symlink to `.claude/rules/docs.md`. |
@@ -171,7 +171,7 @@ Pages in the Getting started, Patterns & verification, and Configuration nav gro
171
171
 
172
172
  **`philosophy.md`**
173
173
 
174
- - Owns the cumulative argument: execution model, harness evidence, design consequences (resilience, scoped execution contexts, tool exposure, multi-agent coordination, tradeoffs), runtime evaluation rationale, and design landscape.
174
+ - Owns the cumulative argument: execution model, design landscape, boundary-first consequences (resilience, scoped execution contexts, tool exposure, multi-agent coordination), trust and safety boundaries, tradeoffs, and runtime evaluation rationale.
175
175
  - External references acceptable. Prefer stable URLs with enough inline context to survive link rot.
176
176
  - No how-to code examples for patterns in `natural-blocks.md` or `patterns.md`. Exception: positioning examples may reuse function names from those pages.
177
177
 
@@ -0,0 +1,14 @@
1
+ {
2
+ "features": {
3
+ "ghcr.io/devcontainers/features/docker-in-docker:4.1.0": {
4
+ "version": "4.1.0",
5
+ "resolved": "ghcr.io/devcontainers/features/docker-in-docker@sha256:7d979c4a36d595eb4ab79909caa3f396daf3b6152dcf38e8328ea4eff42206c7",
6
+ "integrity": "sha256:7d979c4a36d595eb4ab79909caa3f396daf3b6152dcf38e8328ea4eff42206c7"
7
+ },
8
+ "ghcr.io/devcontainers/features/node:2.1.0": {
9
+ "version": "2.1.0",
10
+ "resolved": "ghcr.io/devcontainers/features/node@sha256:586c9a6f7dd40bd3ba2cd41e7f2f88dcc31fbe5d1442afcbf07ffbc66b686857",
11
+ "integrity": "sha256:586c9a6f7dd40bd3ba2cd41e7f2f88dcc31fbe5d1442afcbf07ffbc66b686857"
12
+ }
13
+ }
14
+ }
@@ -0,0 +1,16 @@
1
+ {
2
+ "name": "Python 3.13",
3
+ "image": "mcr.microsoft.com/devcontainers/python:3-3.13-bookworm",
4
+ "features": {
5
+ "ghcr.io/devcontainers/features/docker-in-docker:4.1.0": {},
6
+ "ghcr.io/devcontainers/features/node:2.1.0": {}
7
+ },
8
+ "postCreateCommand": "/bin/sh .devcontainer/postCreateCommand.sh",
9
+ "customizations": {
10
+ "vscode": {
11
+ "extensions": [
12
+ "charliermarsh.ruff"
13
+ ]
14
+ }
15
+ }
16
+ }
@@ -0,0 +1,16 @@
1
+ #!/bin/sh
2
+ set -eu
3
+
4
+ sudo apt-get update && export DEBIAN_FRONTEND=noninteractive \
5
+ && sudo apt-get -y install --no-install-recommends ripgrep fzf bubblewrap \
6
+ && sudo apt-get clean \
7
+ && sudo rm -rf /var/lib/apt/lists/*
8
+
9
+ curl -LsSf https://astral.sh/uv/install.sh | sh
10
+
11
+ uv sync
12
+
13
+ npm install -g pyright
14
+
15
+ curl -LsSf https://chatgpt.com/codex/install.sh | CODEX_NON_INTERACTIVE=1 sh
16
+ curl -LsSf https://claude.ai/install.sh | bash
@@ -6,6 +6,12 @@
6
6
 
7
7
  version: 2
8
8
  updates:
9
+ - package-ecosystem: "uv"
10
+ directory: "/"
11
+ schedule:
12
+ interval: weekly
13
+ cooldown:
14
+ default-days: 7
9
15
  - package-ecosystem: "devcontainers"
10
16
  directory: "/"
11
17
  schedule:
@@ -34,7 +34,7 @@ jobs:
34
34
  - name: Build docs
35
35
  run: uv run mkdocs build
36
36
 
37
- - uses: actions/upload-pages-artifact@v4
37
+ - uses: actions/upload-pages-artifact@v5
38
38
  with:
39
39
  path: site
40
40
 
@@ -7,6 +7,15 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [0.12.0]
11
+
12
+ ### Changed
13
+ - Clarified the boundary-first philosophy page.
14
+ - Clarified multimodal documentation and specification.
15
+ - Raised the minimum Pydantic AI version to 2.37.0 and migrated coding-agent backends to its native-tool and structured-instruction APIs.
16
+ - Raised the MCP minimum version to 2.1.1 and migrated the embedded tool server and content types to the MCP 2 API.
17
+ - Updated the Claude Agent SDK integration to 0.2.151.
18
+
10
19
  ## [0.11.0]
11
20
 
12
21
  ### Added
@@ -179,7 +188,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
179
188
  - Step executor abstraction and provider integration foundation.
180
189
  - Core documentation and project scaffolding.
181
190
 
182
- [Unreleased]: https://github.com/kurusugawa-computer/nighthawk-python/compare/v0.11.0...HEAD
191
+ [Unreleased]: https://github.com/kurusugawa-computer/nighthawk-python/compare/v0.12.0...HEAD
192
+ [0.12.0]: https://github.com/kurusugawa-computer/nighthawk-python/compare/v0.11.0...v0.12.0
183
193
  [0.11.0]: https://github.com/kurusugawa-computer/nighthawk-python/compare/v0.10.0...v0.11.0
184
194
  [0.10.0]: https://github.com/kurusugawa-computer/nighthawk-python/compare/v0.9.0...v0.10.0
185
195
  [0.9.0]: https://github.com/kurusugawa-computer/nighthawk-python/compare/v0.8.0...v0.9.0
@@ -12,6 +12,14 @@
12
12
  uv sync --all-extras --all-groups
13
13
  ```
14
14
 
15
+ `uv` manages the project virtual environment in `.venv`. Do not create or
16
+ activate a separate virtual environment; run project commands through `uv run`.
17
+ The `.venv` directory is generated and ignored by Git. It can be deleted to
18
+ rebuild the local environment, and the next `uv sync` or `uv run` recreates it.
19
+
20
+ Declare project dependencies in `pyproject.toml` through `uv add`, rather than
21
+ installing them directly into `.venv` with `uv pip install`.
22
+
15
23
  If you see a `uv` warning about hardlinking (common in containers or cross-filesystem workspaces), suppress it with:
16
24
 
17
25
  ```bash
@@ -39,13 +47,8 @@ uv run pytest -q # quiet output
39
47
 
40
48
  ### Integration tests
41
49
 
42
- Integration tests require provider packages and API keys. Install the provider you need:
43
-
44
- ```bash
45
- uv pip install pydantic-ai-slim[openai]
46
- ```
47
-
48
- Then run with your `.env` loaded:
50
+ The full setup command installs the OpenAI provider development dependency.
51
+ Run integration tests with your `.env` loaded:
49
52
 
50
53
  ```bash
51
54
  set -a; source .env; set +a; uv run pytest -q
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.4
1
+ Metadata-Version: 2.5
2
2
  Name: nighthawk-python
3
- Version: 0.11.0
3
+ Version: 0.12.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/
@@ -20,17 +20,17 @@ Classifier: Topic :: Software Development :: Libraries :: Python Modules
20
20
  Classifier: Typing :: Typed
21
21
  Requires-Python: >=3.13
22
22
  Requires-Dist: headson>=0.16.1
23
- Requires-Dist: pydantic-ai-slim>=1.87.0
23
+ Requires-Dist: pydantic-ai-slim>=2.37.0
24
24
  Requires-Dist: pydantic>=2.13.3
25
25
  Requires-Dist: pyyaml>=6.0.3
26
26
  Requires-Dist: tenacity>=9.1.4
27
27
  Requires-Dist: tiktoken>=0.12.0
28
28
  Provides-Extra: claude-code-cli
29
- Requires-Dist: mcp>=1.27.0; extra == 'claude-code-cli'
29
+ Requires-Dist: mcp>=2.1.1; extra == 'claude-code-cli'
30
30
  Provides-Extra: claude-code-sdk
31
- Requires-Dist: claude-agent-sdk==0.1.48; extra == 'claude-code-sdk'
31
+ Requires-Dist: claude-agent-sdk==0.2.151; extra == 'claude-code-sdk'
32
32
  Provides-Extra: codex
33
- Requires-Dist: mcp>=1.27.0; extra == 'codex'
33
+ Requires-Dist: mcp>=2.1.1; extra == 'codex'
34
34
  Description-Content-Type: text/markdown
35
35
 
36
36
  [![PyPI](https://img.shields.io/pypi/v/nighthawk-python)](https://pypi.org/project/nighthawk-python)
@@ -68,12 +68,12 @@ For other providers, see [Pydantic AI providers](https://kurusugawa-computer.git
68
68
  ```py
69
69
  import nighthawk as nh
70
70
 
71
+
71
72
  def python_average(numbers):
72
73
  return sum(numbers) / len(numbers)
73
74
 
74
- step_executor = nh.AgentStepExecutor.from_configuration(
75
- configuration=nh.StepExecutorConfiguration(model="openai-responses:gpt-5.4-nano")
76
- )
75
+
76
+ step_executor = nh.AgentStepExecutor.from_configuration(configuration=nh.StepExecutorConfiguration(model="openai-responses:gpt-5.4-nano"))
77
77
 
78
78
  with nh.run(step_executor):
79
79
 
@@ -33,12 +33,12 @@ For other providers, see [Pydantic AI providers](https://kurusugawa-computer.git
33
33
  ```py
34
34
  import nighthawk as nh
35
35
 
36
+
36
37
  def python_average(numbers):
37
38
  return sum(numbers) / len(numbers)
38
39
 
39
- step_executor = nh.AgentStepExecutor.from_configuration(
40
- configuration=nh.StepExecutorConfiguration(model="openai-responses:gpt-5.4-nano")
41
- )
40
+
41
+ step_executor = nh.AgentStepExecutor.from_configuration(configuration=nh.StepExecutorConfiguration(model="openai-responses:gpt-5.4-nano"))
42
42
 
43
43
  with nh.run(step_executor):
44
44
 
@@ -45,7 +45,7 @@ Coding agent backends do not receive native VLM-style user-prompt multimodal inp
45
45
  - `BinaryContent` is staged to a local file and referenced in the prompt with `<image>` / `<file>` plus a local file path.
46
46
  - URL-based content (`ImageUrl`, `AudioUrl`, `DocumentUrl`, `VideoUrl`) is referenced by URL in the projected prompt text.
47
47
  - `UploadedFile` user-prompt content is rejected because these backends cannot resolve provider-owned file identifiers. Tool results still preserve the rest of the payload and replace only the `UploadedFile` item with explanatory fallback text.
48
- - Tool results cross the backend boundary as MCP content. Image (`BinaryContent.is_image`) and audio (`BinaryContent.is_audio`) items travel natively as `ImageContent` / `AudioContent`; all other `BinaryContent` and `FileUrl` items project to `TextContent` so the MCP path stays symmetric with the text-projected prompt path. When mixed top-level text/multimodal tool-result sequences preserve their original order they satisfy the same `UserContent` rule used for prompt hoisting. The user prompt itself remains text-projected for these backends.
48
+ - Tool results cross the backend boundary as MCP content. Image (`BinaryContent.is_image`) and audio (`BinaryContent.is_audio`) items travel natively as `ImageContent` / `AudioContent`; all other `BinaryContent` and `FileUrl` items project to `TextContent` so the MCP path stays symmetric with the text-projected prompt path. Native MCP video carriage is intentionally unsupported for now because video-modal requirements are unstable; document carriage also remains text-projected unless a future backend advertises an explicit capability. When mixed top-level text/multimodal tool-result sequences preserve their original order they satisfy the same `UserContent` rule used for prompt hoisting. The user prompt itself remains text-projected for these backends.
49
49
  - When a successful tool result splits to no text and no user content, the projected prompt stays header-only rather than inserting the preview JSON envelope.
50
50
 
51
51
  This differs from provider-backed executors that can pass a `tuple[UserContent, ...]` to a multimodal-capable provider API directly.
@@ -281,6 +281,7 @@ Example Natural function that invokes the skill:
281
281
  ```py
282
282
  import nighthawk as nh
283
283
 
284
+
284
285
  @nh.natural_function
285
286
  def summarize_feedback(feedback_items: list[str]) -> str:
286
287
  def group_feedback_by_topic(feedback_items: list[str]) -> dict[str, list[str]]:
@@ -14,12 +14,12 @@ The same mechanism handles lightweight LLM judgments ("classify this sentiment")
14
14
  ```py
15
15
  import nighthawk as nh
16
16
 
17
+
17
18
  def python_average(numbers):
18
19
  return sum(numbers) / len(numbers)
19
20
 
20
- step_executor = nh.AgentStepExecutor.from_configuration(
21
- configuration=nh.StepExecutorConfiguration(model="openai-responses:gpt-5.4-nano")
22
- )
21
+
22
+ step_executor = nh.AgentStepExecutor.from_configuration(configuration=nh.StepExecutorConfiguration(model="openai-responses:gpt-5.4-nano"))
23
23
 
24
24
  with nh.run(step_executor):
25
25
 
@@ -61,7 +61,7 @@ The LLM interprets the mixed-format input list via `<numbers>`, calls the Python
61
61
 
62
62
  ## Background and project
63
63
 
64
- - **[Philosophy](philosophy.md)** -- Design rationale: execution model, harness evidence, design consequences (resilience, scoped execution contexts, tool exposure, multi-agent coordination, tradeoffs), runtime evaluation rationale, and design landscape.
64
+ - **[Philosophy](philosophy.md)** -- Design rationale: execution model, design landscape, boundary-first consequences, trust and safety boundaries, tradeoffs, and runtime evaluation rationale.
65
65
  - **[Roadmap](roadmap.md)** -- Future directions and open questions.
66
66
 
67
67
  ## Origins
@@ -139,11 +139,13 @@ Write bindings can use Pydantic models for structured output with automatic vali
139
139
  ```py
140
140
  from pydantic import BaseModel
141
141
 
142
+
142
143
  class ReviewVerdict(BaseModel):
143
144
  approved: bool
144
145
  reason: str
145
146
  risk_level: str
146
147
 
148
+
147
149
  @nh.natural_function
148
150
  def judge_review(review_data: str) -> ReviewVerdict:
149
151
  verdict: ReviewVerdict
@@ -171,7 +173,21 @@ Transport depends on the executor:
171
173
  - Provider-backed executors that accept Pydantic AI `UserContent` send the value natively to the VLM API.
172
174
  - Coding-agent backends text-project the value (staged local files or URL references). See [Coding agent backends: multimodal inputs](coding-agent-backends.md#multimodal-inputs).
173
175
 
174
- For the normative rendering and transport rules, including dotted-reference hoisting and the remaining v0.11.0 nesting restrictions, see [Specification Section 8.2](specification.md#82-locals-summary).
176
+ Nested multimodal values inside dicts, models, and objects are not recursively hoisted. Keep the prompt boundary explicit by lifting the media-bearing leaf into a helper variable before the Natural block:
177
+
178
+ ```py
179
+ def summarize_report(report: Report) -> str:
180
+ report_scan = report.attachments.scan_image
181
+ summary = ""
182
+ """natural
183
+ Read <report_scan> and summarize the visual evidence in <:summary>.
184
+ """
185
+ return summary
186
+ ```
187
+
188
+ This keeps the media selection visible in ordinary Python, avoids accidental traversal of large object graphs, and lets normal LOCALS budgeting decide whether the helper variable is rendered.
189
+
190
+ For the normative rendering and transport rules, including dotted-reference hoisting and nested multimodal behavior, see [Specification Section 8.2](specification.md#82-locals-summary).
175
191
 
176
192
  ### f-string injection
177
193
 
@@ -180,6 +196,7 @@ Inline f-string blocks embed Python expressions directly into the Natural progra
180
196
  ```py
181
197
  PROJECT_POLICY = ["safety-first", "concise-output", "cite-assumptions"]
182
198
 
199
+
183
200
  @nh.natural_function
184
201
  def choose_policy(post: str) -> str:
185
202
  selected_policy = ""
@@ -219,10 +236,12 @@ Member accesses and function results work too:
219
236
  ```py
220
237
  from pydantic import BaseModel
221
238
 
239
+
222
240
  class Config(BaseModel):
223
241
  max_length: int = 100
224
242
  style: str = "formal"
225
243
 
244
+
226
245
  @nh.natural_function
227
246
  def generate(config: Config, topic: str) -> str:
228
247
  output = ""
@@ -296,6 +315,7 @@ When a Natural block references a module-level name via `<name>`, it appears in
296
315
  def python_average(numbers):
297
316
  return sum(numbers) / len(numbers)
298
317
 
318
+
299
319
  @nh.natural_function
300
320
  def calculate_average(numbers):
301
321
  """natural
@@ -375,11 +395,13 @@ For deterministic functions (no Natural blocks), the boundary is at the function
375
395
  ```py
376
396
  from pydantic import BaseModel
377
397
 
398
+
378
399
  class ScoreInput(BaseModel):
379
400
  base: int
380
401
  bonus: int
381
402
  multiplier: float = 1.0
382
403
 
404
+
383
405
  def compute_score(score_input: ScoreInput) -> int:
384
406
  return int((score_input.base + score_input.bonus) * score_input.multiplier)
385
407
  ```
@@ -392,11 +414,13 @@ For judgment-heavy functions (containing Natural blocks), the boundary moves *in
392
414
  from pydantic import BaseModel
393
415
  from nighthawk import JsonableValue
394
416
 
417
+
395
418
  class ReviewVerdict(BaseModel):
396
419
  approved: bool
397
420
  reason: str
398
421
  risk_level: str
399
422
 
423
+
400
424
  @nh.natural_function
401
425
  def judge_review(review_data: str | JsonableValue) -> ReviewVerdict:
402
426
  verdict: ReviewVerdict
@@ -470,6 +494,7 @@ def get_feedback_summary(topic: str, max_items: int = 10) -> str:
470
494
  items = fetch_feedback(topic=topic, limit=max_items)
471
495
  return format_summary(items)
472
496
 
497
+
473
498
  # Natural block sees only what it needs
474
499
  @nh.natural_function
475
500
  def analyze_feedback(topic: str) -> str:
@@ -117,6 +117,7 @@ def validate(data: str) -> str:
117
117
  """
118
118
  return result
119
119
 
120
+
120
121
  try:
121
122
  validate("corrupted-input")
122
123
  except nh.ExecutionError as e:
@@ -131,6 +132,7 @@ When a Natural block references exception types visible in step locals or global
131
132
  class InputError(Exception):
132
133
  pass
133
134
 
135
+
134
136
  @nh.natural_function
135
137
  def strict_validate(data: str) -> str:
136
138
  result = ""
@@ -140,6 +142,7 @@ def strict_validate(data: str) -> str:
140
142
  """
141
143
  return result
142
144
 
145
+
143
146
  try:
144
147
  strict_validate("bad")
145
148
  except InputError as e:
@@ -165,6 +168,7 @@ async def summarize_async(text: str) -> str:
165
168
  """
166
169
  return result
167
170
 
171
+
168
172
  summary = await summarize_async("A long document about climate change...")
169
173
  ```
170
174
 
@@ -181,6 +185,7 @@ async def fetch_data(query: str) -> list[str]:
181
185
  """Fetch data matching the query from an external API."""
182
186
  ...
183
187
 
188
+
184
189
  @nh.natural_function
185
190
  async def analyze(query: str) -> str:
186
191
  result = ""
@@ -199,6 +204,7 @@ Async natural functions are ordinary coroutines, so you can run multiple Natural
199
204
  ```py
200
205
  import asyncio
201
206
 
207
+
202
208
  @nh.natural_function
203
209
  async def classify(text: str) -> str:
204
210
  label: str = ""
@@ -207,6 +213,7 @@ async def classify(text: str) -> str:
207
213
  """
208
214
  return label
209
215
 
216
+
210
217
  async def classify_batch(texts: list[str]) -> list[str]:
211
218
  return list(await asyncio.gather(*(classify(t) for t in texts)))
212
219
  ```
@@ -242,6 +249,7 @@ def step_1(carry: list[str]) -> int:
242
249
  """
243
250
  return result
244
251
 
252
+
245
253
  @nh.natural_function
246
254
  def step_2(carry: list[str]) -> int:
247
255
  result = 0
@@ -253,9 +261,10 @@ def step_2(carry: list[str]) -> int:
253
261
  """
254
262
  return result
255
263
 
264
+
256
265
  carry: list[str] = []
257
- r1 = step_1(carry) # carry now has 1 entry
258
- r2 = step_2(carry) # carry now has 2 entries
266
+ r1 = step_1(carry) # carry now has 1 entry
267
+ r2 = step_2(carry) # carry now has 2 entries
259
268
  ```
260
269
 
261
270
  Any mutable object works -- `list`, `dict`, Pydantic models, custom classes.
@@ -293,7 +302,7 @@ seed_step(carry)
293
302
  carry_a = carry.copy()
294
303
  carry_b = carry.copy()
295
304
 
296
- result_a = branch_add(carry_a) # diverges from here
305
+ result_a = branch_add(carry_a) # diverges from here
297
306
  result_b = branch_multiply(carry_b) # independent path
298
307
  ```
299
308
 
@@ -443,9 +452,11 @@ For monetary budgets, supply a `cost_function` that converts `RunUsage` to a flo
443
452
  ```py
444
453
  from pydantic_ai.usage import RunUsage
445
454
 
455
+
446
456
  def dollar_cost(usage: RunUsage) -> float:
447
457
  return usage.input_tokens * 3e-6 + usage.output_tokens * 15e-6
448
458
 
459
+
449
460
  budgeted = budget(cost=1.00, cost_function=dollar_cost)(classify)
450
461
  ```
451
462
 
@@ -459,7 +470,7 @@ Prevent repeated calls to a failing service. After `fail_threshold` consecutive
459
470
  from nighthawk.resilience import circuit_breaker, CircuitState
460
471
 
461
472
  protected_api = circuit_breaker(fail_threshold=5, reset_timeout=60)(call_api)
462
- protected_api.state # CircuitState.CLOSED
473
+ protected_api.state # CircuitState.CLOSED
463
474
  protected_api.reset() # manual reset
464
475
  ```
465
476
 
@@ -471,11 +482,11 @@ All transformers produce callables with the original signature, so they compose
471
482
 
472
483
  ```py
473
484
  robust_classify = fallback(
474
- retrying(attempts=2)( # 3. Retry the voted call
475
- vote(count=3)(classify_gpt4) # 2. Vote 3x with GPT-4
476
- ), # 1. Try GPT-4 first
477
- retrying(attempts=2)(classify_mini), # 4. Fall back to mini
478
- default="unknown", # 5. Last resort
485
+ retrying(attempts=2)( # 3. Retry the voted call
486
+ vote(count=3)(classify_gpt4) # 2. Vote 3x with GPT-4
487
+ ), # 1. Try GPT-4 first
488
+ retrying(attempts=2)(classify_mini), # 4. Fall back to mini
489
+ default="unknown", # 5. Last resort
479
490
  )
480
491
 
481
492
  result = robust_classify(text)
@@ -499,6 +510,7 @@ Recommended composition order (innermost to outermost):
499
510
  ```py
500
511
  from functools import lru_cache
501
512
 
513
+
502
514
  @lru_cache(maxsize=256)
503
515
  @nh.natural_function
504
516
  def classify(text: str) -> str: