agentic-runner 3.28.0__tar.gz → 3.29.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 (81) hide show
  1. {agentic_runner-3.28.0 → agentic_runner-3.29.0}/PKG-INFO +2 -2
  2. {agentic_runner-3.28.0 → agentic_runner-3.29.0}/pyproject.toml +1 -1
  3. {agentic_runner-3.28.0 → agentic_runner-3.29.0}/src/agentic_runner/__init__.py +1 -1
  4. agentic_runner-3.29.0/src/agentic_runner/organisation_name.py +30 -0
  5. {agentic_runner-3.28.0 → agentic_runner-3.29.0}/src/agentic_runner/service.py +15 -1
  6. {agentic_runner-3.28.0 → agentic_runner-3.29.0}/src/agentic_runner/workstation.py +38 -6
  7. {agentic_runner-3.28.0 → agentic_runner-3.29.0}/.gitignore +0 -0
  8. {agentic_runner-3.28.0 → agentic_runner-3.29.0}/LICENSE +0 -0
  9. {agentic_runner-3.28.0 → agentic_runner-3.29.0}/README.md +0 -0
  10. {agentic_runner-3.28.0 → agentic_runner-3.29.0}/src/agentic_runner/activities.py +0 -0
  11. {agentic_runner-3.28.0 → agentic_runner-3.29.0}/src/agentic_runner/attempts.py +0 -0
  12. {agentic_runner-3.28.0 → agentic_runner-3.29.0}/src/agentic_runner/auth_mode.py +0 -0
  13. {agentic_runner-3.28.0 → agentic_runner-3.29.0}/src/agentic_runner/build.py +0 -0
  14. {agentic_runner-3.28.0 → agentic_runner-3.29.0}/src/agentic_runner/callback.py +0 -0
  15. {agentic_runner-3.28.0 → agentic_runner-3.29.0}/src/agentic_runner/child_watcher.py +0 -0
  16. {agentic_runner-3.28.0 → agentic_runner-3.29.0}/src/agentic_runner/cli.py +0 -0
  17. {agentic_runner-3.28.0 → agentic_runner-3.29.0}/src/agentic_runner/cli_floor.py +0 -0
  18. {agentic_runner-3.28.0 → agentic_runner-3.29.0}/src/agentic_runner/config.py +0 -0
  19. {agentic_runner-3.28.0 → agentic_runner-3.29.0}/src/agentic_runner/credentials.py +0 -0
  20. {agentic_runner-3.28.0 → agentic_runner-3.29.0}/src/agentic_runner/egress.py +0 -0
  21. {agentic_runner-3.28.0 → agentic_runner-3.29.0}/src/agentic_runner/harness_self_test.py +0 -0
  22. {agentic_runner-3.28.0 → agentic_runner-3.29.0}/src/agentic_runner/heartbeat_link.py +0 -0
  23. {agentic_runner-3.28.0 → agentic_runner-3.29.0}/src/agentic_runner/hooks.py +0 -0
  24. {agentic_runner-3.28.0 → agentic_runner-3.29.0}/src/agentic_runner/host_store.py +0 -0
  25. {agentic_runner-3.28.0 → agentic_runner-3.29.0}/src/agentic_runner/integrations/__init__.py +0 -0
  26. {agentic_runner-3.28.0 → agentic_runner-3.29.0}/src/agentic_runner/integrations/git/__init__.py +0 -0
  27. {agentic_runner-3.28.0 → agentic_runner-3.29.0}/src/agentic_runner/integrations/git/contracts.py +0 -0
  28. {agentic_runner-3.28.0 → agentic_runner-3.29.0}/src/agentic_runner/integrations/git/evidence.py +0 -0
  29. {agentic_runner-3.28.0 → agentic_runner-3.29.0}/src/agentic_runner/integrations/git/fake_workspace.py +0 -0
  30. {agentic_runner-3.28.0 → agentic_runner-3.29.0}/src/agentic_runner/integrations/git/workspace.py +0 -0
  31. {agentic_runner-3.28.0 → agentic_runner-3.29.0}/src/agentic_runner/integrations/github/__init__.py +0 -0
  32. {agentic_runner-3.28.0 → agentic_runner-3.29.0}/src/agentic_runner/integrations/github/auth.py +0 -0
  33. {agentic_runner-3.28.0 → agentic_runner-3.29.0}/src/agentic_runner/integrations/github/fake_client.py +0 -0
  34. {agentic_runner-3.28.0 → agentic_runner-3.29.0}/src/agentic_runner/integrations/github/gh_client.py +0 -0
  35. {agentic_runner-3.28.0 → agentic_runner-3.29.0}/src/agentic_runner/integrations/oauth/__init__.py +0 -0
  36. {agentic_runner-3.28.0 → agentic_runner-3.29.0}/src/agentic_runner/integrations/oauth/fake_provider.py +0 -0
  37. {agentic_runner-3.28.0 → agentic_runner-3.29.0}/src/agentic_runner/launch.py +0 -0
  38. {agentic_runner-3.28.0 → agentic_runner-3.29.0}/src/agentic_runner/lifecycle.py +0 -0
  39. {agentic_runner-3.28.0 → agentic_runner-3.29.0}/src/agentic_runner/llm_contracts.py +0 -0
  40. {agentic_runner-3.28.0 → agentic_runner-3.29.0}/src/agentic_runner/llm_proxy.py +0 -0
  41. {agentic_runner-3.28.0 → agentic_runner-3.29.0}/src/agentic_runner/mcp.py +0 -0
  42. {agentic_runner-3.28.0 → agentic_runner-3.29.0}/src/agentic_runner/message_store.py +0 -0
  43. {agentic_runner-3.28.0 → agentic_runner-3.29.0}/src/agentic_runner/oauth_connectors.py +0 -0
  44. {agentic_runner-3.28.0 → agentic_runner-3.29.0}/src/agentic_runner/pool_key.py +0 -0
  45. {agentic_runner-3.28.0 → agentic_runner-3.29.0}/src/agentic_runner/private_state.py +0 -0
  46. {agentic_runner-3.28.0 → agentic_runner-3.29.0}/src/agentic_runner/py.typed +0 -0
  47. {agentic_runner-3.28.0 → agentic_runner-3.29.0}/src/agentic_runner/recipient_key_secret.py +0 -0
  48. {agentic_runner-3.28.0 → agentic_runner-3.29.0}/src/agentic_runner/registration.py +0 -0
  49. {agentic_runner-3.28.0 → agentic_runner-3.29.0}/src/agentic_runner/runner_limit.py +0 -0
  50. {agentic_runner-3.28.0 → agentic_runner-3.29.0}/src/agentic_runner/runtime/__init__.py +0 -0
  51. {agentic_runner-3.28.0 → agentic_runner-3.29.0}/src/agentic_runner/runtime/verifier_command.py +0 -0
  52. {agentic_runner-3.28.0 → agentic_runner-3.29.0}/src/agentic_runner/sealed_box.py +0 -0
  53. {agentic_runner-3.28.0 → agentic_runner-3.29.0}/src/agentic_runner/sign_in.py +0 -0
  54. {agentic_runner-3.28.0 → agentic_runner-3.29.0}/src/agentic_runner/skill.py +0 -0
  55. {agentic_runner-3.28.0 → agentic_runner-3.29.0}/src/agentic_runner/skills/agentic-runner-setup/SKILL.md +0 -0
  56. {agentic_runner-3.28.0 → agentic_runner-3.29.0}/src/agentic_runner/testing/__init__.py +0 -0
  57. {agentic_runner-3.28.0 → agentic_runner-3.29.0}/src/agentic_runner/testing/__main__.py +0 -0
  58. {agentic_runner-3.28.0 → agentic_runner-3.29.0}/src/agentic_runner/testing/control_plane.py +0 -0
  59. {agentic_runner-3.28.0 → agentic_runner-3.29.0}/src/agentic_runner/testing/plugin.py +0 -0
  60. {agentic_runner-3.28.0 → agentic_runner-3.29.0}/src/agentic_runner/testing/test_conformance.py +0 -0
  61. {agentic_runner-3.28.0 → agentic_runner-3.29.0}/src/agentic_runner/testing/workflow.py +0 -0
  62. {agentic_runner-3.28.0 → agentic_runner-3.29.0}/src/agentic_runner/tiny_http.py +0 -0
  63. {agentic_runner-3.28.0 → agentic_runner-3.29.0}/src/agentic_runner/triage_activities.py +0 -0
  64. {agentic_runner-3.28.0 → agentic_runner-3.29.0}/src/agentic_runner/usage_windows.py +0 -0
  65. {agentic_runner-3.28.0 → agentic_runner-3.29.0}/src/agentic_runner/user_sources.py +0 -0
  66. {agentic_runner-3.28.0 → agentic_runner-3.29.0}/src/agentic_runner/workers/__init__.py +0 -0
  67. {agentic_runner-3.28.0 → agentic_runner-3.29.0}/src/agentic_runner/workers/_runtime_support.py +0 -0
  68. {agentic_runner-3.28.0 → agentic_runner-3.29.0}/src/agentic_runner/workers/acp_runtime.py +0 -0
  69. {agentic_runner-3.28.0 → agentic_runner-3.29.0}/src/agentic_runner/workers/agent_runtime.py +0 -0
  70. {agentic_runner-3.28.0 → agentic_runner-3.29.0}/src/agentic_runner/workers/claude_runtime.py +0 -0
  71. {agentic_runner-3.28.0 → agentic_runner-3.29.0}/src/agentic_runner/workers/claude_sign_in.py +0 -0
  72. {agentic_runner-3.28.0 → agentic_runner-3.29.0}/src/agentic_runner/workers/codex_runtime.py +0 -0
  73. {agentic_runner-3.28.0 → agentic_runner-3.29.0}/src/agentic_runner/workers/command_policy.py +0 -0
  74. {agentic_runner-3.28.0 → agentic_runner-3.29.0}/src/agentic_runner/workers/contract_isolation.py +0 -0
  75. {agentic_runner-3.28.0 → agentic_runner-3.29.0}/src/agentic_runner/workers/fastapi_client.py +0 -0
  76. {agentic_runner-3.28.0 → agentic_runner-3.29.0}/src/agentic_runner/workers/harness_logins.py +0 -0
  77. {agentic_runner-3.28.0 → agentic_runner-3.29.0}/src/agentic_runner/workers/harness_outcome.py +0 -0
  78. {agentic_runner-3.28.0 → agentic_runner-3.29.0}/src/agentic_runner/workers/harness_usage.py +0 -0
  79. {agentic_runner-3.28.0 → agentic_runner-3.29.0}/src/agentic_runner/workers/mcp_config.py +0 -0
  80. {agentic_runner-3.28.0 → agentic_runner-3.29.0}/src/agentic_runner/workers/settings.py +0 -0
  81. {agentic_runner-3.28.0 → agentic_runner-3.29.0}/src/agentic_runner/workers/skills.py +0 -0
