millforge 0.1.0__py3-none-any.whl

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 (116) hide show
  1. millforge/__init__.py +1174 -0
  2. millforge/_forge/LICENSE +21 -0
  3. millforge/_forge/PROVENANCE.json +295 -0
  4. millforge/_forge/UPDATE_POLICY.md +24 -0
  5. millforge/_forge/__init__.py +14 -0
  6. millforge/_forge/adapter.py +2232 -0
  7. millforge/_forge/base_runner.py +121 -0
  8. millforge/_forge/clients/__init__.py +10 -0
  9. millforge/_forge/clients/base.py +200 -0
  10. millforge/_forge/context/__init__.py +23 -0
  11. millforge/_forge/context/manager.py +178 -0
  12. millforge/_forge/context/strategies.py +335 -0
  13. millforge/_forge/core/__init__.py +16 -0
  14. millforge/_forge/core/inference.py +433 -0
  15. millforge/_forge/core/messages.py +119 -0
  16. millforge/_forge/core/runner.py +479 -0
  17. millforge/_forge/core/steps.py +108 -0
  18. millforge/_forge/core/workflow.py +400 -0
  19. millforge/_forge/errors.py +222 -0
  20. millforge/_forge/guardrails/__init__.py +21 -0
  21. millforge/_forge/guardrails/error_tracker.py +71 -0
  22. millforge/_forge/guardrails/guardrails.py +194 -0
  23. millforge/_forge/guardrails/nudge.py +47 -0
  24. millforge/_forge/guardrails/response_validator.py +119 -0
  25. millforge/_forge/guardrails/step_enforcer.py +183 -0
  26. millforge/_forge/prompts/__init__.py +16 -0
  27. millforge/_forge/prompts/nudges.py +95 -0
  28. millforge/_forge/prompts/templates.py +285 -0
  29. millforge/_version.py +3 -0
  30. millforge/artifacts.py +570 -0
  31. millforge/base/__init__.py +97 -0
  32. millforge/base/composition.py +402 -0
  33. millforge/base/context.py +285 -0
  34. millforge/base/harness.py +138 -0
  35. millforge/base/identity.py +465 -0
  36. millforge/base/options.py +34 -0
  37. millforge/base/platform.py +17 -0
  38. millforge/base/prompt.py +317 -0
  39. millforge/base/runner.py +546 -0
  40. millforge/compiled_plan.py +970 -0
  41. millforge/compiler/__init__.py +231 -0
  42. millforge/compiler/artifact_validation.py +257 -0
  43. millforge/compiler/canonicalization.py +169 -0
  44. millforge/compiler/capabilities.py +66 -0
  45. millforge/compiler/catalogs.py +500 -0
  46. millforge/compiler/diagnostics.py +491 -0
  47. millforge/compiler/graph.py +678 -0
  48. millforge/compiler/lowering.py +198 -0
  49. millforge/compiler/output.py +692 -0
  50. millforge/compiler/parsing.py +1424 -0
  51. millforge/compiler/requests.py +1180 -0
  52. millforge/compiler/schema_validation.py +272 -0
  53. millforge/compiler/semantic.py +490 -0
  54. millforge/compiler/service.py +448 -0
  55. millforge/compiler/source.py +375 -0
  56. millforge/compiler/validators.py +184 -0
  57. millforge/connectors/__init__.py +95 -0
  58. millforge/connectors/admission.py +801 -0
  59. millforge/connectors/broker.py +202 -0
  60. millforge/connectors/contracts.py +1159 -0
  61. millforge/connectors/diagnostics.py +189 -0
  62. millforge/connectors/fake.py +66 -0
  63. millforge/connectors/runtime.py +236 -0
  64. millforge/contracts.py +2860 -0
  65. millforge/custom_tools/__init__.py +67 -0
  66. millforge/custom_tools/compiler.py +724 -0
  67. millforge/custom_tools/contracts.py +1093 -0
  68. millforge/custom_tools/diagnostics.py +205 -0
  69. millforge/eval_artifacts.py +952 -0
  70. millforge/eval_boundary.py +2435 -0
  71. millforge/eval_fixtures/__init__.py +1 -0
  72. millforge/eval_fixtures/default_pack/__init__.py +1 -0
  73. millforge/eval_fixtures/default_pack/fixtures/fixture.08a.bug_diagnosis.traceback.v1.json +52 -0
  74. millforge/eval_fixtures/default_pack/fixtures/fixture.08a.direct_edit.import_sort.v1.json +52 -0
  75. millforge/eval_fixtures/default_pack/fixtures/fixture.08a.evidence_discipline.no_source_change.v1.json +51 -0
  76. millforge/eval_fixtures/default_pack/fixtures/fixture.08a.false_closure.visible_green.v1.json +52 -0
  77. millforge/eval_fixtures/default_pack/fixtures/fixture.08a.multi_file.api_contract.v1.json +54 -0
  78. millforge/eval_fixtures/default_pack/fixtures/fixture.08a.recovery.malformed_artifact.v1.json +54 -0
  79. millforge/eval_fixtures/default_pack/manifest.json +12 -0
  80. millforge/eval_modes.py +1282 -0
  81. millforge/eval_presets.py +1398 -0
  82. millforge/eval_reports.py +2517 -0
  83. millforge/eval_suite.py +2429 -0
  84. millforge/eval_trials.py +2632 -0
  85. millforge/eval_workflow.py +794 -0
  86. millforge/exceptions.py +122 -0
  87. millforge/model_backend.py +2098 -0
  88. millforge/protocols.py +340 -0
  89. millforge/py.typed +0 -0
  90. millforge/runtime.py +1791 -0
  91. millforge/testing/__init__.py +1089 -0
  92. millforge/tools/__init__.py +83 -0
  93. millforge/tools/builtin_runtime.py +1339 -0
  94. millforge/tools/builtins.py +773 -0
  95. millforge/tools/execution.py +1545 -0
  96. millforge/tools/path_policy.py +155 -0
  97. millforge/tools/pi_compat/PI_LICENSE +21 -0
  98. millforge/tools/pi_compat/PROVENANCE.json +55 -0
  99. millforge/tools/pi_compat/UPDATE_POLICY.md +36 -0
  100. millforge/tools/pi_compat/__init__.py +34 -0
  101. millforge/tools/pi_compat/contracts.py +49 -0
  102. millforge/tools/pi_compat/editing.py +390 -0
  103. millforge/tools/pi_compat/mutations.py +57 -0
  104. millforge/tools/pi_compat/operations.py +401 -0
  105. millforge/tools/pi_compat/paths.py +155 -0
  106. millforge/tools/pi_compat/process.py +1375 -0
  107. millforge/tools/pi_compat/search.py +738 -0
  108. millforge/tools/pi_compat/truncation.py +267 -0
  109. millforge/tools/pi_compat_catalog.py +396 -0
  110. millforge/tools/pi_compat_runtime.py +460 -0
  111. millforge/tools/registry.py +553 -0
  112. millforge/tools/results.py +533 -0
  113. millforge-0.1.0.dist-info/METADATA +844 -0
  114. millforge-0.1.0.dist-info/RECORD +116 -0
  115. millforge-0.1.0.dist-info/WHEEL +4 -0
  116. millforge-0.1.0.dist-info/licenses/LICENSE +201 -0
