agentic-runner 3.19.0__tar.gz → 3.20.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (77) hide show
  1. {agentic_runner-3.19.0 → agentic_runner-3.20.0}/PKG-INFO +2 -2
  2. {agentic_runner-3.19.0 → agentic_runner-3.20.0}/pyproject.toml +1 -1
  3. {agentic_runner-3.19.0 → agentic_runner-3.20.0}/src/agentic_runner/__init__.py +1 -1
  4. {agentic_runner-3.19.0 → agentic_runner-3.20.0}/src/agentic_runner/activities.py +25 -9
  5. {agentic_runner-3.19.0 → agentic_runner-3.20.0}/src/agentic_runner/heartbeat_link.py +14 -1
  6. {agentic_runner-3.19.0 → agentic_runner-3.20.0}/.gitignore +0 -0
  7. {agentic_runner-3.19.0 → agentic_runner-3.20.0}/LICENSE +0 -0
  8. {agentic_runner-3.19.0 → agentic_runner-3.20.0}/README.md +0 -0
  9. {agentic_runner-3.19.0 → agentic_runner-3.20.0}/src/agentic_runner/attempts.py +0 -0
  10. {agentic_runner-3.19.0 → agentic_runner-3.20.0}/src/agentic_runner/auth_mode.py +0 -0
  11. {agentic_runner-3.19.0 → agentic_runner-3.20.0}/src/agentic_runner/build.py +0 -0
  12. {agentic_runner-3.19.0 → agentic_runner-3.20.0}/src/agentic_runner/callback.py +0 -0
  13. {agentic_runner-3.19.0 → agentic_runner-3.20.0}/src/agentic_runner/child_watcher.py +0 -0
  14. {agentic_runner-3.19.0 → agentic_runner-3.20.0}/src/agentic_runner/cli.py +0 -0
  15. {agentic_runner-3.19.0 → agentic_runner-3.20.0}/src/agentic_runner/cli_floor.py +0 -0
  16. {agentic_runner-3.19.0 → agentic_runner-3.20.0}/src/agentic_runner/config.py +0 -0
  17. {agentic_runner-3.19.0 → agentic_runner-3.20.0}/src/agentic_runner/credentials.py +0 -0
  18. {agentic_runner-3.19.0 → agentic_runner-3.20.0}/src/agentic_runner/device_login_activities.py +0 -0
  19. {agentic_runner-3.19.0 → agentic_runner-3.20.0}/src/agentic_runner/egress.py +0 -0
  20. {agentic_runner-3.19.0 → agentic_runner-3.20.0}/src/agentic_runner/harness_self_test.py +0 -0
  21. {agentic_runner-3.19.0 → agentic_runner-3.20.0}/src/agentic_runner/hooks.py +0 -0
  22. {agentic_runner-3.19.0 → agentic_runner-3.20.0}/src/agentic_runner/host_store.py +0 -0
  23. {agentic_runner-3.19.0 → agentic_runner-3.20.0}/src/agentic_runner/integrations/__init__.py +0 -0
  24. {agentic_runner-3.19.0 → agentic_runner-3.20.0}/src/agentic_runner/integrations/git/__init__.py +0 -0
  25. {agentic_runner-3.19.0 → agentic_runner-3.20.0}/src/agentic_runner/integrations/git/contracts.py +0 -0
  26. {agentic_runner-3.19.0 → agentic_runner-3.20.0}/src/agentic_runner/integrations/git/evidence.py +0 -0
  27. {agentic_runner-3.19.0 → agentic_runner-3.20.0}/src/agentic_runner/integrations/git/fake_workspace.py +0 -0
  28. {agentic_runner-3.19.0 → agentic_runner-3.20.0}/src/agentic_runner/integrations/git/workspace.py +0 -0
  29. {agentic_runner-3.19.0 → agentic_runner-3.20.0}/src/agentic_runner/integrations/github/__init__.py +0 -0
  30. {agentic_runner-3.19.0 → agentic_runner-3.20.0}/src/agentic_runner/integrations/github/auth.py +0 -0
  31. {agentic_runner-3.19.0 → agentic_runner-3.20.0}/src/agentic_runner/integrations/github/fake_client.py +0 -0
  32. {agentic_runner-3.19.0 → agentic_runner-3.20.0}/src/agentic_runner/integrations/github/gh_client.py +0 -0
  33. {agentic_runner-3.19.0 → agentic_runner-3.20.0}/src/agentic_runner/integrations/oauth/__init__.py +0 -0
  34. {agentic_runner-3.19.0 → agentic_runner-3.20.0}/src/agentic_runner/integrations/oauth/fake_provider.py +0 -0
  35. {agentic_runner-3.19.0 → agentic_runner-3.20.0}/src/agentic_runner/launch.py +0 -0
  36. {agentic_runner-3.19.0 → agentic_runner-3.20.0}/src/agentic_runner/lifecycle.py +0 -0
  37. {agentic_runner-3.19.0 → agentic_runner-3.20.0}/src/agentic_runner/llm_proxy.py +0 -0
  38. {agentic_runner-3.19.0 → agentic_runner-3.20.0}/src/agentic_runner/mcp.py +0 -0
  39. {agentic_runner-3.19.0 → agentic_runner-3.20.0}/src/agentic_runner/message_store.py +0 -0
  40. {agentic_runner-3.19.0 → agentic_runner-3.20.0}/src/agentic_runner/oauth_connectors.py +0 -0
  41. {agentic_runner-3.19.0 → agentic_runner-3.20.0}/src/agentic_runner/private_state.py +0 -0
  42. {agentic_runner-3.19.0 → agentic_runner-3.20.0}/src/agentic_runner/py.typed +0 -0
  43. {agentic_runner-3.19.0 → agentic_runner-3.20.0}/src/agentic_runner/recipient_key_secret.py +0 -0
  44. {agentic_runner-3.19.0 → agentic_runner-3.20.0}/src/agentic_runner/registration.py +0 -0
  45. {agentic_runner-3.19.0 → agentic_runner-3.20.0}/src/agentic_runner/runtime/__init__.py +0 -0
  46. {agentic_runner-3.19.0 → agentic_runner-3.20.0}/src/agentic_runner/runtime/verifier_command.py +0 -0
  47. {agentic_runner-3.19.0 → agentic_runner-3.20.0}/src/agentic_runner/sealed_box.py +0 -0
  48. {agentic_runner-3.19.0 → agentic_runner-3.20.0}/src/agentic_runner/service.py +0 -0
  49. {agentic_runner-3.19.0 → agentic_runner-3.20.0}/src/agentic_runner/skill.py +0 -0
  50. {agentic_runner-3.19.0 → agentic_runner-3.20.0}/src/agentic_runner/skills/agentic-runner-setup/SKILL.md +0 -0
  51. {agentic_runner-3.19.0 → agentic_runner-3.20.0}/src/agentic_runner/testing/__init__.py +0 -0
  52. {agentic_runner-3.19.0 → agentic_runner-3.20.0}/src/agentic_runner/testing/__main__.py +0 -0
  53. {agentic_runner-3.19.0 → agentic_runner-3.20.0}/src/agentic_runner/testing/control_plane.py +0 -0
  54. {agentic_runner-3.19.0 → agentic_runner-3.20.0}/src/agentic_runner/testing/plugin.py +0 -0
  55. {agentic_runner-3.19.0 → agentic_runner-3.20.0}/src/agentic_runner/testing/test_conformance.py +0 -0
  56. {agentic_runner-3.19.0 → agentic_runner-3.20.0}/src/agentic_runner/testing/workflow.py +0 -0
  57. {agentic_runner-3.19.0 → agentic_runner-3.20.0}/src/agentic_runner/tiny_http.py +0 -0
  58. {agentic_runner-3.19.0 → agentic_runner-3.20.0}/src/agentic_runner/triage_activities.py +0 -0
  59. {agentic_runner-3.19.0 → agentic_runner-3.20.0}/src/agentic_runner/usage_windows.py +0 -0
  60. {agentic_runner-3.19.0 → agentic_runner-3.20.0}/src/agentic_runner/user_sources.py +0 -0
  61. {agentic_runner-3.19.0 → agentic_runner-3.20.0}/src/agentic_runner/workers/__init__.py +0 -0
  62. {agentic_runner-3.19.0 → agentic_runner-3.20.0}/src/agentic_runner/workers/_runtime_support.py +0 -0
  63. {agentic_runner-3.19.0 → agentic_runner-3.20.0}/src/agentic_runner/workers/acp_runtime.py +0 -0
  64. {agentic_runner-3.19.0 → agentic_runner-3.20.0}/src/agentic_runner/workers/agent_runtime.py +0 -0
  65. {agentic_runner-3.19.0 → agentic_runner-3.20.0}/src/agentic_runner/workers/claude_runtime.py +0 -0
  66. {agentic_runner-3.19.0 → agentic_runner-3.20.0}/src/agentic_runner/workers/claude_sign_in.py +0 -0
  67. {agentic_runner-3.19.0 → agentic_runner-3.20.0}/src/agentic_runner/workers/codex_runtime.py +0 -0
  68. {agentic_runner-3.19.0 → agentic_runner-3.20.0}/src/agentic_runner/workers/command_policy.py +0 -0
  69. {agentic_runner-3.19.0 → agentic_runner-3.20.0}/src/agentic_runner/workers/contract_device_login.py +0 -0
  70. {agentic_runner-3.19.0 → agentic_runner-3.20.0}/src/agentic_runner/workers/contract_isolation.py +0 -0
  71. {agentic_runner-3.19.0 → agentic_runner-3.20.0}/src/agentic_runner/workers/fastapi_client.py +0 -0
  72. {agentic_runner-3.19.0 → agentic_runner-3.20.0}/src/agentic_runner/workers/harness_outcome.py +0 -0
  73. {agentic_runner-3.19.0 → agentic_runner-3.20.0}/src/agentic_runner/workers/harness_usage.py +0 -0
  74. {agentic_runner-3.19.0 → agentic_runner-3.20.0}/src/agentic_runner/workers/mcp_config.py +0 -0
  75. {agentic_runner-3.19.0 → agentic_runner-3.20.0}/src/agentic_runner/workers/settings.py +0 -0
  76. {agentic_runner-3.19.0 → agentic_runner-3.20.0}/src/agentic_runner/workers/skills.py +0 -0
  77. {agentic_runner-3.19.0 → agentic_runner-3.20.0}/src/agentic_runner/workstation.py +0 -0