@@ -1,11 +1,11 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: agentic-runner
3
- Version: 3.28.0
3
+ Version: 3.29.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.28.0
8
+ Requires-Dist: agentic-runner-contracts==3.29.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.28.0",
13
+ "agentic-runner-contracts==3.29.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.28.0"
10
+ __version__ = "3.29.0"
11
11
 
12
12
  __all__ = ["__version__"]
@@ -0,0 +1,30 @@
1
+ """The Organisation this Runner serves, by the name the last heartbeat ack gave (QTS-1909).
2
+
3
+ ``agentic-runner list`` is a separate process and reads it from this file, which the
4
+ running Runner rewrites whenever an ack changes it. Absent while the control plane names
5
+ no Organisation -- one below contracts 3.29 -- so ``list`` shows the id instead.
6
+ """
7
+
8
+ from __future__ import annotations
9
+
10
+ from pathlib import Path
11
+ from typing import Final
12
+
13
+ from agentic_runner.private_state import private_read, private_write
14
+
15
+ __all__ = ["ORGANISATION_NAME_FILENAME", "load_organisation_name", "save_organisation_name"]
16
+
17
+ ORGANISATION_NAME_FILENAME: Final[str] = "organisation-name"
18
+
19
+
20
+ def save_organisation_name(state_dir: Path, name: str | None) -> None:
21
+ path = state_dir / ORGANISATION_NAME_FILENAME
22
+ if name is None:
23
+ path.unlink(missing_ok=True)
24
+ else:
25
+ private_write(path, name.encode("utf-8"))
26
+
27
+
28
+ def load_organisation_name(state_dir: Path) -> str | None:
29
+ raw = private_read(state_dir / ORGANISATION_NAME_FILENAME)
30
+ return None if raw is None else raw.decode("utf-8", errors="replace")
@@ -33,7 +33,7 @@ from collections.abc import AsyncIterator, Awaitable, Callable, Mapping, Sequenc
33
33
  from dataclasses import dataclass, field
