agentic-runner 3.0.0__tar.gz → 3.1.1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (64) hide show
  1. {agentic_runner-3.0.0 → agentic_runner-3.1.1}/PKG-INFO +1 -1
  2. {agentic_runner-3.0.0 → agentic_runner-3.1.1}/src/agentic_runner/__init__.py +1 -1
  3. agentic_runner-3.1.1/src/agentic_runner/build.py +33 -0
  4. {agentic_runner-3.0.0 → agentic_runner-3.1.1}/src/agentic_runner/registration.py +23 -0
  5. {agentic_runner-3.0.0 → agentic_runner-3.1.1}/src/agentic_runner/testing/control_plane.py +4 -0
  6. {agentic_runner-3.0.0 → agentic_runner-3.1.1}/src/agentic_runner/workstation.py +1 -18
  7. {agentic_runner-3.0.0 → agentic_runner-3.1.1}/.gitignore +0 -0
  8. {agentic_runner-3.0.0 → agentic_runner-3.1.1}/LICENSE +0 -0
  9. {agentic_runner-3.0.0 → agentic_runner-3.1.1}/README.md +0 -0
  10. {agentic_runner-3.0.0 → agentic_runner-3.1.1}/pyproject.toml +0 -0
  11. {agentic_runner-3.0.0 → agentic_runner-3.1.1}/src/agentic_runner/activities.py +0 -0
  12. {agentic_runner-3.0.0 → agentic_runner-3.1.1}/src/agentic_runner/attempts.py +0 -0
  13. {agentic_runner-3.0.0 → agentic_runner-3.1.1}/src/agentic_runner/callback.py +0 -0
  14. {agentic_runner-3.0.0 → agentic_runner-3.1.1}/src/agentic_runner/child_watcher.py +0 -0
  15. {agentic_runner-3.0.0 → agentic_runner-3.1.1}/src/agentic_runner/cli.py +0 -0
  16. {agentic_runner-3.0.0 → agentic_runner-3.1.1}/src/agentic_runner/config.py +0 -0
  17. {agentic_runner-3.0.0 → agentic_runner-3.1.1}/src/agentic_runner/credentials.py +0 -0
  18. {agentic_runner-3.0.0 → agentic_runner-3.1.1}/src/agentic_runner/device_login_activities.py +0 -0
  19. {agentic_runner-3.0.0 → agentic_runner-3.1.1}/src/agentic_runner/egress.py +0 -0
  20. {agentic_runner-3.0.0 → agentic_runner-3.1.1}/src/agentic_runner/heartbeat_link.py +0 -0
  21. {agentic_runner-3.0.0 → agentic_runner-3.1.1}/src/agentic_runner/hooks.py +0 -0
  22. {agentic_runner-3.0.0 → agentic_runner-3.1.1}/src/agentic_runner/host_store.py +0 -0
  23. {agentic_runner-3.0.0 → agentic_runner-3.1.1}/src/agentic_runner/integrations/__init__.py +0 -0
  24. {agentic_runner-3.0.0 → agentic_runner-3.1.1}/src/agentic_runner/integrations/git/__init__.py +0 -0
  25. {agentic_runner-3.0.0 → agentic_runner-3.1.1}/src/agentic_runner/integrations/git/contracts.py +0 -0
  26. {agentic_runner-3.0.0 → agentic_runner-3.1.1}/src/agentic_runner/integrations/git/evidence.py +0 -0
  27. {agentic_runner-3.0.0 → agentic_runner-3.1.1}/src/agentic_runner/integrations/git/fake_workspace.py +0 -0
  28. {agentic_runner-3.0.0 → agentic_runner-3.1.1}/src/agentic_runner/integrations/git/workspace.py +0 -0
  29. {agentic_runner-3.0.0 → agentic_runner-3.1.1}/src/agentic_runner/integrations/github/__init__.py +0 -0
  30. {agentic_runner-3.0.0 → agentic_runner-3.1.1}/src/agentic_runner/integrations/github/auth.py +0 -0
  31. {agentic_runner-3.0.0 → agentic_runner-3.1.1}/src/agentic_runner/integrations/github/fake_client.py +0 -0
  32. {agentic_runner-3.0.0 → agentic_runner-3.1.1}/src/agentic_runner/integrations/github/gh_client.py +0 -0
  33. {agentic_runner-3.0.0 → agentic_runner-3.1.1}/src/agentic_runner/lifecycle.py +0 -0
  34. {agentic_runner-3.0.0 → agentic_runner-3.1.1}/src/agentic_runner/llm_proxy.py +0 -0
  35. {agentic_runner-3.0.0 → agentic_runner-3.1.1}/src/agentic_runner/mcp.py +0 -0
  36. {agentic_runner-3.0.0 → agentic_runner-3.1.1}/src/agentic_runner/message_store.py +0 -0
  37. {agentic_runner-3.0.0 → agentic_runner-3.1.1}/src/agentic_runner/private_state.py +0 -0
  38. {agentic_runner-3.0.0 → agentic_runner-3.1.1}/src/agentic_runner/py.typed +0 -0
  39. {agentic_runner-3.0.0 → agentic_runner-3.1.1}/src/agentic_runner/recipient_key_secret.py +0 -0
  40. {agentic_runner-3.0.0 → agentic_runner-3.1.1}/src/agentic_runner/runtime/__init__.py +0 -0
  41. {agentic_runner-3.0.0 → agentic_runner-3.1.1}/src/agentic_runner/runtime/verifier_command.py +0 -0
  42. {agentic_runner-3.0.0 → agentic_runner-3.1.1}/src/agentic_runner/sealed_box.py +0 -0
  43. {agentic_runner-3.0.0 → agentic_runner-3.1.1}/src/agentic_runner/service.py +0 -0
  44. {agentic_runner-3.0.0 → agentic_runner-3.1.1}/src/agentic_runner/testing/__init__.py +0 -0
  45. {agentic_runner-3.0.0 → agentic_runner-3.1.1}/src/agentic_runner/testing/__main__.py +0 -0
  46. {agentic_runner-3.0.0 → agentic_runner-3.1.1}/src/agentic_runner/testing/plugin.py +0 -0
  47. {agentic_runner-3.0.0 → agentic_runner-3.1.1}/src/agentic_runner/testing/test_conformance.py +0 -0
  48. {agentic_runner-3.0.0 → agentic_runner-3.1.1}/src/agentic_runner/testing/workflow.py +0 -0
  49. {agentic_runner-3.0.0 → agentic_runner-3.1.1}/src/agentic_runner/tiny_http.py +0 -0
  50. {agentic_runner-3.0.0 → agentic_runner-3.1.1}/src/agentic_runner/triage_activities.py +0 -0
  51. {agentic_runner-3.0.0 → agentic_runner-3.1.1}/src/agentic_runner/user_sources.py +0 -0
  52. {agentic_runner-3.0.0 → agentic_runner-3.1.1}/src/agentic_runner/workers/__init__.py +0 -0
  53. {agentic_runner-3.0.0 → agentic_runner-3.1.1}/src/agentic_runner/workers/_runtime_support.py +0 -0
  54. {agentic_runner-3.0.0 → agentic_runner-3.1.1}/src/agentic_runner/workers/agent_runtime.py +0 -0
  55. {agentic_runner-3.0.0 → agentic_runner-3.1.1}/src/agentic_runner/workers/claude_runtime.py +0 -0
  56. {agentic_runner-3.0.0 → agentic_runner-3.1.1}/src/agentic_runner/workers/codex_runtime.py +0 -0
  57. {agentic_runner-3.0.0 → agentic_runner-3.1.1}/src/agentic_runner/workers/command_policy.py +0 -0
  58. {agentic_runner-3.0.0 → agentic_runner-3.1.1}/src/agentic_runner/workers/contract_device_login.py +0 -0
  59. {agentic_runner-3.0.0 → agentic_runner-3.1.1}/src/agentic_runner/workers/contract_isolation.py +0 -0
  60. {agentic_runner-3.0.0 → agentic_runner-3.1.1}/src/agentic_runner/workers/fastapi_client.py +0 -0
  61. {agentic_runner-3.0.0 → agentic_runner-3.1.1}/src/agentic_runner/workers/harness_usage.py +0 -0
  62. {agentic_runner-3.0.0 → agentic_runner-3.1.1}/src/agentic_runner/workers/mcp_config.py +0 -0
  63. {agentic_runner-3.0.0 → agentic_runner-3.1.1}/src/agentic_runner/workers/settings.py +0 -0
  64. {agentic_runner-3.0.0 → agentic_runner-3.1.1}/src/agentic_runner/workers/skills.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: agentic-runner
