nighthawk-python 0.2.0__tar.gz → 0.3.1__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 (105) hide show
  1. {nighthawk_python-0.2.0 → nighthawk_python-0.3.1}/.claude/rules/docs.md +2 -0
  2. nighthawk_python-0.3.1/.claude/settings.json +5 -0
  3. nighthawk_python-0.3.1/CHANGELOG.md +56 -0
  4. {nighthawk_python-0.2.0 → nighthawk_python-0.3.1}/CONTRIBUTING.md +18 -1
  5. {nighthawk_python-0.2.0 → nighthawk_python-0.3.1}/PKG-INFO +3 -2
  6. {nighthawk_python-0.2.0 → nighthawk_python-0.3.1}/docs/for-coding-agents.md +3 -33
  7. {nighthawk_python-0.2.0 → nighthawk_python-0.3.1}/pyproject.toml +3 -2
  8. {nighthawk_python-0.2.0 → nighthawk_python-0.3.1}/src/nighthawk/runtime/scoping.py +29 -11
  9. {nighthawk_python-0.2.0 → nighthawk_python-0.3.1}/src/nighthawk/runtime/step_executor.py +11 -25
  10. {nighthawk_python-0.2.0 → nighthawk_python-0.3.1}/src/nighthawk/runtime/tool_calls.py +3 -2
  11. nighthawk_python-0.3.1/src/nighthawk/ulid.py +18 -0
  12. {nighthawk_python-0.2.0 → nighthawk_python-0.3.1}/tests/public/test_public_api.py +42 -1
  13. {nighthawk_python-0.2.0 → nighthawk_python-0.3.1}/uv.lock +23 -23
  14. nighthawk_python-0.2.0/CHANGELOG.md +0 -35
  15. {nighthawk_python-0.2.0 → nighthawk_python-0.3.1}/.claude/rules/coding.md +0 -0
  16. {nighthawk_python-0.2.0 → nighthawk_python-0.3.1}/.claude/unset_envs.sh +0 -0
  17. {nighthawk_python-0.2.0 → nighthawk_python-0.3.1}/.devcontainer/Dockerfile.devcontainer +0 -0
  18. {nighthawk_python-0.2.0 → nighthawk_python-0.3.1}/.devcontainer/Dockerfile.litellm +0 -0
  19. {nighthawk_python-0.2.0 → nighthawk_python-0.3.1}/.devcontainer/devcontainer.json +0 -0
  20. {nighthawk_python-0.2.0 → nighthawk_python-0.3.1}/.devcontainer/docker-compose.yaml +0 -0
  21. {nighthawk_python-0.2.0 → nighthawk_python-0.3.1}/.devcontainer/litellm-config.yaml +0 -0
  22. {nighthawk_python-0.2.0 → nighthawk_python-0.3.1}/.github/dependabot.yml +0 -0
  23. {nighthawk_python-0.2.0 → nighthawk_python-0.3.1}/.github/workflows/ci.yml +0 -0
  24. {nighthawk_python-0.2.0 → nighthawk_python-0.3.1}/.github/workflows/docs.yml +0 -0
  25. {nighthawk_python-0.2.0 → nighthawk_python-0.3.1}/.github/workflows/publish.yml +0 -0
  26. {nighthawk_python-0.2.0 → nighthawk_python-0.3.1}/.gitignore +0 -0
  27. {nighthawk_python-0.2.0 → nighthawk_python-0.3.1}/.python-version +0 -0
  28. {nighthawk_python-0.2.0 → nighthawk_python-0.3.1}/AGENTS.md +0 -0
  29. {nighthawk_python-0.2.0 → nighthawk_python-0.3.1}/CLAUDE.md +0 -0
  30. {nighthawk_python-0.2.0 → nighthawk_python-0.3.1}/LICENSE +0 -0
  31. {nighthawk_python-0.2.0 → nighthawk_python-0.3.1}/README.md +0 -0
  32. {nighthawk_python-0.2.0 → nighthawk_python-0.3.1}/docs/api.md +0 -0
  33. {nighthawk_python-0.2.0 → nighthawk_python-0.3.1}/docs/assets/nighthawk_logo-128x128.png +0 -0
  34. {nighthawk_python-0.2.0 → nighthawk_python-0.3.1}/docs/coding-agent-backends.md +0 -0
  35. {nighthawk_python-0.2.0 → nighthawk_python-0.3.1}/docs/design.md +0 -0
  36. {nighthawk_python-0.2.0 → nighthawk_python-0.3.1}/docs/index.md +0 -0
  37. {nighthawk_python-0.2.0 → nighthawk_python-0.3.1}/docs/providers.md +0 -0
  38. {nighthawk_python-0.2.0 → nighthawk_python-0.3.1}/docs/quickstart.md +0 -0
  39. {nighthawk_python-0.2.0 → nighthawk_python-0.3.1}/docs/roadmap.md +0 -0
  40. {nighthawk_python-0.2.0 → nighthawk_python-0.3.1}/docs/tutorial.md +0 -0
  41. {nighthawk_python-0.2.0 → nighthawk_python-0.3.1}/mkdocs.yml +0 -0
  42. {nighthawk_python-0.2.0 → nighthawk_python-0.3.1}/pyrightconfig.json +0 -0
  43. {nighthawk_python-0.2.0 → nighthawk_python-0.3.1}/src/nighthawk/__init__.py +0 -0
  44. {nighthawk_python-0.2.0 → nighthawk_python-0.3.1}/src/nighthawk/backends/__init__.py +0 -0
  45. {nighthawk_python-0.2.0 → nighthawk_python-0.3.1}/src/nighthawk/backends/base.py +0 -0
  46. {nighthawk_python-0.2.0 → nighthawk_python-0.3.1}/src/nighthawk/backends/claude_code_cli.py +0 -0
  47. {nighthawk_python-0.2.0 → nighthawk_python-0.3.1}/src/nighthawk/backends/claude_code_sdk.py +0 -0
  48. {nighthawk_python-0.2.0 → nighthawk_python-0.3.1}/src/nighthawk/backends/codex.py +0 -0
  49. {nighthawk_python-0.2.0 → nighthawk_python-0.3.1}/src/nighthawk/backends/mcp_boundary.py +0 -0
  50. {nighthawk_python-0.2.0 → nighthawk_python-0.3.1}/src/nighthawk/backends/mcp_server.py +0 -0
  51. {nighthawk_python-0.2.0 → nighthawk_python-0.3.1}/src/nighthawk/backends/tool_bridge.py +0 -0
  52. {nighthawk_python-0.2.0 → nighthawk_python-0.3.1}/src/nighthawk/configuration.py +0 -0
  53. {nighthawk_python-0.2.0 → nighthawk_python-0.3.1}/src/nighthawk/errors.py +0 -0
  54. {nighthawk_python-0.2.0 → nighthawk_python-0.3.1}/src/nighthawk/identifier_path.py +0 -0
  55. {nighthawk_python-0.2.0 → nighthawk_python-0.3.1}/src/nighthawk/json_renderer.py +0 -0
  56. {nighthawk_python-0.2.0 → nighthawk_python-0.3.1}/src/nighthawk/natural/__init__.py +0 -0
  57. {nighthawk_python-0.2.0 → nighthawk_python-0.3.1}/src/nighthawk/natural/blocks.py +0 -0
  58. {nighthawk_python-0.2.0 → nighthawk_python-0.3.1}/src/nighthawk/natural/decorator.py +0 -0
  59. {nighthawk_python-0.2.0 → nighthawk_python-0.3.1}/src/nighthawk/natural/transform.py +0 -0
  60. {nighthawk_python-0.2.0 → nighthawk_python-0.3.1}/src/nighthawk/runtime/__init__.py +0 -0
  61. {nighthawk_python-0.2.0 → nighthawk_python-0.3.1}/src/nighthawk/runtime/async_bridge.py +0 -0
  62. {nighthawk_python-0.2.0 → nighthawk_python-0.3.1}/src/nighthawk/runtime/prompt.py +0 -0
  63. {nighthawk_python-0.2.0 → nighthawk_python-0.3.1}/src/nighthawk/runtime/runner.py +0 -0
  64. {nighthawk_python-0.2.0 → nighthawk_python-0.3.1}/src/nighthawk/runtime/step_context.py +0 -0
  65. {nighthawk_python-0.2.0 → nighthawk_python-0.3.1}/src/nighthawk/runtime/step_contract.py +0 -0
  66. {nighthawk_python-0.2.0 → nighthawk_python-0.3.1}/src/nighthawk/tools/__init__.py +0 -0
  67. {nighthawk_python-0.2.0 → nighthawk_python-0.3.1}/src/nighthawk/tools/assignment.py +0 -0
  68. {nighthawk_python-0.2.0 → nighthawk_python-0.3.1}/src/nighthawk/tools/contracts.py +0 -0
  69. {nighthawk_python-0.2.0 → nighthawk_python-0.3.1}/src/nighthawk/tools/execution.py +0 -0
  70. {nighthawk_python-0.2.0 → nighthawk_python-0.3.1}/src/nighthawk/tools/provided.py +0 -0
  71. {nighthawk_python-0.2.0 → nighthawk_python-0.3.1}/src/nighthawk/tools/registry.py +0 -0
  72. {nighthawk_python-0.2.0 → nighthawk_python-0.3.1}/tests/__init__.py +0 -0
  73. {nighthawk_python-0.2.0 → nighthawk_python-0.3.1}/tests/backends/__init__.py +0 -0
  74. {nighthawk_python-0.2.0 → nighthawk_python-0.3.1}/tests/backends/test_claude_code_cli.py +0 -0
  75. {nighthawk_python-0.2.0 → nighthawk_python-0.3.1}/tests/backends/test_claude_code_sdk.py +0 -0
  76. {nighthawk_python-0.2.0 → nighthawk_python-0.3.1}/tests/backends/test_codex.py +0 -0
  77. {nighthawk_python-0.2.0 → nighthawk_python-0.3.1}/tests/conftest.py +0 -0
  78. {nighthawk_python-0.2.0 → nighthawk_python-0.3.1}/tests/docs/__init__.py +0 -0
  79. {nighthawk_python-0.2.0 → nighthawk_python-0.3.1}/tests/docs/test_prompt_examples.py +0 -0
  80. {nighthawk_python-0.2.0 → nighthawk_python-0.3.1}/tests/execution/__init__.py +0 -0
  81. {nighthawk_python-0.2.0 → nighthawk_python-0.3.1}/tests/execution/prompt_test_helpers.py +0 -0
  82. {nighthawk_python-0.2.0 → nighthawk_python-0.3.1}/tests/execution/stub_executor.py +0 -0
  83. {nighthawk_python-0.2.0 → nighthawk_python-0.3.1}/tests/execution/test_execution_outcome_prompt_fragment.py +0 -0
  84. {nighthawk_python-0.2.0 → nighthawk_python-0.3.1}/tests/execution/test_globals_prompt.py +0 -0
  85. {nighthawk_python-0.2.0 → nighthawk_python-0.3.1}/tests/execution/test_natural_block_ordering.py +0 -0
  86. {nighthawk_python-0.2.0 → nighthawk_python-0.3.1}/tests/execution/test_natural_traceback.py +0 -0
  87. {nighthawk_python-0.2.0 → nighthawk_python-0.3.1}/tests/execution/test_runtime.py +0 -0
  88. {nighthawk_python-0.2.0 → nighthawk_python-0.3.1}/tests/execution/test_variables_prompt.py +0 -0
  89. {nighthawk_python-0.2.0 → nighthawk_python-0.3.1}/tests/integration/__init__.py +0 -0
  90. {nighthawk_python-0.2.0 → nighthawk_python-0.3.1}/tests/integration/skip_helpers.py +0 -0
  91. {nighthawk_python-0.2.0 → nighthawk_python-0.3.1}/tests/integration/test_carry_pattern.py +0 -0
  92. {nighthawk_python-0.2.0 → nighthawk_python-0.3.1}/tests/integration/test_claude_code_cli_integration.py +0 -0
  93. {nighthawk_python-0.2.0 → nighthawk_python-0.3.1}/tests/integration/test_claude_code_sdk_integration.py +0 -0
  94. {nighthawk_python-0.2.0 → nighthawk_python-0.3.1}/tests/integration/test_codex_integration.py +0 -0
  95. {nighthawk_python-0.2.0 → nighthawk_python-0.3.1}/tests/integration/test_llm_integration.py +0 -0
  96. {nighthawk_python-0.2.0 → nighthawk_python-0.3.1}/tests/natural/__init__.py +0 -0
  97. {nighthawk_python-0.2.0 → nighthawk_python-0.3.1}/tests/natural/test_blocks.py +0 -0
  98. {nighthawk_python-0.2.0 → nighthawk_python-0.3.1}/tests/public/__init__.py +0 -0
  99. {nighthawk_python-0.2.0 → nighthawk_python-0.3.1}/tests/public/test_readme_example.py +0 -0
  100. {nighthawk_python-0.2.0 → nighthawk_python-0.3.1}/tests/test_renderer.py +0 -0
  101. {nighthawk_python-0.2.0 → nighthawk_python-0.3.1}/tests/tools/__init__.py +0 -0
  102. {nighthawk_python-0.2.0 → nighthawk_python-0.3.1}/tests/tools/test_assignment_async.py +0 -0
  103. {nighthawk_python-0.2.0 → nighthawk_python-0.3.1}/tests/tools/test_contracts.py +0 -0
  104. {nighthawk_python-0.2.0 → nighthawk_python-0.3.1}/tests/tools/test_registry.py +0 -0
  105. {nighthawk_python-0.2.0 → nighthawk_python-0.3.1}/tests/tools/test_tool_boundary.py +0 -0
