agentic-runner 3.12.1__tar.gz → 3.13.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 (76) hide show
  1. {agentic_runner-3.12.1 → agentic_runner-3.13.0}/PKG-INFO +1 -1
  2. {agentic_runner-3.12.1 → agentic_runner-3.13.0}/src/agentic_runner/__init__.py +1 -1
  3. {agentic_runner-3.12.1 → agentic_runner-3.13.0}/src/agentic_runner/cli.py +44 -1
  4. agentic_runner-3.13.0/src/agentic_runner/skill.py +58 -0
  5. agentic_runner-3.13.0/src/agentic_runner/skills/agentic-runner-setup/SKILL.md +148 -0
  6. {agentic_runner-3.12.1 → agentic_runner-3.13.0}/src/agentic_runner/workstation.py +28 -1
  7. {agentic_runner-3.12.1 → agentic_runner-3.13.0}/.gitignore +0 -0
  8. {agentic_runner-3.12.1 → agentic_runner-3.13.0}/LICENSE +0 -0
  9. {agentic_runner-3.12.1 → agentic_runner-3.13.0}/README.md +0 -0
  10. {agentic_runner-3.12.1 → agentic_runner-3.13.0}/pyproject.toml +0 -0
  11. {agentic_runner-3.12.1 → agentic_runner-3.13.0}/src/agentic_runner/activities.py +0 -0
  12. {agentic_runner-3.12.1 → agentic_runner-3.13.0}/src/agentic_runner/attempts.py +0 -0
  13. {agentic_runner-3.12.1 → agentic_runner-3.13.0}/src/agentic_runner/auth_mode.py +0 -0
  14. {agentic_runner-3.12.1 → agentic_runner-3.13.0}/src/agentic_runner/build.py +0 -0
  15. {agentic_runner-3.12.1 → agentic_runner-3.13.0}/src/agentic_runner/callback.py +0 -0
  16. {agentic_runner-3.12.1 → agentic_runner-3.13.0}/src/agentic_runner/child_watcher.py +0 -0
  17. {agentic_runner-3.12.1 → agentic_runner-3.13.0}/src/agentic_runner/cli_floor.py +0 -0
  18. {agentic_runner-3.12.1 → agentic_runner-3.13.0}/src/agentic_runner/config.py +0 -0
  19. {agentic_runner-3.12.1 → agentic_runner-3.13.0}/src/agentic_runner/credentials.py +0 -0
  20. {agentic_runner-3.12.1 → agentic_runner-3.13.0}/src/agentic_runner/device_login_activities.py +0 -0
  21. {agentic_runner-3.12.1 → agentic_runner-3.13.0}/src/agentic_runner/egress.py +0 -0
  22. {agentic_runner-3.12.1 → agentic_runner-3.13.0}/src/agentic_runner/harness_self_test.py +0 -0
  23. {agentic_runner-3.12.1 → agentic_runner-3.13.0}/src/agentic_runner/heartbeat_link.py +0 -0
  24. {agentic_runner-3.12.1 → agentic_runner-3.13.0}/src/agentic_runner/hooks.py +0 -0
  25. {agentic_runner-3.12.1 → agentic_runner-3.13.0}/src/agentic_runner/host_store.py +0 -0
  26. {agentic_runner-3.12.1 → agentic_runner-3.13.0}/src/agentic_runner/integrations/__init__.py +0 -0
  27. {agentic_runner-3.12.1 → agentic_runner-3.13.0}/src/agentic_runner/integrations/git/__init__.py +0 -0
  28. {agentic_runner-3.12.1 → agentic_runner-3.13.0}/src/agentic_runner/integrations/git/contracts.py +0 -0
  29. {agentic_runner-3.12.1 → agentic_runner-3.13.0}/src/agentic_runner/integrations/git/evidence.py +0 -0
  30. {agentic_runner-3.12.1 → agentic_runner-3.13.0}/src/agentic_runner/integrations/git/fake_workspace.py +0 -0
  31. {agentic_runner-3.12.1 → agentic_runner-3.13.0}/src/agentic_runner/integrations/git/workspace.py +0 -0
  32. {agentic_runner-3.12.1 → agentic_runner-3.13.0}/src/agentic_runner/integrations/github/__init__.py +0 -0
  33. {agentic_runner-3.12.1 → agentic_runner-3.13.0}/src/agentic_runner/integrations/github/auth.py +0 -0
  34. {agentic_runner-3.12.1 → agentic_runner-3.13.0}/src/agentic_runner/integrations/github/fake_client.py +0 -0
  35. {agentic_runner-3.12.1 → agentic_runner-3.13.0}/src/agentic_runner/integrations/github/gh_client.py +0 -0
  36. {agentic_runner-3.12.1 → agentic_runner-3.13.0}/src/agentic_runner/integrations/oauth/__init__.py +0 -0
  37. {agentic_runner-3.12.1 → agentic_runner-3.13.0}/src/agentic_runner/integrations/oauth/fake_provider.py +0 -0
  38. {agentic_runner-3.12.1 → agentic_runner-3.13.0}/src/agentic_runner/lifecycle.py +0 -0
  39. {agentic_runner-3.12.1 → agentic_runner-3.13.0}/src/agentic_runner/llm_proxy.py +0 -0
  40. {agentic_runner-3.12.1 → agentic_runner-3.13.0}/src/agentic_runner/mcp.py +0 -0
  41. {agentic_runner-3.12.1 → agentic_runner-3.13.0}/src/agentic_runner/message_store.py +0 -0
  42. {agentic_runner-3.12.1 → agentic_runner-3.13.0}/src/agentic_runner/oauth_connectors.py +0 -0
  43. {agentic_runner-3.12.1 → agentic_runner-3.13.0}/src/agentic_runner/private_state.py +0 -0
  44. {agentic_runner-3.12.1 → agentic_runner-3.13.0}/src/agentic_runner/py.typed +0 -0
  45. {agentic_runner-3.12.1 → agentic_runner-3.13.0}/src/agentic_runner/recipient_key_secret.py +0 -0
  46. {agentic_runner-3.12.1 → agentic_runner-3.13.0}/src/agentic_runner/registration.py +0 -0
  47. {agentic_runner-3.12.1 → agentic_runner-3.13.0}/src/agentic_runner/runtime/__init__.py +0 -0
  48. {agentic_runner-3.12.1 → agentic_runner-3.13.0}/src/agentic_runner/runtime/verifier_command.py +0 -0
  49. {agentic_runner-3.12.1 → agentic_runner-3.13.0}/src/agentic_runner/sealed_box.py +0 -0
  50. {agentic_runner-3.12.1 → agentic_runner-3.13.0}/src/agentic_runner/service.py +0 -0
  51. {agentic_runner-3.12.1 → agentic_runner-3.13.0}/src/agentic_runner/testing/__init__.py +0 -0
  52. {agentic_runner-3.12.1 → agentic_runner-3.13.0}/src/agentic_runner/testing/__main__.py +0 -0
  53. {agentic_runner-3.12.1 → agentic_runner-3.13.0}/src/agentic_runner/testing/control_plane.py +0 -0
  54. {agentic_runner-3.12.1 → agentic_runner-3.13.0}/src/agentic_runner/testing/plugin.py +0 -0
  55. {agentic_runner-3.12.1 → agentic_runner-3.13.0}/src/agentic_runner/testing/test_conformance.py +0 -0
  56. {agentic_runner-3.12.1 → agentic_runner-3.13.0}/src/agentic_runner/testing/workflow.py +0 -0
  57. {agentic_runner-3.12.1 → agentic_runner-3.13.0}/src/agentic_runner/tiny_http.py +0 -0
  58. {agentic_runner-3.12.1 → agentic_runner-3.13.0}/src/agentic_runner/triage_activities.py +0 -0
  59. {agentic_runner-3.12.1 → agentic_runner-3.13.0}/src/agentic_runner/usage_windows.py +0 -0
  60. {agentic_runner-3.12.1 → agentic_runner-3.13.0}/src/agentic_runner/user_sources.py +0 -0
  61. {agentic_runner-3.12.1 → agentic_runner-3.13.0}/src/agentic_runner/workers/__init__.py +0 -0
  62. {agentic_runner-3.12.1 → agentic_runner-3.13.0}/src/agentic_runner/workers/_runtime_support.py +0 -0
  63. {agentic_runner-3.12.1 → agentic_runner-3.13.0}/src/agentic_runner/workers/acp_runtime.py +0 -0
  64. {agentic_runner-3.12.1 → agentic_runner-3.13.0}/src/agentic_runner/workers/agent_runtime.py +0 -0
  65. {agentic_runner-3.12.1 → agentic_runner-3.13.0}/src/agentic_runner/workers/claude_runtime.py +0 -0
  66. {agentic_runner-3.12.1 → agentic_runner-3.13.0}/src/agentic_runner/workers/claude_sign_in.py +0 -0
  67. {agentic_runner-3.12.1 → agentic_runner-3.13.0}/src/agentic_runner/workers/codex_runtime.py +0 -0
  68. {agentic_runner-3.12.1 → agentic_runner-3.13.0}/src/agentic_runner/workers/command_policy.py +0 -0
  69. {agentic_runner-3.12.1 → agentic_runner-3.13.0}/src/agentic_runner/workers/contract_device_login.py +0 -0
  70. {agentic_runner-3.12.1 → agentic_runner-3.13.0}/src/agentic_runner/workers/contract_isolation.py +0 -0
  71. {agentic_runner-3.12.1 → agentic_runner-3.13.0}/src/agentic_runner/workers/fastapi_client.py +0 -0
  72. {agentic_runner-3.12.1 → agentic_runner-3.13.0}/src/agentic_runner/workers/harness_outcome.py +0 -0
  73. {agentic_runner-3.12.1 → agentic_runner-3.13.0}/src/agentic_runner/workers/harness_usage.py +0 -0
  74. {agentic_runner-3.12.1 → agentic_runner-3.13.0}/src/agentic_runner/workers/mcp_config.py +0 -0
  75. {agentic_runner-3.12.1 → agentic_runner-3.13.0}/src/agentic_runner/workers/settings.py +0 -0
  76. {agentic_runner-3.12.1 → agentic_runner-3.13.0}/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.12.1
