agentloop-profiler 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 (166) hide show
  1. agentloop/__init__.py +136 -0
  2. agentloop/ablation_protocol.py +229 -0
  3. agentloop/ablations.py +564 -0
  4. agentloop/aggregate_cli.py +88 -0
  5. agentloop/aggregate_stats.py +175 -0
  6. agentloop/aggregates.py +532 -0
  7. agentloop/audit.py +39 -0
  8. agentloop/autoinstrument.py +145 -0
  9. agentloop/batch_experiments.py +295 -0
  10. agentloop/batching.py +95 -0
  11. agentloop/budget_types.py +335 -0
  12. agentloop/budgets.py +539 -0
  13. agentloop/calibration.py +872 -0
  14. agentloop/checkpoint_state.py +306 -0
  15. agentloop/checkpoint_store.py +86 -0
  16. agentloop/ci.py +202 -0
  17. agentloop/cli.py +1314 -0
  18. agentloop/client.py +228 -0
  19. agentloop/completion.py +345 -0
  20. agentloop/completion_types.py +199 -0
  21. agentloop/config.py +50 -0
  22. agentloop/context_controls.py +443 -0
  23. agentloop/context_experiments.py +337 -0
  24. agentloop/context_types.py +216 -0
  25. agentloop/costs.py +667 -0
  26. agentloop/cursor_validation.py +48 -0
  27. agentloop/decorators.py +445 -0
  28. agentloop/demo.py +300 -0
  29. agentloop/doctor.py +341 -0
  30. agentloop/drift.py +361 -0
  31. agentloop/drift_cli.py +147 -0
  32. agentloop/drift_types.py +110 -0
  33. agentloop/drift_windows.py +340 -0
  34. agentloop/entrypoint.py +201 -0
  35. agentloop/estimates.py +133 -0
  36. agentloop/events.py +75 -0
  37. agentloop/experiment_artifacts.py +80 -0
  38. agentloop/experiment_observations.py +169 -0
  39. agentloop/experiment_reports.py +545 -0
  40. agentloop/experiment_types.py +355 -0
  41. agentloop/experiments.py +392 -0
  42. agentloop/exporters.py +117 -0
  43. agentloop/finding_benchmark_types.py +131 -0
  44. agentloop/finding_benchmarks.py +424 -0
  45. agentloop/findings.py +419 -0
  46. agentloop/graph.py +464 -0
  47. agentloop/harness.py +825 -0
  48. agentloop/harness_bench/__init__.py +1 -0
  49. agentloop/harness_bench/cli.py +77 -0
  50. agentloop/harness_bench/drivers.py +96 -0
  51. agentloop/harness_bench/offline.py +507 -0
  52. agentloop/harness_evidence.py +585 -0
  53. agentloop/harness_usage.py +91 -0
  54. agentloop/html_report.py +860 -0
  55. agentloop/integrations/__init__.py +1 -0
  56. agentloop/integrations/crewai.py +144 -0
  57. agentloop/integrations/harbor/__init__.py +1 -0
  58. agentloop/integrations/harbor/atif.py +982 -0
  59. agentloop/integrations/harbor/atif_validation.py +329 -0
  60. agentloop/integrations/harbor/cli.py +207 -0
  61. agentloop/integrations/harbor/manifest.py +86 -0
  62. agentloop/integrations/harbor/trial_evidence.py +196 -0
  63. agentloop/integrations/harbor/trials.py +1109 -0
  64. agentloop/integrations/harbor/verifier.py +118 -0
  65. agentloop/integrations/langgraph.py +150 -0
  66. agentloop/integrations/langgraph_harness.py +466 -0
  67. agentloop/integrations/langgraph_recovery.py +366 -0
  68. agentloop/integrations/omnigent/__init__.py +1 -0
  69. agentloop/integrations/omnigent/cli.py +78 -0
  70. agentloop/integrations/omnigent/evidence.py +93 -0
  71. agentloop/integrations/omnigent/telemetry.py +422 -0
  72. agentloop/integrations/openai.py +415 -0
  73. agentloop/integrations/openai_agents.py +291 -0
  74. agentloop/integrations/sdk.py +82 -0
  75. agentloop/integrations/vercel_ai.py +83 -0
  76. agentloop/interoperability/__init__.py +5 -0
  77. agentloop/interoperability/artifacts.py +112 -0
  78. agentloop/interoperability/cli.py +59 -0
  79. agentloop/interoperability/cohort_evidence.py +175 -0
  80. agentloop/interoperability/cohorts.py +681 -0
  81. agentloop/interoperability/contracts.py +427 -0
  82. agentloop/interoperability/coordination.py +554 -0
  83. agentloop/interoperability/evidence.py +215 -0
  84. agentloop/interoperability/otlp_jsonl.py +775 -0
  85. agentloop/interoperability/otlp_validation.py +496 -0
  86. agentloop/interoperability/registry.py +561 -0
  87. agentloop/interoperability/validation.py +333 -0
  88. agentloop/intervention_service.py +110 -0
  89. agentloop/intervention_store.py +118 -0
  90. agentloop/interventions.py +190 -0
  91. agentloop/issues.py +90 -0
  92. agentloop/judgment_adapters.py +125 -0
  93. agentloop/judgment_benchmark_types.py +98 -0
  94. agentloop/judgment_benchmarks.py +606 -0
  95. agentloop/judgment_types.py +241 -0
  96. agentloop/judgment_views.py +38 -0
  97. agentloop/judgments.py +342 -0
  98. agentloop/loop_guards.py +274 -0
  99. agentloop/loop_types.py +66 -0
  100. agentloop/markdown.py +102 -0
  101. agentloop/metrics.py +342 -0
  102. agentloop/migrations.py +303 -0
  103. agentloop/model_routing.py +470 -0
  104. agentloop/onboarding.py +315 -0
  105. agentloop/operations.py +44 -0
  106. agentloop/optimizer.py +199 -0
  107. agentloop/otel.py +715 -0
  108. agentloop/otel_ids.py +81 -0
  109. agentloop/otel_semantics.py +134 -0
  110. agentloop/parallelism.py +141 -0
  111. agentloop/patches.py +603 -0
  112. agentloop/plan_export.py +126 -0
  113. agentloop/policy_promotion.py +325 -0
  114. agentloop/promotion_evidence.py +304 -0
  115. agentloop/promotion_types.py +209 -0
  116. agentloop/quality.py +446 -0
  117. agentloop/ranking.py +529 -0
  118. agentloop/recovery_evidence.py +96 -0
  119. agentloop/replay.py +753 -0
  120. agentloop/retention.py +375 -0
  121. agentloop/retention_types.py +113 -0
  122. agentloop/reuse_experiments.py +247 -0
  123. agentloop/routing_checks.py +116 -0
  124. agentloop/routing_types.py +241 -0
  125. agentloop/rules.py +396 -0
  126. agentloop/runtime.py +286 -0
  127. agentloop/savings.py +158 -0
  128. agentloop/scheduling_plan.py +99 -0
  129. agentloop/scheduling_types.py +169 -0
  130. agentloop/schema.py +257 -0
  131. agentloop/semantic_rules.py +200 -0
  132. agentloop/semantic_waste.py +345 -0
  133. agentloop/semantic_waste_types.py +88 -0
  134. agentloop/server.py +494 -0
  135. agentloop/store.py +1429 -0
  136. agentloop/structural_experiments.py +431 -0
  137. agentloop/structural_types.py +113 -0
  138. agentloop/structured_quality.py +1020 -0
  139. agentloop/studies.py +629 -0
  140. agentloop/study_statistics.py +58 -0
  141. agentloop/substitution_evidence.py +199 -0
  142. agentloop/substitution_export.py +190 -0
  143. agentloop/substitution_reports.py +407 -0
  144. agentloop/substitution_types.py +284 -0
  145. agentloop/substitutions.py +267 -0
  146. agentloop/timing.py +142 -0
  147. agentloop/tokens.py +188 -0
  148. agentloop/tool_scheduling.py +410 -0
  149. agentloop/trace_sources.py +31 -0
  150. agentloop/tracer.py +494 -0
  151. agentloop/value.py +306 -0
  152. agentloop/version.py +3 -0
  153. agentloop/workflow_types.py +264 -0
  154. agentloop/workflows.py +284 -0
  155. agentloop_profiler-0.1.0.dist-info/METADATA +513 -0
  156. agentloop_profiler-0.1.0.dist-info/RECORD +166 -0
  157. agentloop_profiler-0.1.0.dist-info/WHEEL +5 -0
  158. agentloop_profiler-0.1.0.dist-info/entry_points.txt +2 -0
  159. agentloop_profiler-0.1.0.dist-info/licenses/LICENSE +201 -0
  160. agentloop_profiler-0.1.0.dist-info/licenses/THIRD_PARTY_LICENSES.md +101 -0
  161. agentloop_profiler-0.1.0.dist-info/top_level.txt +2 -0
  162. dashboard/__init__.py +5 -0
  163. dashboard/app.py +922 -0
  164. dashboard/streamlit_compat.py +63 -0
  165. dashboard/trace_upload.py +57 -0
  166. dashboard/value_view.py +105 -0
