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.
- millforge/__init__.py +1174 -0
- millforge/_forge/LICENSE +21 -0
- millforge/_forge/PROVENANCE.json +295 -0
- millforge/_forge/UPDATE_POLICY.md +24 -0
- millforge/_forge/__init__.py +14 -0
- millforge/_forge/adapter.py +2232 -0
- millforge/_forge/base_runner.py +121 -0
- millforge/_forge/clients/__init__.py +10 -0
- millforge/_forge/clients/base.py +200 -0
- millforge/_forge/context/__init__.py +23 -0
- millforge/_forge/context/manager.py +178 -0
- millforge/_forge/context/strategies.py +335 -0
- millforge/_forge/core/__init__.py +16 -0
- millforge/_forge/core/inference.py +433 -0
- millforge/_forge/core/messages.py +119 -0
- millforge/_forge/core/runner.py +479 -0
- millforge/_forge/core/steps.py +108 -0
- millforge/_forge/core/workflow.py +400 -0
- millforge/_forge/errors.py +222 -0
- millforge/_forge/guardrails/__init__.py +21 -0
- millforge/_forge/guardrails/error_tracker.py +71 -0
- millforge/_forge/guardrails/guardrails.py +194 -0
- millforge/_forge/guardrails/nudge.py +47 -0
- millforge/_forge/guardrails/response_validator.py +119 -0
- millforge/_forge/guardrails/step_enforcer.py +183 -0
- millforge/_forge/prompts/__init__.py +16 -0
- millforge/_forge/prompts/nudges.py +95 -0
- millforge/_forge/prompts/templates.py +285 -0
- millforge/_version.py +3 -0
- millforge/artifacts.py +570 -0
- millforge/base/__init__.py +97 -0
- millforge/base/composition.py +402 -0
- millforge/base/context.py +285 -0
- millforge/base/harness.py +138 -0
- millforge/base/identity.py +465 -0
- millforge/base/options.py +34 -0
- millforge/base/platform.py +17 -0
- millforge/base/prompt.py +317 -0
- millforge/base/runner.py +546 -0
- millforge/compiled_plan.py +970 -0
- millforge/compiler/__init__.py +231 -0
- millforge/compiler/artifact_validation.py +257 -0
- millforge/compiler/canonicalization.py +169 -0
- millforge/compiler/capabilities.py +66 -0
- millforge/compiler/catalogs.py +500 -0
- millforge/compiler/diagnostics.py +491 -0
- millforge/compiler/graph.py +678 -0
- millforge/compiler/lowering.py +198 -0
- millforge/compiler/output.py +692 -0
- millforge/compiler/parsing.py +1424 -0
- millforge/compiler/requests.py +1180 -0
- millforge/compiler/schema_validation.py +272 -0
- millforge/compiler/semantic.py +490 -0
- millforge/compiler/service.py +448 -0
- millforge/compiler/source.py +375 -0
- millforge/compiler/validators.py +184 -0
- millforge/connectors/__init__.py +95 -0
- millforge/connectors/admission.py +801 -0
- millforge/connectors/broker.py +202 -0
- millforge/connectors/contracts.py +1159 -0
- millforge/connectors/diagnostics.py +189 -0
- millforge/connectors/fake.py +66 -0
- millforge/connectors/runtime.py +236 -0
- millforge/contracts.py +2860 -0
- millforge/custom_tools/__init__.py +67 -0
- millforge/custom_tools/compiler.py +724 -0
- millforge/custom_tools/contracts.py +1093 -0
- millforge/custom_tools/diagnostics.py +205 -0
- millforge/eval_artifacts.py +952 -0
- millforge/eval_boundary.py +2435 -0
- millforge/eval_fixtures/__init__.py +1 -0
- millforge/eval_fixtures/default_pack/__init__.py +1 -0
- millforge/eval_fixtures/default_pack/fixtures/fixture.08a.bug_diagnosis.traceback.v1.json +52 -0
- millforge/eval_fixtures/default_pack/fixtures/fixture.08a.direct_edit.import_sort.v1.json +52 -0
- millforge/eval_fixtures/default_pack/fixtures/fixture.08a.evidence_discipline.no_source_change.v1.json +51 -0
- millforge/eval_fixtures/default_pack/fixtures/fixture.08a.false_closure.visible_green.v1.json +52 -0
- millforge/eval_fixtures/default_pack/fixtures/fixture.08a.multi_file.api_contract.v1.json +54 -0
- millforge/eval_fixtures/default_pack/fixtures/fixture.08a.recovery.malformed_artifact.v1.json +54 -0
- millforge/eval_fixtures/default_pack/manifest.json +12 -0
- millforge/eval_modes.py +1282 -0
- millforge/eval_presets.py +1398 -0
- millforge/eval_reports.py +2517 -0
- millforge/eval_suite.py +2429 -0
- millforge/eval_trials.py +2632 -0
- millforge/eval_workflow.py +794 -0
- millforge/exceptions.py +122 -0
- millforge/model_backend.py +2098 -0
- millforge/protocols.py +340 -0
- millforge/py.typed +0 -0
- millforge/runtime.py +1791 -0
- millforge/testing/__init__.py +1089 -0
- millforge/tools/__init__.py +83 -0
- millforge/tools/builtin_runtime.py +1339 -0
- millforge/tools/builtins.py +773 -0
- millforge/tools/execution.py +1545 -0
- millforge/tools/path_policy.py +155 -0
- millforge/tools/pi_compat/PI_LICENSE +21 -0
- millforge/tools/pi_compat/PROVENANCE.json +55 -0
- millforge/tools/pi_compat/UPDATE_POLICY.md +36 -0
- millforge/tools/pi_compat/__init__.py +34 -0
- millforge/tools/pi_compat/contracts.py +49 -0
- millforge/tools/pi_compat/editing.py +390 -0
- millforge/tools/pi_compat/mutations.py +57 -0
- millforge/tools/pi_compat/operations.py +401 -0
- millforge/tools/pi_compat/paths.py +155 -0
- millforge/tools/pi_compat/process.py +1375 -0
- millforge/tools/pi_compat/search.py +738 -0
- millforge/tools/pi_compat/truncation.py +267 -0
- millforge/tools/pi_compat_catalog.py +396 -0
- millforge/tools/pi_compat_runtime.py +460 -0
- millforge/tools/registry.py +553 -0
- millforge/tools/results.py +533 -0
- millforge-0.1.0.dist-info/METADATA +844 -0
- millforge-0.1.0.dist-info/RECORD +116 -0
- millforge-0.1.0.dist-info/WHEEL +4 -0
- millforge-0.1.0.dist-info/licenses/LICENSE +201 -0
|
@@ -0,0 +1,402 @@
|
|
|
1
|
+
"""Composition boundary for the unrestricted Millforge base preset."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import datetime
|
|
6
|
+
import hashlib
|
|
7
|
+
import platform
|
|
8
|
+
import re
|
|
9
|
+
from dataclasses import dataclass
|
|
10
|
+
from pathlib import Path
|
|
11
|
+
from typing import Literal
|
|
12
|
+
|
|
13
|
+
from pydantic import (
|
|
14
|
+
BaseModel,
|
|
15
|
+
ConfigDict,
|
|
16
|
+
Field,
|
|
17
|
+
StrictBool,
|
|
18
|
+
StrictInt,
|
|
19
|
+
StrictStr,
|
|
20
|
+
field_validator,
|
|
21
|
+
)
|
|
22
|
+
|
|
23
|
+
from millforge.compiled_plan import CompiledHarnessPlan, CompiledModelProfile
|
|
24
|
+
from millforge.compiler.catalogs import ModelProfileCatalogLookup, ToolCatalogSnapshot
|
|
25
|
+
from millforge.compiler.service import compile_harness_source_in_memory
|
|
26
|
+
from millforge.compiler.source import HarnessSource
|
|
27
|
+
from millforge.contracts import CapabilityEnvelope, CapabilityGrant
|
|
28
|
+
from millforge.model_backend import (
|
|
29
|
+
CapabilitySupport,
|
|
30
|
+
ResolvedModelProfile,
|
|
31
|
+
UnsupportedModelCapabilityError,
|
|
32
|
+
)
|
|
33
|
+
from millforge.protocols import CancellationResolver
|
|
34
|
+
from millforge.tools.execution import CompiledToolBindingExecutor
|
|
35
|
+
from millforge.tools.pi_compat.process import (
|
|
36
|
+
PiCompatShellConfig,
|
|
37
|
+
resolve_pi_compat_shell,
|
|
38
|
+
)
|
|
39
|
+
from millforge.tools.pi_compat_catalog import (
|
|
40
|
+
PI_COMPAT_TOOL_DESCRIPTORS,
|
|
41
|
+
_create_pi_compat_tool_snapshot_for_terminal_results,
|
|
42
|
+
)
|
|
43
|
+
from millforge.tools.pi_compat_runtime import (
|
|
44
|
+
_create_pi_compat_tool_executor_for_terminal_results,
|
|
45
|
+
)
|
|
46
|
+
|
|
47
|
+
from .context import (
|
|
48
|
+
MillforgeBaseContextSnapshot,
|
|
49
|
+
_load_millforge_base_context_resolved,
|
|
50
|
+
)
|
|
51
|
+
from .harness import (
|
|
52
|
+
_CONFIG_ID,
|
|
53
|
+
_HARNESS_ID,
|
|
54
|
+
_STAGE_KIND,
|
|
55
|
+
_TOOL_PACK_ID,
|
|
56
|
+
DEFAULT_BASE_TERMINAL_RESULTS,
|
|
57
|
+
_millforge_base_harness_source_for_terminal_results,
|
|
58
|
+
canonicalize_base_terminal_results,
|
|
59
|
+
)
|
|
60
|
+
from .options import MillforgeBaseOptions
|
|
61
|
+
from .platform import _require_supported_platform
|
|
62
|
+
from .prompt import (
|
|
63
|
+
MillforgeBasePromptSnapshot,
|
|
64
|
+
_build_millforge_base_system_prompt_resolved,
|
|
65
|
+
)
|
|
66
|
+
|
|
67
|
+
__all__ = [
|
|
68
|
+
"MillforgeBaseComponents",
|
|
69
|
+
"MillforgeBaseMetadata",
|
|
70
|
+
"create_millforge_base_components",
|
|
71
|
+
]
|
|
72
|
+
|
|
73
|
+
_UPSTREAM_PACKAGE = "@earendil-works/pi-coding-agent"
|
|
74
|
+
_UPSTREAM_VERSION = "0.79.6"
|
|
75
|
+
_COMPATIBILITY_CLAIM = (
|
|
76
|
+
"A Python behavioral port of Pi 0.79.6's complete built-in coding tool pack, "
|
|
77
|
+
"adapted to Millforge's compiler and runtime contracts."
|
|
78
|
+
)
|
|
79
|
+
_SECURITY_WARNING = (
|
|
80
|
+
"millforge-base runs with the permissions of the Millforge process. It can read, "
|
|
81
|
+
"write, delete, execute commands, access the network, and access credentials "
|
|
82
|
+
"available to that process. Use only in trusted environments."
|
|
83
|
+
)
|
|
84
|
+
_ENABLED_ALIASES = (
|
|
85
|
+
"read",
|
|
86
|
+
"bash",
|
|
87
|
+
"edit",
|
|
88
|
+
"write",
|
|
89
|
+
"grep",
|
|
90
|
+
"find",
|
|
91
|
+
"ls",
|
|
92
|
+
"submit",
|
|
93
|
+
"block",
|
|
94
|
+
"reject",
|
|
95
|
+
)
|
|
96
|
+
_SHA256_RE = re.compile(r"[0-9a-f]{64}")
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
class MillforgeBaseMetadata(BaseModel):
|
|
100
|
+
"""Sanitized, deterministic metadata for one composed base preset."""
|
|
101
|
+
|
|
102
|
+
model_config = ConfigDict(extra="forbid", frozen=True)
|
|
103
|
+
|
|
104
|
+
schema_version: Literal[1]
|
|
105
|
+
config_id: StrictStr
|
|
106
|
+
harness_id: StrictStr
|
|
107
|
+
tool_pack_id: StrictStr
|
|
108
|
+
upstream_package: StrictStr
|
|
109
|
+
upstream_version: StrictStr
|
|
110
|
+
compatibility_claim: StrictStr
|
|
111
|
+
security_warning: StrictStr
|
|
112
|
+
unrestricted: Literal[True]
|
|
113
|
+
enabled_aliases: tuple[StrictStr, ...]
|
|
114
|
+
model_profile_id: StrictStr
|
|
115
|
+
provider_id: StrictStr
|
|
116
|
+
model_id: StrictStr
|
|
117
|
+
transport_id: StrictStr
|
|
118
|
+
os_name: StrictStr
|
|
119
|
+
shell_name: StrictStr
|
|
120
|
+
cwd_sha256: StrictStr
|
|
121
|
+
descriptor_snapshot_sha256: StrictStr
|
|
122
|
+
compiled_sha256: StrictStr
|
|
123
|
+
effective_prompt_sha256: StrictStr
|
|
124
|
+
context_sha256: StrictStr
|
|
125
|
+
context_file_count: StrictInt = Field(ge=0)
|
|
126
|
+
context_truncated: StrictBool
|
|
127
|
+
prompt_truncated: StrictBool
|
|
128
|
+
|
|
129
|
+
@field_validator(
|
|
130
|
+
"config_id",
|
|
131
|
+
"harness_id",
|
|
132
|
+
"tool_pack_id",
|
|
133
|
+
"upstream_package",
|
|
134
|
+
"upstream_version",
|
|
135
|
+
"compatibility_claim",
|
|
136
|
+
"security_warning",
|
|
137
|
+
"model_profile_id",
|
|
138
|
+
"provider_id",
|
|
139
|
+
"model_id",
|
|
140
|
+
"transport_id",
|
|
141
|
+
"os_name",
|
|
142
|
+
"shell_name",
|
|
143
|
+
)
|
|
144
|
+
@classmethod
|
|
145
|
+
def _strings_are_nonblank(cls, value: str) -> str:
|
|
146
|
+
if not value.strip():
|
|
147
|
+
raise ValueError("metadata strings must be nonblank")
|
|
148
|
+
return value
|
|
149
|
+
|
|
150
|
+
@field_validator("enabled_aliases")
|
|
151
|
+
@classmethod
|
|
152
|
+
def _aliases_are_nonblank(cls, value: tuple[str, ...]) -> tuple[str, ...]:
|
|
153
|
+
if not value or any(not alias.strip() for alias in value):
|
|
154
|
+
raise ValueError("enabled_aliases must contain only nonblank values")
|
|
155
|
+
return value
|
|
156
|
+
|
|
157
|
+
@field_validator(
|
|
158
|
+
"cwd_sha256",
|
|
159
|
+
"descriptor_snapshot_sha256",
|
|
160
|
+
"compiled_sha256",
|
|
161
|
+
"effective_prompt_sha256",
|
|
162
|
+
"context_sha256",
|
|
163
|
+
)
|
|
164
|
+
@classmethod
|
|
165
|
+
def _hashes_are_lowercase_sha256(cls, value: str) -> str:
|
|
166
|
+
if not _SHA256_RE.fullmatch(value):
|
|
167
|
+
raise ValueError("metadata hashes must be lowercase SHA-256 values")
|
|
168
|
+
return value
|
|
169
|
+
|
|
170
|
+
|
|
171
|
+
@dataclass(frozen=True)
|
|
172
|
+
class MillforgeBaseComponents:
|
|
173
|
+
"""Fully composed transient components for one base-preset invocation."""
|
|
174
|
+
|
|
175
|
+
legal_terminal_results: tuple[str, ...]
|
|
176
|
+
options: MillforgeBaseOptions
|
|
177
|
+
context: MillforgeBaseContextSnapshot
|
|
178
|
+
prompt: MillforgeBasePromptSnapshot
|
|
179
|
+
harness_source: HarnessSource
|
|
180
|
+
compiled_plan: CompiledHarnessPlan
|
|
181
|
+
model_profile: ResolvedModelProfile
|
|
182
|
+
tool_snapshot: ToolCatalogSnapshot
|
|
183
|
+
tool_executor: CompiledToolBindingExecutor
|
|
184
|
+
capability_envelope: CapabilityEnvelope
|
|
185
|
+
metadata: MillforgeBaseMetadata
|
|
186
|
+
|
|
187
|
+
|
|
188
|
+
@dataclass(frozen=True)
|
|
189
|
+
class _SingleModelProfileCatalog:
|
|
190
|
+
"""One admitted logical model profile for a single in-memory compilation."""
|
|
191
|
+
|
|
192
|
+
profile: CompiledModelProfile
|
|
193
|
+
snapshot_id: str
|
|
194
|
+
snapshot_sha256: str
|
|
195
|
+
|
|
196
|
+
def resolve_exact(self, profile_id: str) -> ModelProfileCatalogLookup:
|
|
197
|
+
if profile_id == self.profile.profile_id:
|
|
198
|
+
return ModelProfileCatalogLookup.found(self.profile)
|
|
199
|
+
return ModelProfileCatalogLookup.missing(
|
|
200
|
+
error_code="profile.missing",
|
|
201
|
+
evidence={"profile_id": profile_id},
|
|
202
|
+
)
|
|
203
|
+
|
|
204
|
+
|
|
205
|
+
def _resolved_absolute_path(path: Path, field_name: str) -> Path:
|
|
206
|
+
if not path.is_absolute():
|
|
207
|
+
raise ValueError(f"{field_name} must be absolute")
|
|
208
|
+
return path.resolve()
|
|
209
|
+
|
|
210
|
+
|
|
211
|
+
def _sha256(value: str) -> str:
|
|
212
|
+
return hashlib.sha256(value.encode("utf-8")).hexdigest()
|
|
213
|
+
|
|
214
|
+
|
|
215
|
+
def _capability_envelope() -> CapabilityEnvelope:
|
|
216
|
+
required_capabilities = sorted(
|
|
217
|
+
{
|
|
218
|
+
capability_id
|
|
219
|
+
for descriptor in PI_COMPAT_TOOL_DESCRIPTORS
|
|
220
|
+
for capability_id in descriptor.required_capabilities
|
|
221
|
+
}
|
|
222
|
+
)
|
|
223
|
+
return CapabilityEnvelope(
|
|
224
|
+
grants=tuple(
|
|
225
|
+
CapabilityGrant(capability_id=capability_id)
|
|
226
|
+
for capability_id in required_capabilities
|
|
227
|
+
)
|
|
228
|
+
)
|
|
229
|
+
|
|
230
|
+
|
|
231
|
+
def _model_profile_catalog(profile_id: str) -> _SingleModelProfileCatalog:
|
|
232
|
+
snapshot_id = _sha256(f"millforge-base.model-profile:{profile_id}")
|
|
233
|
+
return _SingleModelProfileCatalog(
|
|
234
|
+
profile=CompiledModelProfile(profile_id=profile_id),
|
|
235
|
+
snapshot_id=snapshot_id,
|
|
236
|
+
snapshot_sha256=_sha256(f"millforge-base.model-profile.snapshot:{profile_id}"),
|
|
237
|
+
)
|
|
238
|
+
|
|
239
|
+
|
|
240
|
+
def _metadata(
|
|
241
|
+
*,
|
|
242
|
+
model_profile: ResolvedModelProfile,
|
|
243
|
+
cwd: Path,
|
|
244
|
+
shell_config: PiCompatShellConfig,
|
|
245
|
+
context: MillforgeBaseContextSnapshot,
|
|
246
|
+
prompt: MillforgeBasePromptSnapshot,
|
|
247
|
+
tool_snapshot: ToolCatalogSnapshot,
|
|
248
|
+
compiled_plan: CompiledHarnessPlan,
|
|
249
|
+
) -> MillforgeBaseMetadata:
|
|
250
|
+
return MillforgeBaseMetadata(
|
|
251
|
+
schema_version=1,
|
|
252
|
+
config_id=_CONFIG_ID,
|
|
253
|
+
harness_id=_HARNESS_ID,
|
|
254
|
+
tool_pack_id=_TOOL_PACK_ID,
|
|
255
|
+
upstream_package=_UPSTREAM_PACKAGE,
|
|
256
|
+
upstream_version=_UPSTREAM_VERSION,
|
|
257
|
+
compatibility_claim=_COMPATIBILITY_CLAIM,
|
|
258
|
+
security_warning=_SECURITY_WARNING,
|
|
259
|
+
unrestricted=True,
|
|
260
|
+
enabled_aliases=_ENABLED_ALIASES,
|
|
261
|
+
model_profile_id=model_profile.profile_id,
|
|
262
|
+
provider_id=model_profile.provider_id,
|
|
263
|
+
model_id=model_profile.model_id,
|
|
264
|
+
transport_id=model_profile.transport_id,
|
|
265
|
+
os_name=platform.system().lower(),
|
|
266
|
+
shell_name=Path(shell_config.executable).name,
|
|
267
|
+
cwd_sha256=_sha256(cwd.as_posix()),
|
|
268
|
+
descriptor_snapshot_sha256=tool_snapshot.snapshot_sha256,
|
|
269
|
+
compiled_sha256=compiled_plan.compiled_sha256,
|
|
270
|
+
effective_prompt_sha256=prompt.effective_prompt_sha256,
|
|
271
|
+
context_sha256=context.context_sha256,
|
|
272
|
+
context_file_count=len(context.files),
|
|
273
|
+
context_truncated=context.truncated,
|
|
274
|
+
prompt_truncated=prompt.truncated,
|
|
275
|
+
)
|
|
276
|
+
|
|
277
|
+
|
|
278
|
+
def create_millforge_base_components(
|
|
279
|
+
*,
|
|
280
|
+
legal_terminal_results: tuple[str, ...] = DEFAULT_BASE_TERMINAL_RESULTS,
|
|
281
|
+
model_profile: ResolvedModelProfile,
|
|
282
|
+
cwd: Path,
|
|
283
|
+
cancellation_resolver: CancellationResolver,
|
|
284
|
+
options: MillforgeBaseOptions | None = None,
|
|
285
|
+
prompt_date: datetime.date | None = None,
|
|
286
|
+
home_directory: Path | None = None,
|
|
287
|
+
) -> MillforgeBaseComponents:
|
|
288
|
+
"""Compose the unrestricted preset without making a provider request."""
|
|
289
|
+
|
|
290
|
+
return _create_millforge_base_components(
|
|
291
|
+
legal_terminal_results=canonicalize_base_terminal_results(
|
|
292
|
+
legal_terminal_results
|
|
293
|
+
),
|
|
294
|
+
model_profile=model_profile,
|
|
295
|
+
cwd=cwd,
|
|
296
|
+
cancellation_resolver=cancellation_resolver,
|
|
297
|
+
options=options,
|
|
298
|
+
prompt_date=prompt_date,
|
|
299
|
+
home_directory=home_directory,
|
|
300
|
+
)
|
|
301
|
+
|
|
302
|
+
|
|
303
|
+
def _create_millforge_base_components(
|
|
304
|
+
*,
|
|
305
|
+
legal_terminal_results: tuple[str, ...],
|
|
306
|
+
model_profile: ResolvedModelProfile,
|
|
307
|
+
cwd: Path,
|
|
308
|
+
cancellation_resolver: CancellationResolver,
|
|
309
|
+
options: MillforgeBaseOptions | None = None,
|
|
310
|
+
prompt_date: datetime.date | None = None,
|
|
311
|
+
home_directory: Path | None = None,
|
|
312
|
+
) -> MillforgeBaseComponents:
|
|
313
|
+
"""Compose a base preset from already-canonical terminal configuration."""
|
|
314
|
+
|
|
315
|
+
_require_supported_platform()
|
|
316
|
+
|
|
317
|
+
unsupported_model_capabilities = tuple(
|
|
318
|
+
capability
|
|
319
|
+
for capability in ("tool_calls", "system_messages", "tool_result_messages")
|
|
320
|
+
if model_profile.capabilities.state_for(capability)
|
|
321
|
+
is not CapabilitySupport.SUPPORTED
|
|
322
|
+
)
|
|
323
|
+
if unsupported_model_capabilities:
|
|
324
|
+
raise UnsupportedModelCapabilityError(
|
|
325
|
+
"millforge-base requires supported model capabilities: "
|
|
326
|
+
+ ", ".join(unsupported_model_capabilities)
|
|
327
|
+
)
|
|
328
|
+
|
|
329
|
+
resolved_cwd = _resolved_absolute_path(cwd, "cwd")
|
|
330
|
+
resolved_home = _resolved_absolute_path(
|
|
331
|
+
Path.home() if home_directory is None else home_directory,
|
|
332
|
+
"home_directory",
|
|
333
|
+
)
|
|
334
|
+
effective_options = options or MillforgeBaseOptions()
|
|
335
|
+
effective_prompt_date = prompt_date or datetime.date.today()
|
|
336
|
+
context = _load_millforge_base_context_resolved(
|
|
337
|
+
cwd=resolved_cwd,
|
|
338
|
+
home_directory=resolved_home,
|
|
339
|
+
enabled=effective_options.load_context_files,
|
|
340
|
+
)
|
|
341
|
+
prompt = _build_millforge_base_system_prompt_resolved(
|
|
342
|
+
options=effective_options,
|
|
343
|
+
context=context,
|
|
344
|
+
cwd=resolved_cwd,
|
|
345
|
+
home_directory=resolved_home,
|
|
346
|
+
prompt_date=effective_prompt_date,
|
|
347
|
+
)
|
|
348
|
+
shell_config = resolve_pi_compat_shell()
|
|
349
|
+
source = _millforge_base_harness_source_for_terminal_results(
|
|
350
|
+
model_profile_id=model_profile.profile_id,
|
|
351
|
+
system_instructions=prompt.system_instructions,
|
|
352
|
+
legal_terminal_results=legal_terminal_results,
|
|
353
|
+
)
|
|
354
|
+
tool_snapshot = _create_pi_compat_tool_snapshot_for_terminal_results(
|
|
355
|
+
legal_terminal_results
|
|
356
|
+
)
|
|
357
|
+
capability_envelope = _capability_envelope()
|
|
358
|
+
compiled_plan = compile_harness_source_in_memory(
|
|
359
|
+
request_id="millforge-base.v1",
|
|
360
|
+
source=source,
|
|
361
|
+
stage_kind_id=_STAGE_KIND,
|
|
362
|
+
legal_terminal_results=legal_terminal_results,
|
|
363
|
+
capability_envelope=capability_envelope,
|
|
364
|
+
tool_catalog=tool_snapshot,
|
|
365
|
+
model_profile_catalog=_model_profile_catalog(model_profile.profile_id),
|
|
366
|
+
)
|
|
367
|
+
if tuple(
|
|
368
|
+
grant.capability_id for grant in capability_envelope.grants
|
|
369
|
+
) != compiled_plan.required_capabilities or any(
|
|
370
|
+
grant.constraints is not None for grant in capability_envelope.grants
|
|
371
|
+
):
|
|
372
|
+
raise RuntimeError("millforge-base capability envelope does not match its plan")
|
|
373
|
+
|
|
374
|
+
tool_executor = _create_pi_compat_tool_executor_for_terminal_results(
|
|
375
|
+
compiled_plan,
|
|
376
|
+
cwd=resolved_cwd,
|
|
377
|
+
cancellation_resolver=cancellation_resolver,
|
|
378
|
+
shell_config=shell_config,
|
|
379
|
+
legal_terminal_results=legal_terminal_results,
|
|
380
|
+
)
|
|
381
|
+
metadata = _metadata(
|
|
382
|
+
model_profile=model_profile,
|
|
383
|
+
cwd=resolved_cwd,
|
|
384
|
+
shell_config=shell_config,
|
|
385
|
+
context=context,
|
|
386
|
+
prompt=prompt,
|
|
387
|
+
tool_snapshot=tool_snapshot,
|
|
388
|
+
compiled_plan=compiled_plan,
|
|
389
|
+
)
|
|
390
|
+
return MillforgeBaseComponents(
|
|
391
|
+
legal_terminal_results=legal_terminal_results,
|
|
392
|
+
options=effective_options,
|
|
393
|
+
context=context,
|
|
394
|
+
prompt=prompt,
|
|
395
|
+
harness_source=source,
|
|
396
|
+
compiled_plan=compiled_plan,
|
|
397
|
+
model_profile=model_profile,
|
|
398
|
+
tool_snapshot=tool_snapshot,
|
|
399
|
+
tool_executor=tool_executor,
|
|
400
|
+
capability_envelope=capability_envelope,
|
|
401
|
+
metadata=metadata,
|
|
402
|
+
)
|
|
@@ -0,0 +1,285 @@
|
|
|
1
|
+
"""Pi-derived project-context discovery for ``millforge-base``."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import hashlib
|
|
6
|
+
import json
|
|
7
|
+
import re
|
|
8
|
+
from dataclasses import dataclass
|
|
9
|
+
from pathlib import Path
|
|
10
|
+
from typing import Literal
|
|
11
|
+
|
|
12
|
+
from pydantic import (
|
|
13
|
+
BaseModel,
|
|
14
|
+
ConfigDict,
|
|
15
|
+
Field,
|
|
16
|
+
StrictBool,
|
|
17
|
+
StrictInt,
|
|
18
|
+
StrictStr,
|
|
19
|
+
field_validator,
|
|
20
|
+
)
|
|
21
|
+
|
|
22
|
+
__all__ = [
|
|
23
|
+
"MillforgeBaseContextFile",
|
|
24
|
+
"MillforgeBaseContextSnapshot",
|
|
25
|
+
"load_millforge_base_context",
|
|
26
|
+
]
|
|
27
|
+
|
|
28
|
+
_CONTEXT_BYTE_LIMIT = 49_152
|
|
29
|
+
_DIAGNOSTIC_BYTE_LIMIT = 2_048
|
|
30
|
+
_CONTEXT_CANDIDATES = ("AGENTS.md", "AGENTS.MD", "CLAUDE.md", "CLAUDE.MD")
|
|
31
|
+
_SHA256_RE = re.compile(r"[0-9a-f]{64}")
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def _truncate_to_utf8_bytes(value: str, limit: int) -> str:
|
|
35
|
+
"""Keep a valid UTF-8 prefix within ``limit`` bytes."""
|
|
36
|
+
|
|
37
|
+
encoded = value.encode("utf-8")
|
|
38
|
+
if len(encoded) <= limit:
|
|
39
|
+
return value
|
|
40
|
+
return encoded[:limit].decode("utf-8", errors="ignore")
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def _bounded_diagnostic(value: str) -> str:
|
|
44
|
+
bounded = _truncate_to_utf8_bytes(value, _DIAGNOSTIC_BYTE_LIMIT)
|
|
45
|
+
return bounded or "unreadable file"
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def _validate_sha256(value: str, field_name: str) -> str:
|
|
49
|
+
if not _SHA256_RE.fullmatch(value):
|
|
50
|
+
raise ValueError(f"{field_name} must be exactly 64 lowercase hex characters")
|
|
51
|
+
return value
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def _validate_diagnostics(value: tuple[str, ...]) -> tuple[str, ...]:
|
|
55
|
+
for diagnostic in value:
|
|
56
|
+
if not diagnostic.strip():
|
|
57
|
+
raise ValueError("diagnostics must be nonblank")
|
|
58
|
+
if len(diagnostic.encode("utf-8")) > _DIAGNOSTIC_BYTE_LIMIT:
|
|
59
|
+
raise ValueError("diagnostics may contain at most 2048 UTF-8 bytes")
|
|
60
|
+
return value
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
class MillforgeBaseContextFile(BaseModel):
|
|
64
|
+
"""One included private project-context record."""
|
|
65
|
+
|
|
66
|
+
model_config = ConfigDict(extra="forbid", frozen=True)
|
|
67
|
+
|
|
68
|
+
scope: Literal["global", "project"]
|
|
69
|
+
path: Path
|
|
70
|
+
content: StrictStr
|
|
71
|
+
raw_sha256: StrictStr
|
|
72
|
+
original_byte_count: StrictInt = Field(ge=0)
|
|
73
|
+
included_byte_count: StrictInt = Field(ge=0)
|
|
74
|
+
truncated: StrictBool
|
|
75
|
+
|
|
76
|
+
@field_validator("raw_sha256")
|
|
77
|
+
@classmethod
|
|
78
|
+
def _raw_sha256_is_valid(cls, value: str) -> str:
|
|
79
|
+
return _validate_sha256(value, "raw_sha256")
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
class MillforgeBaseContextSnapshot(BaseModel):
|
|
83
|
+
"""The bounded, ordered private context available to prompt assembly."""
|
|
84
|
+
|
|
85
|
+
model_config = ConfigDict(extra="forbid", frozen=True)
|
|
86
|
+
|
|
87
|
+
files: tuple[MillforgeBaseContextFile, ...]
|
|
88
|
+
diagnostics: tuple[StrictStr, ...]
|
|
89
|
+
context_sha256: StrictStr
|
|
90
|
+
truncated: StrictBool
|
|
91
|
+
|
|
92
|
+
@field_validator("diagnostics")
|
|
93
|
+
@classmethod
|
|
94
|
+
def _diagnostics_are_valid(cls, value: tuple[str, ...]) -> tuple[str, ...]:
|
|
95
|
+
return _validate_diagnostics(value)
|
|
96
|
+
|
|
97
|
+
@field_validator("context_sha256")
|
|
98
|
+
@classmethod
|
|
99
|
+
def _context_sha256_is_valid(cls, value: str) -> str:
|
|
100
|
+
return _validate_sha256(value, "context_sha256")
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
@dataclass(frozen=True)
|
|
104
|
+
class _DiscoveredContextFile:
|
|
105
|
+
scope: Literal["global", "project"]
|
|
106
|
+
path: Path
|
|
107
|
+
raw: bytes
|
|
108
|
+
content: str
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
def _resolved_absolute_path(path: Path, field_name: str) -> Path:
|
|
112
|
+
if not path.is_absolute():
|
|
113
|
+
raise ValueError(f"{field_name} must be absolute")
|
|
114
|
+
return path.resolve()
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
def _context_file_from_directory(
|
|
118
|
+
directory: Path,
|
|
119
|
+
scope: Literal["global", "project"],
|
|
120
|
+
diagnostics: list[str],
|
|
121
|
+
) -> _DiscoveredContextFile | None:
|
|
122
|
+
attempted_candidates: list[Path] = []
|
|
123
|
+
for filename in _CONTEXT_CANDIDATES:
|
|
124
|
+
candidate = (directory / filename).resolve()
|
|
125
|
+
if not candidate.exists():
|
|
126
|
+
continue
|
|
127
|
+
if any(
|
|
128
|
+
_paths_refer_to_same_file(candidate, attempted)
|
|
129
|
+
for attempted in attempted_candidates
|
|
130
|
+
):
|
|
131
|
+
continue
|
|
132
|
+
attempted_candidates.append(candidate)
|
|
133
|
+
try:
|
|
134
|
+
raw = candidate.read_bytes()
|
|
135
|
+
except OSError as error:
|
|
136
|
+
diagnostics.append(
|
|
137
|
+
_bounded_diagnostic(f"Could not read {candidate}: {error}")
|
|
138
|
+
)
|
|
139
|
+
continue
|
|
140
|
+
return _DiscoveredContextFile(
|
|
141
|
+
scope=scope,
|
|
142
|
+
path=candidate,
|
|
143
|
+
raw=raw,
|
|
144
|
+
content=raw.decode("utf-8", errors="replace"),
|
|
145
|
+
)
|
|
146
|
+
return None
|
|
147
|
+
|
|
148
|
+
|
|
149
|
+
def _project_directories(cwd: Path) -> tuple[Path, ...]:
|
|
150
|
+
return tuple(reversed(cwd.parents)) + (cwd,)
|
|
151
|
+
|
|
152
|
+
|
|
153
|
+
def _paths_refer_to_same_file(left: Path, right: Path) -> bool:
|
|
154
|
+
"""Compare host filesystem identity, falling back across removal races."""
|
|
155
|
+
|
|
156
|
+
if left == right:
|
|
157
|
+
return True
|
|
158
|
+
try:
|
|
159
|
+
return left.samefile(right)
|
|
160
|
+
except OSError:
|
|
161
|
+
return False
|
|
162
|
+
|
|
163
|
+
|
|
164
|
+
def _context_sha256(files: tuple[MillforgeBaseContextFile, ...]) -> str:
|
|
165
|
+
payload = [
|
|
166
|
+
{
|
|
167
|
+
"scope": file.scope,
|
|
168
|
+
"path": file.path.as_posix(),
|
|
169
|
+
"content": file.content,
|
|
170
|
+
"raw_sha256": file.raw_sha256,
|
|
171
|
+
"original_byte_count": file.original_byte_count,
|
|
172
|
+
"included_byte_count": file.included_byte_count,
|
|
173
|
+
"truncated": file.truncated,
|
|
174
|
+
}
|
|
175
|
+
for file in files
|
|
176
|
+
]
|
|
177
|
+
canonical = json.dumps(
|
|
178
|
+
payload,
|
|
179
|
+
sort_keys=True,
|
|
180
|
+
ensure_ascii=True,
|
|
181
|
+
allow_nan=False,
|
|
182
|
+
separators=(",", ":"),
|
|
183
|
+
).encode("ascii")
|
|
184
|
+
return hashlib.sha256(canonical).hexdigest()
|
|
185
|
+
|
|
186
|
+
|
|
187
|
+
def _allocate_context(
|
|
188
|
+
discovered_files: tuple[_DiscoveredContextFile, ...],
|
|
189
|
+
) -> tuple[tuple[MillforgeBaseContextFile, ...], bool]:
|
|
190
|
+
remaining = _CONTEXT_BYTE_LIMIT
|
|
191
|
+
included: list[MillforgeBaseContextFile] = []
|
|
192
|
+
|
|
193
|
+
for index, discovered in enumerate(discovered_files):
|
|
194
|
+
content_bytes = len(discovered.content.encode("utf-8"))
|
|
195
|
+
raw_sha256 = hashlib.sha256(discovered.raw).hexdigest()
|
|
196
|
+
if content_bytes <= remaining:
|
|
197
|
+
included.append(
|
|
198
|
+
MillforgeBaseContextFile(
|
|
199
|
+
scope=discovered.scope,
|
|
200
|
+
path=discovered.path,
|
|
201
|
+
content=discovered.content,
|
|
202
|
+
raw_sha256=raw_sha256,
|
|
203
|
+
original_byte_count=len(discovered.raw),
|
|
204
|
+
included_byte_count=content_bytes,
|
|
205
|
+
truncated=False,
|
|
206
|
+
)
|
|
207
|
+
)
|
|
208
|
+
remaining -= content_bytes
|
|
209
|
+
if remaining == 0 and index < len(discovered_files) - 1:
|
|
210
|
+
return tuple(included), True
|
|
211
|
+
continue
|
|
212
|
+
|
|
213
|
+
content = _truncate_to_utf8_bytes(discovered.content, remaining)
|
|
214
|
+
included.append(
|
|
215
|
+
MillforgeBaseContextFile(
|
|
216
|
+
scope=discovered.scope,
|
|
217
|
+
path=discovered.path,
|
|
218
|
+
content=content,
|
|
219
|
+
raw_sha256=raw_sha256,
|
|
220
|
+
original_byte_count=len(discovered.raw),
|
|
221
|
+
included_byte_count=len(content.encode("utf-8")),
|
|
222
|
+
truncated=True,
|
|
223
|
+
)
|
|
224
|
+
)
|
|
225
|
+
return tuple(included), True
|
|
226
|
+
|
|
227
|
+
return tuple(included), False
|
|
228
|
+
|
|
229
|
+
|
|
230
|
+
def load_millforge_base_context(
|
|
231
|
+
*, cwd: Path, home_directory: Path, enabled: bool
|
|
232
|
+
) -> MillforgeBaseContextSnapshot:
|
|
233
|
+
"""Load Pi-ordered context records under Millforge's finite byte bound."""
|
|
234
|
+
|
|
235
|
+
resolved_cwd = _resolved_absolute_path(cwd, "cwd")
|
|
236
|
+
resolved_home = _resolved_absolute_path(home_directory, "home_directory")
|
|
237
|
+
return _load_millforge_base_context_resolved(
|
|
238
|
+
cwd=resolved_cwd,
|
|
239
|
+
home_directory=resolved_home,
|
|
240
|
+
enabled=enabled,
|
|
241
|
+
)
|
|
242
|
+
|
|
243
|
+
|
|
244
|
+
def _load_millforge_base_context_resolved(
|
|
245
|
+
*, cwd: Path, home_directory: Path, enabled: bool
|
|
246
|
+
) -> MillforgeBaseContextSnapshot:
|
|
247
|
+
if not enabled:
|
|
248
|
+
empty_files: tuple[MillforgeBaseContextFile, ...] = ()
|
|
249
|
+
return MillforgeBaseContextSnapshot(
|
|
250
|
+
files=empty_files,
|
|
251
|
+
diagnostics=(),
|
|
252
|
+
context_sha256=_context_sha256(empty_files),
|
|
253
|
+
truncated=False,
|
|
254
|
+
)
|
|
255
|
+
|
|
256
|
+
diagnostics: list[str] = []
|
|
257
|
+
discovered: list[_DiscoveredContextFile] = []
|
|
258
|
+
native_global = _context_file_from_directory(
|
|
259
|
+
home_directory / ".millforge", "global", diagnostics
|
|
260
|
+
)
|
|
261
|
+
if native_global is None:
|
|
262
|
+
native_global = _context_file_from_directory(
|
|
263
|
+
home_directory / ".pi" / "agent", "global", diagnostics
|
|
264
|
+
)
|
|
265
|
+
if native_global is not None:
|
|
266
|
+
discovered.append(native_global)
|
|
267
|
+
|
|
268
|
+
seen_paths = [file.path for file in discovered]
|
|
269
|
+
for directory in _project_directories(cwd):
|
|
270
|
+
project_file = _context_file_from_directory(directory, "project", diagnostics)
|
|
271
|
+
if project_file is None or any(
|
|
272
|
+
_paths_refer_to_same_file(project_file.path, seen_path)
|
|
273
|
+
for seen_path in seen_paths
|
|
274
|
+
):
|
|
275
|
+
continue
|
|
276
|
+
discovered.append(project_file)
|
|
277
|
+
seen_paths.append(project_file.path)
|
|
278
|
+
|
|
279
|
+
files, truncated = _allocate_context(tuple(discovered))
|
|
280
|
+
return MillforgeBaseContextSnapshot(
|
|
281
|
+
files=files,
|
|
282
|
+
diagnostics=tuple(diagnostics),
|
|
283
|
+
context_sha256=_context_sha256(files),
|
|
284
|
+
truncated=truncated,
|
|
285
|
+
)
|