nighthawk-python 0.7.0__tar.gz → 0.9.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 (162) hide show
  1. {nighthawk_python-0.7.0 → nighthawk_python-0.9.0}/.github/workflows/docs.yml +1 -1
  2. {nighthawk_python-0.7.0 → nighthawk_python-0.9.0}/CHANGELOG.md +23 -2
  3. {nighthawk_python-0.7.0 → nighthawk_python-0.9.0}/PKG-INFO +1 -1
  4. {nighthawk_python-0.7.0 → nighthawk_python-0.9.0}/docs/api.md +0 -1
  5. {nighthawk_python-0.7.0 → nighthawk_python-0.9.0}/docs/for-coding-agents.md +8 -2
  6. {nighthawk_python-0.7.0 → nighthawk_python-0.9.0}/docs/runtime-configuration.md +89 -18
  7. {nighthawk_python-0.7.0 → nighthawk_python-0.9.0}/docs/specification.md +52 -13
  8. {nighthawk_python-0.7.0 → nighthawk_python-0.9.0}/evals/promptfoo/prompts/eval_default.txt +1 -1
  9. {nighthawk_python-0.7.0 → nighthawk_python-0.9.0}/pyproject.toml +1 -1
  10. {nighthawk_python-0.7.0 → nighthawk_python-0.9.0}/src/nighthawk/__init__.py +0 -2
  11. {nighthawk_python-0.7.0 → nighthawk_python-0.9.0}/src/nighthawk/configuration.py +9 -44
  12. nighthawk_python-0.9.0/src/nighthawk/runtime/prompt.py +641 -0
  13. {nighthawk_python-0.7.0 → nighthawk_python-0.9.0}/src/nighthawk/runtime/scoping.py +42 -47
  14. {nighthawk_python-0.7.0 → nighthawk_python-0.9.0}/src/nighthawk/runtime/step_executor.py +4 -2
  15. {nighthawk_python-0.7.0 → nighthawk_python-0.9.0}/src/nighthawk/tools/contracts.py +1 -1
  16. {nighthawk_python-0.7.0 → nighthawk_python-0.9.0}/tests/execution/prompt_test_helpers.py +8 -3
  17. {nighthawk_python-0.7.0 → nighthawk_python-0.9.0}/tests/execution/test_globals_prompt.py +55 -1
  18. {nighthawk_python-0.7.0 → nighthawk_python-0.9.0}/tests/execution/test_runtime.py +23 -2
  19. {nighthawk_python-0.7.0 → nighthawk_python-0.9.0}/tests/execution/test_variables_prompt.py +150 -0
  20. {nighthawk_python-0.7.0 → nighthawk_python-0.9.0}/tests/integration/test_llm_integration.py +2 -2
  21. {nighthawk_python-0.7.0 → nighthawk_python-0.9.0}/tests/public/test_public_api.py +105 -1
  22. {nighthawk_python-0.7.0 → nighthawk_python-0.9.0}/uv.lock +161 -161
  23. nighthawk_python-0.7.0/src/nighthawk/runtime/prompt.py +0 -345
  24. {nighthawk_python-0.7.0 → nighthawk_python-0.9.0}/.claude/rules/docs.md +0 -0
  25. {nighthawk_python-0.7.0 → nighthawk_python-0.9.0}/.claude/rules/promptfoo.md +0 -0
  26. {nighthawk_python-0.7.0 → nighthawk_python-0.9.0}/.claude/rules/src.md +0 -0
  27. {nighthawk_python-0.7.0 → nighthawk_python-0.9.0}/.claude/rules/tests.md +0 -0
  28. {nighthawk_python-0.7.0 → nighthawk_python-0.9.0}/.claude/settings.json +0 -0
  29. {nighthawk_python-0.7.0 → nighthawk_python-0.9.0}/.claude/unset_envs.sh +0 -0
  30. {nighthawk_python-0.7.0 → nighthawk_python-0.9.0}/.devcontainer/Dockerfile.devcontainer +0 -0
  31. {nighthawk_python-0.7.0 → nighthawk_python-0.9.0}/.devcontainer/Dockerfile.litellm +0 -0
  32. {nighthawk_python-0.7.0 → nighthawk_python-0.9.0}/.devcontainer/devcontainer.json +0 -0
  33. {nighthawk_python-0.7.0 → nighthawk_python-0.9.0}/.devcontainer/docker-compose.yaml +0 -0
  34. {nighthawk_python-0.7.0 → nighthawk_python-0.9.0}/.devcontainer/litellm-config.yaml +0 -0
  35. {nighthawk_python-0.7.0 → nighthawk_python-0.9.0}/.github/dependabot.yml +0 -0
  36. {nighthawk_python-0.7.0 → nighthawk_python-0.9.0}/.github/workflows/ci.yml +0 -0
  37. {nighthawk_python-0.7.0 → nighthawk_python-0.9.0}/.github/workflows/publish.yml +0 -0
  38. {nighthawk_python-0.7.0 → nighthawk_python-0.9.0}/.gitignore +0 -0
  39. {nighthawk_python-0.7.0 → nighthawk_python-0.9.0}/.python-version +0 -0
  40. {nighthawk_python-0.7.0 → nighthawk_python-0.9.0}/AGENTS.md +0 -0
  41. {nighthawk_python-0.7.0 → nighthawk_python-0.9.0}/CLAUDE.md +0 -0
  42. {nighthawk_python-0.7.0 → nighthawk_python-0.9.0}/CONTRIBUTING.md +0 -0
  43. {nighthawk_python-0.7.0 → nighthawk_python-0.9.0}/LICENSE +0 -0
  44. {nighthawk_python-0.7.0 → nighthawk_python-0.9.0}/README.md +0 -0
  45. {nighthawk_python-0.7.0 → nighthawk_python-0.9.0}/docs/AGENTS.md +0 -0
  46. {nighthawk_python-0.7.0 → nighthawk_python-0.9.0}/docs/assets/nighthawk_logo-128x128.png +0 -0
  47. {nighthawk_python-0.7.0 → nighthawk_python-0.9.0}/docs/coding-agent-backends.md +0 -0
  48. {nighthawk_python-0.7.0 → nighthawk_python-0.9.0}/docs/executors.md +0 -0
  49. {nighthawk_python-0.7.0 → nighthawk_python-0.9.0}/docs/index.md +0 -0
  50. {nighthawk_python-0.7.0 → nighthawk_python-0.9.0}/docs/natural-blocks.md +0 -0
  51. {nighthawk_python-0.7.0 → nighthawk_python-0.9.0}/docs/patterns.md +0 -0
  52. {nighthawk_python-0.7.0 → nighthawk_python-0.9.0}/docs/philosophy.md +0 -0
  53. {nighthawk_python-0.7.0 → nighthawk_python-0.9.0}/docs/pydantic-ai-providers.md +0 -0
  54. {nighthawk_python-0.7.0 → nighthawk_python-0.9.0}/docs/quickstart.md +0 -0
  55. {nighthawk_python-0.7.0 → nighthawk_python-0.9.0}/docs/roadmap.md +0 -0
  56. {nighthawk_python-0.7.0 → nighthawk_python-0.9.0}/docs/verification.md +0 -0
  57. {nighthawk_python-0.7.0 → nighthawk_python-0.9.0}/evals/promptfoo/assertions/__init__.py +0 -0
  58. {nighthawk_python-0.7.0 → nighthawk_python-0.9.0}/evals/promptfoo/assertions/binding_value.py +0 -0
  59. {nighthawk_python-0.7.0 → nighthawk_python-0.9.0}/evals/promptfoo/assertions/outcome_kind.py +0 -0
  60. {nighthawk_python-0.7.0 → nighthawk_python-0.9.0}/evals/promptfoo/assertions/raise_message.py +0 -0
  61. {nighthawk_python-0.7.0 → nighthawk_python-0.9.0}/evals/promptfoo/evidence/2026-03-26-baseline-prompt-ab.md +0 -0
  62. {nighthawk_python-0.7.0 → nighthawk_python-0.9.0}/evals/promptfoo/evidence/2026-03-26-baseline-regression.md +0 -0
  63. {nighthawk_python-0.7.0 → nighthawk_python-0.9.0}/evals/promptfoo/evidence/2026-03-26-baseline-suffix-ab.md +0 -0
  64. {nighthawk_python-0.7.0 → nighthawk_python-0.9.0}/evals/promptfoo/promptfooconfig-agents.yaml +0 -0
  65. {nighthawk_python-0.7.0 → nighthawk_python-0.9.0}/evals/promptfoo/promptfooconfig-prompt-ab.yaml +0 -0
  66. {nighthawk_python-0.7.0 → nighthawk_python-0.9.0}/evals/promptfoo/promptfooconfig-suffix-ab.yaml +0 -0
  67. {nighthawk_python-0.7.0 → nighthawk_python-0.9.0}/evals/promptfoo/promptfooconfig.yaml +0 -0
  68. {nighthawk_python-0.7.0 → nighthawk_python-0.9.0}/evals/promptfoo/prompts/eval_coding_agent.txt +0 -0
  69. {nighthawk_python-0.7.0 → nighthawk_python-0.9.0}/evals/promptfoo/prompts/eval_mutation_aware.txt +0 -0
  70. {nighthawk_python-0.7.0 → nighthawk_python-0.9.0}/evals/promptfoo/prompts/eval_sequenced.txt +0 -0
  71. {nighthawk_python-0.7.0 → nighthawk_python-0.9.0}/evals/promptfoo/provider.py +0 -0
  72. {nighthawk_python-0.7.0 → nighthawk_python-0.9.0}/evals/promptfoo/research-result.md +0 -0
  73. {nighthawk_python-0.7.0 → nighthawk_python-0.9.0}/evals/promptfoo/test_cases/binding_operations.yaml +0 -0
  74. {nighthawk_python-0.7.0 → nighthawk_python-0.9.0}/evals/promptfoo/test_cases/edge_cases.yaml +0 -0
  75. {nighthawk_python-0.7.0 → nighthawk_python-0.9.0}/evals/promptfoo/test_cases/loop_outcomes.yaml +0 -0
  76. {nighthawk_python-0.7.0 → nighthawk_python-0.9.0}/evals/promptfoo/test_cases/multi_step.yaml +0 -0
  77. {nighthawk_python-0.7.0 → nighthawk_python-0.9.0}/evals/promptfoo/test_cases/null_handling.yaml +0 -0
  78. {nighthawk_python-0.7.0 → nighthawk_python-0.9.0}/evals/promptfoo/test_cases/outcome_kinds.yaml +0 -0
  79. {nighthawk_python-0.7.0 → nighthawk_python-0.9.0}/evals/promptfoo/test_cases/tool_selection.yaml +0 -0
  80. {nighthawk_python-0.7.0 → nighthawk_python-0.9.0}/mkdocs.yml +0 -0
  81. {nighthawk_python-0.7.0 → nighthawk_python-0.9.0}/pyrightconfig.json +0 -0
  82. {nighthawk_python-0.7.0 → nighthawk_python-0.9.0}/src/AGENTS.md +0 -0
  83. {nighthawk_python-0.7.0 → nighthawk_python-0.9.0}/src/nighthawk/backends/__init__.py +0 -0
  84. {nighthawk_python-0.7.0 → nighthawk_python-0.9.0}/src/nighthawk/backends/base.py +0 -0
  85. {nighthawk_python-0.7.0 → nighthawk_python-0.9.0}/src/nighthawk/backends/claude_code_cli.py +0 -0
  86. {nighthawk_python-0.7.0 → nighthawk_python-0.9.0}/src/nighthawk/backends/claude_code_sdk.py +0 -0
  87. {nighthawk_python-0.7.0 → nighthawk_python-0.9.0}/src/nighthawk/backends/claude_code_settings.py +0 -0
  88. {nighthawk_python-0.7.0 → nighthawk_python-0.9.0}/src/nighthawk/backends/codex.py +0 -0
  89. {nighthawk_python-0.7.0 → nighthawk_python-0.9.0}/src/nighthawk/backends/mcp_boundary.py +0 -0
  90. {nighthawk_python-0.7.0 → nighthawk_python-0.9.0}/src/nighthawk/backends/mcp_server.py +0 -0
  91. {nighthawk_python-0.7.0 → nighthawk_python-0.9.0}/src/nighthawk/backends/tool_bridge.py +0 -0
  92. {nighthawk_python-0.7.0 → nighthawk_python-0.9.0}/src/nighthawk/errors.py +0 -0
  93. {nighthawk_python-0.7.0 → nighthawk_python-0.9.0}/src/nighthawk/identifier_path.py +0 -0
  94. {nighthawk_python-0.7.0 → nighthawk_python-0.9.0}/src/nighthawk/json_renderer.py +0 -0
  95. {nighthawk_python-0.7.0 → nighthawk_python-0.9.0}/src/nighthawk/natural/__init__.py +0 -0
  96. {nighthawk_python-0.7.0 → nighthawk_python-0.9.0}/src/nighthawk/natural/blocks.py +0 -0
  97. {nighthawk_python-0.7.0 → nighthawk_python-0.9.0}/src/nighthawk/natural/decorator.py +0 -0
  98. {nighthawk_python-0.7.0 → nighthawk_python-0.9.0}/src/nighthawk/natural/transform.py +0 -0
  99. {nighthawk_python-0.7.0 → nighthawk_python-0.9.0}/src/nighthawk/resilience/__init__.py +0 -0
  100. {nighthawk_python-0.7.0 → nighthawk_python-0.9.0}/src/nighthawk/resilience/_budget.py +0 -0
  101. {nighthawk_python-0.7.0 → nighthawk_python-0.9.0}/src/nighthawk/resilience/_circuit_breaker.py +0 -0
  102. {nighthawk_python-0.7.0 → nighthawk_python-0.9.0}/src/nighthawk/resilience/_fallback.py +0 -0
  103. {nighthawk_python-0.7.0 → nighthawk_python-0.9.0}/src/nighthawk/resilience/_retry.py +0 -0
  104. {nighthawk_python-0.7.0 → nighthawk_python-0.9.0}/src/nighthawk/resilience/_timeout.py +0 -0
  105. {nighthawk_python-0.7.0 → nighthawk_python-0.9.0}/src/nighthawk/resilience/_vote.py +0 -0
  106. {nighthawk_python-0.7.0 → nighthawk_python-0.9.0}/src/nighthawk/runtime/__init__.py +0 -0
  107. {nighthawk_python-0.7.0 → nighthawk_python-0.9.0}/src/nighthawk/runtime/async_bridge.py +0 -0
  108. {nighthawk_python-0.7.0 → nighthawk_python-0.9.0}/src/nighthawk/runtime/runner.py +0 -0
  109. {nighthawk_python-0.7.0 → nighthawk_python-0.9.0}/src/nighthawk/runtime/step_context.py +0 -0
  110. {nighthawk_python-0.7.0 → nighthawk_python-0.9.0}/src/nighthawk/runtime/step_contract.py +0 -0
  111. {nighthawk_python-0.7.0 → nighthawk_python-0.9.0}/src/nighthawk/runtime/tool_calls.py +0 -0
  112. {nighthawk_python-0.7.0 → nighthawk_python-0.9.0}/src/nighthawk/testing.py +0 -0
  113. {nighthawk_python-0.7.0 → nighthawk_python-0.9.0}/src/nighthawk/tools/__init__.py +0 -0
  114. {nighthawk_python-0.7.0 → nighthawk_python-0.9.0}/src/nighthawk/tools/assignment.py +0 -0
  115. {nighthawk_python-0.7.0 → nighthawk_python-0.9.0}/src/nighthawk/tools/execution.py +0 -0
  116. {nighthawk_python-0.7.0 → nighthawk_python-0.9.0}/src/nighthawk/tools/provided.py +0 -0
  117. {nighthawk_python-0.7.0 → nighthawk_python-0.9.0}/src/nighthawk/tools/registry.py +0 -0
  118. {nighthawk_python-0.7.0 → nighthawk_python-0.9.0}/src/nighthawk/ulid.py +0 -0
  119. {nighthawk_python-0.7.0 → nighthawk_python-0.9.0}/tests/AGENTS.md +0 -0
  120. {nighthawk_python-0.7.0 → nighthawk_python-0.9.0}/tests/__init__.py +0 -0
  121. {nighthawk_python-0.7.0 → nighthawk_python-0.9.0}/tests/backends/__init__.py +0 -0
  122. {nighthawk_python-0.7.0 → nighthawk_python-0.9.0}/tests/backends/test_claude_code_cli.py +0 -0
  123. {nighthawk_python-0.7.0 → nighthawk_python-0.9.0}/tests/backends/test_claude_code_sdk.py +0 -0
  124. {nighthawk_python-0.7.0 → nighthawk_python-0.9.0}/tests/backends/test_codex.py +0 -0
  125. {nighthawk_python-0.7.0 → nighthawk_python-0.9.0}/tests/conftest.py +0 -0
  126. {nighthawk_python-0.7.0 → nighthawk_python-0.9.0}/tests/docs/__init__.py +0 -0
  127. {nighthawk_python-0.7.0 → nighthawk_python-0.9.0}/tests/docs/test_coding_agent_examples.py +0 -0
  128. {nighthawk_python-0.7.0 → nighthawk_python-0.9.0}/tests/docs/test_docs_architecture.py +0 -0
  129. {nighthawk_python-0.7.0 → nighthawk_python-0.9.0}/tests/docs/test_prompt_examples.py +0 -0
  130. {nighthawk_python-0.7.0 → nighthawk_python-0.9.0}/tests/execution/__init__.py +0 -0
  131. {nighthawk_python-0.7.0 → nighthawk_python-0.9.0}/tests/execution/stub_executor.py +0 -0
  132. {nighthawk_python-0.7.0 → nighthawk_python-0.9.0}/tests/execution/test_execution_outcome_prompt_fragment.py +0 -0
  133. {nighthawk_python-0.7.0 → nighthawk_python-0.9.0}/tests/execution/test_infer_binding_types.py +0 -0
  134. {nighthawk_python-0.7.0 → nighthawk_python-0.9.0}/tests/execution/test_natural_block_ordering.py +0 -0
  135. {nighthawk_python-0.7.0 → nighthawk_python-0.9.0}/tests/execution/test_natural_traceback.py +0 -0
  136. {nighthawk_python-0.7.0 → nighthawk_python-0.9.0}/tests/integration/__init__.py +0 -0
  137. {nighthawk_python-0.7.0 → nighthawk_python-0.9.0}/tests/integration/skip_helpers.py +0 -0
  138. {nighthawk_python-0.7.0 → nighthawk_python-0.9.0}/tests/integration/test_carry_pattern.py +0 -0
  139. {nighthawk_python-0.7.0 → nighthawk_python-0.9.0}/tests/integration/test_claude_code_cli_integration.py +0 -0
  140. {nighthawk_python-0.7.0 → nighthawk_python-0.9.0}/tests/integration/test_claude_code_sdk_integration.py +0 -0
  141. {nighthawk_python-0.7.0 → nighthawk_python-0.9.0}/tests/integration/test_codex_integration.py +0 -0
  142. {nighthawk_python-0.7.0 → nighthawk_python-0.9.0}/tests/natural/__init__.py +0 -0
  143. {nighthawk_python-0.7.0 → nighthawk_python-0.9.0}/tests/natural/test_blocks.py +0 -0
  144. {nighthawk_python-0.7.0 → nighthawk_python-0.9.0}/tests/public/__init__.py +0 -0
  145. {nighthawk_python-0.7.0 → nighthawk_python-0.9.0}/tests/public/test_readme_example.py +0 -0
  146. {nighthawk_python-0.7.0 → nighthawk_python-0.9.0}/tests/public/test_usage_meter.py +0 -0
  147. {nighthawk_python-0.7.0 → nighthawk_python-0.9.0}/tests/resilience/__init__.py +0 -0
  148. {nighthawk_python-0.7.0 → nighthawk_python-0.9.0}/tests/resilience/test_budget.py +0 -0
  149. {nighthawk_python-0.7.0 → nighthawk_python-0.9.0}/tests/resilience/test_circuit_breaker.py +0 -0
  150. {nighthawk_python-0.7.0 → nighthawk_python-0.9.0}/tests/resilience/test_composition.py +0 -0
  151. {nighthawk_python-0.7.0 → nighthawk_python-0.9.0}/tests/resilience/test_fallback.py +0 -0
  152. {nighthawk_python-0.7.0 → nighthawk_python-0.9.0}/tests/resilience/test_retry.py +0 -0
  153. {nighthawk_python-0.7.0 → nighthawk_python-0.9.0}/tests/resilience/test_timeout.py +0 -0
  154. {nighthawk_python-0.7.0 → nighthawk_python-0.9.0}/tests/resilience/test_vote.py +0 -0
  155. {nighthawk_python-0.7.0 → nighthawk_python-0.9.0}/tests/test_renderer.py +0 -0
  156. {nighthawk_python-0.7.0 → nighthawk_python-0.9.0}/tests/test_testing.py +0 -0
  157. {nighthawk_python-0.7.0 → nighthawk_python-0.9.0}/tests/tools/__init__.py +0 -0
  158. {nighthawk_python-0.7.0 → nighthawk_python-0.9.0}/tests/tools/test_assignment_async.py +0 -0
  159. {nighthawk_python-0.7.0 → nighthawk_python-0.9.0}/tests/tools/test_contracts.py +0 -0
  160. {nighthawk_python-0.7.0 → nighthawk_python-0.9.0}/tests/tools/test_provided_async.py +0 -0
  161. {nighthawk_python-0.7.0 → nighthawk_python-0.9.0}/tests/tools/test_registry.py +0 -0
  162. {nighthawk_python-0.7.0 → nighthawk_python-0.9.0}/tests/tools/test_tool_boundary.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,26 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ### Changed