@@ -0,0 +1,460 @@
1
+ """Runtime adapters for the Pi-compatible unrestricted tool catalog."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import math
6
+ from collections.abc import Callable, Mapping, Sequence
7
+ from pathlib import Path
8
+ from typing import Any, cast
9
+
10
+ from millforge import (
11
+ CancellationResolver,
12
+ SideEffectCertainty,
13
+ SideEffectRecord,
14
+ ToolExecutionContext,
15
+ ToolExecutionResult,
16
+ ToolExecutionStatus,
17
+ ValidatedToolCall,
18
+ )
19
+ from millforge.compiled_plan import CompiledHarnessPlan
20
+ from millforge.tools.execution import CompiledToolBindingExecutor, RuntimeToolRegistry
21
+ from millforge.tools.pi_compat.contracts import (
22
+ PiCompatErrorKind,
23
+ PiCompatOperationResult,
24
+ )
25
+ from millforge.tools.pi_compat.editing import execute_edit
26
+ from millforge.tools.pi_compat.operations import execute_ls, execute_read, execute_write
27
+ from millforge.tools.pi_compat.process import PiCompatShellConfig, execute_bash
28
+ from millforge.tools.pi_compat.search import execute_find, execute_grep
29
+ from millforge.tools.results import (
30
+ ToolExecutionErrorCode,
31
+ canonical_sha256,
32
+ make_tool_result,
33
+ )
34
+ from millforge.tools.registry import ToolDescriptor
35
+
36
+ from .pi_compat_catalog import (
37
+ DEFAULT_BASE_TERMINAL_RESULTS,
38
+ _create_pi_compat_tool_snapshot_for_terminal_results,
39
+ _descriptors_for_terminal_results,
40
+ )
41
+
42
+ __all__ = ["create_pi_compat_tool_executor"]
43
+
44
+
45
+ _ERROR_STATUS_AND_CODE: dict[
46
+ PiCompatErrorKind, tuple[ToolExecutionStatus, ToolExecutionErrorCode]
47
+ ] = {
48
+ PiCompatErrorKind.INVALID_ARGUMENTS: (
49
+ ToolExecutionStatus.NOT_EXECUTED,
50
+ ToolExecutionErrorCode.INVALID_ARGUMENTS,
51
+ ),
52
+ PiCompatErrorKind.NOT_FOUND: (
53
+ ToolExecutionStatus.SOFT_FAILURE,
54
+ ToolExecutionErrorCode.NOT_FOUND,
55
+ ),
56
+ PiCompatErrorKind.PERMISSION_DENIED: (
57
+ ToolExecutionStatus.SOFT_FAILURE,
58
+ ToolExecutionErrorCode.PERMISSION_DENIED,
59
+ ),
60
+ PiCompatErrorKind.CONFLICT: (
61
+ ToolExecutionStatus.SOFT_FAILURE,
62
+ ToolExecutionErrorCode.CONFLICT,
63
+ ),
64
+ PiCompatErrorKind.IO_ERROR: (
65
+ ToolExecutionStatus.SOFT_FAILURE,
66
+ ToolExecutionErrorCode.IO_ERROR,
67
+ ),
68
+ PiCompatErrorKind.PROCESS_EXIT_NONZERO: (
69
+ ToolExecutionStatus.SOFT_FAILURE,
70
+ ToolExecutionErrorCode.PROCESS_EXIT_NONZERO,
71
+ ),
72
+ PiCompatErrorKind.PROCESS_TIMEOUT: (
73
+ ToolExecutionStatus.TIMED_OUT,
74
+ ToolExecutionErrorCode.TIMEOUT,
75
+ ),
76
+ PiCompatErrorKind.CANCELLED: (
77
+ ToolExecutionStatus.CANCELLED,
78
+ ToolExecutionErrorCode.CANCELLED,
79
+ ),
80
+ PiCompatErrorKind.PROCESS_LAUNCH_ERROR: (
81
+ ToolExecutionStatus.HARD_FAILURE,
82
+ ToolExecutionErrorCode.PROCESS_LAUNCH_ERROR,
83
+ ),
84
+ }
85
+
86
+ _SIDE_EFFECT_RECORD_CERTAINTIES = frozenset(
87
+ {
88
+ SideEffectCertainty.CONFIRMED_ABSENT,
89
+ SideEffectCertainty.ROLLED_BACK,
90
+ SideEffectCertainty.COMPLETION_UNKNOWN,
91
+ }
92
+ )
93
+
94
+
95
+ class _PiCompatCancellationBridge:
96
+ """Expose only the poll/wait protocol owned by the 11A bash operation."""
97
+
98
+ def __init__(self, token: Any) -> None:
99
+ self._token = token
100
+
101
+ def is_cancelled(self) -> bool:
102
+ return self._token.is_cancelled()
103
+
104
+ async def wait(self) -> None:
105
+ await self._token.wait()
106
+
107
+
108
+ def create_pi_compat_tool_executor(
109
+ plan: CompiledHarnessPlan,
110
+ *,
111
+ cwd: Path,
112
+ cancellation_resolver: CancellationResolver,
113
+ shell_config: PiCompatShellConfig | None = None,
114
+ ) -> CompiledToolBindingExecutor:
115
+ """Create a plan-scoped executor for the Pi-compatible tool catalog."""
116
+
117
+ return _create_pi_compat_tool_executor_for_terminal_results(
118
+ plan,
119
+ cwd=cwd,
120
+ cancellation_resolver=cancellation_resolver,
121
+ shell_config=shell_config,
122
+ legal_terminal_results=DEFAULT_BASE_TERMINAL_RESULTS,
123
+ )
124
+
125
+
126
+ def _create_pi_compat_tool_executor_for_terminal_results(
127
+ plan: CompiledHarnessPlan,
128
+ *,
129
+ cwd: Path,
130
+ cancellation_resolver: CancellationResolver,
131
+ shell_config: PiCompatShellConfig | None,
132
+ legal_terminal_results: tuple[str, ...],
133
+ ) -> CompiledToolBindingExecutor:
134
+ """Create an executor from already-canonical terminal configuration."""
135
+
136
+ if not cwd.is_absolute():
137
+ raise ValueError("cwd must be absolute")
138
+ if _plan_uses_bash(plan) and shell_config is None:
139
+ raise ValueError("Pi-compatible bash requires a resolved shell_config")
140
+
141
+ descriptors = {
142
+ descriptor.tool_id: descriptor
143
+ for descriptor in _descriptors_for_terminal_results(legal_terminal_results)
144
+ }
145
+ read = descriptors["builtin.pi_compat.read"]
146
+ bash = descriptors["builtin.pi_compat.bash"]
147
+ edit = descriptors["builtin.pi_compat.edit"]
148
+ write = descriptors["builtin.pi_compat.write"]
149
+ grep = descriptors["builtin.pi_compat.grep"]
150
+ find = descriptors["builtin.pi_compat.find"]
151
+ ls = descriptors["builtin.pi_compat.ls"]
152
+
153
+ registry = RuntimeToolRegistry()
154
+
155
+ def read_implementation(
156
+ call: ValidatedToolCall, _context: ToolExecutionContext
157
+ ) -> ToolExecutionResult:
158
+ return _adapt_operation_result(
159
+ call,
160
+ read,
161
+ execute_read(
162
+ cwd=cwd,
163
+ path=cast(str, call.arguments["path"]),
164
+ offset=cast(int | float | None, call.arguments.get("offset")),
165
+ limit=cast(int | float | None, call.arguments.get("limit")),
166
+ ),
167
+ )
168
+
169
+ async def bash_implementation(
170
+ call: ValidatedToolCall, context: ToolExecutionContext
171
+ ) -> ToolExecutionResult:
172
+ timeout_seconds = _bash_timeout_seconds(call, context, bash)
173
+ if timeout_seconds <= 0 or not math.isfinite(timeout_seconds):
174
+ return _pre_entry_result(
175
+ call,
176
+ bash,
177
+ ToolExecutionErrorCode.TIMEOUT,
178
+ "tool deadline expired before implementation entry",
179
+ )
180
+ if context.cancellation_requested:
181
+ return _pre_entry_result(
182
+ call,
183
+ bash,
184
+ ToolExecutionErrorCode.CANCELLED,
185
+ "tool call was cancelled before implementation entry",
186
+ )
187
+ token = cancellation_resolver.resolve(context.cancellation)
188
+ cancellation = _PiCompatCancellationBridge(token)
189
+ if cancellation.is_cancelled():
190
+ return _pre_entry_result(
191
+ call,
192
+ bash,
193
+ ToolExecutionErrorCode.CANCELLED,
194
+ "tool call was cancelled before implementation entry",
195
+ )
196
+ assert shell_config is not None
197
+ operation_result = await execute_bash(
198
+ cwd=cwd,
199
+ command=cast(str, call.arguments["command"]),
200
+ timeout_seconds=timeout_seconds,
201
+ cancellation=cancellation,
202
+ shell_config=shell_config,
203
+ )
204
+ return _adapt_operation_result(call, bash, operation_result)
205
+
206
+ def edit_implementation(
207
+ call: ValidatedToolCall, _context: ToolExecutionContext
208
+ ) -> ToolExecutionResult:
209
+ return _adapt_operation_result(
210
+ call,
211
+ edit,
212
+ execute_edit(
213
+ cwd=cwd,
214
+ path=cast(str, call.arguments["path"]),
215
+ edits=cast(Sequence[Mapping[str, object]], call.arguments["edits"]),
216
+ ),
217
+ )
218
+
219
+ def write_implementation(
220
+ call: ValidatedToolCall, _context: ToolExecutionContext
221
+ ) -> ToolExecutionResult:
222
+ return _adapt_operation_result(
223
+ call,
224
+ write,
225
+ execute_write(
226
+ cwd=cwd,
227
+ path=cast(str, call.arguments["path"]),
228
+ content=cast(str, call.arguments["content"]),
229
+ ),
230
+ )
231
+
232
+ def grep_implementation(
233
+ call: ValidatedToolCall, _context: ToolExecutionContext
234
+ ) -> ToolExecutionResult:
235
+ return _adapt_operation_result(
236
+ call,
237
+ grep,
238
+ execute_grep(
239
+ cwd=cwd,
240
+ pattern=cast(str, call.arguments["pattern"]),
241
+ path=cast(str | None, call.arguments.get("path")),
242
+ glob=cast(str | None, call.arguments.get("glob")),
243
+ ignoreCase=cast(bool | None, call.arguments.get("ignoreCase")),
244
+ literal=cast(bool | None, call.arguments.get("literal")),
245
+ context=cast(int | float | None, call.arguments.get("context")),
246
+ limit=cast(int | float | None, call.arguments.get("limit")),
247
+ ),
248
+ )
249
+
250
+ def find_implementation(
251
+ call: ValidatedToolCall, _context: ToolExecutionContext
252
+ ) -> ToolExecutionResult:
253
+ return _adapt_operation_result(
254
+ call,
255
+ find,
256
+ execute_find(
257
+ cwd=cwd,
258
+ pattern=cast(str, call.arguments["pattern"]),
259
+ path=cast(str | None, call.arguments.get("path")),
260
+ limit=cast(int | float | None, call.arguments.get("limit")),
261
+ ),
262
+ )
263
+
264
+ def ls_implementation(
265
+ call: ValidatedToolCall, _context: ToolExecutionContext
266
+ ) -> ToolExecutionResult:
267
+ return _adapt_operation_result(
268
+ call,
269
+ ls,
270
+ execute_ls(
271
+ cwd=cwd,
272
+ path=cast(str | None, call.arguments.get("path")),
273
+ limit=cast(int | float | None, call.arguments.get("limit")),
274
+ ),
275
+ )
276
+
277
+ registry.register(read.implementation_id, read_implementation)
278
+ registry.register(bash.implementation_id, bash_implementation)
279
+ registry.register(edit.implementation_id, edit_implementation)
280
+ registry.register(write.implementation_id, write_implementation)
281
+ registry.register(grep.implementation_id, grep_implementation)
282
+ registry.register(find.implementation_id, find_implementation)
283
+ registry.register(ls.implementation_id, ls_implementation)
284
+ for descriptor in descriptors.values():
285
+ if descriptor.side_effect_class.value == "terminal":
286
+ registry.register(
287
+ descriptor.implementation_id,
288
+ _terminal_implementation(descriptor),
289
+ )
290
+
291
+ return CompiledToolBindingExecutor(
292
+ plan=plan,
293
+ descriptor_snapshot=_create_pi_compat_tool_snapshot_for_terminal_results(
294
+ legal_terminal_results
295
+ ),
296
+ runtime_registry=registry,
297
+ )
298
+
299
+
300
+ def _plan_uses_bash(plan: CompiledHarnessPlan) -> bool:
301
+ return any(node.binding.tool_id == "builtin.pi_compat.bash" for node in plan.nodes)
302
+
303
+
304
+ def _terminal_implementation(
305
+ descriptor: ToolDescriptor,
306
+ ) -> Callable[[ValidatedToolCall, ToolExecutionContext], ToolExecutionResult]:
307
+ def implementation(
308
+ call: ValidatedToolCall, _context: ToolExecutionContext
309
+ ) -> ToolExecutionResult:
310
+ return _terminal_result(call, descriptor)
311
+
312
+ return implementation
313
+
314
+
315
+ def _bash_timeout_seconds(
316
+ call: ValidatedToolCall,
317
+ context: ToolExecutionContext,
318
+ descriptor: ToolDescriptor,
319
+ ) -> float:
320
+ supplied_timeout = cast(int | float | None, call.arguments.get("timeout"))
321
+ model_timeout = (
322
+ float(supplied_timeout)
323
+ if supplied_timeout is not None and supplied_timeout > 0
324
+ else math.inf
325
+ )
326
+ return min(
327
+ float(descriptor.timeout_policy.timeout_seconds),
328
+ model_timeout,
329
+ context.timeout.timeout_seconds,
330
+ context.deadline.remaining(lambda: context.current_monotonic),
331
+ )
332
+
333
+
334
+ def _adapt_operation_result(
335
+ call: ValidatedToolCall,
336
+ descriptor: ToolDescriptor,
337
+ operation_result: PiCompatOperationResult,
338
+ ) -> ToolExecutionResult:
339
+ error_code = _operation_error_code(operation_result.error_kind)
340
+ status = (
341
+ ToolExecutionStatus.SUCCESS
342
+ if operation_result.error_kind is None
343
+ else _ERROR_STATUS_AND_CODE[operation_result.error_kind][0]
344
+ )
345
+ certainty = SideEffectCertainty(operation_result.side_effect_state.value)
346
+ side_effect_record = _side_effect_record(
347
+ certainty=certainty,
348
+ error_code=error_code,
349
+ summary=operation_result.model_text,
350
+ )
351
+ structured_data: dict[str, Any] = {
352
+ "model_text": operation_result.model_text,
353
+ "truncated": operation_result.truncated,
354
+ }
355
+ if operation_result.exit_code is not None:
356
+ structured_data["exit_code"] = operation_result.exit_code
357
+ if (
358
+ operation_result.error_kind is None
359
+ and operation_result.changed_path is not None
360
+ and descriptor.tool_id in {"builtin.pi_compat.edit", "builtin.pi_compat.write"}
361
+ ):
362
+ structured_data["changed_path"] = operation_result.changed_path.as_posix()
363
+ return make_tool_result(
364
+ call_id=call.call_id,
365
+ status=status,
366
+ code=error_code,
367
+ summary=operation_result.model_text,
368
+ structured_data=structured_data,
369
+ side_effect_class=descriptor.side_effect_class,
370
+ idempotency=descriptor.idempotency,
371
+ side_effect_certainty=certainty,
372
+ side_effect_record=side_effect_record,
373
+ input_sha256=canonical_sha256(call.arguments),
374
+ retryable=False,
375
+ output_policy=descriptor.output_policy,
376
+ )
377
+
378
+
379
+ def _operation_error_code(
380
+ error_kind: PiCompatErrorKind | None,
381
+ ) -> ToolExecutionErrorCode | None:
382
+ if error_kind is None:
383
+ return None
384
+ return _ERROR_STATUS_AND_CODE[error_kind][1]
385
+
386
+
387
+ def _side_effect_record(
388
+ *,
389
+ certainty: SideEffectCertainty,
390
+ error_code: ToolExecutionErrorCode | None,
391
+ summary: str,
392
+ ) -> SideEffectRecord | None:
393
+ if certainty not in _SIDE_EFFECT_RECORD_CERTAINTIES:
394
+ return None
395
+ if error_code is None:
396
+ raise ValueError("uncertain Pi-compatible operation result requires an error")
397
+ return SideEffectRecord(
398
+ certainty=certainty,
399
+ detail_code=error_code.value,
400
+ summary=summary,
401
+ retry_allowed=False,
402
+ )
403
+
404
+
405
+ def _terminal_result(
406
+ call: ValidatedToolCall,
407
+ descriptor: ToolDescriptor,
408
+ ) -> ToolExecutionResult:
409
+ summary = cast(str, call.arguments["summary"])
410
+ if not summary.strip():
411
+ return make_tool_result(
412
+ call_id=call.call_id,
413
+ status=ToolExecutionStatus.NOT_EXECUTED,
414
+ code=ToolExecutionErrorCode.INVALID_ARGUMENTS,
415
+ summary="terminal summary must not be blank",
416
+ structured_data={
417
+ "model_text": "terminal summary must not be blank",
418
+ "truncated": False,
419
+ },
420
+ side_effect_class=descriptor.side_effect_class,
421
+ idempotency=descriptor.idempotency,
422
+ side_effect_certainty=SideEffectCertainty.NOT_ATTEMPTED,
423
+ input_sha256=canonical_sha256(call.arguments),
424
+ retryable=False,
425
+ output_policy=descriptor.output_policy,
426
+ )
427
+ return make_tool_result(
428
+ call_id=call.call_id,
429
+ status=ToolExecutionStatus.SUCCESS,
430
+ code=None,
431
+ summary=summary,
432
+ structured_data={"model_text": summary, "truncated": False},
433
+ side_effect_class=descriptor.side_effect_class,
434
+ idempotency=descriptor.idempotency,
435
+ side_effect_certainty=SideEffectCertainty.CONFIRMED_COMPLETE,
436
+ input_sha256=canonical_sha256(call.arguments),
437
+ retryable=False,
438
+ output_policy=descriptor.output_policy,
439
+ )
440
+
441
+
442
+ def _pre_entry_result(
443
+ call: ValidatedToolCall,
444
+ descriptor: ToolDescriptor,
445
+ code: ToolExecutionErrorCode,
446
+ summary: str,
447
+ ) -> ToolExecutionResult:
448
+ return make_tool_result(
449
+ call_id=call.call_id,
450
+ status=ToolExecutionStatus.NOT_EXECUTED,
451
+ code=code,
452
+ summary=summary,
453
+ structured_data={"model_text": summary, "truncated": False},
454
+ side_effect_class=descriptor.side_effect_class,
455
+ idempotency=descriptor.idempotency,
456
+ side_effect_certainty=SideEffectCertainty.NOT_ATTEMPTED,
457
+ input_sha256=canonical_sha256(call.arguments),
458
+ retryable=False,
459
+ output_policy=descriptor.output_policy,
460
+ )