3
- Version: 3.0.0
3
+ Version: 3.1.1
4
4
  Summary: The Agentic OS Runner: an activity-only Temporal executor for workspaces, Agent Runtimes and verb seams
5
5
  License-Expression: AGPL-3.0-only
6
6
  License-File: LICENSE
@@ -7,6 +7,6 @@ stay on the platform; this package imports none of them, and a CI partition test
7
7
  (``tests/test_package_partition.py``) fails the build if that changes.
8
8
  """
9
9
 
10
- __version__ = "3.0.0"
10
+ __version__ = "3.1.1"
11
11
 
12
12
  __all__ = ["__version__"]
@@ -0,0 +1,33 @@
1
+ """This build's own digest: the ``build_id`` a Runner reports and a release publishes.
2
+
3
+ Its own module, with no Runner import, because registration (the bootstrap request) and
4
+ the workstation (the heartbeat attestation) both need it and the workstation imports
5
+ registration. The release pipeline calls it on the installed wheel and on the image, so
6
+ any change to how it is computed changes every published ``build-id.txt`` with it.
7
+ """
8
+
9
+ from __future__ import annotations
10
+
11
+ from hashlib import sha256
12
+ from pathlib import Path
13
+
14
+ import agentic_runner
15
+
16
+ __all__ = ["build_id"]
17
+
18
+ _PACKAGE = Path(agentic_runner.__file__).parent
19
+
20
+
21
+ def build_id(package: Path = _PACKAGE) -> str:
22
+ """The SHA-256 of the package's ``.py`` sources, path and bytes, in sorted order.
23
+
24
+ Self-reported build identity (12 B7), not a signature: the consoles say
25
+ "self-reported" beside it. Only ``.py`` files count, so a wheel, the image and a
26
+ Homebrew install of one release give the same value whatever bytecode each compiled.
27
+ """
28
+
29
+ digest = sha256()
30
+ for source in sorted(package.rglob("*.py")):
31
+ digest.update(source.relative_to(package).as_posix().encode())
32
+ digest.update(source.read_bytes())
33
+ return digest.hexdigest()
@@ -33,6 +33,7 @@ from cryptography.hazmat.primitives.asymmetric.ed25519 import Ed25519PrivateKey
33
33
  from pydantic import BaseModel, ConfigDict
34
34
 
35
35
  from agentic_runner import __version__ as runner_version
36
+ from agentic_runner.build import build_id
36
37
  from agentic_runner.private_state import private_read, private_write
37
38
  from agentic_runner_contracts import __version__ as contracts_version
38
39
  from agentic_runner_contracts.channel_messages import TRANSCRIPT_DELIVERIES_PATH, TranscriptDelivery
@@ -68,6 +69,7 @@ INTAKE_IGNORED_PATH = "/api/runner/v1/runners/intake/ignored"
68
69
  # The refusal a revoked Runner's heartbeat earns (06 §2: revocation *is* the refused
69
70
  # heartbeat). The one reason this process stops on rather than retries.
70
71
  RUNNER_REVOKED_REASON = "runner_revoked"
72
+ CONTROL_PLANE_OLDER_REASON = "control_plane_older"
71
73
 
72
74
  # What the state directory holds after a successful bootstrap. One file, mode 0600: the
73
75
  # private key is in it, and a Runner that loses it re-bootstraps rather than recovering.
@@ -252,6 +254,7 @@ class RunnerRegistrationClient:
252
254
  runner_version=runner_version,
253
255
  recipient_key=recipient_key,
254
256
  max_concurrent_directives=max_concurrent_directives,
257
+ build_id=build_id(),
255
258
  )
256
259
  payload = await self._post(BOOTSTRAP_PATH, content=request.model_dump_json(), headers={})
257
260
  response = BootstrapResponse.model_validate(payload)
@@ -344,9 +347,29 @@ def _refusal(response: httpx.Response) -> tuple[str, str]:
344
347
  detail = None
345
348
  if isinstance(detail, dict) and "reason" in detail:
346
349
  return str(detail["reason"]), str(detail.get("detail", detail["reason"]))
350
+ if unknown := _fields_the_control_plane_does_not_know(response.status_code, detail):
351
+ return CONTROL_PLANE_OLDER_REASON, (
352
+ f"the control plane is older than this Runner (agentic-runner {runner_version}, "
353
+ f"agentic-runner-contracts {contracts_version}): it does not know "
354
+ f"{', '.join(unknown)}. Install the agentic-runner release that matches the "
355
+ "control plane, or ask your Organisation's Admin to upgrade it."
356
+ )
347
357
  return "refused", f"{response.status_code}: {response.text[:200]}"
348
358
 
349
359
 
360
+ def _fields_the_control_plane_does_not_know(status_code: int, detail: Any) -> list[str]:
361
+ # A strict request model (extra="forbid") answering 422 with nothing but
362
+ # `extra_forbidden` errors is version skew, not a bad token: this Runner's contracts
363
+ # added fields the control plane's pinned contracts have not caught up to (RR-13).
364
+ if status_code != 422 or not isinstance(detail, list) or not detail:
365
+ return []
366
+ if not all(
367
+ isinstance(error, dict) and error.get("type") == "extra_forbidden" for error in detail
368
+ ):
369
+ return []
370
+ return [str(error.get("loc", ["?"])[-1]) for error in detail]
371
+
372
+
350
373
  class SignedIntakeStream:
351
374
  """:class:`agentic_runner.user_sources.IntakeStream` over the signed outbound stream."""
352
375
 
@@ -175,6 +175,10 @@ class FakeControlPlane:
175
175
  "isolation_modes": sorted({b.isolation_mode.value for b in self.bootstraps}),
176
176
  "heartbeats": len(self.heartbeats),
177
177
  "heartbeat_runner_ids": sorted({str(r) for r in self.heartbeat_runner_ids}),
178
+ "bootstrap_build_ids": sorted({b.build_id or "" for b in self.bootstraps}),
179
+ "heartbeat_build_ids": sorted(
180
+ {h.attestation.build_id if h.attestation else "" for h in self.heartbeats}
181
+ ),
178
182
  "directive_tokens": len(self.directive_tokens),
179
183
  "evidence": len(self.evidence),
180
184
  "refusals": [f"{r.method} {r.path} {r.status} {r.reason}" for r in self.refusals],
@@ -36,7 +36,6 @@ import tempfile
36
36
  from collections.abc import Callable, Mapping, Sequence
37
37
  from dataclasses import dataclass
38
38
  from datetime import UTC, datetime
39
- from hashlib import sha256
40
39
  from pathlib import Path
41
40
  from typing import Any, Final
42
41
  from xml.sax.saxutils import escape
@@ -44,8 +43,8 @@ from xml.sax.saxutils import escape
44
43
  import httpx
45
44
  from pydantic import BaseModel, ConfigDict
46
45
 
47
- import agentic_runner
48
46
  from agentic_runner import service
47
+ from agentic_runner.build import build_id
49
48
  from agentic_runner.host_store import open_workstation_store
50
49
  from agentic_runner.lifecycle import LifecycleOutbox
51
50
  from agentic_runner.registration import load_state
@@ -333,22 +332,6 @@ def cli_versions(found: Mapping[str, str], *, run: Run = subprocess.run) -> list
333
332
  # ------------------------------------------------------------------ attestation
334
333
 
335
334
 
336
- def build_id() -> str:
337
- """A digest of this build's own source: self-reported build identity (12 B7).
338
-
339
- Not a signature and not claimed to be one -- the consoles say "self-reported" beside
340
- it. What it buys is that two Runners reporting the same version but running
341
- different code show different ids.
342
- """
343
-
344
- package = Path(agentic_runner.__file__).parent
345
- digest = sha256()
346
- for source in sorted(package.rglob("*.py")):
347
- digest.update(source.relative_to(package).as_posix().encode())
348
- digest.update(source.read_bytes())
349
- return digest.hexdigest()[:16]
350
-
351
-
352
335
  def os_release() -> str:
353
336
  system = re.sub(r"[^a-z0-9_]", "", platform_module.system().lower())[:16] or "unknown"
354
337
  release = re.sub(r"[^0-9A-Za-z._+-]", "", platform_module.release())[:48]
File without changes
File without changes