3
+ Version: 3.13.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,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.12.1"
10
+ __version__ = "3.13.0"
11
11
 
12
12
  __all__ = ["__version__"]
@@ -15,6 +15,9 @@ the Organisation's cap.
15
15
  Runner (PRD issue 47, ``workstation.py``): one login agent per Organisation, managed by
16
16
  the user who hosts it, with no ``sudo`` anywhere.
17
17
 
18
+ ``skill install | print`` hands the setup skill to the person's own Claude Code or Codex
19
+ (``skill.py``, console-v2 issue 21).
20
+
18
21
  The remaining subcommands are what a Directive calls back with, over its own attempt
19
22
  socket (``callback.py``): ``annotate``, ``artifact upload``, ``verb`` and ``message send |
20
23
  list`` (the Channel seam, PRD issue 52) and ``ask`` (a Question, issue 60). They read the
@@ -35,7 +38,7 @@ from collections.abc import Sequence
35
38
  from pathlib import Path
36
39
 
37
40
  from agentic_runner import __version__ as runner_version
38
- from agentic_runner import service, workstation
41
+ from agentic_runner import service, skill, workstation
39
42
  from agentic_runner.callback import (
40
43
  CALLBACK_SOCKET_ENV,
41
44
  CALLBACK_TOKEN_ENV,
@@ -158,6 +161,12 @@ def main(argv: Sequence[str] | None = None) -> int:
158
161
  action="store_true",
159
162
  help="no TLS to Temporal -- a local development server only",
160
163
  )