11
+ - Redesigned `nh.scope()` around `mode` semantics (`"inherit"` default, `"replace"` for explicit replacement).
12
+ - Scope prompt suffix arguments now use list-based forms: `system_prompt_suffix_fragments` and `user_prompt_suffix_fragments`.
13
+
14
+ ### Removed
15
+ - Removed `step_executor_configuration_patch` from `nh.scope()`.
16
+ - Removed `StepExecutorConfigurationPatch` from the public API.
17
+
18
+ ### Fixed
19
+ - Clarified and aligned docs/tests for scope merge vs replace behavior.
20
+
21
+ ## [0.8.0]
22
+
23
+ ### Added
24
+ - Unit tests covering prompt token-budget injection: system prompt resolves `$tool_result_max_tokens`, and custom user prompt templates can resolve the same placeholder.
25
+
26
+ ### Changed
27
+ - Default step system prompt now states that tool result `value` is a preview and includes the injected max-token limit.
28
+ - User prompt template rendering now uses `Template.safe_substitute`, aligned with system prompt injection behavior and compatible with optional `$tool_result_max_tokens` placeholders.
29
+
10
30
  ## [0.7.0]
11
31
 
12
32
  ### Added
@@ -118,8 +138,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
118
138
  - Step executor abstraction and provider integration foundation.