@@ -91,6 +91,8 @@ Each file has a distinct audience and scope. Content belongs in exactly one file
91
91
  - This file should be self-contained: a coding agent reading only this file should be able to write correct Nighthawk code without consulting other docs.
92
92
  - This file is consumed standalone (`@docs/for-coding-agents.md` in CLAUDE.md/AGENTS.md, GitHub raw URL, etc.). Do not assume sibling files exist at relative paths.
93
93
  - All external references to other docs use absolute URLs based on `site_url` from `mkdocs.yml` (currently `https://kurusugawa-computer.github.io/nighthawk-python/`). If `site_url` changes, update the URLs in this file.
94
+ - `@nh.tool` is deprecated. Do not add examples, recommendations, or references to `@nh.tool` in this file. Binding functions are the only recommended callable exposure mechanism.
95
+ - Filter content for coding-agent relevance. Omit infrastructure-level concerns (scoped overrides parameter lists, exception hierarchy beyond `ExecutionError`, observability/tracing) that do not affect how an agent writes Natural blocks or binding functions. Mention existence and link to Tutorial or Design for details.
94
96
 
95
97
  ### api.md specifics
96
98
 
@@ -0,0 +1,5 @@
1
+ {
2
+ "enabledPlugins": {
3
+ "pyright-lsp@claude-plugins-official": true
4
+ }
5
+ }
@@ -0,0 +1,56 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project will be documented in this file.
4
+
5
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
+ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
+
8
+ ## [Unreleased]
9
+
10
+ ## [0.3.1] - 2026-03-19
11
+
12
+ ### Changed
13
+ - Internal ID generation now uses `ulid.generate_ulid()` (ULID,
14
+ 26-character Crockford Base32, timestamp-sortable) in a dedicated
15
+ module, replacing the former `generate_id` embedded in
16
+ `runtime.scoping`.
17
+
18
+ ## [0.3.0] - 2026-03-18
19
+
20
+ ### Added
21
+ - `system_prompt_suffix_fragment_scope` and `user_prompt_suffix_fragment_scope` context managers for lightweight prompt fragment management without full scope overhead.
22
+ - OpenTelemetry tracer now reports `instrumenting_library_version`.
23
+
24
+ ### Changed
25
+ - Simplified OpenTelemetry span hierarchy: removed implicit `nighthawk.scope` spans and `nighthawk.step_executor` spans. `nighthawk.scope` spans are now emitted only for explicit `nh.scope()` calls.
26
+ - `nighthawk.run` span no longer includes `scope.id` attribute; only `run.id` is emitted.
27
+ - Trimmed `for-coding-agents.md` for coding-agent relevance: removed deprecated `@nh.tool` references, condensed exception hierarchy, scoped overrides, and added debugging context to `StepContextLimits`.
28
+
29
+ ## [0.2.0] - 2026-03-16
30
+
31
+ ### Added
32
+ - Added compact step trace support for Natural block execution attempts:
33
+ - `StepTrace`
34
+ - `StepTraceError`
35
+ - `nighthawk.get_step_traces()`
36
+
37
+ ### Changed
38
+ - Updated CI workflow setup (`setup-uv`) in project automation.
39
+
40
+ ### Fixed
41
+ - License badge reference in README.
42
+ - Documentation formatting inconsistencies.
43
+
44
+ ## [0.1.0] - 2026-03-13
45
+
46
+ ### Added
47
+ - Initial public release of `nighthawk-python`.
48
+ - Natural DSL execution runtime with run/scope execution context model.
49
+ - Step executor abstraction and provider integration foundation.
50
+ - Core documentation and project scaffolding.
51
+
52
+ [Unreleased]: https://github.com/kurusugawa-computer/nighthawk-python/compare/v0.3.1...HEAD
53
+ [0.3.1]: https://github.com/kurusugawa-computer/nighthawk-python/compare/v0.3.0...v0.3.1
54
+ [0.3.0]: https://github.com/kurusugawa-computer/nighthawk-python/compare/v0.2.0...v0.3.0
55
+ [0.2.0]: https://github.com/kurusugawa-computer/nighthawk-python/compare/v0.1.0...v0.2.0
56
+ [0.1.0]: https://github.com/kurusugawa-computer/nighthawk-python/tree/v0.1.0
@@ -53,6 +53,23 @@ Then run with your `.env` loaded:
53
53
  set -a; source .env; set +a; uv run pytest -q