@@ -0,0 +1,561 @@
1
+ """Versioned capability declarations and source/version-scoped probe evidence.
2
+
3
+ Catalog JSON is data. Enumeration does not load entry points, optional SDKs or
4
+ vendor runtimes, and registry state never changes an existing harness policy.
5
+ """
6
+
7
+ from __future__ import annotations
8
+
9
+ import json
10
+ import re
11
+ from dataclasses import asdict, dataclass, field
12
+ from datetime import datetime
13
+ from hashlib import sha256
14
+ from pathlib import Path
15
+ from typing import Any, Iterable
16
+
17
+ from agentloop.interoperability.validation import (
18
+ ImportValidationError,
19
+ load_json_artifact,
20
+ relative_reference,
21
+ validate_json_tree,
22
+ )
23
+ from agentloop.interventions import canonical_json
24
+
25
+ CAPABILITY_NAMES = frozenset(
26
+ {
27
+ "observation.turns",
28
+ "observation.model_calls",
29
+ "observation.tool_calls",
30
+ "observation.subagents",
31
+ "observation.policy_decisions",
32
+ "observation.usage",
33
+ "observation.outcomes",
34
+ "enforcement.pre_request",
35
+ "enforcement.pre_model",
36
+ "enforcement.pre_tool",
37
+ "enforcement.before_finish",
38
+ "enforcement.interrupt",
39
+ "enforcement.policy_failure",
40
+ "lifecycle.sync",
41
+ "lifecycle.async",
42
+ "lifecycle.stream",
43
+ "lifecycle.async_stream",
44
+ "lifecycle.partial_close",
45
+ "lifecycle.cancel",
46
+ "lifecycle.resume",
47
+ "lifecycle.fork",
48
+ "lifecycle.subagent_handoff",
49
+ "compatibility.model_override",
50
+ "compatibility.tool_registry",
51
+ "compatibility.mcp",
52
+ "compatibility.acp",
53
+ "compatibility.telemetry_export",
54
+ "compatibility.context_handling",
55
+ "compatibility.approval",
56
+ }
57
+ )
58
+ _LIVE_ONLY = frozenset(
59
+ {
60
+ "compatibility.model_override",
61
+ "compatibility.approval",
62
+ "compatibility.context_handling",
63
+ "lifecycle.resume",
64
+ "lifecycle.fork",
65
+ }
66
+ )
67
+ _ID = re.compile(r"[A-Za-z0-9_.:-]{1,128}\Z")
68
+ _HASH = re.compile(r"[0-9a-f]{64}\Z")
69
+
70
+
71
+ def _fail(field: str, reason: str) -> None:
72
+ raise ImportValidationError("invalid_capability", field, reason)
73
+
74
+
75
+ def _label(value: Any, field: str, *, nullable: bool = False) -> None:
76
+ if value is None and nullable:
77
+ return
78
+ if not isinstance(value, str) or not value.strip() or len(value.encode()) > 512:
79
+ _fail(field, "must be a bounded nonempty string")
80
+
81
+
82
+ @dataclass(frozen=True)
83
+ class CapabilityDeclaration:
84
+ name: str
85
+ declared: bool | None
86
+ boundaries: tuple[str, ...] = ()
87
+ reason: str = "not declared"
88
+ requires_live: bool = False
89
+
90
+ def __post_init__(self) -> None:
91
+ if not isinstance(self.name, str) or self.name not in CAPABILITY_NAMES:
92
+ _fail("name", "unsupported capability dimension")
93
+ if self.declared is not None and type(self.declared) is not bool:
94
+ _fail("declared", "must be boolean or null")
95
+ if type(self.requires_live) is not bool:
96
+ _fail("requires_live", "must be boolean")
97
+ if not isinstance(self.boundaries, (tuple, list)) or any(
98
+ not isinstance(value, str) or not _ID.fullmatch(value) for value in self.boundaries
99
+ ):
100
+ _fail("boundaries", "must be bounded boundary identifiers")
101
+ object.__setattr__(self, "boundaries", tuple(sorted(set(self.boundaries))))
102
+ _label(self.reason, "reason")
103
+
104
+
105
+ @dataclass(frozen=True)
106
+ class AdapterManifest:
107
+ adapter_id: str
108
+ adapter_version: str
109
+ upstream_harness: str
110
+ upstream_version: str | None
111
+ transport: str
112
+ kind: str
113
+ observation_source: str
114
+ declaration_source: str
115
+ capabilities: tuple[CapabilityDeclaration, ...]
116
+ code_fingerprint: str | None = None
117
+ schema_version: str = "1.0"
118
+
119
+ def __post_init__(self) -> None:
120
+ for name in (
121
+ "adapter_id",
122
+ "adapter_version",
123
+ "upstream_harness",
124
+ "transport",
125
+ "observation_source",
126
+ "declaration_source",
127
+ ):
128
+ _label(getattr(self, name), name)
129
+ _label(self.upstream_version, "upstream_version", nullable=True)
130
+ if (
131
+ not isinstance(self.kind, str)
132
+ or self.kind not in {"control", "observation"}
133
+ or self.schema_version != "1.0"
134
+ ):
135
+ _fail("manifest", "unsupported adapter kind or version")
136
+ if self.code_fingerprint is not None and (
137
+ not isinstance(self.code_fingerprint, str) or not _HASH.fullmatch(self.code_fingerprint)
138
+ ):
139
+ _fail("code_fingerprint", "must be a SHA-256 digest or null")
140
+ if not isinstance(self.capabilities, (tuple, list)):
141
+ _fail("capabilities", "must be a declaration collection")
142
+ values = tuple(self.capabilities)
143
+ if any(not isinstance(item, CapabilityDeclaration) for item in values) or len(
144
+ {item.name for item in values}
145
+ ) != len(values):
146
+ _fail("capabilities", "declarations must be typed and unique")
147
+ if self.kind == "observation" and any(
148
+ item.declared is True and item.name.startswith("enforcement.") for item in values
149
+ ):
150
+ _fail("capabilities", "observation adapters cannot declare runtime enforcement")
151
+ object.__setattr__(self, "capabilities", tuple(sorted(values, key=lambda item: item.name)))
152
+
153
+ def to_dict(self) -> dict:
154
+ return json.loads(canonical_json(asdict(self)))
155
+
156
+ @property
157
+ def fingerprint(self) -> str:
158
+ return sha256(canonical_json(self.to_dict()).encode()).hexdigest()
159
+
160
+ @classmethod
161
+ def from_dict(cls, value: dict) -> AdapterManifest:
162
+ validate_json_tree(value)
163
+ expected = {
164
+ "schema_version",
165
+ "adapter_id",
166
+ "adapter_version",
167
+ "upstream_harness",
168
+ "upstream_version",
169
+ "transport",
170
+ "kind",
171
+ "observation_source",
172
+ "declaration_source",
173
+ "capabilities",
174
+ "code_fingerprint",
175
+ }
176
+ if (
177
+ not isinstance(value, dict)
178
+ or set(value) != expected
179
+ or not isinstance(value["capabilities"], list)
180
+ ):
181
+ _fail("manifest", "must contain exactly the documented fields")
182
+ declarations = []
183
+ for item in value["capabilities"]:
184
+ if not isinstance(item, dict) or set(item) != {
185
+ "name",
186
+ "declared",
187
+ "boundaries",
188
+ "reason",
189
+ "requires_live",
190
+ }:
191
+ _fail("capabilities", "unsupported declaration fields")
192
+ declarations.append(CapabilityDeclaration(**item))
193
+ return cls(**{**value, "capabilities": tuple(declarations)})
194
+
195
+
196
+ @dataclass(frozen=True)
197
+ class CapabilityObservation:
198
+ capability: str
199
+ verdict: str
200
+ probe_id: str | None
201
+ probe_version: str | None
202
+ tested_adapter_version: str | None
203
+ tested_transport: str | None
204
+ manifest_sha256: str | None
205
+ evidence_ref: str | None
206
+ evidence_sha256: str | None
207
+ environment_fingerprint: str | None
208
+ test_timestamp: str | None
209
+ boundaries: tuple[str, ...] = ()
210
+ source: str = "external_reported"
211
+ reason: str | None = None
212
+ _trusted: bool = field(default=False, init=False, repr=False, compare=False)
213
+
214
+ def __post_init__(self) -> None:
215
+ if (
216
+ not isinstance(self.capability, str)
217
+ or self.capability not in CAPABILITY_NAMES
218
+ or not isinstance(self.verdict, str)
219
+ or self.verdict
220
+ not in {
221
+ "verified_supported",
222
+ "verified_failed",
223
+ "skipped",
224
+ "unknown",
225
+ }
226
+ ):
227
+ _fail("observation", "unsupported capability or verdict")
228
+ if not isinstance(self.source, str) or self.source not in {
229
+ "offline_behavioral",
230
+ "live_behavioral",
231
+ "external_reported",
232
+ }:
233
+ _fail("source", "unsupported evidence source")
234
+ for name in (
235
+ "probe_id",
236
+ "probe_version",
237
+ "tested_adapter_version",
238
+ "tested_transport",
239
+ "test_timestamp",
240
+ "reason",
241
+ ):
242
+ _label(getattr(self, name), name, nullable=True)
243
+ if self.test_timestamp is not None:
244
+ try:
245
+ parsed = datetime.fromisoformat(self.test_timestamp.replace("Z", "+00:00"))
246
+ if parsed.tzinfo is None:
247
+ _fail("test_timestamp", "must include a timezone")
248
+ except ValueError as exc:
249
+ if isinstance(exc, ImportValidationError):
250
+ raise
251
+ _fail("test_timestamp", "must be an ISO timestamp")
252
+ for name in ("manifest_sha256", "evidence_sha256", "environment_fingerprint"):
253
+ value = getattr(self, name)
254
+ if value is not None and (not isinstance(value, str) or not _HASH.fullmatch(value)):
255
+ _fail(name, "must be a SHA-256 digest or null")
256
+ if self.evidence_ref is not None:
257
+ relative_reference(self.evidence_ref)
258
+ if self.verdict == "skipped" and self.reason is None:
259
+ _fail("reason", "skipped probes require a reason")
260
+ if not isinstance(self.boundaries, (tuple, list)) or any(
261
+ not isinstance(value, str) or not _ID.fullmatch(value) for value in self.boundaries
262
+ ):
263
+ _fail("boundaries", "invalid observation boundaries")
264
+ object.__setattr__(self, "boundaries", tuple(sorted(set(self.boundaries))))
265
+
266
+ def to_dict(self) -> dict:
267
+ value = asdict(self)
268
+ value.pop("_trusted")
269
+ return json.loads(canonical_json(value))
270
+
271
+ @classmethod
272
+ def from_dict(cls, value: dict) -> CapabilityObservation:
273
+ validate_json_tree(value)
274
+ expected = set(cls.__dataclass_fields__) - {"_trusted"}
275
+ if not isinstance(value, dict) or set(value) != expected:
276
+ _fail("observation", "must contain exactly the documented fields")
277
+ # Deserialization preserves recorded claims, but does not turn a catalog
278
+ # or historical report into a fresh trusted behavioral probe.
279
+ return cls(**value)
280
+
281
+
282
+ def _probe_observation(**kwargs: Any) -> CapabilityObservation:
283
+ """Internal bridge used only after an executable driver records its outcome."""
284
+ result = CapabilityObservation(**kwargs)
285
+ object.__setattr__(result, "_trusted", True)
286
+ return result
287
+
288
+
289
+ def _source_hash(paths: Iterable[Path]) -> str | None:
290
+ try:
291
+ return sha256(
292
+ canonical_json([sha256(path.read_bytes()).hexdigest() for path in paths]).encode()
293
+ ).hexdigest()
294
+ except OSError:
295
+ return None
296
+
297
+
298
+ def builtin_adapters() -> tuple[AdapterManifest, ...]:
299
+ """Enumerate core descriptors without SDK imports or plugin discovery."""
300
+ from agentloop import harness
301
+ from agentloop.integrations import langgraph_harness
302
+
303
+ def control(adapter_id, capabilities, upstream, upstream_version, transport, paths):
304
+ declarations = {
305
+ name: CapabilityDeclaration(
306
+ name,
307
+ None,
308
+ reason="not declared by the current wrapped-call contract",
309
+ requires_live=name in _LIVE_ONLY,
310
+ )
311
+ for name in CAPABILITY_NAMES
312
+ }
313
+ for name, boundary in (
314
+ ("enforcement.pre_model", "model"),
315
+ ("enforcement.pre_tool", "tool"),
316
+ ("enforcement.before_finish", "completion"),
317
+ ):
318
+ declarations[name] = CapabilityDeclaration(
319
+ name,
320
+ harness.Hook(boundary) in capabilities.hooks and "deny" in capabilities.actions,
321
+ (boundary,),
322
+ "explicitly wrapped boundary only",
323
+ )
324
+ for name, kind in (
325
+ ("lifecycle.sync", "sync"),
326
+ ("lifecycle.async", "async"),
327
+ ("lifecycle.stream", "generator"),
328
+ ("lifecycle.async_stream", "async_generator"),
329
+ ):
330
+ declarations[name] = CapabilityDeclaration(
331
+ name,
332
+ kind in capabilities.execution_kinds,
333
+ ("wrapped_callable",),
334
+ "explicit callable lifecycle; hidden SDK work is outside coverage",
335
+ )
336
+ for name in ("lifecycle.partial_close", "lifecycle.cancel", "enforcement.policy_failure"):
337
+ declarations[name] = CapabilityDeclaration(
338
+ name,
339
+ True,
340
+ ("wrapped_callable",),
341
+ "declared wrapper lifecycle, awaiting behavioral probe",
342
+ )
343
+ for name in ("observation.policy_decisions", "observation.usage", "observation.outcomes"):
344
+ declarations[name] = CapabilityDeclaration(
345
+ name,
346
+ True,
347
+ ("wrapped_callable",),
348
+ "hook evidence/explicit usage reader; not complete application capture",
349
+ )
350
+ return AdapterManifest(
351
+ adapter_id,
352
+ harness.CONTRACT_VERSION,
353
+ upstream,
354
+ upstream_version,
355
+ transport,
356
+ "control",
357
+ "explicit_wrapped_calls",
358
+ "agentloop.harness.AdapterCapabilities",
359
+ tuple(declarations.values()),
360
+ _source_hash(paths),
361
+ )
362
+
363
+ python = control(
364
+ "python-wrapped",
365
+ harness.PYTHON_CAPABILITIES,
366
+ "python",
367
+ None,
368
+ "python-in-process",
369
+ (Path(harness.__file__),),
370
+ )
371
+ langgraph = control(
372
+ "langgraph-1.2.11",
373
+ langgraph_harness.LangGraphHarness(boundaries=harness.BOUNDARIES).capabilities,
374
+ "langgraph",
375
+ langgraph_harness.SUPPORTED_VERSION,
376
+ "langgraph-runnable",
377
+ (Path(harness.__file__), Path(langgraph_harness.__file__)),
378
+ )
379
+ sources = []
380
+ for adapter_id, system, module_path in (
381
+ ("harbor-atif", "harbor", "integrations/harbor/atif.py"),
382
+ ("omnigent-otel", "omnigent", "integrations/omnigent/telemetry.py"),
383
+ ):
384
+ declarations = []
385
+ for name in sorted(CAPABILITY_NAMES):
386
+ value = (
387
+ False
388
+ if name.startswith("enforcement.")
389
+ else True
390
+ if name
391
+ in {
392
+ "observation.turns",
393
+ "observation.model_calls",
394
+ "observation.tool_calls",
395
+ "observation.usage",
396
+ "compatibility.telemetry_export",
397
+ }
398
+ or system == "omnigent"
399
+ and name == "observation.policy_decisions"
400
+ else None
401
+ )
402
+ declarations.append(
403
+ CapabilityDeclaration(
404
+ name,
405
+ value,
406
+ ("file_import",),
407
+ "read-only source projection; no runtime interception or complete capture",
408
+ name in _LIVE_ONLY,
409
+ )
410
+ )
411
+ sources.append(
412
+ AdapterManifest(
413
+ adapter_id,
414
+ "1.0",
415
+ system,
416
+ None,
417
+ "atif-file" if system == "harbor" else "otlp-file",
418
+ "observation",
419
+ "external_reported",
420
+ module_path,
421
+ tuple(declarations),
422
+ _source_hash((Path(harness.__file__).parent / module_path,)),
423
+ )
424
+ )
425
+ return (python, langgraph, *sources)
426
+
427
+
428
+ def get_adapter(adapter_id: str) -> AdapterManifest:
429
+ for descriptor in builtin_adapters():
430
+ if descriptor.adapter_id == adapter_id:
431
+ return descriptor
432
+ _fail("adapter_id", "adapter is not registered")
433
+
434
+
435
+ def load_manifest(path: str | Path) -> AdapterManifest:
436
+ """Read a strict data-only description; never import its named adapter."""
437
+ path = Path(path)
438
+ return AdapterManifest.from_dict(load_json_artifact(path.parent, path.name).payload)
439
+
440
+
441
+ def capability_report(
442
+ manifest: AdapterManifest, observations: Iterable[CapabilityObservation] = ()
443
+ ) -> dict:
444
+ by_name = {}
445
+ for item in observations:
446
+ if not isinstance(item, CapabilityObservation) or item.capability in by_name:
447
+ _fail("observations", "observations must be typed and unique per capability")
448
+ by_name[item.capability] = item
449
+ if set(by_name) - {item.name for item in manifest.capabilities}:
450
+ _fail("observations", "probe refers to an undeclared capability dimension")
451
+ rows, drift = [], []
452
+ for declaration in manifest.capabilities:
453
+ observed = by_name.get(declaration.name)
454
+ state = (
455
+ "declared_supported"
456
+ if declaration.declared is True
457
+ else "declared_unsupported"
458
+ if declaration.declared is False
459
+ else "unknown"
460
+ )
461
+ reason = declaration.reason
462
+ if observed is not None:
463
+ missing = any(
464
+ getattr(observed, key) is None
465
+ for key in (
466
+ "probe_id",
467
+ "probe_version",
468
+ "tested_adapter_version",
469
+ "tested_transport",
470
+ "manifest_sha256",
471
+ "evidence_ref",
472
+ "evidence_sha256",
473
+ "environment_fingerprint",
474
+ "test_timestamp",
475
+ )
476
+ )
477
+ current = (
478
+ observed.tested_adapter_version == manifest.adapter_version
479
+ and observed.tested_transport == manifest.transport
480
+ and observed.manifest_sha256 == manifest.fingerprint
481
+ )
482
+ scope = set(declaration.boundaries) <= set(observed.boundaries)
483
+ if observed.verdict == "skipped":
484
+ state, reason = "skipped", observed.reason
485
+ elif (
486
+ missing
487
+ or not current
488
+ or not scope
489
+ or not observed._trusted
490
+ or observed.source == "external_reported"
491
+ ):
492
+ state, reason = (
493
+ "unknown",
494
+ "probe identity/evidence is incomplete, stale, out of scope or externally supplied",
495
+ )
496
+ elif (
497
+ declaration.requires_live or declaration.name in _LIVE_ONLY
498
+ ) and observed.source != "live_behavioral":
499
+ state, reason = "unknown", "this capability requires a relevant live probe"
500
+ else:
501
+ state, reason = (
502
+ observed.verdict,
503
+ observed.reason or "current scoped behavioral probe",
504
+ )
505
+ if (
506
+ declaration.declared is True
507
+ and state == "verified_failed"
508
+ or declaration.declared is False
509
+ and state == "verified_supported"
510
+ ):
511
+ drift.append(
512
+ {
513
+ "capability": declaration.name,
514
+ "declared": declaration.declared,
515
+ "observed": state,
516
+ }
517
+ )
518
+ rows.append(
519
+ {
520
+ **asdict(declaration),
521
+ "state": state,
522
+ "reason": reason,
523
+ "observation": observed.to_dict() if observed else None,
524
+ }
525
+ )
526
+ return {
527
+ "schema_version": "1.0",
528
+ "adapter": manifest.to_dict(),
529
+ "manifest_sha256": manifest.fingerprint,
530
+ "capabilities": rows,
531
+ "drift": drift,
532
+ "drift_status": "failed" if drift else "no_observed_drift",
533
+ "coverage": "explicit boundaries only; no claim of all application work",
534
+ }
535
+
536
+
537
+ def require_verified_boundary(
538
+ adapter_id: str, capability: str, observations: Iterable[CapabilityObservation]
539
+ ) -> None:
540
+ """Gate new registry-dependent controls on current built-in probe evidence."""
541
+ manifest = get_adapter(adapter_id)
542
+ if manifest.upstream_harness == "langgraph":
543
+ from importlib.metadata import PackageNotFoundError, version
544
+
545
+ try:
546
+ installed = version("langgraph")
547
+ except PackageNotFoundError:
548
+ installed = None
549
+ if installed != manifest.upstream_version:
550
+ _fail("enforcement", "current upstream runtime version is unavailable or incompatible")
551
+ report = capability_report(manifest, observations)
552
+ row = next((row for row in report["capabilities"] if row["name"] == capability), None)
553
+ if (
554
+ manifest.kind != "control"
555
+ or manifest.code_fingerprint is None
556
+ or row is None
557
+ or not capability.startswith("enforcement.")
558
+ or row["declared"] is not True
559
+ or row["state"] != "verified_supported"
560
+ ):
561
+ _fail("enforcement", "current adapter has no verified supported dispatch boundary")