119
139
  - Core documentation and project scaffolding.
120
140
 
121
- [Unreleased]: https://github.com/kurusugawa-computer/nighthawk-python/compare/v0.7.0...HEAD
122
- [0.6.1]: https://github.com/kurusugawa-computer/nighthawk-python/compare/v0.6.1...v0.7.0
141
+ [Unreleased]: https://github.com/kurusugawa-computer/nighthawk-python/compare/v0.8.0...HEAD
142
+ [0.8.0]: https://github.com/kurusugawa-computer/nighthawk-python/compare/v0.7.0...v0.8.0
143
+ [0.7.0]: https://github.com/kurusugawa-computer/nighthawk-python/compare/v0.6.1...v0.7.0
123
144
  [0.6.1]: https://github.com/kurusugawa-computer/nighthawk-python/compare/v0.6.0...v0.6.1
124
145
  [0.6.0]: https://github.com/kurusugawa-computer/nighthawk-python/compare/v0.5.0...v0.6.0
125
146
  [0.5.0]: https://github.com/kurusugawa-computer/nighthawk-python/compare/v0.4.0...v0.5.0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: nighthawk-python
3
- Version: 0.7.0
3
+ Version: 0.9.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,7 +14,6 @@
14
14
  - scope
15
15
  - AgentStepExecutor