54
54
  ```
55
55
 
56
+ ### Trace inspection with otel-tui
57
+
58
+ Nighthawk emits OpenTelemetry traces for step execution. You can inspect them
59
+ locally using [otel-tui](https://github.com/ymtdzzz/otel-tui):
60
+
61
+ ```bash
62
+ docker run --rm -it -p 4318:4318 --name otel-tui ymtdzzz/otel-tui:latest
63
+ ```
64
+
65
+ Then run integration tests with the collector endpoint:
66
+
67
+ ```bash
68
+ OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:4318 uv run pytest -q tests/integration/
69
+ ```
70
+
71
+ Traces appear in the otel-tui terminal UI in real time.
72
+
56
73
  ### Environment variables
57
74
 
58
75
  - `OPENAI_API_KEY`: Required for OpenAI integration tests (also requires `pydantic-ai-slim[openai]`).
@@ -131,7 +148,7 @@ def run(
131
148
 
132
149
  Args:
133
150
  step_executor: The step executor to use for Natural block execution.
134
- run_id: Optional identifier for the run. If not provided, a UUID is
151
+ run_id: Optional identifier for the run. If not provided, a ULID is
135
152
  generated automatically.
136
153
 
137
154
  Yields:
@@ -1,9 +1,10 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: nighthawk-python
3
- Version: 0.2.0
3
+ Version: 0.3.1
4
4
  Summary: An experimental Python library that embeds Natural blocks inside Python functions and executes them using an LLM.
5
5
  Project-URL: Repository, https://github.com/kurusugawa-computer/nighthawk-python
6
6
  Project-URL: Documentation, https://kurusugawa-computer.github.io/nighthawk-python/
7
+ Project-URL: Changelog, https://github.com/kurusugawa-computer/nighthawk-python/blob/main/CHANGELOG.md
7
8
  Project-URL: Bug Tracker, https://github.com/kurusugawa-computer/nighthawk-python/issues
8
9
  Author-email: "Kurusugawa Computer Inc." <oss@kurusugawa.jp>
9
10
  License-Expression: MIT
@@ -26,7 +27,7 @@ Requires-Dist: tiktoken>=0.12
26
27
  Provides-Extra: claude-code-cli
27
28
  Requires-Dist: mcp>=1.26; extra == 'claude-code-cli'
28
29
  Provides-Extra: claude-code-sdk
29
- Requires-Dist: claude-agent-sdk>=0.1; extra == 'claude-code-sdk'
30
+ Requires-Dist: claude-agent-sdk==0.1.48; extra == 'claude-code-sdk'
30
31
  Provides-Extra: codex
31
32
  Requires-Dist: mcp>=1.26; extra == 'codex'
32
33
  Description-Content-Type: text/markdown
@@ -64,10 +64,6 @@ Each Natural block should make exactly one independent judgment. If a block make
64
64
 
65
65
  Binding functions (local or module-level callables) are the preferred way to expose functions to the LLM. The LLM discovers them from the LOCALS/GLOBALS sections of the prompt, rendered as their signature with the first docstring line as `# intent:`.