34
34
  from datetime import UTC, datetime, timedelta
35
35
  from pathlib import Path
36
- from typing import Any, Final, Protocol
36
+ from typing import Any, Final, Literal, Protocol
37
37
  from uuid import UUID
38
38
 
39
39
  import httpx
@@ -85,6 +85,7 @@ from agentic_runner.llm_proxy import (
85
85
  from agentic_runner.mcp import ToolServerHealthLog
86
86
  from agentic_runner.message_store import MESSAGES_DIR, MessageStore, TemporalWorkflowSignaller
87
87
  from agentic_runner.oauth_connectors import OAuthConnectors
88
+ from agentic_runner.organisation_name import save_organisation_name
88
89
  from agentic_runner.pool_key import (
89
90
  POOL_KEY_ENV,
90
91
  POOL_KEY_FILENAME,
@@ -852,6 +853,9 @@ class ControlPlaneStream:
852
853
  # QTS-1773: the control plane's contracts version, from its ack; None until an ack from
853
854
  # one on 3.27 or later, the first to send it and to parse `RunnerInstall.name`.
854
855
  _control_plane_contracts: str | None = None
856
+ # QTS-1909: the name in the copy `list` reads, None while there is none. "unwritten"
857
+ # until this process first writes it, so the first ack replaces what a previous one left.
858
+ _organisation_name: str | None | Literal["unwritten"] = "unwritten"
855
859
  _last_seen: tuple[float, datetime] | None = None
856
860
 
857
861
  def _observe_sleep(self, now: datetime) -> None:
@@ -1081,6 +1085,7 @@ class ControlPlaneStream:
1081
1085
  self._take_pool(ack.pool)
1082
1086
  if ack.contracts_version is not None:
1083
1087
  self._control_plane_contracts = ack.contracts_version
1088
+ self._take_organisation_name(ack.organisation_name)
1084
1089
  await self._sync_llm_slots()
1085
1090
  if ack.runner_token is not None:
1086
1091
  self.runner_token = ack.runner_token
@@ -1139,6 +1144,15 @@ class ControlPlaneStream:
1139
1144
  if self.state_dir is not None:
1140
1145
  save_llm_contracts(self.state_dir, terms)
1141
1146
 
1147
+ def _take_organisation_name(self, name: str | None) -> None:
1148
+ """Keep the copy ``list`` reads when the ack's name changes. Unlike the Contract
1149
+ terms, an ack without one clears it: only the name, never the id, can go stale."""
1150
+
1151
+ if self.state_dir is None or name == self._organisation_name:
1152
+ return
1153
+ save_organisation_name(self.state_dir, name)
1154
+ self._organisation_name = name
1155
+
1142
1156
  def _take_pool(self, pool: PoolTerms | None) -> None:
1143
1157
  """Keep the ack's pool. An ack without one (a personal Runner, or a control plane
1144
1158
  below 3.26) leaves what is held."""
@@ -55,7 +55,9 @@ from agentic_runner.config import CONFIG_FILE_ENV
55
55
  from agentic_runner.host_store import open_workstation_store
56
56
  from agentic_runner.lifecycle import LifecycleOutbox
57
57
  from agentic_runner.llm_contracts import load_llm_contracts
58
+ from agentic_runner.organisation_name import load_organisation_name
58
59
  from agentic_runner.pool_key import parse_pool_key, save_pool_key
60
+ from agentic_runner.private_state import UnsafeStateError
59
61
  from agentic_runner.registration import load_state
60
62
  from agentic_runner.service import HEARTBEAT_STAMP_NAME, PIDFILE_NAME
61
63
  from agentic_runner.workers.acp_runtime import ACP_BRIDGES
@@ -63,6 +65,7 @@ from agentic_runner_contracts.public_metadata import NAMESPACE_PREFIX
63
65
  from agentic_runner_contracts.runner_registration import (
64
66
  RUNNER_NAME_PATTERN,
65
67
  CliVersion,
68
+ ContractLlmTerms,
66
69
  EgressPosture,
67
70
  HostAttestation,
68
71
  InstallChannel,
@@ -669,15 +672,18 @@ _LIST_HEADER: Final = (
669
672
  def list_lines(root: Path) -> list[str]:
670
673
  """``list``: one row per Runner installed here (QTS-1774).
671
674
 
672
- Ids, not names: the Runner is told neither the Organisation's nor the Contract's name.
673
- Eight characters, as the console shows a Runner's id beside its name (Spec: Runners
674
- rev 7).
675
+ The Organisation's and the Contract's names once an ack from a control plane on 3.29
676
+ named them (QTS-1909); before that, the first eight characters of their ids, as the
677
+ console shows a Runner's id beside its name (Spec: Runners rev 7).
675
678
  """
676
679
 
677
680
  names = installed_names(root)
678
681
  if not names:
679
682
  return [f"no workstation Runner is installed under {root}"]
680
- rows = [_LIST_HEADER, *(_list_row(RunnerPaths(root=root, name=name)) for name in names)]
683
+ rows = [
684
+ _LIST_HEADER,
685
+ *(_list_row_or_reason(RunnerPaths(root=root, name=name)) for name in names),
686
+ ]
681
687
  widths = [max(len(row[column]) for row in rows) for column in range(len(_LIST_HEADER) - 1)]
682
688
  return [
683
689
  " ".join([*(cell.ljust(width) for cell, width in zip(row, widths, strict=False)), row[-1]])
@@ -685,20 +691,37 @@ def list_lines(root: Path) -> list[str]:
685
691
  ]
686
692
 
687
693
 
694
+ def _list_row_or_reason(paths: RunnerPaths) -> tuple[str, str, str, str, str, str]:
695
+ """One install that cannot be read must not hide the others' rows (QTS-1910).
696
+
697
+ The first line only: a pydantic error runs to one line per field, and the row is one.
698
+ """
699
+
700
+ try:
701
+ return _list_row(paths)
702
+ except (OSError, ValueError, UnsafeStateError) as error:
703
+ reason = str(error).splitlines()[0] if str(error) else type(error).__name__
704
+ return (paths.name, "-", "-", "-", "-", f"unreadable: {reason}")
705
+
706
+
688
707
  def _list_row(paths: RunnerPaths) -> tuple[str, str, str, str, str, str]:
689
708
  state = load_state(paths.state_dir)
690
709
  contracts = load_llm_contracts(paths.state_dir)
710
+ organisation_name = _printable(load_organisation_name(paths.state_dir))
691
711
  try:
692
712
  version = paths.started_version.read_text(encoding="utf-8").strip() or "-"
693
713
  except OSError:
694
714
  version = "-"
695
715
  return (
696
716
  paths.name,
697
- state.temporal_namespace.removeprefix(NAMESPACE_PREFIX)[:8] if state else "-",
717
+ (
718
+ organisation_name
719
+ or (state.temporal_namespace.removeprefix(NAMESPACE_PREFIX)[:8] if state else "-")
720
+ ),
698
721
  (
699
722
  "-"
700
723
  if not contracts
701
- else str(contracts[0].contract_id)[:8]
724
+ else _contract_label(contracts[0])
702
725
  if len(contracts) == 1
703
726
  else f"{len(contracts)} Contracts"
704
727
  ),
@@ -708,6 +731,15 @@ def _list_row(paths: RunnerPaths) -> tuple[str, str, str, str, str, str]:
708
731
  )
709
732
 
710
733
 
734
+ def _contract_label(terms: ContractLlmTerms) -> str:
735
+ return _printable(terms.contract_name) or str(terms.contract_id)[:8]
736
+
737
+
738
+ def _printable(name: str | None) -> str:
739
+ # A name someone typed in the console: an escape sequence in it would reach the terminal.
740
+ return "".join(character for character in name or "" if character.isprintable())
741
+
742
+
711
743
  def next_step_lines(host_party: str) -> list[str]:
712
744
  """Where to go once the Runner is live (QTS-1314): the dogfood install ended at
713
745
  ``started`` and left the person with no idea what the console wanted next.
File without changes