16
16
  - StepExecutorConfiguration
17
- - StepExecutorConfigurationPatch
18
17
  - StepPromptTemplates
19
18
  - StepContextLimits
20
19
  - JsonableValue
@@ -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
 
@@ -273,6 +277,8 @@ Do not inject untrusted raw text into Natural source. If input is user-controlle
273
277
  Rules:
274
278
 
275
279
  - The model sees callable signatures from both LOCALS and GLOBALS.
280
+ - For object read bindings, the model also sees a capability view: object header, public methods (with signatures), and public fields (with typed previews).
281
+ - Object capability views expose public members only. Private/dunder members are omitted, and properties are not evaluated.
276
282
  - Put per-invocation data in function parameters. Put stable, reusable capabilities at module level.
277
283
  - Do not annotate callable parameters as `object` or `Any` -- this erases the signature the model needs:
278
284
 
@@ -2,7 +2,7 @@
2
2
 
3
3
  > This page assumes you have completed [Executors](executors.md).
4
4
 
5
- This page covers how to configure execution at runtime: scoping, configuration patching, 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.
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
6
 
7
7
  ## Scoped overrides with `nh.scope()`
8
8
 
@@ -11,17 +11,20 @@ Use `nh.scope()` to override execution settings within an existing run. Each sco
11
11
  ```py
12
12
  with nh.run(step_executor):
13
13
 
14
- # Override model for a specific section
14
+ # Inherit mode (default): merge/append into current scope state
15
15
  with nh.scope(
16
- step_executor_configuration_patch=nh.StepExecutorConfigurationPatch(
16
+ step_executor_configuration=nh.StepExecutorConfiguration(
17
17
  model="openai-responses:gpt-5.4-mini",
18
18
  ),
19
19
  ) as scoped_executor:
20
20
  expensive_analysis(data)
21
21
 
22
- # Append a system prompt suffix for a section
22
+ # Replace mode: replace only explicitly provided values
23
+ # None means "no change". [] / {} means "clear".
23
24
  with nh.scope(
24
- system_prompt_suffix_fragment="Always respond in formal English.",
25
+ mode="replace",
26
+ system_prompt_suffix_fragments=["Always respond in formal English."],
27
+ implicit_references={},
25
28
  ):
26
29
  formal_summary(text)
27
30
 
@@ -29,31 +32,35 @@ with nh.run(step_executor):
29
32
  with nh.scope(step_executor=another_executor):
30
33
  specialized_step(data)
31
34
 
32
- # Add implicit global references for this scope (merged across nested scopes)
35
+ # Add implicit global references for this scope
33
36
  with nh.scope(implicit_references={"search_repository": search_repository}):
34
37
  typed_labeling_step(ticket_text)
35
38
  ```