@@ -1,11 +1,11 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: agentic-runner
3
- Version: 3.19.0
3
+ Version: 3.20.0
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
7
  Requires-Python: >=3.12
8
- Requires-Dist: agentic-runner-contracts==3.19.0
8
+ Requires-Dist: agentic-runner-contracts==3.20.0
9
9
  Requires-Dist: cryptography<51,>=50
10
10
  Requires-Dist: httpx<1.0,>=0.27
11
11
  Requires-Dist: pydantic-settings<3.0,>=2.0
@@ -10,7 +10,7 @@ dependencies = [
10
10
  # Exactly this release's contracts: the two ship as one version, and an unpinned
11
11
  # requirement let `uv tool upgrade` keep an older contracts wheel the Runner cannot
12
12
  # import from (QTS-1473). `scripts/set-version.py` moves it with the version.
13
- "agentic-runner-contracts==3.19.0",
13
+ "agentic-runner-contracts==3.20.0",
14
14
  # Imported directly by `registration.py` (the Ed25519 identity that signs every
15
15
  # heartbeat); already present transitively through pyjwt[crypto], declared because
16
16
  # we import it.
@@ -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.19.0"
10
+ __version__ = "3.20.0"
11
11
 
12
12
  __all__ = ["__version__"]
@@ -1710,8 +1710,9 @@ class RunnerRalphActivities:
1710
1710
  self._workspace_root = workspace_root
1711
1711
  self._contract_isolation = contract_isolation
1712
1712
  # What this process is, as the control plane registered it (PRD issue 42): its
1713
- # Runner id, its host party and its config-declared Tags. Every activity payload
1714
- # is checked against it before the body runs, so a stolen or misrouted task costs
1713
+ # Runner id, its host party and its config-declared Tags, which the last heartbeat
1714
+ # ack's Tags replace (`_current_routing_identity`). Every activity payload is
1715
+ # checked against it before the body runs, so a stolen or misrouted task costs
1715
1716
  # no verb. ``None`` in a process that never registered -- it polls no
1716
1717
  # ``runner.{runner_id}`` queue, so nothing routes to it and there is nothing to
1717
1718
  # assert against; the fail-closed check is on every Runner the router can pick.
@@ -1791,10 +1792,11 @@ class RunnerRalphActivities:
1791
1792
  poller into a visible intra-Organisation incident rather than silence (17 A11).
1792
1793
  """
1793
1794
 
1794
- if self._routing_identity is not None:
1795
+ identity = self._current_routing_identity()
1796
+ if identity is not None:
1795
1797
  routing = getattr(request, "routing", None)
1796
1798
  try:
1797
- assert_routed(routing, self._routing_identity)
1799
+ assert_routed(routing, identity)
1798
1800
  except RoutingRefusedError as refusal:
1799
1801
  work_record_id = getattr(request, "work_record_id", "")
1800
1802
  if not work_record_id:
@@ -1804,11 +1806,11 @@ class RunnerRalphActivities:
1804
1806
  self._fastapi_client,
1805
1807
  str(work_record_id),
1806
1808
  source=_ROUTING_EVIDENCE_SOURCE,
1807
- actor=f"runner:{self._routing_identity.runner_id}",
1809
+ actor=f"runner:{identity.runner_id}",
1808
1810
  payload={
1809
1811
  "event": "directive.routing_refused",
1810
1812
  "reason": refusal.reason,
1811
- "runner_id": self._routing_identity.runner_id,
1813
+ "runner_id": identity.runner_id,
1812
1814
  "routed_runner_id": None if routing is None else routing.runner_id,
1813
1815
  "detail": str(refusal),
1814
1816
  },
@@ -1818,6 +1820,19 @@ class RunnerRalphActivities:
1818
1820
  self._token_source, self._fastapi_client, request, actor=self._actor()
1819
1821
  )
1820
1822
 
1823
+ def _current_routing_identity(self) -> RunnerRoutingIdentity | None:
1824
+ """The registered identity, with the Tags the last heartbeat ack carried.
1825
+
1826
+ The console edits a Runner's Tags after registration and the control plane routes
1827
+ on the edit at once (ADR-0022 §13); checking the bootstrap set instead would
1828
+ refuse work routed on an added Tag on every retry.
1829
+ """
1830
+
1831
+ acked = self._heartbeat_link.tags if self._heartbeat_link is not None else None
1832
+ if self._routing_identity is None or acked is None:
1833
+ return self._routing_identity
1834
+ return replace(self._routing_identity, tags=acked)
1835
+
1821
1836
  def _actor(self) -> str:
1822
1837
  return (
1823
1838
  f"runner:{self._routing_identity.runner_id}"
@@ -3887,10 +3902,11 @@ class RunnerRalphActivities:
3887
3902
  "remedy": "work.open",
3888
3903
  },
3889
3904
  )
3890
- if state.repository or self._routing_identity is None:
3905
+ identity = self._current_routing_identity()
3906
+ if state.repository or identity is None:
3891
3907
  return None
3892
3908
  selector = snapshot.runner_selector_of(product_id)
3893
- if selector is not None and selector_satisfied(selector, self._routing_identity.tags):
3909
+ if selector is not None and selector_satisfied(selector, identity.tags):
3894
3910
  return None
3895
3911
  return (
3896
3912
  f"this Runner's tags do not satisfy the Runner selector of Product {product_id}, "
@@ -3898,7 +3914,7 @@ class RunnerRalphActivities:
3898
3914
  {
3899
3915
  "event": "directive.repository_refused",
3900
3916
  "product_id": product_id,
3901
- "runner_id": str(self._routing_identity.runner_id),
3917
+ "runner_id": str(identity.runner_id),
3902
3918
  "selector": [requirement.as_payload() for requirement in selector or ()],
3903
3919
  },
3904
3920
  )
@@ -121,6 +121,7 @@ class HeartbeatLink:
121
121
  # finished is never read again, while one under a running Directive is read at
122
122
  # every verb -- and `max_concurrent_directives` is itself bounded by this number.
123
123
  self._held: OrderedDict[str, _Held] = OrderedDict()
124
+ self._tags: dict[str, str] | None = None
124
125
  self._last_ack: float | None = None
125
126
  self._seen_monotonic, self._seen_wall = self._clock()
126
127
  self._woke = False
@@ -147,6 +148,16 @@ class HeartbeatLink:
147
148
  self._held.move_to_end(agent_id)
148
149
  return held
149
150
 
151
+ @property
152
+ def tags(self) -> dict[str, str] | None:
153
+ """This Runner's Tags as the last ack carried them (ADR-0022 §13).
154
+
155
+ ``None`` until an ack carries them, from a control plane below contracts 3.20 or
156
+ before the first beat: routing then checks the Tags this Runner bootstrapped with.
157
+ """
158
+
159
+ return None if self._tags is None else dict(self._tags)
160
+
150
161
  def applied(self) -> list[AppliedSnapshot]:
151
162
  return [
152
163
  AppliedSnapshot(agent_id=UUID(agent_id), version=held.version)
@@ -154,13 +165,15 @@ class HeartbeatLink:
154
165
  ]
155
166
 
156
167
  def apply(self, ack: HeartbeatAck) -> None:
157
- """Apply one ack's pushes: snapshots per Agent, ceilings per Contract.
168
+ """Apply one ack's pushes: snapshots per Agent, ceilings per Contract, the Tags.
158
169
 
159
170
  Replacement, never a merge. A narrowed Grant that merged into what it narrowed
160
171
  would leave the wider entry standing, which is the one mistake this whole layer
161
172
  exists to prevent.
162
173
  """
163
174
 
175
+ if ack.tags is not None:
176
+ self._tags = dict(ack.tags)
164
177
  for push in ack.grant_pushes:
165
178
  agent_id = str(push.agent_id)
166
179
  self._held[agent_id] = _Held(
File without changes