66
66
 
67
- ### Prefer binding functions over `@nh.tool`
68
-
69
- `@nh.tool` is reserved strictly for cases requiring `RunContext[StepContext]` access. Binding functions incur no per-definition token overhead beyond a signature line. Always use binding functions unless `RunContext` access is needed.
70
-
71
67
  ### Keep locals minimal
72
68
 
73
69
  Module-level names that are stable across invocations (constants, classes, utility functions) should stay in GLOBALS via `<name>` read bindings. Reserve function parameters for data that genuinely varies per call.
@@ -156,11 +152,7 @@ except nh.ExecutionError as e:
156
152
  print(f"Validation failed: {e}")
157
153
  ```
158
154
 
159
- Custom exception types referenced in step locals or globals are available as raise targets.
160
-
161
- ### Exception hierarchy
162
-
163
- All exceptions inherit from `NighthawkError`: `ExecutionError`, `NaturalParseError`, `ToolEvaluationError`, `ToolValidationError`, `ToolRegistrationError`.
155
+ Custom exception types referenced in step locals or globals are available as raise targets. Catch `nh.ExecutionError` for Natural block failures; all Nighthawk exceptions inherit from `nh.NighthawkError`.
164
156
 
165
157
  ## 6. Cross-block composition
166
158
 
@@ -221,30 +213,9 @@ with nh.run(step_executor):
221
213
  result = my_natural_function(data)
222
214
  ```
223
215
 