36
39
 
37
40
  Parameters:
38
41
 
42
+ - `mode`: scope composition mode. Default: `"inherit"`.
39
43
  - `step_executor_configuration`: replace the entire configuration.
40
- - `step_executor_configuration_patch`: partially override specific fields.
41
44
  - `step_executor`: replace the step executor entirely.
42
- - `system_prompt_suffix_fragment`: append text to the system prompt for the scope.
43
- - `user_prompt_suffix_fragment`: append text to the user prompt for the scope.
44
- - `implicit_references`: add implicit global references for this scope as a name-to-value mapping. Nested scopes merge references additively (set union by key).
45
+ - `system_prompt_suffix_fragments`: scope-level system suffix fragments.
46
+ - `user_prompt_suffix_fragments`: scope-level user suffix fragments.
47
+ - `implicit_references`: scope-level implicit global references.
45
48
 
46
- Use `step_executor_configuration` when you want a full configuration replacement for a scope.
47
- Use `step_executor_configuration_patch` for targeted changes (for example, switching only the model).
48
- Use `step_executor` to swap the executor implementation for that scope.
49
- Use `system_prompt_suffix_fragment` and `user_prompt_suffix_fragment` to append one-off scope-level prompt text.
50
- Use `implicit_references` when a step should always expose specific globals even without explicit `<name>` bindings.
49
+ Mode semantics:
50
+
51
+ - `mode="inherit"` (default):
52
+ - `system_prompt_suffix_fragments` and `user_prompt_suffix_fragments` are appended.
53
+ - `implicit_references` are merged additively with conflict checks.
54
+ - `mode="replace"`:
55
+ - `None` means no change.
56
+ - Explicit `[]` or `{}` clears inherited list/dict values.
57
+ - Explicit list/dict values (for example `[e1, e2]` or `{k1: v1, k2: v2}`) fully replace inherited values.
51
58
 