164
+ install.add_argument(
165
+ "--acp-cli-kinds",
166
+ default=None,
167
+ help="comma-separated cli_kinds served through the pinned ACP bridge (codex_cli, "
168
+ "claude_code); omitted keeps the previous install's, '' turns ACP off",
169
+ )
161
170
  install.add_argument("--no-start", action="store_true", help="write the agent, do not start")
162
171
  install.add_argument("--root", default=None, type=Path, help="state root (default per OS)")
163
172
  for service_verb in ("start", "stop"):
@@ -244,6 +253,18 @@ def main(argv: Sequence[str] | None = None) -> int:
244
253
  "--base", default="", help="`branch` only: the base branch; default the repository's"
245
254
  )
246
255
 
256
+ skill_parser = subcommands.add_parser(
257
+ "skill", help="the setup skill for your own Claude Code or Codex (console-v2 issue 21)"
258
+ )
259
+ skill_parser.add_argument("action", choices=("install", "print"))
260
+ only = skill_parser.add_mutually_exclusive_group()
261
+ only.add_argument(
262
+ "--claude", dest="targets", action="store_const", const=("claude",), help="Claude only"
263
+ )
264
+ only.add_argument(
265
+ "--codex", dest="targets", action="store_const", const=("codex",), help="Codex only"
266
+ )
267
+
247
268
  subcommands.add_parser("config", help="print the resolved configuration (flag > env > file)")