224
- ### Scoped overrides
225
-
226
- Use `nh.scope()` to override execution settings within an existing run. Each scope generates a new `scope_id` while keeping the current `run_id`.
227
-
228
- Parameters:
229
-
230
- - `step_executor_configuration`: replace the entire configuration.
231
- - `step_executor_configuration_patch`: partially override specific fields.
232
- - `step_executor`: replace the step executor entirely.
233
- - `system_prompt_suffix_fragment`: append text to the system prompt for the scope.
234
- - `user_prompt_suffix_fragment`: append text to the user prompt for the scope.
235
-
236
- ```python
237
- with nh.scope(
238
- step_executor_configuration_patch=nh.StepExecutorConfigurationPatch(
239
- model="openai-responses:gpt-5-mini",
240
- ),
241
- ):
242
- expensive_analysis(data)
243
- ```
244
-
245
- ### Context limits
216
+ Use `nh.scope()` to override model, prompts, or context limits within an existing run. For details, see [Tutorial](https://kurusugawa-computer.github.io/nighthawk-python/tutorial/).
246
217
 
247
- LOCALS and GLOBALS sections are bounded by `StepContextLimits`. Configure via `StepExecutorConfiguration`:
218
+ LOCALS and GLOBALS sections are bounded by `StepContextLimits`. When bindings are missing or truncated (`<snipped>`), adjust the limits:
248
219
 
249
220
  ```python
250
221
  configuration = nh.StepExecutorConfiguration(
@@ -304,7 +275,6 @@ def judge_review(review_data: str | nh.JsonableValue) -> ReviewVerdict:
304
275
  | Use `<:carry>` (write binding) for mutable context | Rebinding breaks the caller's reference | Use `<carry>` (read binding); mutate in-place |
305
276
  | Put two independent judgments in one block | Non-deterministic, hard to test, unclear contract | Split into two blocks connected by Python |
306
277
  | Use Natural for deterministic computation | Wastes latency/cost, adds non-determinism | Use Python |
307
- | Use `@nh.tool` when a binding function suffices | Unnecessary per-definition token overhead | Use binding functions; reserve `@nh.tool` for `RunContext[StepContext]` access |
308
278
  | Forget type annotations on write bindings | No validation or coercion at commit time | Always annotate `<:name>` bindings |
309
279
  | Duplicate module-level constants as function parameters | Moves stable values from GLOBALS to LOCALS, wastes tokens | Reference via `<name>` read binding |
310
280
 
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "nighthawk-python"
3
- version = "0.2.0"
3
+ version = "0.3.1"
4
4
  description = "An experimental Python library that embeds Natural blocks inside Python functions and executes them using an LLM."
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.13"
@@ -32,11 +32,12 @@ dependencies = [
32
32
  [project.urls]
33
33
  Repository = "https://github.com/kurusugawa-computer/nighthawk-python"
34
34
  Documentation = "https://kurusugawa-computer.github.io/nighthawk-python/"
35
+ Changelog = "https://github.com/kurusugawa-computer/nighthawk-python/blob/main/CHANGELOG.md"
35
36
  "Bug Tracker" = "https://github.com/kurusugawa-computer/nighthawk-python/issues"
36
37
 
37
38
  [project.optional-dependencies]
38
39
  claude-code-sdk = [
39
- "claude-agent-sdk>=0.1",
40
+ "claude-agent-sdk==0.1.48",
40
41
  ]
41
42
  claude-code-cli = [
42
43
  "mcp>=1.26",
@@ -1,6 +1,6 @@
1
1
  from __future__ import annotations
2
2
 
3
- import uuid
3
+ import importlib.metadata
4
4
  from collections.abc import Iterator
5
5
  from contextlib import contextmanager
6
6
  from contextvars import ContextVar
@@ -12,6 +12,7 @@ from opentelemetry.trace import Span, get_tracer_provider
12
12
  from ..configuration import StepExecutorConfiguration, StepExecutorConfigurationPatch
13
13
  from ..errors import NighthawkError
14
14
  from ..tools.registry import tool_scope
15
+ from ..ulid import generate_ulid
15
16
 
16
17
  if TYPE_CHECKING:
17
18
  from .step_executor import AgentStepExecutor, StepExecutor
@@ -36,7 +37,9 @@ STEP_ID = "step.id"
36
37
  TOOL_CALL_ID = "tool_call.id"
37
38
 
38
39
 
39
- _tracer = get_tracer_provider().get_tracer("nighthawk")
40
+ _LIBRARY_VERSION = importlib.metadata.version("nighthawk-python")
41
+
42
+ _tracer = get_tracer_provider().get_tracer("nighthawk", _LIBRARY_VERSION)
40
43
 
41
44
 
42
45
  @contextmanager
@@ -50,10 +53,6 @@ def span(span_name: str, /, **attributes: Any) -> Iterator[Span]:
50
53
  yield current_span
51
54
 
52
55
 
53
- def _generate_id() -> str:
54
- return uuid.uuid4().hex
55
-
56
-
57
56
  _step_executor_var: ContextVar[StepExecutor | None] = ContextVar(
58
57
  "nighthawk_step_executor",
59
58
  default=None,
@@ -108,6 +107,26 @@ def get_user_prompt_suffix_fragments() -> tuple[str, ...]:
108
107
  return _user_prompt_suffix_fragments_var.get()
109
108
 
110
109
 
110
+ @contextmanager
111
+ def system_prompt_suffix_fragment_scope(fragment: str) -> Iterator[None]:
112
+ current = _system_prompt_suffix_fragments_var.get()
113
+ token = _system_prompt_suffix_fragments_var.set((*current, fragment))
114
+ try:
115
+ yield
116
+ finally:
117
+ _system_prompt_suffix_fragments_var.reset(token)
118
+
119
+
120
+ @contextmanager
121
+ def user_prompt_suffix_fragment_scope(fragment: str) -> Iterator[None]:
122
+ current = _user_prompt_suffix_fragments_var.get()
123
+ token = _user_prompt_suffix_fragments_var.set((*current, fragment))
124
+ try:
125
+ yield
126
+ finally:
127
+ _user_prompt_suffix_fragments_var.reset(token)
128
+
129
+
111
130
  def _resolve_agent_step_executor(step_executor: StepExecutor) -> AgentStepExecutor:
112
131
  from .step_executor import AgentStepExecutor
113
132
 
@@ -149,7 +168,7 @@ def run(
149
168
 
150
169
  Args:
151
170
  step_executor: The step executor to use for Natural block execution.
152
- run_id: Optional identifier for the run. If not provided, a UUID is
171
+ run_id: Optional identifier for the run. If not provided, a ULID is
153
172
  generated automatically.
154
173
 
155
174
  Yields:
@@ -165,8 +184,8 @@ def run(
165
184
  ```
166
185
  """
167
186
  execution_context = ExecutionContext(
168
- run_id=run_id or _generate_id(),
169
- scope_id=_generate_id(),
187
+ run_id=run_id or generate_ulid(),
188
+ scope_id=generate_ulid(),
170
189
  )
171
190
 
172
191
  with tool_scope():
@@ -179,7 +198,6 @@ def run(
179
198
  "nighthawk.run",
180
199
  **{
181
200
  RUN_ID: execution_context.run_id,
182
- SCOPE_ID: execution_context.scope_id,
183
201
  },
184
202
  ):
185
203
  yield
@@ -260,7 +278,7 @@ def scope(
260
278
 
261
279
  next_execution_context = replace(
262
280
  current_execution_context,
263
- scope_id=_generate_id(),
281
+ scope_id=generate_ulid(),
264
282
  )
265
283
 
266
284
  next_system_fragments = _system_prompt_suffix_fragments_var.get()
@@ -13,13 +13,8 @@ from ..tools.registry import get_visible_tools
13
13
  from .async_bridge import run_coroutine_synchronously
14
14
  from .prompt import build_user_prompt, extract_references_and_program
15
15
  from .scoping import (
16
- RUN_ID,
17
- SCOPE_ID,
18
- STEP_ID,
19
- get_execution_context,
20
16
  get_system_prompt_suffix_fragments,
21
- scope,
22
- span,
17
+ system_prompt_suffix_fragment_scope,
23
18
  )
24
19
  from .step_context import (
25
20
  _MISSING,
@@ -315,25 +310,16 @@ class AgentStepExecutor:
315
310
  allowed_step_kinds=allowed_step_kinds,
316
311
  )
317
312
 
318
- with scope(system_prompt_suffix_fragment=step_system_prompt_fragment):
319
- execution_context = get_execution_context()
320
- with (
321
- span(
322
- "nighthawk.step_executor",
323
- **{
324
- RUN_ID: execution_context.run_id,
325
- SCOPE_ID: execution_context.scope_id,
326
- STEP_ID: step_context.step_id,
327
- },
328
- ),
329
- step_context_scope(step_context),
330
- ):
331
- result = await self._run_agent(
332
- user_prompt=user_prompt,
333
- step_context=step_context,
334
- toolset=toolset,
335
- structured_output_type=structured_output_type,
336
- )
313
+ with (
314
+ system_prompt_suffix_fragment_scope(step_system_prompt_fragment),
315
+ step_context_scope(step_context),
316
+ ):
317
+ result = await self._run_agent(
318
+ user_prompt=user_prompt,
319
+ step_context=step_context,
320
+ toolset=toolset,
321
+ structured_output_type=structured_output_type,
322
+ )
337
323
 
338
324
  step_outcome = self._parse_agent_result(result)
339
325
  bindings = self._extract_bindings(binding_names=binding_names, step_context=step_context)
@@ -1,7 +1,6 @@
1
1
  from __future__ import annotations
2
2
 
3
3
  import json
4
- import uuid
5
4
  from collections.abc import Awaitable, Callable, Iterator
6
5
  from contextlib import contextmanager
7
6
  from typing import Any
@@ -9,9 +8,11 @@ from typing import Any
9
8
  from pydantic_ai import RunContext
10
9
  from pydantic_ai._instrumentation import InstrumentationNames
11
10
 
11
+ from ..ulid import generate_ulid
12
+
12
13
 
13
14
  def generate_tool_call_id() -> str:
14
- return str(uuid.uuid4())
15
+ return generate_ulid()
15
16
 
16
17
 
17
18
  def _resolve_instrumentation_names(*, run_context: RunContext[Any]) -> InstrumentationNames:
@@ -0,0 +1,18 @@
1
+ from __future__ import annotations
2
+
3
+ import os
4
+ import time
5
+
6
+ _CROCKFORD = "0123456789ABCDEFGHJKMNPQRSTVWXYZ"
7
+
8
+
9
+ def generate_ulid() -> str:
10
+ """Generate a ULID string (timestamp-based, sortable, 26 chars, Crockford Base32)."""
11
+ timestamp_ms = int(time.time() * 1000)
12
+ random_int = int.from_bytes(os.urandom(10))
13
+ chars: list[str] = []
14
+ for shift in (45, 40, 35, 30, 25, 20, 15, 10, 5, 0):
15
+ chars.append(_CROCKFORD[(timestamp_ms >> shift) & 0x1F])
16
+ for shift in (75, 70, 65, 60, 55, 50, 45, 40, 35, 30, 25, 20, 15, 10, 5, 0):
17
+ chars.append(_CROCKFORD[(random_int >> shift) & 0x1F])
18
+ return "".join(chars)
@@ -1,4 +1,5 @@
1
1
  import asyncio
2
+ import importlib.metadata
2
3
  from collections.abc import Generator
3
4
 
4
5
  import pytest
@@ -26,7 +27,7 @@ def step_span_exporter() -> Generator[InMemorySpanExporter, None, None]:
26
27
  tracer_provider.add_span_processor(SimpleSpanProcessor(span_exporter))
27
28
 
28
29
  previous_tracer_provider = runtime_scoping._tracer
29
- runtime_scoping._tracer = tracer_provider.get_tracer("nighthawk")
30
+ runtime_scoping._tracer = tracer_provider.get_tracer("nighthawk", importlib.metadata.version("nighthawk-python"))
30
31
  try:
31
32
  yield span_exporter
32
33
  finally:
@@ -428,3 +429,43 @@ def test_step_span_failure_event_structure_is_compact(step_span_exporter: InMemo
428
429
  assert "exception" in exception_event_name_set
429
430
  assert "nighthawk.step.failed" in exception_event_name_set
430
431
  assert len(step_span.events) == 2
432
+
433
+
434
+ def test_run_span_has_only_run_id_attribute(step_span_exporter: InMemorySpanExporter) -> None:
435
+ with nh.run(StubExecutor(), run_id="attr-test"):
436
+
437
+ @nh.natural_function
438
+ def natural_value_function() -> int:
439
+ """natural
440
+ <:result>
441
+ {"step_outcome": {"kind": "pass"}, "bindings": {"result": 42}}
442
+ """
443
+ return result # noqa: F821 # pyright: ignore[reportUndefinedVariable]
444
+
445
+ assert natural_value_function() == 42
446
+
447
+ run_span_list = [s for s in step_span_exporter.get_finished_spans() if s.name == "nighthawk.run"]
448
+ assert len(run_span_list) == 1
449
+ run_span = run_span_list[0]
450
+ assert _require_attribute_key_set(run_span) == {"run.id"}
451
+ assert _require_attribute_value(run_span, "run.id") == "attr-test"
452
+
453
+
454
+ def test_no_implicit_scope_or_step_executor_spans(step_span_exporter: InMemorySpanExporter) -> None:
455
+ with nh.run(StubExecutor()):
456
+
457
+ @nh.natural_function
458
+ def natural_value_function() -> int:
459
+ """natural
460
+ <:result>
461
+ {"step_outcome": {"kind": "pass"}, "bindings": {"result": 7}}
462
+ """
463
+ return result # noqa: F821 # pyright: ignore[reportUndefinedVariable]
464
+
465
+ assert natural_value_function() == 7
466
+
467
+ span_name_set = {s.name for s in step_span_exporter.get_finished_spans()}
468
+ assert "nighthawk.scope" not in span_name_set
469
+ assert "nighthawk.step_executor" not in span_name_set
470
+ assert "nighthawk.run" in span_name_set
471
+ assert "nighthawk.step" in span_name_set
@@ -761,7 +761,7 @@ wheels = [
761
761
 
762
762
  [[package]]
763
763
  name = "nighthawk-python"
764
- version = "0.2.0"
764
+ version = "0.3.1"
765
765
  source = { editable = "." }
766
766
  dependencies = [
767
767
  { name = "headson" },
@@ -799,7 +799,7 @@ docs = [
799
799
 
800
800
  [package.metadata]
801
801
  requires-dist = [
802
- { name = "claude-agent-sdk", marker = "extra == 'claude-code-sdk'", specifier = ">=0.1" },
802
+ { name = "claude-agent-sdk", marker = "extra == 'claude-code-sdk'", specifier = "==0.1.48" },
803
803
  { name = "headson", specifier = ">=0.16.1" },
804
804
  { name = "mcp", marker = "extra == 'claude-code-cli'", specifier = ">=1.26" },
805
805
  { name = "mcp", marker = "extra == 'codex'", specifier = ">=1.26" },
@@ -836,7 +836,7 @@ wheels = [
836
836
 
837
837
  [[package]]
838
838
  name = "openai"
839
- version = "2.28.0"
839
+ version = "2.29.0"
840
840
  source = { registry = "https://pypi.org/simple" }
841
841
  dependencies = [
842
842
  { name = "anyio" },
@@ -848,9 +848,9 @@ dependencies = [
848
848
  { name = "tqdm" },
849
849
  { name = "typing-extensions" },
850
850
  ]
851
- sdist = { url = "https://files.pythonhosted.org/packages/56/87/eb0abb4ef88ddb95b3c13149384c4c288f584f3be17d6a4f63f8c3e3c226/openai-2.28.0.tar.gz", hash = "sha256:bb7fdff384d2a787fa82e8822d1dd3c02e8cf901d60f1df523b7da03cbb6d48d", size = 670334, upload-time = "2026-03-13T19:56:27.306Z" }
851
+ sdist = { url = "https://files.pythonhosted.org/packages/b4/15/203d537e58986b5673e7f232453a2a2f110f22757b15921cbdeea392e520/openai-2.29.0.tar.gz", hash = "sha256:32d09eb2f661b38d3edd7d7e1a2943d1633f572596febe64c0cd370c86d52bec", size = 671128, upload-time = "2026-03-17T17:53:49.599Z" }
852
852
  wheels = [
853
- { url = "https://files.pythonhosted.org/packages/c0/5a/df122348638885526e53140e9c6b0d844af7312682b3bde9587eebc28b47/openai-2.28.0-py3-none-any.whl", hash = "sha256:79aa5c45dba7fef84085701c235cf13ba88485e1ef4f8dfcedc44fc2a698fc1d", size = 1141218, upload-time = "2026-03-13T19:56:25.46Z" },
853
+ { url = "https://files.pythonhosted.org/packages/d0/b1/35b6f9c8cf9318e3dbb7146cc82dab4cf61182a8d5406fc9b50864362895/openai-2.29.0-py3-none-any.whl", hash = "sha256:b7c5de513c3286d17c5e29b92c4c98ceaf0d775244ac8159aeb1bddf840eb42a", size = 1141533, upload-time = "2026-03-17T17:53:47.348Z" },
854
854
  ]
855
855
 
856
856
  [[package]]
@@ -997,17 +997,17 @@ wheels = [
997
997
 
998
998
  [[package]]
999
999
  name = "protobuf"
1000
- version = "6.33.5"
1000
+ version = "6.33.6"
1001
1001
  source = { registry = "https://pypi.org/simple" }
1002
- sdist = { url = "https://files.pythonhosted.org/packages/ba/25/7c72c307aafc96fa87062aa6291d9f7c94836e43214d43722e86037aac02/protobuf-6.33.5.tar.gz", hash = "sha256:6ddcac2a081f8b7b9642c09406bc6a4290128fce5f471cddd165960bb9119e5c", size = 444465, upload-time = "2026-01-29T21:51:33.494Z" }
1002
+ sdist = { url = "https://files.pythonhosted.org/packages/66/70/e908e9c5e52ef7c3a6c7902c9dfbb34c7e29c25d2f81ade3856445fd5c94/protobuf-6.33.6.tar.gz", hash = "sha256:a6768d25248312c297558af96a9f9c929e8c4cee0659cb07e780731095f38135", size = 444531, upload-time = "2026-03-18T19:05:00.988Z" }
1003
1003
  wheels = [
1004
- { url = "https://files.pythonhosted.org/packages/b1/79/af92d0a8369732b027e6d6084251dd8e782c685c72da161bd4a2e00fbabb/protobuf-6.33.5-cp310-abi3-win32.whl", hash = "sha256:d71b040839446bac0f4d162e758bea99c8251161dae9d0983a3b88dee345153b", size = 425769, upload-time = "2026-01-29T21:51:21.751Z" },
1005
- { url = "https://files.pythonhosted.org/packages/55/75/bb9bc917d10e9ee13dee8607eb9ab963b7cf8be607c46e7862c748aa2af7/protobuf-6.33.5-cp310-abi3-win_amd64.whl", hash = "sha256:3093804752167bcab3998bec9f1048baae6e29505adaf1afd14a37bddede533c", size = 437118, upload-time = "2026-01-29T21:51:24.022Z" },
1006
- { url = "https://files.pythonhosted.org/packages/a2/6b/e48dfc1191bc5b52950246275bf4089773e91cb5ba3592621723cdddca62/protobuf-6.33.5-cp39-abi3-macosx_10_9_universal2.whl", hash = "sha256:a5cb85982d95d906df1e2210e58f8e4f1e3cdc088e52c921a041f9c9a0386de5", size = 427766, upload-time = "2026-01-29T21:51:25.413Z" },
1007
- { url = "https://files.pythonhosted.org/packages/4e/b1/c79468184310de09d75095ed1314b839eb2f72df71097db9d1404a1b2717/protobuf-6.33.5-cp39-abi3-manylinux2014_aarch64.whl", hash = "sha256:9b71e0281f36f179d00cbcb119cb19dec4d14a81393e5ea220f64b286173e190", size = 324638, upload-time = "2026-01-29T21:51:26.423Z" },
1008
- { url = "https://files.pythonhosted.org/packages/c5/f5/65d838092fd01c44d16037953fd4c2cc851e783de9b8f02b27ec4ffd906f/protobuf-6.33.5-cp39-abi3-manylinux2014_s390x.whl", hash = "sha256:8afa18e1d6d20af15b417e728e9f60f3aa108ee76f23c3b2c07a2c3b546d3afd", size = 339411, upload-time = "2026-01-29T21:51:27.446Z" },
1009
- { url = "https://files.pythonhosted.org/packages/9b/53/a9443aa3ca9ba8724fdfa02dd1887c1bcd8e89556b715cfbacca6b63dbec/protobuf-6.33.5-cp39-abi3-manylinux2014_x86_64.whl", hash = "sha256:cbf16ba3350fb7b889fca858fb215967792dc125b35c7976ca4818bee3521cf0", size = 323465, upload-time = "2026-01-29T21:51:28.925Z" },
1010
- { url = "https://files.pythonhosted.org/packages/57/bf/2086963c69bdac3d7cff1cc7ff79b8ce5ea0bec6797a017e1be338a46248/protobuf-6.33.5-py3-none-any.whl", hash = "sha256:69915a973dd0f60f31a08b8318b73eab2bd6a392c79184b3612226b0a3f8ec02", size = 170687, upload-time = "2026-01-29T21:51:32.557Z" },
1004
+ { url = "https://files.pythonhosted.org/packages/fc/9f/2f509339e89cfa6f6a4c4ff50438db9ca488dec341f7e454adad60150b00/protobuf-6.33.6-cp310-abi3-win32.whl", hash = "sha256:7d29d9b65f8afef196f8334e80d6bc1d5d4adedb449971fefd3723824e6e77d3", size = 425739, upload-time = "2026-03-18T19:04:48.373Z" },
1005
+ { url = "https://files.pythonhosted.org/packages/76/5d/683efcd4798e0030c1bab27374fd13a89f7c2515fb1f3123efdfaa5eab57/protobuf-6.33.6-cp310-abi3-win_amd64.whl", hash = "sha256:0cd27b587afca21b7cfa59a74dcbd48a50f0a6400cfb59391340ad729d91d326", size = 437089, upload-time = "2026-03-18T19:04:50.381Z" },
1006
+ { url = "https://files.pythonhosted.org/packages/5c/01/a3c3ed5cd186f39e7880f8303cc51385a198a81469d53d0fdecf1f64d929/protobuf-6.33.6-cp39-abi3-macosx_10_9_universal2.whl", hash = "sha256:9720e6961b251bde64edfdab7d500725a2af5280f3f4c87e57c0208376aa8c3a", size = 427737, upload-time = "2026-03-18T19:04:51.866Z" },
1007
+ { url = "https://files.pythonhosted.org/packages/ee/90/b3c01fdec7d2f627b3a6884243ba328c1217ed2d978def5c12dc50d328a3/protobuf-6.33.6-cp39-abi3-manylinux2014_aarch64.whl", hash = "sha256:e2afbae9b8e1825e3529f88d514754e094278bb95eadc0e199751cdd9a2e82a2", size = 324610, upload-time = "2026-03-18T19:04:53.096Z" },
1008
+ { url = "https://files.pythonhosted.org/packages/9b/ca/25afc144934014700c52e05103c2421997482d561f3101ff352e1292fb81/protobuf-6.33.6-cp39-abi3-manylinux2014_s390x.whl", hash = "sha256:c96c37eec15086b79762ed265d59ab204dabc53056e3443e702d2681f4b39ce3", size = 339381, upload-time = "2026-03-18T19:04:54.616Z" },
1009
+ { url = "https://files.pythonhosted.org/packages/16/92/d1e32e3e0d894fe00b15ce28ad4944ab692713f2e7f0a99787405e43533a/protobuf-6.33.6-cp39-abi3-manylinux2014_x86_64.whl", hash = "sha256:e9db7e292e0ab79dd108d7f1a94fe31601ce1ee3f7b79e0692043423020b0593", size = 323436, upload-time = "2026-03-18T19:04:55.768Z" },
1010
+ { url = "https://files.pythonhosted.org/packages/c4/72/02445137af02769918a93807b2b7890047c32bfb9f90371cbc12688819eb/protobuf-6.33.6-py3-none-any.whl", hash = "sha256:77179e006c476e69bf8e8ce866640091ec42e1beb80b213c3900006ecfba6901", size = 170656, upload-time = "2026-03-18T19:04:59.826Z" },
1011
1011
  ]
1012
1012
 
1013
1013
  [[package]]
@@ -1036,7 +1036,7 @@ wheels = [
1036
1036
 
1037
1037
  [[package]]
1038
1038
  name = "pydantic-ai-slim"
1039
- version = "1.68.0"
1039
+ version = "1.70.0"
1040
1040
  source = { registry = "https://pypi.org/simple" }
1041
1041
  dependencies = [
1042
1042
  { name = "genai-prices" },
@@ -1047,9 +1047,9 @@ dependencies = [
1047
1047
  { name = "pydantic-graph" },
1048
1048
  { name = "typing-inspection" },
1049
1049
  ]
1050
- sdist = { url = "https://files.pythonhosted.org/packages/d4/00/3e48684694e424a8d05dc1538fe53322854b0290fbb494f0007db62cd243/pydantic_ai_slim-1.68.0.tar.gz", hash = "sha256:38edda1dbe20137326903d8a223a9f4901d62b0a70799842cae3c7d60b3bebd2", size = 436924, upload-time = "2026-03-13T03:39:08.572Z" }
1050
+ sdist = { url = "https://files.pythonhosted.org/packages/ac/97/d57ee44976c349658ea7c645c5c2e1a26830e4b60fdeeee2669d4aaef6eb/pydantic_ai_slim-1.70.0.tar.gz", hash = "sha256:3df0c0e92f72c35e546d24795bce1f4d38f81da2d10addd2e9f255b2d2c83c91", size = 445474, upload-time = "2026-03-18T04:24:34.393Z" }
1051
1051
  wheels = [
1052
- { url = "https://files.pythonhosted.org/packages/46/14/4e850e54024b453ed905aa92b50b286ed9b096979e7d0896005be5e5b74c/pydantic_ai_slim-1.68.0-py3-none-any.whl", hash = "sha256:c3234c743ab256c7f26aecb2296428a55ae3db9f9ebb8d725941cae887e8e027", size = 567829, upload-time = "2026-03-13T03:39:00.91Z" },
1052
+ { url = "https://files.pythonhosted.org/packages/da/8c/8545d28d0b3a9957aa21393cfdab8280bb854362360b296cd486ed1713ec/pydantic_ai_slim-1.70.0-py3-none-any.whl", hash = "sha256:162907092a562b3160d9ef0418d317ec941c5c0e6dd6e0aa0dbb53b5a5cd3450", size = 576244, upload-time = "2026-03-18T04:24:27.301Z" },
1053
1053
  ]
1054
1054
 
1055
1055
  [package.optional-dependencies]
@@ -1113,7 +1113,7 @@ wheels = [
1113
1113
 
1114
1114
  [[package]]
1115
1115
  name = "pydantic-graph"
1116
- version = "1.68.0"
1116
+ version = "1.70.0"
1117
1117
  source = { registry = "https://pypi.org/simple" }
1118
1118
  dependencies = [
1119
1119
  { name = "httpx" },
@@ -1121,9 +1121,9 @@ dependencies = [
1121
1121
  { name = "pydantic" },
1122
1122
  { name = "typing-inspection" },
1123
1123
  ]
1124
- sdist = { url = "https://files.pythonhosted.org/packages/08/75/de53b774d7b96adc7a75ddc4cac4dfaea25d5538b5004710fc1e9a74180c/pydantic_graph-1.68.0.tar.gz", hash = "sha256:fa48d15659e9514393f0596f62a0355783309e725deedb14d8f3e68fccf3974a", size = 58534, upload-time = "2026-03-13T03:39:10.896Z" }
1124
+ sdist = { url = "https://files.pythonhosted.org/packages/07/27/f7a71ca2a3705e7c24fd777959cf5515646cc5f23b5b16c886a2ed373340/pydantic_graph-1.70.0.tar.gz", hash = "sha256:3f76d9137369ef8748b0e8a6df1a08262118af20a32bc139d23e5c0509c6b711", size = 58578, upload-time = "2026-03-18T04:24:37.007Z" }
1125
1125
  wheels = [
1126
- { url = "https://files.pythonhosted.org/packages/d0/bf/cd45f1987468679e8d631d1c0e6014c4156815440a985389bd11aeb4465f/pydantic_graph-1.68.0-py3-none-any.whl", hash = "sha256:a563291109c3efb69fe7553f20b164651fe98680252e8f07a3cd9a1db2f8a879", size = 72350, upload-time = "2026-03-13T03:39:04.439Z" },
1126
+ { url = "https://files.pythonhosted.org/packages/38/fd/19c42b60c37dfdbbf5b76c7b218e8309b43dac501f7aaf2025527ca05023/pydantic_graph-1.70.0-py3-none-any.whl", hash = "sha256:6083c1503a2587990ee1b8a15915106e3ddabc8f3f11fbc4a108a7d7496af4a5", size = 72351, upload-time = "2026-03-18T04:24:30.291Z" },
1127
1127
  ]
1128
1128
 
1129
1129
  [[package]]
@@ -1532,15 +1532,15 @@ wheels = [
1532
1532
 
1533
1533
  [[package]]
1534
1534
  name = "sse-starlette"
1535
- version = "3.3.2"
1535
+ version = "3.3.3"
1536
1536
  source = { registry = "https://pypi.org/simple" }
1537
1537
  dependencies = [
1538
1538
  { name = "anyio" },
1539
1539
  { name = "starlette" },
1540
1540
  ]
1541
- sdist = { url = "https://files.pythonhosted.org/packages/5a/9f/c3695c2d2d4ef70072c3a06992850498b01c6bc9be531950813716b426fa/sse_starlette-3.3.2.tar.gz", hash = "sha256:678fca55a1945c734d8472a6cad186a55ab02840b4f6786f5ee8770970579dcd", size = 32326, upload-time = "2026-02-28T11:24:34.36Z" }
1541
+ sdist = { url = "https://files.pythonhosted.org/packages/14/2f/9223c24f568bb7a0c03d751e609844dce0968f13b39a3f73fbb3a96cd27a/sse_starlette-3.3.3.tar.gz", hash = "sha256:72a95d7575fd5129bd0ae15275ac6432bb35ac542fdebb82889c24bb9f3f4049", size = 32420, upload-time = "2026-03-17T20:05:55.529Z" }
1542
1542
  wheels = [
1543
- { url = "https://files.pythonhosted.org/packages/61/28/8cb142d3fe80c4a2d8af54ca0b003f47ce0ba920974e7990fa6e016402d1/sse_starlette-3.3.2-py3-none-any.whl", hash = "sha256:5c3ea3dad425c601236726af2f27689b74494643f57017cafcb6f8c9acfbb862", size = 14270, upload-time = "2026-02-28T11:24:32.984Z" },
1543
+ { url = "https://files.pythonhosted.org/packages/78/e2/b8cff57a67dddf9a464d7e943218e031617fb3ddc133aeeb0602ff5f6c85/sse_starlette-3.3.3-py3-none-any.whl", hash = "sha256:c5abb5082a1cc1c6294d89c5290c46b5f67808cfdb612b7ec27e8ba061c22e8d", size = 14329, upload-time = "2026-03-17T20:05:54.35Z" },
1544
1544
  ]
1545
1545
 
1546
1546
  [[package]]
@@ -1,35 +0,0 @@
1
- # Changelog
2
-
3
- All notable changes to this project will be documented in this file.
4
-
5
- The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
- and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
-
8
- ## [Unreleased]
9
-
10
- ## [0.2.0] - 2026-03-16
11
-
12
- ### Added
13
- - Added compact step trace support for Natural block execution attempts:
14
- - `StepTrace`
15
- - `StepTraceError`
16
- - `nighthawk.get_step_traces()`
17
-
18
- ### Changed
19
- - Updated CI workflow setup (`setup-uv`) in project automation.
20
-
21
- ### Fixed
22
- - License badge reference in README.
23
- - Documentation formatting inconsistencies.
24
-
25
- ## [0.1.0] - 2026-03-13
26
-
27
- ### Added
28
- - Initial public release of `nighthawk-python`.
29
- - Natural DSL execution runtime with run/scope execution context model.
30
- - Step executor abstraction and provider integration foundation.
31
- - Core documentation and project scaffolding.
32
-
33
- [Unreleased]: https://github.com/kurusugawa-computer/nighthawk-python/compare/v0.2.0...HEAD
34
- [0.2.0]: https://github.com/kurusugawa-computer/nighthawk-python/compare/v0.1.0...v0.2.0
35
- [0.1.0]: https://github.com/kurusugawa-computer/nighthawk-python/tree/v0.1.0