52
59
  The context manager yields the resolved `StepExecutor` for the scope.
53
60
 
54
- `StepExecutorConfiguration` also accepts `system_prompt_suffix_fragments` and `user_prompt_suffix_fragments` (tuples of strings) as baseline suffix fragments for the whole run. Scope-level fragments are appended after configuration-level fragments.
61
+ `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.
55
62
 
56
- ## Additive scoped implicit references
63
+ ## Scoped implicit references
57
64
 
58
65
  `implicit_references` can inject global helper functions as step capabilities:
59
66
 
@@ -65,8 +72,72 @@ with nh.run(step_executor):
65
72
  triage_issue(ticket_text)
66
73
  ```
67
74
 
68
- Nested scopes merge names additively (set union by key).
75
+ In `mode="inherit"` (default), nested scopes merge additively with conflict checks.
76
+ In `mode="replace"`, explicit mappings fully replace inherited mappings.
77
+
78
+ ```py
79
+ with nh.run(step_executor), nh.scope(implicit_references={"parent": search_repository}):
80
+ with nh.scope(mode="replace", implicit_references={}):
81
+ triage_issue(ticket_text)
82
+ ```
83
+
84
+ The inner scope above clears inherited implicit references.
85
+
86
+ ```py
87
+ with nh.scope(mode="replace", implicit_references={"child": search_repository}):
88
+ triage_issue(ticket_text)
89
+ ```
90
+
91
+ This fully replaces inherited references with the provided mapping.
92
+
93
+ ```py
94
+ with nh.scope(mode="replace", implicit_references=None):
95
+ triage_issue(ticket_text)
96
+ ```
97
+
98
+ This keeps inherited references unchanged.
99
+
100
+ ## Prompt suffix fragments in scopes
101
+
102
+ Use list values when setting scope-level suffix fragments:
103
+
104
+ ```py
105
+ with nh.run(step_executor):
106
+ with nh.scope(system_prompt_suffix_fragments=["Use concise answers."]):
107
+ summarize_ticket(ticket_text)
108
+ ```
109
+
110
+ `user_prompt_suffix_fragments` follows the same rules.
111
+
112
+ ```py
113
+ with nh.scope(user_prompt_suffix_fragments=["Focus on actionable output."]):
114
+ summarize_ticket(ticket_text)
115
+ ```
116
+
117
+ In `mode="inherit"`, provided lists are appended.
118
+ In `mode="replace"`, provided lists fully replace inherited lists.
119
+
120
+ ```py
121
+ with nh.run(step_executor), nh.scope(system_prompt_suffix_fragments=["parent"]):
122
+ with nh.scope(mode="replace", system_prompt_suffix_fragments=["child_1", "child_2"]):
123
+ summarize_ticket(ticket_text)
124
+ ```
125
+
126
+ Pass `[]` to clear inherited fragments.
127
+
128
+ ```py
129
+ with nh.scope(mode="replace", system_prompt_suffix_fragments=[]):
130
+ summarize_ticket(ticket_text)
131
+ ```
132
+
133
+ Pass `None` to keep inherited fragments unchanged.
134
+
135
+ ```py
136
+ with nh.scope(mode="replace", system_prompt_suffix_fragments=None):
137
+ summarize_ticket(ticket_text)
138
+ ```
69
139
 
140
+ The same `replace` semantics apply to `user_prompt_suffix_fragments`.
70
141
  ## Mixing executors
71
142
 
72
143
  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:
@@ -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
 
@@ -103,7 +100,7 @@ The condensed coding agent guide (`for-coding-agents.md`) is a derivative docume
103
100
  - `step_user_prompt_template`: user prompt template.
104
101
  - `StepContextLimits`
105
102
  - Limits for rendering dynamic context into the LLM prompt.
106
- - Fields: `locals_max_tokens`, `locals_max_items`, `globals_max_tokens`, `globals_max_items`, `value_max_tokens`, `tool_result_max_tokens`.
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
106
  - `ExecutionContext`
@@ -254,12 +251,49 @@ Ordering:
254
251
 
255
252
  Rendering format:
256
253
 
257
- - Non-callable values: `name: type_name = json_value`, where `json_value` is the compact JSON rendering of the value (bounded by `context_limits.value_max_tokens`).
254
+ - `TypeAliasType` values (PEP 695): `name: type = underlying_type`.
258
255
  - Callable values: `name: (signature)`, where `(signature)` is the result of `inspect.signature`. Type annotations are included when available (e.g., `(base: int, bonus: int) -> int`).
