jharness-kernel 0.1.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 (55) hide show
  1. jharness_kernel-0.1.0/.gitignore +45 -0
  2. jharness_kernel-0.1.0/LICENSE +21 -0
  3. jharness_kernel-0.1.0/PKG-INFO +49 -0
  4. jharness_kernel-0.1.0/README.md +24 -0
  5. jharness_kernel-0.1.0/pyproject.toml +36 -0
  6. jharness_kernel-0.1.0/src/jharness/kernel/__init__.py +200 -0
  7. jharness_kernel-0.1.0/src/jharness/kernel/_digest.py +242 -0
  8. jharness_kernel-0.1.0/src/jharness/kernel/_engine/__init__.py +1 -0
  9. jharness_kernel-0.1.0/src/jharness/kernel/_engine/change.py +117 -0
  10. jharness_kernel-0.1.0/src/jharness/kernel/_engine/commit.py +82 -0
  11. jharness_kernel-0.1.0/src/jharness/kernel/_engine/deadline.py +108 -0
  12. jharness_kernel-0.1.0/src/jharness/kernel/_engine/engine.py +428 -0
  13. jharness_kernel-0.1.0/src/jharness/kernel/_engine/planning.py +263 -0
  14. jharness_kernel-0.1.0/src/jharness/kernel/_engine/tooling.py +563 -0
  15. jharness_kernel-0.1.0/src/jharness/kernel/_engine/verification.py +315 -0
  16. jharness_kernel-0.1.0/src/jharness/kernel/_history.py +132 -0
  17. jharness_kernel-0.1.0/src/jharness/kernel/_validation.py +133 -0
  18. jharness_kernel-0.1.0/src/jharness/kernel/approval.py +66 -0
  19. jharness_kernel-0.1.0/src/jharness/kernel/checkpoint.py +468 -0
  20. jharness_kernel-0.1.0/src/jharness/kernel/commands.py +124 -0
  21. jharness_kernel-0.1.0/src/jharness/kernel/context.py +55 -0
  22. jharness_kernel-0.1.0/src/jharness/kernel/control.py +163 -0
  23. jharness_kernel-0.1.0/src/jharness/kernel/diagnostics/__init__.py +21 -0
  24. jharness_kernel-0.1.0/src/jharness/kernel/diagnostics/trace.py +162 -0
  25. jharness_kernel-0.1.0/src/jharness/kernel/diagnostics/verification.py +604 -0
  26. jharness_kernel-0.1.0/src/jharness/kernel/errors.py +109 -0
  27. jharness_kernel-0.1.0/src/jharness/kernel/events.py +59 -0
  28. jharness_kernel-0.1.0/src/jharness/kernel/history.py +49 -0
  29. jharness_kernel-0.1.0/src/jharness/kernel/invocation.py +180 -0
  30. jharness_kernel-0.1.0/src/jharness/kernel/json_values.py +243 -0
  31. jharness_kernel-0.1.0/src/jharness/kernel/limits.py +58 -0
  32. jharness_kernel-0.1.0/src/jharness/kernel/messages.py +316 -0
  33. jharness_kernel-0.1.0/src/jharness/kernel/models.py +356 -0
  34. jharness_kernel-0.1.0/src/jharness/kernel/py.typed +0 -0
  35. jharness_kernel-0.1.0/src/jharness/kernel/repository.py +320 -0
  36. jharness_kernel-0.1.0/src/jharness/kernel/runtime.py +168 -0
  37. jharness_kernel-0.1.0/src/jharness/kernel/snapshot.py +79 -0
  38. jharness_kernel-0.1.0/src/jharness/kernel/state.py +165 -0
  39. jharness_kernel-0.1.0/src/jharness/kernel/tools.py +430 -0
  40. jharness_kernel-0.1.0/src/jharness/kernel/wire/__init__.py +107 -0
  41. jharness_kernel-0.1.0/src/jharness/kernel/wire/_helpers.py +175 -0
  42. jharness_kernel-0.1.0/src/jharness/kernel/wire/checkpoint.py +362 -0
  43. jharness_kernel-0.1.0/src/jharness/kernel/wire/events.py +311 -0
  44. jharness_kernel-0.1.0/src/jharness/kernel/wire/messages.py +377 -0
  45. jharness_kernel-0.1.0/src/jharness/kernel/wire/models.py +132 -0
  46. jharness_kernel-0.1.0/src/jharness/kernel/wire/requests.py +127 -0
  47. jharness_kernel-0.1.0/src/jharness/kernel/wire/snapshot.py +109 -0
  48. jharness_kernel-0.1.0/src/jharness/kernel/wire/state.py +169 -0
  49. jharness_kernel-0.1.0/src/jharness/kernel/wire/tools.py +175 -0
  50. jharness_kernel-0.1.0/src/jharness/kernel/wire/trace.py +102 -0
  51. jharness_kernel-0.1.0/tests/test_diagnostics.py +750 -0
  52. jharness_kernel-0.1.0/tests/test_runtime.py +473 -0
  53. jharness_kernel-0.1.0/tests/test_runtime_concurrency_edges.py +308 -0
  54. jharness_kernel-0.1.0/tests/test_values.py +535 -0
  55. jharness_kernel-0.1.0/tests/test_wire.py +1135 -0