248
269
 
249
270
  arguments = parser.parse_args(argv)
@@ -282,6 +303,13 @@ def main(argv: Sequence[str] | None = None) -> int:
282
303
  for line in lines:
283
304
  print(line)
284
305
  return 0
306
+ if arguments.command == "skill":
307
+ if arguments.action == "print":
308
+ sys.stdout.write(skill.text())
309
+ return 0
310
+ for path in skill.install(arguments.targets or skill.TARGETS):
311
+ print(f"installed {path}")
312
+ return 0
285
313
  if arguments.command == "config":
286
314
  config = load_config(config_file=arguments.config, hooks_path=arguments.hooks_path)
287
315
  print(config.model_dump_json(indent=2))
@@ -342,6 +370,11 @@ def _install(arguments: argparse.Namespace, paths: workstation.OrgPaths) -> int:
342
370
  if not arguments.control_plane:
343
371
  print(f"--control-plane (or {CONTROL_PLANE_ENV}) is required", file=sys.stderr)
344
372
  return 2
373
+ try:
374
+ acp_cli_kinds = _acp_cli_kinds(arguments.acp_cli_kinds, paths)
375
+ except ValueError as error:
376
+ print(str(error), file=sys.stderr)
377
+ return 2
345
378
  # Pasted, never a flag: a flag is in the process table for every uid on the box.
346
379
  token = os.environ.get(AGENT_TOKEN_ENV, "").strip() or getpass.getpass(
347
380
  "Agent Token (yours from /me/runners, or the Organisation Admin's for an "
@@ -354,6 +387,7 @@ def _install(arguments: argparse.Namespace, paths: workstation.OrgPaths) -> int:
354
387
  temporal_tls=not arguments.temporal_plaintext,
355
388
  path=os.environ.get("PATH", ""),
356
389
  install_channel=workstation.install_channel(),
390
+ acp_cli_kinds=acp_cli_kinds,
357
391
  )
358
392
  try:
359
393
  lines = asyncio.run(
@@ -369,6 +403,15 @@ def _install(arguments: argparse.Namespace, paths: workstation.OrgPaths) -> int:
369
403
  return 0
370
404
 
371
405
 
406
+ def _acp_cli_kinds(flag: str | None, paths: workstation.OrgPaths) -> str:
407
+ # A reinstall to pick up a new PATH or retry a token must not turn ACP off (QTS-1355).
408
+ if flag is not None:
409
+ return workstation.parse_acp_cli_kinds(flag)
410
+ if not paths.settings_file.is_file():
411
+ return ""
412
+ return workstation.WorkstationSettings.load(paths).acp_cli_kinds
413
+
414
+
372
415
  def _callback(arguments: argparse.Namespace) -> int:
373
416
  socket_path = os.environ.get(CALLBACK_SOCKET_ENV, "")
374
417
  token = os.environ.get(CALLBACK_TOKEN_ENV, "")
@@ -0,0 +1,58 @@
1
+ """``agentic-runner skill install | print``: the setup skill for the person's own coding CLI.
2
+
3
+ The skill (console-v2 issue 21) is read and run by the person's own Claude Code or Codex,
4
+ so the control plane calls no LLM to onboard anyone (ADR-0010 §4). It ships inside the
5
+ wheel so ``uvx agentic-runner skill install`` needs nothing but PyPI, and the copy a CLI
6
+ reads is always the one that matches the Runner it installs.
7
+ """
8
+
9
+ from __future__ import annotations
10
+
11
+ import os
12
+ from collections.abc import Mapping
13
+ from importlib.resources import files
14
+ from pathlib import Path
15
+ from typing import Literal
16
+
17
+ SKILL_NAME = "agentic-runner-setup"
18
+
19
+ Target = Literal["claude", "codex"]
20
+ TARGETS: tuple[Target, ...] = ("claude", "codex")
21
+
22
+
23
+ def text() -> str:
24
+ return files("agentic_runner").joinpath("skills", SKILL_NAME, "SKILL.md").read_text("utf-8")
25
+
26
+
27
+ def skill_dir(target: Target, *, home: Path, environ: Mapping[str, str]) -> Path:
28
+ """Where each CLI reads a user skill from.
29
+
30
+ Claude Code honours ``CLAUDE_CONFIG_DIR`` for its whole user directory; Codex reads user
31
+ skills from ``~/.agents/skills``, not from ``CODEX_HOME``.
32
+ """
33
+
34
+ if target == "claude":
35
+ root = Path(environ.get("CLAUDE_CONFIG_DIR") or home / ".claude")
36
+ else:
37
+ root = home / ".agents"
38
+ return root / "skills" / SKILL_NAME
39
+
40
+
41
+ def install(
42
+ targets: tuple[Target, ...],
43
+ *,
44
+ home: Path | None = None,
45
+ environ: Mapping[str, str] | None = None,
46
+ ) -> list[Path]:
47
+ written = []
48
+ for target in targets:
49
+ directory = skill_dir(
50
+ target,
51
+ home=home or Path.home(),
52
+ environ=os.environ if environ is None else environ,
53
+ )
54
+ directory.mkdir(parents=True, exist_ok=True)
55
+ path = directory / "SKILL.md"
56
+ path.write_text(text(), encoding="utf-8")
57
+ written.append(path)
58
+ return written
@@ -0,0 +1,148 @@
1
+ ---
2
+ name: agentic-runner-setup
3
+ description: Install and register an Agentic OS Runner on this machine, a Docker host or a Kubernetes cluster, put the Contract's LLM credential in place, and confirm the Runner is online. Use when the person asks to set up, install or connect a Runner, or pastes the prompt from the console's welcome page.
4
+ license: AGPL-3.0-only
5
+ ---
6
+
7
+ # Set up an Agentic OS Runner
8
+
9
+ You help a person who has never run a Runner. A **Runner** is the program that does their
10
+ Agents' work: it runs Codex or Claude Code on a machine they choose and connects out to the
11
+ Agentic OS control plane. It belongs to one **Organisation** (the company the work is for).
12
+
13
+ The install documents in the `qtsone/agentic-runner` repository are the source for every
14
+ command. Do not reconstruct a command from memory: open the section named in each step and
15
+ follow it.
16
+
17
+ ## What the person gives you
18
+
19
+ The prompt they pasted from the console holds these. Ask for any that is missing.
20
+
21
+ - **Organisation**: a short local name for it, lowercase letters, digits and `-` (`acme`).
22
+ - **Control plane URL**: the Runner's `controlPlane.url`, copied from the console's
23
+ Runners page. It is the only control plane address you use, and only as a value you
24
+ pass to the Runner. Never call it or any other control plane address yourself.
25
+ - **Temporal address**: `host:port`, from the same page.
26
+
27
+ The **Agent Token** is not one of them. It is the reusable credential a Runner registers
28
+ with, and the person types it only where the Runner reads it, never into this
29
+ conversation.
30
+
31
+ ## Rules
32
+
33
+ - **Say before you run.** Before each command, show the person the exact command and what
34
+ it does, in one line.
35
+ - **Stop on the first failure.** Show the exact error, link the doc section below that
36
+ covers it, and stop. Do not retry with other flags, do not work around it.
37
+ - **Never ask for the Agent Token or any credential value in this conversation.** Anything
38
+ said here reaches the model vendor and stays in this CLI's history. Every command that
39
+ reads a credential is one the person runs in their own terminal, and the person types the
40
+ value there. If the person pastes a token or a key here anyway, do not use it: tell them
41
+ to revoke it in the console and mint or create a new one.
42
+ - You use only the `agentic-runner` command, the standard tools named in the docs (`uv`,
43
+ `docker`, `kubectl`, `helm`) and read-only checks such as `command -v`.
44
+
45
+ ## Steps
46
+
47
+ ### 1. Look at the machine
48
+
49
+ Run and report, in one short list:
50
+
51
+ ```sh
52
+ uname -sm
53
+ command -v uv python3 docker kubectl helm codex claude
54
+ ```
55
+
56
+ A Runner does its work with **Codex** or **Claude Code** (each is an Agent Runtime). If the
57
+ way the person picks runs on this machine and neither `codex` nor `claude` is found, say
58
+ so and link their install pages
59
+ (https://developers.openai.com/codex/cli, https://code.claude.com/docs/en/setup). You do
60
+ not install them.
61
+
62
+ ### 2. Ask which way
63
+
64
+ Ask the person to pick one, and open the matching document:
65
+
66
+ | Way | When | Document |
67
+ | --- | --- | --- |
68
+ | This machine | A laptop or desktop they use every day. Needs `uv`. | https://github.com/qtsone/agentic-runner/blob/main/docs/workstation.md |
69
+ | Docker | A server or always-on machine with Docker. | https://github.com/qtsone/agentic-runner/blob/main/docs/docker.md |
70
+ | Kubernetes | A cluster they run, with `kubectl` and `helm`. | https://github.com/qtsone/agentic-runner/blob/main/docs/helm.md |
71
+
72
+ Read its "Before you start" section with the person, and stop if something on it is
73
+ missing.
74
+
75
+ ### 3. Install and register
76
+
77
+ **This machine.** Follow
78
+ https://github.com/qtsone/agentic-runner/blob/main/docs/workstation.md#install. Install the
79
+ Runner, then show the person the command that registers it for the Organisation and ask
80
+ them to run it in their own terminal. The Runner asks them for the Agent Token there,
81
+ without showing it:
82
+
83
+ ```sh
84
+ uv tool install agentic-runner
85
+ agentic-runner install <org> --control-plane <control-plane-url> --temporal-address <temporal-address>
86
+ ```
87
+
88
+ You run the first line. Do not run the second yourself: wait until the person says it
89
+ finished, and ask for its last lines of output.
90
+
91
+ **Docker.** Follow
92
+ https://github.com/qtsone/agentic-runner/blob/main/docs/docker.md#the-env-file and then
93
+ https://github.com/qtsone/agentic-runner/blob/main/docs/docker.md#docker-run. Create
94
+ `runner.env` with the URL and the address filled in, and ask the person to add the Agent
95
+ Token line to it themselves.
96
+
97
+ **Kubernetes.** Follow
98
+ https://github.com/qtsone/agentic-runner/blob/main/docs/helm.md#install. The person creates
99
+ the token Secret themselves (the `read -rs` lines in that section). You run the `helm`
100
+ command.
101
+
102
+ `registration refused (…)` means the token is revoked or expired, the Account is on hold,
103
+ or the Organisation has reached its Runner limit: the person mints a new token in the
104
+ console. Other errors: the "Check it" section of the same document.
105
+
106
+ ### 4. Put the Contract's LLM credential in place
107
+
108
+ A **Contract** is the agreement under which the person works for the Organisation. It says
109
+ whose LLM key or subscription the Agents spend. Ask which of the two the Contract uses.
110
+
111
+ - **An API key (a Credential Reference).** A Credential Reference is the name of a secret
112
+ whose value lives only on the Runner. On this machine, ask the person to run, in their own
113
+ terminal, the line from
114
+ https://github.com/qtsone/agentic-runner/blob/main/docs/workstation.md#operate:
115
+
116
+ ```sh
117
+ printf %s "$VALUE" | agentic-runner credential set <org> <reference>
118
+ ```
119
+
120
+ For Docker and Kubernetes the value is delivered from the console, sealed to the Runner:
121
+ https://github.com/qtsone/agentic-runner/blob/main/docs/helm.md#the-recipient-key.
122
+ - **A Codex or Claude Code subscription.** This works only on a Runner the person hosts
123
+ themselves. Send them to the Contract's page in the console and its **Connect** button:
124
+ the console opens the vendor's sign-in in their browser and the Runner finishes it. See
125
+ https://github.com/qtsone/agentic-runner/blob/main/docs/workstation.md#sign-in-to-codex-and-claude-code.
126
+ You have no sign-in of your own to run, and you do not run `codex login` or
127
+ `claude auth login`.
128
+
129
+ ### 5. Wait for the Runner to come online
130
+
131
+ On this machine, run every 10 seconds, for at most 3 minutes:
132
+
133
+ ```sh
134
+ agentic-runner status <org>
135
+ ```
136
+
137
+ The Runner is online when `process` says `running` and `heartbeat` shows a number of
138
+ seconds, not `never`. For Docker and Kubernetes, use the commands in the document's "Check
139
+ it" section. If 3 minutes pass, stop and show the last output and the "Check it" link.
140
+
141
+ ### 6. Report
142
+
143
+ One screen, nothing more:
144
+
145
+ - the way, the Organisation and the Runner's identity line from `status`;
146
+ - whether the LLM credential is in place, or the one thing the person still does in the
147
+ console;
148
+ - the `next` line `status` prints: where in the console to see the Runner and give it work.
@@ -50,6 +50,7 @@ from agentic_runner.host_store import open_workstation_store
50
50
  from agentic_runner.lifecycle import LifecycleOutbox
51
51
  from agentic_runner.registration import load_state
52
52
  from agentic_runner.service import HEARTBEAT_STAMP_NAME, PIDFILE_NAME
53
+ from agentic_runner.workers.acp_runtime import ACP_BRIDGES
53
54
  from agentic_runner_contracts.runner_registration import (
54
55
  CliVersion,
55
56
  EgressPosture,
@@ -75,6 +76,7 @@ __all__ = [
75
76
  "install",
76
77
  "install_channel",
77
78
  "locate_clis",
79
+ "parse_acp_cli_kinds",
78
80
  "require_any_cli",
79
81
  "run_environment",
80
82
  "run_process",
@@ -204,6 +206,9 @@ class WorkstationSettings(BaseModel):
204
206
  # would not find a Homebrew or `~/.local/bin` CLI without it.
205
207
  path: str
206
208
  install_channel: InstallChannel
209
+ # The Runner's `ACP_CLI_KINDS`, kept here rather than hand-added to the login agent,
210
+ # which every `install` rewrites (QTS-1355).
211
+ acp_cli_kinds: str = ""
207
212
 
208
213
  def save(self, paths: OrgPaths) -> None:
209
214
  paths.settings_file.write_text(self.model_dump_json(indent=2), encoding="utf-8")
@@ -218,6 +223,23 @@ class WorkstationSettings(BaseModel):
218
223
  return cls.model_validate_json(paths.settings_file.read_text(encoding="utf-8"))
219
224
 
220
225
 
226
+ def parse_acp_cli_kinds(value: str) -> str:
227
+ """``--acp-cli-kinds``, refused if it names a kind with no pinned bridge.
228
+
229
+ The Runner itself drops such a kind without a word, so a typo here would turn ACP off
230
+ as quietly as the rewritten login agent did (QTS-1355).
231
+ """
232
+
233
+ kinds = [kind.strip() for kind in value.split(",") if kind.strip()]
234
+ unknown = sorted(set(kinds) - ACP_BRIDGES.keys())
235
+ if unknown:
236
+ raise ValueError(
237
+ f"--acp-cli-kinds names {', '.join(unknown)}; a pinned ACP bridge exists only for "
238
+ f"{', '.join(sorted(ACP_BRIDGES))}"
239
+ )
240
+ return ",".join(dict.fromkeys(kinds))
241
+
242
+
221
243
  def install_channel(
222
244
  platform: str = sys.platform, *, executable: str = sys.executable
223
245
  ) -> InstallChannel:
@@ -235,7 +257,7 @@ def run_environment(paths: OrgPaths, settings: WorkstationSettings) -> dict[str,
235
257
  a unit, a task -- stays a one-liner, and the three OSes cannot drift apart.
236
258
  """
237
259
 
238
- return {
260
+ environment = {
239
261
  "PATH": settings.path,
240
262
  "AGENTIC_RUNNER_STATE_DIR": str(paths.state_dir),
241
263
  "AGENTIC_RUNNER_WORKSPACE_ROOT": str(paths.workspaces),
@@ -254,6 +276,11 @@ def run_environment(paths: OrgPaths, settings: WorkstationSettings) -> dict[str,
254
276
  # port inherited from a pod-style environment would collide on the second one.
255
277
  service.READINESS_PORT_ENV: "0",
256
278
  }
279
+ # Only when set: a login agent hand-edited with `ACP_CLI_KINDS` before QTS-1355 keeps
280
+ # ACP on across an upgrade until its next `install`.
281
+ if settings.acp_cli_kinds:
282
+ environment["ACP_CLI_KINDS"] = settings.acp_cli_kinds
283
+ return environment
257
284
 
258
285
 
259
286
  # ------------------------------------------------------------------ the CLIs
File without changes