259
256
  - If the callable has a meaningful docstring, the first line is appended as `# first_line`.
260
- - If multiple callable entries share the same signature text, each is annotated with `# disambiguation: use name` to help the LLM distinguish them.
257
+ - If the callable is async, `async` is appended in metadata comments.
258
+ - If multiple callable entries share the same signature text, each is annotated with `# disambiguation: use name`.
261
259
  - If the signature cannot be resolved (e.g., `__signature__` raises), the entry renders as `name: <callable; signature-unavailable>`.
262
- - `TypeAliasType` values (PEP 695): `name: type = underlying_type`.
260
+ - Object capability values (non-callable, non-scalar, non-container values):
261
+ - Header line: `name: object = TypeName`.
262
+ - Public method lines: `name.method: (signature)` using callable rules above.
263
+ - Public field lines: `name.field: type_name = json_value`.
264
+ - Public means names that do not start with `_`; private and dunder names are excluded.
265
+ - Properties are not evaluated.
266
+ - Field discovery uses safe sources: instance `__dict__`, dataclass fields, Pydantic model fields, and readable public `__slots__` entries.
267
+ - Class attributes that are public and non-callable are included as fields.
268
+ - Method expansion is bounded by `context_limits.object_max_methods`; field expansion is bounded by `context_limits.object_max_fields`; each field value preview is bounded by `context_limits.object_field_value_max_tokens`.
269
+ - If object member limits are exceeded, explicit omission lines are added: `name.<methods>: <snipped N public methods>` and/or `name.<fields>: <snipped N public fields>`.
270
+ - Other non-callable values: `name: type_name = json_value`, where `json_value` is bounded by `context_limits.value_max_tokens`.
271
+
272
+ Callable disambiguation considers both top-level callable entries and object method entries in the same section.
273
+
274
+ Ordering:
275
+
276
+ - Top-level entries are rendered in lexicographic order by top-level name.
277
+ - Object methods and fields are rendered in lexicographic order by member name.
278
+ - Methods are rendered before fields for each object entry.
279
+ - If a section-level budget prevents rendering all top-level entries, `<snipped>` is appended at the end of the section.
280
+
281
+ Safety:
282
+
283
+ - Prompt rendering does not call user methods.
284
+ - Prompt rendering does not evaluate properties or descriptors requiring attribute execution.
285
+ - Slot and field access failures are ignored for rendering purposes.
286
+
287
+ Token budgeting:
288
+
289
+ - Section budgets remain governed by `locals_max_tokens` / `globals_max_tokens` and item budgets by `locals_max_items` / `globals_max_items`.
290
+ - Object method and field expansion is additionally governed by object-specific limits listed above.
291
+ - Line-level token counting includes newline separators during budget checks.
292
+
293
+ Observability:
294
+
295
+ - Section-level token truncation emits `prompt_context_truncated` logs with section, reason, and configured max token details.
296
+ - Object member omission due to object-specific member limits does not emit a separate log event.
263
297
 
264
298
  Truncation:
265
299
 
@@ -421,7 +455,7 @@ Atomicity requirement:
421
455
  #### 8.3.1. Supporting types (internal)
422
456
 
423
457
  - `ToolBoundaryError`: Exception carrying `kind` (ErrorKind), `message`, and optional `guidance`. Raised by tool implementations to signal structured failures.
424
- - `ToolResultRenderingPolicy`: Frozen dataclass controlling how tool results are rendered (tokenizer encoding name, max tokens, JSON renderer style).
458
+ - `ToolResultRenderingPolicy`: Frozen dataclass controlling how tool result previews are rendered (tokenizer encoding name, max tokens, JSON renderer style).
425
459
 
426
460
  ### 8.4. Execution contract (final JSON)
427
461
 
@@ -552,13 +586,18 @@ API:
552
586
  - Uses provided `run_id` when given; otherwise generates a new `run_id` (trace root).
553
587
  - Always generates a fresh `scope_id`.
554
588
  - Can be used even when no step executor is currently set.
555
- - `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]`
589
+ - `nighthawk.scope(*, mode: Literal["inherit", "replace"] = "inherit", step_executor_configuration: StepExecutorConfiguration | None = None, step_executor: StepExecutor | 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]`
556
590
  - Enter a nested scope within the current run.
557
591
  - Requires an existing step executor.
558
592
  - Generates a new `scope_id` (keeps the current `run_id`).
559
- - Only specified fields are overridden for the duration of the `with`.
560
- - `system_prompt_suffix_fragment`: optional string appended to the system prompt for the duration of the scope.
561
- - `user_prompt_suffix_fragment`: optional string appended to the user prompt for the duration of the scope.
593
+ - `mode="inherit"` (default):
594
+ - Appends prompt suffix fragment lists.
595
+ - Merges `implicit_references` additively with conflict checks.
596
+ - `mode="replace"`:
597
+ - Replaces provided list/mapping values.
598
+ - `None` means no change.
599
+ - Explicit `[]` / `{}` clears inherited list/mapping values.
600
+ - Explicit list/mapping values (for example `[e1, e2]` or `{k1: v1, k2: v2}`) fully replace inherited values.
562
601
  - Yields the resolved `StepExecutor` for the scope.
563
602
  - `nighthawk.get_step_executor() -> StepExecutor`
564
603
  - Get the current step executor. Raises if unset.
@@ -691,7 +730,7 @@ Instance attributes:
691
730
  - `configuration: StepExecutorConfiguration` — the resolved configuration.
692
731
  - `agent_is_managed: bool` — `True` when the agent was built internally from the configuration, `False` when provided externally via `from_agent`.
693
732
  - `token_encoding` — tiktoken encoding resolved from the configuration.