@@ -0,0 +1,45 @@
1
+ # OS/editor noise
2
+ .DS_Store
3
+ Thumbs.db
4
+ *.swp
5
+ *.swo
6
+
7
+ # Local AI assistant/workspace state
8
+ .codex/
9
+ .claude/
10
+ .gemini/
11
+ .aider*
12
+
13
+ # Pinned specification snapshot generated by scripts/sync_spec.py
14
+ .jharness-spec/
15
+
16
+ # Python
17
+ __pycache__/
18
+ *.py[cod]
19
+ .python-version
20
+ .venv/
21
+ venv/
22
+ ENV/
23
+
24
+ # Python tooling caches
25
+ .pytest_cache/
26
+ .ruff_cache/
27
+ .pyright/
28
+ .mypy_cache/
29
+ .coverage
30
+ htmlcov/
31
+
32
+ # Packaging/build artifacts
33
+ dist/
34
+ build/
35
+ *.egg-info/
36
+
37
+ # Node/TypeScript SDK artifacts
38
+ node_modules/
39
+ coverage/
40
+ .turbo/
41
+ .next/
42
+ *.tsbuildinfo
43
+
44
+ # Logs
45
+ *.log
@@ -0,0 +1,21 @@
1
+ +MIT License
2
+
3
+ Copyright (c) 2026 JHarness contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,49 @@
1
+ Metadata-Version: 2.4
2
+ Name: jharness-kernel
3
+ Version: 0.1.0
4
+ Summary: JHarness model-neutral immutable runtime kernel
5
+ Project-URL: Documentation, https://github.com/Ezio2000/jharness-python/tree/main/docs
6
+ Project-URL: Homepage, https://github.com/Ezio2000/jharness-python
7
+ Project-URL: Issues, https://github.com/Ezio2000/jharness-python/issues
8
+ Project-URL: Repository, https://github.com/Ezio2000/jharness-python.git
9
+ Author: JHarness contributors
10
+ License-Expression: MIT
11
+ License-File: LICENSE
12
+ Keywords: agent,ai,model,runtime,tools
13
+ Classifier: Development Status :: 3 - Alpha
14
+ Classifier: License :: OSI Approved :: MIT License
15
+ Classifier: Operating System :: OS Independent
16
+ Classifier: Programming Language :: Python :: 3
17
+ Classifier: Programming Language :: Python :: 3 :: Only
18
+ Classifier: Programming Language :: Python :: 3.11
19
+ Classifier: Programming Language :: Python :: 3.12
20
+ Classifier: Programming Language :: Python :: 3.13
21
+ Classifier: Programming Language :: Python :: 3.14
22
+ Classifier: Typing :: Typed
23
+ Requires-Python: >=3.11
24
+ Description-Content-Type: text/markdown
25
+
26
+ # jharness-kernel
27
+
28
+ `jharness-kernel` is the dependency-free, model-neutral runtime distribution. It owns
29
+ immutable run state, `Runtime` and `Invocation`, model and tool protocols,
30
+ control, events, limits, atomic checkpoints, repository ports, and explicit
31
+ wire codecs.
32
+
33
+ Stable runtime concepts are imported from `jharness.kernel`. Portable codecs and
34
+ diagnostics are opt-in namespaces:
35
+
36
+ ```bash
37
+ uv add jharness-kernel
38
+ ```
39
+
40
+ ```python
41
+ from jharness.kernel import Message, Runtime
42
+ from jharness.kernel.diagnostics import build_trace, verify_trace
43
+ from jharness.kernel.wire import decode_checkpoint, encode_checkpoint
44
+ ```
45
+
46
+ See the repository
47
+ [architecture](https://github.com/Ezio2000/jharness/blob/v0.1.0/docs/architecture.md),
48
+ [state machine](https://github.com/Ezio2000/jharness/blob/v0.1.0/docs/state-machine.md), and
49
+ [Python package boundaries](https://github.com/Ezio2000/jharness-python/blob/main/docs/python-package-boundaries.md).
@@ -0,0 +1,24 @@
1
+ # jharness-kernel
2
+
3
+ `jharness-kernel` is the dependency-free, model-neutral runtime distribution. It owns
4
+ immutable run state, `Runtime` and `Invocation`, model and tool protocols,
5
+ control, events, limits, atomic checkpoints, repository ports, and explicit
6
+ wire codecs.
7
+
8
+ Stable runtime concepts are imported from `jharness.kernel`. Portable codecs and
9
+ diagnostics are opt-in namespaces:
10
+
11
+ ```bash
12
+ uv add jharness-kernel
13
+ ```
14
+
15
+ ```python
16
+ from jharness.kernel import Message, Runtime
17
+ from jharness.kernel.diagnostics import build_trace, verify_trace
18
+ from jharness.kernel.wire import decode_checkpoint, encode_checkpoint
19
+ ```
20
+
21
+ See the repository
22
+ [architecture](https://github.com/Ezio2000/jharness/blob/v0.1.0/docs/architecture.md),
23
+ [state machine](https://github.com/Ezio2000/jharness/blob/v0.1.0/docs/state-machine.md), and
24
+ [Python package boundaries](https://github.com/Ezio2000/jharness-python/blob/main/docs/python-package-boundaries.md).
@@ -0,0 +1,36 @@
1
+ [project]
2
+ name = "jharness-kernel"
3
+ version = "0.1.0"
4
+ description = "JHarness model-neutral immutable runtime kernel"
5
+ readme = "README.md"
6
+ requires-python = ">=3.11"
7
+ license = "MIT"
8
+ license-files = ["LICENSE"]
9
+ authors = [{ name = "JHarness contributors" }]
10
+ keywords = ["ai", "agent", "model", "runtime", "tools"]
11
+ classifiers = [
12
+ "Development Status :: 3 - Alpha",
13
+ "License :: OSI Approved :: MIT License",
14
+ "Operating System :: OS Independent",
15
+ "Programming Language :: Python :: 3",
16
+ "Programming Language :: Python :: 3 :: Only",
17
+ "Programming Language :: Python :: 3.11",
18
+ "Programming Language :: Python :: 3.12",
19
+ "Programming Language :: Python :: 3.13",
20
+ "Programming Language :: Python :: 3.14",
21
+ "Typing :: Typed",
22
+ ]
23
+ dependencies = []
24
+
25
+ [project.urls]
26
+ Documentation = "https://github.com/Ezio2000/jharness-python/tree/main/docs"
27
+ Homepage = "https://github.com/Ezio2000/jharness-python"
28
+ Issues = "https://github.com/Ezio2000/jharness-python/issues"
29
+ Repository = "https://github.com/Ezio2000/jharness-python.git"
30
+
31
+ [tool.hatch.build.targets.wheel]
32
+ packages = ["src/jharness"]
33
+
34
+ [build-system]
35
+ requires = ["hatchling"]
36
+ build-backend = "hatchling.build"
@@ -0,0 +1,200 @@
1
+ """Model-neutral immutable execution kernel."""
2
+
3
+ from jharness.kernel.approval import (
4
+ ApprovalAllow,
5
+ ApprovalDecision,
6
+ ApprovalDeny,
7
+ ApprovalPolicy,
8
+ ApprovalRequest,
9
+ ApprovalSuspend,
10
+ )
11
+ from jharness.kernel.checkpoint import (
12
+ Checkpoint,
13
+ ControlFact,
14
+ ConversationInsertFact,
15
+ Fact,
16
+ FailedControl,
17
+ HistoryRewriteFact,
18
+ LimitedControl,
19
+ ModelTurnFact,
20
+ ModelTurnResult,
21
+ ResumedFact,
22
+ StartedFact,
23
+ SuspendedControl,
24
+ SuspensionView,
25
+ ToolBatchFact,
26
+ ToolOutcomeKind,
27
+ )
28
+ from jharness.kernel.commands import SuspensionSelector
29
+ from jharness.kernel.context import RunContext
30
+ from jharness.kernel.errors import (
31
+ CommitError,
32
+ KernelError,
33
+ ModelError,
34
+ ModelErrorInfo,
35
+ ProtocolError,
36
+ RepositoryError,
37
+ RevisionConflict,
38
+ ToolError,
39
+ )
40
+ from jharness.kernel.events import Event, EventKind
41
+ from jharness.kernel.history import HistoryReducer, HistoryRewrite
42
+ from jharness.kernel.invocation import Invocation
43
+ from jharness.kernel.json_values import (
44
+ JsonScalar,
45
+ JsonValue,
46
+ freeze_json_value,
47
+ thaw_json_value,
48
+ )
49
+ from jharness.kernel.limits import LimitReason, RunLimits
50
+ from jharness.kernel.messages import ArtifactRef, ContentPart, ErrorInfo, Message, TaskRef, ToolCall
51
+ from jharness.kernel.models import (
52
+ DeltaSink,
53
+ Model,
54
+ ModelCapabilities,
55
+ ModelContentDelta,
56
+ ModelDelta,
57
+ ModelOptions,
58
+ ModelReasoningDelta,
59
+ ModelRequest,
60
+ ModelResponse,
61
+ ModelToolCallDelta,
62
+ ModelUsage,
63
+ ModelUsageDelta,
64
+ ResponseFormat,
65
+ ToolChoice,
66
+ )
67
+ from jharness.kernel.repository import EphemeralRepository, RunRepository
68
+ from jharness.kernel.runtime import Runtime
69
+ from jharness.kernel.snapshot import RunSnapshot
70
+ from jharness.kernel.state import (
71
+ ActiveState,
72
+ Completed,
73
+ Failed,
74
+ Limited,
75
+ Planning,
76
+ RunMetrics,
77
+ RunState,
78
+ Suspended,
79
+ Suspension,
80
+ ToolsPending,
81
+ )
82
+ from jharness.kernel.tools import (
83
+ BatchPolicy,
84
+ SettledOutcome,
85
+ SettledResult,
86
+ ToolAccepted,
87
+ ToolBatch,
88
+ ToolBinding,
89
+ ToolCatalog,
90
+ ToolCatalogProvider,
91
+ ToolContext,
92
+ ToolExecution,
93
+ ToolFailure,
94
+ ToolOutcome,
95
+ ToolResult,
96
+ ToolRisk,
97
+ ToolSpec,
98
+ ToolSuccess,
99
+ ToolWaiting,
100
+ WaitingResult,
101
+ tool_message,
102
+ )
103
+
104
+ __all__ = [
105
+ "ActiveState",
106
+ "ApprovalAllow",
107
+ "ApprovalDecision",
108
+ "ApprovalDeny",
109
+ "ApprovalPolicy",
110
+ "ApprovalRequest",
111
+ "ApprovalSuspend",
112
+ "ArtifactRef",
113
+ "BatchPolicy",
114
+ "Checkpoint",
115
+ "CommitError",
116
+ "Completed",
117
+ "ContentPart",
118
+ "ControlFact",
119
+ "ConversationInsertFact",
120
+ "DeltaSink",
121
+ "EphemeralRepository",
122
+ "ErrorInfo",
123
+ "Event",
124
+ "EventKind",
125
+ "Fact",
126
+ "Failed",
127
+ "FailedControl",
128
+ "HistoryReducer",
129
+ "HistoryRewrite",
130
+ "HistoryRewriteFact",
131
+ "Invocation",
132
+ "JsonScalar",
133
+ "JsonValue",
134
+ "KernelError",
135
+ "LimitReason",
136
+ "Limited",
137
+ "LimitedControl",
138
+ "Message",
139
+ "Model",
140
+ "ModelCapabilities",
141
+ "ModelContentDelta",
142
+ "ModelDelta",
143
+ "ModelError",
144
+ "ModelErrorInfo",
145
+ "ModelOptions",
146
+ "ModelReasoningDelta",
147
+ "ModelRequest",
148
+ "ModelResponse",
149
+ "ModelToolCallDelta",
150
+ "ModelTurnFact",
151
+ "ModelTurnResult",
152
+ "ModelUsage",
153
+ "ModelUsageDelta",
154
+ "Planning",
155
+ "ProtocolError",
156
+ "RepositoryError",
157
+ "ResponseFormat",
158
+ "ResumedFact",
159
+ "RevisionConflict",
160
+ "RunContext",
161
+ "RunLimits",
162
+ "RunMetrics",
163
+ "RunRepository",
164
+ "RunSnapshot",
165
+ "RunState",
166
+ "Runtime",
167
+ "SettledOutcome",
168
+ "SettledResult",
169
+ "StartedFact",
170
+ "Suspended",
171
+ "SuspendedControl",
172
+ "Suspension",
173
+ "SuspensionSelector",
174
+ "SuspensionView",
175
+ "TaskRef",
176
+ "ToolAccepted",
177
+ "ToolBatch",
178
+ "ToolBatchFact",
179
+ "ToolBinding",
180
+ "ToolCall",
181
+ "ToolCatalog",
182
+ "ToolCatalogProvider",
183
+ "ToolChoice",
184
+ "ToolContext",
185
+ "ToolError",
186
+ "ToolExecution",
187
+ "ToolFailure",
188
+ "ToolOutcome",
189
+ "ToolOutcomeKind",
190
+ "ToolResult",
191
+ "ToolRisk",
192
+ "ToolSpec",
193
+ "ToolSuccess",
194
+ "ToolWaiting",
195
+ "ToolsPending",
196
+ "WaitingResult",
197
+ "freeze_json_value",
198
+ "thaw_json_value",
199
+ "tool_message",
200
+ ]
@@ -0,0 +1,242 @@
1
+ """Small explicit digest writer for immutable kernel content."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from collections.abc import Mapping, Sequence
6
+ from hashlib import sha256
7
+ from typing import Protocol, cast
8
+
9
+ from jharness.kernel.messages import ArtifactRef, ContentPart, ErrorInfo, Message, TaskRef, ToolCall
10
+ from jharness.kernel.tools import ToolAccepted, ToolFailure, ToolOutcome, ToolWaiting
11
+
12
+
13
+ class _Hash(Protocol):
14
+ def update(self, data: bytes, /) -> None: ...
15
+
16
+ def digest(self) -> bytes: ...
17
+
18
+
19
+ class DigestWriter:
20
+ """Domain-separated, length-prefixed writer with explicit scalar types."""
21
+
22
+ __slots__ = ("_hash",)
23
+
24
+ def __init__(self, domain: str) -> None:
25
+ self._hash: _Hash = sha256()
26
+ self._chunk(b"d", domain.encode())
27
+
28
+ def field(self, name: str) -> None:
29
+ self._chunk(b"k", name.encode())
30
+
31
+ def none(self) -> None:
32
+ self._hash.update(b"n")
33
+
34
+ def boolean(self, value: bool) -> None:
35
+ self._hash.update(b"b1" if value else b"b0")
36
+
37
+ def integer(self, value: int) -> None:
38
+ self._chunk(b"i", str(value).encode())
39
+
40
+ def number(self, value: float) -> None:
41
+ self._chunk(b"f", value.hex().encode())
42
+
43
+ def string(self, value: str) -> None:
44
+ self._chunk(b"s", value.encode())
45
+
46
+ def bytes(self, value: bytes) -> None:
47
+ self._chunk(b"x", value)
48
+
49
+ def sequence(self, length: int) -> None:
50
+ self._chunk(b"q", str(length).encode())
51
+
52
+ def mapping(self, length: int) -> None:
53
+ self._chunk(b"m", str(length).encode())
54
+
55
+ def json(self, value: object) -> None:
56
+ """Write a validated JSON value with stable mapping order and scalar types."""
57
+
58
+ if value is None:
59
+ self.none()
60
+ elif isinstance(value, bool):
61
+ self.boolean(value)
62
+ elif isinstance(value, int):
63
+ self.integer(value)
64
+ elif isinstance(value, float):
65
+ self.number(value)
66
+ elif isinstance(value, str):
67
+ self.string(value)
68
+ elif isinstance(value, Mapping):
69
+ self._json_mapping(cast(Mapping[object, object], value))
70
+ elif isinstance(value, Sequence) and not isinstance(value, bytes | bytearray):
71
+ items = cast(Sequence[object], value)
72
+ self.sequence(len(items))
73
+ for item in items:
74
+ self.json(item)
75
+ else:
76
+ raise TypeError(f"unsupported JSON digest value: {type(value).__qualname__}")
77
+
78
+ def finish(self) -> bytes:
79
+ return self._hash.digest()
80
+
81
+ def _json_mapping(self, value: Mapping[object, object]) -> None:
82
+ if any(not isinstance(key, str) for key in value):
83
+ raise TypeError("JSON digest mapping keys must be strings")
84
+ items = cast(Mapping[str, object], value)
85
+ keys = sorted(items)
86
+ self.mapping(len(keys))
87
+ for key in keys:
88
+ self.string(key)
89
+ self.json(items[key])
90
+
91
+ def _chunk(self, tag: bytes, value: bytes) -> None:
92
+ self._hash.update(tag)
93
+ self._hash.update(len(value).to_bytes(8, "big"))
94
+ self._hash.update(value)
95
+
96
+
97
+ def empty_history_digest() -> bytes:
98
+ return DigestWriter("jharness.kernel.history.empty.v0").finish()
99
+
100
+
101
+ def append_history_digest(previous: bytes, message: Message) -> bytes:
102
+ writer = DigestWriter("jharness.kernel.history.append.v0")
103
+ writer.field("previous")
104
+ writer.bytes(previous)
105
+ writer.field("message")
106
+ write_message(writer, message)
107
+ return writer.finish()
108
+
109
+
110
+ def write_message(writer: DigestWriter, value: Message) -> None:
111
+ writer.field("role")
112
+ writer.string(value.role)
113
+ writer.field("parts")
114
+ write_parts(writer, value.parts)
115
+ writer.field("tool_calls")
116
+ write_tool_calls(writer, value.tool_calls)
117
+ writer.field("tool_call_id")
118
+ write_optional_string(writer, value.tool_call_id)
119
+ writer.field("outcome")
120
+ if value.outcome is None:
121
+ writer.none()
122
+ else:
123
+ _write_tool_outcome(writer, value.outcome)
124
+ writer.field("metadata")
125
+ writer.json(value.metadata)
126
+
127
+
128
+ def write_parts(writer: DigestWriter, values: tuple[ContentPart, ...]) -> None:
129
+ writer.sequence(len(values))
130
+ for value in values:
131
+ _write_content_part(writer, value)
132
+
133
+
134
+ def _write_content_part(writer: DigestWriter, value: ContentPart) -> None:
135
+ writer.field("content_part")
136
+ writer.field("type")
137
+ writer.string(value.type)
138
+ writer.field("text")
139
+ write_optional_string(writer, value.text)
140
+ writer.field("uri")
141
+ write_optional_string(writer, value.uri)
142
+ writer.field("artifact")
143
+ if value.artifact is None:
144
+ writer.none()
145
+ else:
146
+ _write_artifact(writer, value.artifact)
147
+ writer.field("media_type")
148
+ write_optional_string(writer, value.media_type)
149
+ writer.field("name")
150
+ write_optional_string(writer, value.name)
151
+ writer.field("data")
152
+ writer.json(value.data)
153
+ writer.field("metadata")
154
+ writer.json(value.metadata)
155
+
156
+
157
+ def _write_artifact(writer: DigestWriter, value: ArtifactRef) -> None:
158
+ writer.field("artifact_ref")
159
+ writer.field("ref")
160
+ writer.string(value.ref)
161
+ writer.field("media_type")
162
+ write_optional_string(writer, value.media_type)
163
+ writer.field("name")
164
+ write_optional_string(writer, value.name)
165
+ writer.field("size_bytes")
166
+ write_optional_integer(writer, value.size_bytes)
167
+ writer.field("sha256")
168
+ write_optional_string(writer, value.sha256)
169
+ writer.field("metadata")
170
+ writer.json(value.metadata)
171
+
172
+
173
+ def write_tool_calls(writer: DigestWriter, values: tuple[ToolCall, ...]) -> None:
174
+ writer.sequence(len(values))
175
+ for value in values:
176
+ _write_tool_call(writer, value)
177
+
178
+
179
+ def _write_tool_call(writer: DigestWriter, value: ToolCall) -> None:
180
+ writer.field("tool_call")
181
+ writer.field("id")
182
+ writer.string(value.id)
183
+ writer.field("name")
184
+ writer.string(value.name)
185
+ writer.field("arguments")
186
+ writer.json(value.arguments)
187
+
188
+
189
+ def _write_tool_outcome(writer: DigestWriter, value: ToolOutcome) -> None:
190
+ writer.field("tool_outcome")
191
+ writer.field("kind")
192
+ writer.string(value.kind)
193
+ writer.field("parts")
194
+ write_parts(writer, value.parts)
195
+ writer.field("structured_content")
196
+ writer.json(value.structured_content)
197
+ if isinstance(value, ToolFailure):
198
+ writer.field("error")
199
+ write_error(writer, value.error)
200
+ elif isinstance(value, ToolAccepted):
201
+ writer.field("correlation_id")
202
+ writer.string(value.correlation_id)
203
+ writer.field("task")
204
+ _write_optional_task(writer, value.task)
205
+ elif isinstance(value, ToolWaiting):
206
+ writer.field("task")
207
+ _write_optional_task(writer, value.task)
208
+
209
+
210
+ def _write_optional_task(writer: DigestWriter, value: TaskRef | None) -> None:
211
+ if value is None:
212
+ writer.none()
213
+ return
214
+ writer.field("task_ref")
215
+ writer.field("id")
216
+ writer.string(value.id)
217
+ writer.field("status")
218
+ writer.string(value.status)
219
+ writer.field("metadata")
220
+ writer.json(value.metadata)
221
+
222
+
223
+ def write_error(writer: DigestWriter, value: ErrorInfo) -> None:
224
+ writer.field("error_info")
225
+ writer.field("code")
226
+ writer.string(value.code)
227
+ writer.field("message")
228
+ writer.string(value.message)
229
+
230
+
231
+ def write_optional_string(writer: DigestWriter, value: str | None) -> None:
232
+ if value is None:
233
+ writer.none()
234
+ else:
235
+ writer.string(value)
236
+
237
+
238
+ def write_optional_integer(writer: DigestWriter, value: int | None) -> None:
239
+ if value is None:
240
+ writer.none()
241
+ else:
242
+ writer.integer(value)
@@ -0,0 +1 @@
1
+ """Private execution engine."""