694
- - `tool_result_rendering_policy: ToolResultRenderingPolicy` — policy for rendering tool results, derived from configuration.
733
+ - `tool_result_rendering_policy: ToolResultRenderingPolicy` — policy for rendering tool result previews, derived from configuration.
695
734
 
696
735
  ### 14.3. Custom backends
697
736
 
@@ -22,4 +22,4 @@ Trust boundaries:
22
22
 
23
23
  Notes:
24
24
  - In async Natural functions, expressions may use `await`.
25
- - Tool calls return {"value": ..., "error": ...}. Values may contain … where content was omitted. Check "error" for failures.
25
+ - Tool calls return {"value": ..., "error": ...}; "value" is a preview (max $tool_result_max_tokens tokens), so persist large/intermediate data via nh_assign and inspect with focused nh_eval reads.
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "nighthawk-python"
3
- version = "0.7.0"
3
+ version = "0.9.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"
@@ -3,7 +3,6 @@ from __future__ import annotations
3
3
  from .configuration import (
4
4
  StepContextLimits,
5
5
  StepExecutorConfiguration,
6
- StepExecutorConfigurationPatch,
7
6
  StepPromptTemplates,
8
7
  )
9
8
  from .errors import (
@@ -32,7 +31,6 @@ __all__ = [
32
31
  "StepContextLimits",
33
32
  "StepExecutor",
34
33
  "StepExecutorConfiguration",
35
- "StepExecutorConfigurationPatch",
36
34
  "StepPromptTemplates",
37
35
  "ToolEvaluationError",
38
36
  "ToolRegistrationError",
@@ -32,7 +32,7 @@ Trust boundaries:
32
32
 
33
33
  Notes:
34
34
  - In async Natural functions, expressions may use `await`.
35
- - Tool calls return {"value": ..., "error": ...}. Values may contain \u2026 where content was omitted. Check "error" for failures.
35
+ - Tool calls return {"value": ..., "error": ...}; "value" is a preview (max $tool_result_max_tokens tokens), so persist large/intermediate data via nh_assign and inspect with focused nh_eval reads.
36
36
  """
37
37
 
38
38
 
@@ -81,8 +81,11 @@ class StepContextLimits(BaseModel):
81
81
  locals_max_items: Maximum items rendered in the locals section.
82
82
  globals_max_tokens: Maximum tokens for the globals section.
83
83
  globals_max_items: Maximum items rendered in the globals section.
84
- value_max_tokens: Maximum tokens for a single value rendering.
85
- tool_result_max_tokens: Maximum tokens for a tool result rendering.
84
+ value_max_tokens: Maximum tokens for a single value preview.
85
+ object_max_methods: Maximum public methods rendered for one object capability view.
86
+ object_max_fields: Maximum public fields rendered for one object capability view.
87
+ object_field_value_max_tokens: Maximum tokens for one object field value preview.
88
+ tool_result_max_tokens: Maximum tokens for a tool result preview.
86
89
  """
87
90
 
88
91
  model_config = ConfigDict(extra="forbid", frozen=True)
@@ -94,6 +97,9 @@ class StepContextLimits(BaseModel):
94
97
  globals_max_items: int = Field(default=40, ge=1)
95
98
 
96
99
  value_max_tokens: int = Field(default=200, ge=1)
100
+ object_max_methods: int = Field(default=16, ge=0)
101
+ object_max_fields: int = Field(default=16, ge=0)
102
+ object_field_value_max_tokens: int = Field(default=120, ge=1)
97
103
  tool_result_max_tokens: int = Field(default=1_200, ge=1)
98
104
 
99
105
 
@@ -154,44 +160,3 @@ class StepExecutorConfiguration(BaseModel):
154
160
  return tiktoken.encoding_for_model(model_name)
155
161
  except Exception:
156
162
  return tiktoken.get_encoding("o200k_base")
157
-
158
-
159
- class StepExecutorConfigurationPatch(BaseModel):
160
- """Partial override for StepExecutorConfiguration.
161
-
162
- Non-None fields replace the corresponding fields in the target configuration.
163
-
164
- Attributes:
165
- model: Model identifier override.
166
- model_settings: Model settings override. Accepts a dict or a
167
- backend-specific BaseModel instance (auto-converted to dict).
168
- prompts: Prompt templates override.
169
- context_limits: Context limits override.
170
- json_renderer_style: JSON renderer style override.
171
- tokenizer_encoding: Tokenizer encoding override.
172
- system_prompt_suffix_fragments: System prompt suffix fragments override.
173
- user_prompt_suffix_fragments: User prompt suffix fragments override.
174
- """
175
-
176
- model_config = ConfigDict(extra="forbid", frozen=True)
177
-
178
- model: str | None = None
179
- model_settings: dict[str, Any] | BaseModel | None = None
180
-
181
- @field_validator("model_settings", mode="before")
182
- @classmethod
183
- def _normalize_model_settings(cls, value: Any) -> dict[str, Any] | None:
184
- if isinstance(value, BaseModel):
185
- return value.model_dump()
186
- return value
187
-
188
- prompts: StepPromptTemplates | None = None
189
- context_limits: StepContextLimits | None = None
190
- json_renderer_style: JsonRendererStyle | None = None
191
- tokenizer_encoding: str | None = None
192
- system_prompt_suffix_fragments: tuple[str, ...] | None = None
193
- user_prompt_suffix_fragments: tuple[str, ...] | None = None
194
-
195
- def apply_to(self, configuration: StepExecutorConfiguration) -> StepExecutorConfiguration:
196
- """Apply non-None fields to the given configuration and return a new copy."""
197
- return configuration.model_copy(update=self.model_dump(exclude_none=True))