leos-agent 10.2.0 → 10.6.0

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.
@@ -23,7 +23,14 @@ import sys
23
23
  import tempfile
24
24
  from pathlib import Path
25
25
 
26
- HARNESSES = ("claude", "codex", "cursor", "hermes", "pi", "opencode")
26
+ sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
27
+ import routing # noqa: E402 owns the harness list and the machine-local model config
28
+
29
+ HARNESSES = routing.HARNESSES
30
+
31
+ # The routing region inside the payload, replaced per harness at install time.
32
+ ROUTING_OPEN = "<!-- leos-agent:routing -->"
33
+ ROUTING_CLOSE = "<!-- /leos-agent:routing -->"
27
34
 
28
35
  OPEN_RE = re.compile(r"^<leos-agent\b[^>]*>[ \t]*$", re.MULTILINE)
29
36
  CLOSE_RE = re.compile(r"^</leos-agent>[ \t]*$", re.MULTILINE)
@@ -36,10 +43,18 @@ CODEX_SOFT_CAP = 28 * 1024
36
43
  # copies apart from a file the user happens to have put at the same path.
37
44
  PROVENANCE = "leos-agent"
38
45
 
46
+ # Skill and command text refers to scripts as <plugin-root>/scripts/… and the
47
+ # model resolves the root once. OpenCode copies live apart from the scripts,
48
+ # and OpenCode sets no resolution env var, so their copies are installed with
49
+ # the token already replaced by this machine's absolute plugin root. Installing
50
+ # alternately from a checkout and a cache re-bakes the root each time — last
51
+ # install wins, and --check reports a stale root as out of date.
52
+ PLUGIN_ROOT_TOKEN = "<plugin-root>"
53
+
39
54
  # OpenCode plugins cannot register skills or commands from JS, so these are
40
55
  # copied to disk instead. check.py asserts every file they name carries
41
56
  # PROVENANCE, without which the installer would refuse to upgrade its own copy.
42
- OPENCODE_SKILLS = ("doctor", "review-pr", "handoff", "handon")
57
+ OPENCODE_SKILLS = ("doctor", "review-pr", "handoff", "handon", "tune-routing")
43
58
  OPENCODE_COMMANDS = ("review-pr", "handoff", "handon")
44
59
 
45
60
  # Codex plugins cannot package custom agent definitions directly, so these are
@@ -100,19 +115,71 @@ def read_version(root):
100
115
  sys.exit(f"leo-install: {manifest} has no version field")
101
116
 
102
117
 
103
- def payload_body(root):
104
- """The canonical payload: rules/preferences.md with its frontmatter stripped."""
118
+ def render_routing(body, harness, config):
119
+ """Replace the routing region with the stanza for this machine's config.
120
+
121
+ The payload ships with a default inside the region, so an un-rendered read of
122
+ rules/preferences.md -- Cursor's plugin-delivered rule, a human opening the
123
+ file -- still says something true. Rendering only ever narrows it to the one
124
+ harness being installed, which is why the installed payload is smaller than
125
+ the file on disk rather than larger.
126
+ """
127
+ start = body.find(ROUTING_OPEN)
128
+ end = body.find(ROUTING_CLOSE)
129
+ if start < 0 or end < start:
130
+ sys.exit(f"leo-install: rules/preferences.md is missing its {ROUTING_OPEN} region")
131
+ return body[:start] + routing.stanza(harness, config) + body[end + len(ROUTING_CLOSE):]
132
+
133
+
134
+ def payload_body(root, harness=None, config=None):
135
+ """The canonical payload: rules/preferences.md with its frontmatter stripped.
136
+
137
+ With a harness, the routing region is rendered for it; without one the region
138
+ keeps its shipped default, markers and all.
139
+ """
105
140
  text = (root / "rules" / "preferences.md").read_text(encoding="utf-8")
106
141
  body = re.sub(r"(?s)\A---\n.*?\n---\n", "", text, count=1).strip()
107
142
  if not body:
108
143
  sys.exit("leo-install: rules/preferences.md has no body below its frontmatter")
109
144
  if OPEN_RE.search(body) or CLOSE_RE.search(body):
110
145
  sys.exit("leo-install: rules/preferences.md contains a <leos-agent> marker; it must not")
146
+ if harness:
147
+ body = render_routing(body, harness, config if config is not None else routing.load())
111
148
  return body
112
149
 
113
150
 
114
- def build_block(root):
115
- return f'<leos-agent version="{read_version(root)}">\n{payload_body(root)}\n</leos-agent>\n'
151
+ def build_block(root, harness=None, config=None):
152
+ return f'<leos-agent version="{read_version(root)}">\n{payload_body(root, harness, config)}\n</leos-agent>\n'
153
+
154
+
155
+ def render_codex_agent(text, agent_name, config):
156
+ """Substitute a Codex profile's model, leaving the shipped default when unset."""
157
+ entry = routing.profile(config, "codex", agent_name.split("-", 1)[1])
158
+ if not entry:
159
+ return text
160
+ text = re.sub(r'(?m)^model = ".*"$', f'model = "{entry["model"]}"', text, count=1)
161
+ if entry["effort"]:
162
+ text = re.sub(
163
+ r'(?m)^model_reasoning_effort = ".*"$',
164
+ f'model_reasoning_effort = "{entry["effort"]}"',
165
+ text,
166
+ count=1,
167
+ )
168
+ return text
169
+
170
+
171
+ def cursor_routing_rule(harness, config):
172
+ """Cursor reads its rules straight out of the plugin, so the per-machine half
173
+ has to arrive as its own always-applied rule file."""
174
+ return (
175
+ "---\n"
176
+ "description: leos-agent model routing for this machine.\n"
177
+ "alwaysApply: true\n"
178
+ "---\n"
179
+ "This supersedes the model-routing dispatch line in Leo's agent operating\n"
180
+ "preferences:\n\n"
181
+ f"{routing.stanza(harness, config)}\n"
182
+ )
116
183
 
117
184
 
118
185
  def scan_markers(text):
@@ -124,16 +191,19 @@ def scan_markers(text):
124
191
  into a permanent "two blocks" error, so fenced regions are skipped.
125
192
  """
126
193
  opens, closes = [], []
127
- fence = None
194
+ fence = None # (char, run length) of the currently open fence
128
195
  offset = 0
129
196
  for line in text.splitlines(keepends=True):
130
197
  stripped = line.lstrip()
131
- marker = stripped[:3]
132
- if marker in ("```", "~~~"):
133
- token = marker
198
+ run = re.match(r"(`{3,}|~{3,})", stripped)
199
+ if run:
200
+ token = run.group(1)
134
201
  if fence is None:
135
- fence = token
136
- elif fence == token:
202
+ fence = (token[0], len(token))
203
+ # CommonMark: only a run of the same character at least as long as
204
+ # the opener closes a fence; a shorter run is fence content, so a
205
+ # ``` line inside a ```` example must not end the example.
206
+ elif fence[0] == token[0] and len(token) >= fence[1]:
137
207
  fence = None
138
208
  elif fence is None:
139
209
  if OPEN_RE.match(line.rstrip("\n")):
@@ -283,11 +353,16 @@ def install_markdown(path, block, args, label, create=True):
283
353
  return write_if_changed(path, inject(current, block), current, existed, crlf, args, label)
284
354
 
285
355
 
286
- def install_file_copy(src, dest, args, label, owned_parent=False):
287
- """Install a payload file the harness's plugin system cannot deliver itself."""
356
+ def install_file_copy(src, dest, args, label, owned_parent=False, payload=None):
357
+ """Install a payload file the harness's plugin system cannot deliver itself.
358
+
359
+ `payload` overrides the source text for files rendered from the machine's
360
+ routing config rather than copied verbatim.
361
+ """
288
362
  dest = dest.expanduser()
289
363
  existed = dest.is_file()
290
- payload = src.read_text(encoding="utf-8")
364
+ if payload is None:
365
+ payload = src.read_text(encoding="utf-8")
291
366
  current = dest.read_text(encoding="utf-8") if existed else ""
292
367
 
293
368
  # Never clobber or delete a same-named file this tool did not put there.
@@ -307,8 +382,27 @@ def install_file_copy(src, dest, args, label, owned_parent=False):
307
382
  return write_if_changed(dest, payload, current, existed, False, args, label)
308
383
 
309
384
 
385
+ def opencode_payload(src, root, rename_install=False):
386
+ """An OpenCode copy's content: the plugin root baked in, optionally renamed.
387
+
388
+ OpenCode reads the copies out of ~/.config/opencode, far from the scripts
389
+ they invoke, and sets none of the resolution env vars — so the placeholder
390
+ is resolved here, at install time, where the root is known for certain.
391
+ The install skill is additionally renamed to match the leo-install/
392
+ directory it is copied into, keeping directory and frontmatter in
393
+ agreement whichever one OpenCode keys on.
394
+ """
395
+ text = src.read_text(encoding="utf-8").replace(PLUGIN_ROOT_TOKEN, str(root))
396
+ if rename_install:
397
+ text = re.sub(r"(?m)^name:\s*install\s*$", "name: leo-install", text, count=1)
398
+ return text
399
+
400
+
310
401
  def run(harness, root, args):
311
- block = build_block(root)
402
+ # Read once per run: rendering has to be a pure function of (version, config)
403
+ # or a second install would not come back "unchanged".
404
+ config = routing.load()
405
+ block = build_block(root, harness, config)
312
406
  home = Path.home()
313
407
  targets = []
314
408
 
@@ -328,22 +422,43 @@ def run(harness, root, args):
328
422
  home / ".codex" / "agents" / f"{n}.toml",
329
423
  args,
330
424
  l,
425
+ payload=render_codex_agent(
426
+ (root / "payload" / "codex-agents" / f"{n}.toml").read_text(encoding="utf-8"),
427
+ n,
428
+ config,
429
+ ),
331
430
  ),
332
431
  )
333
432
  )
334
433
 
335
434
  elif harness == "cursor":
336
- targets.append(
337
- (
338
- "cursor",
339
- lambda: Result(
340
- "cursor",
341
- "skipped",
342
- "Cursor has no on-disk global rules file; the plugin's alwaysApply rule delivers the payload natively",
343
- ),
344
- )
435
+ # The payload itself arrives natively through the plugin's alwaysApply
436
+ # rule, which is the file in the plugin directory -- so there is nothing
437
+ # per-machine in it. Only the routing stanza needs installing, and only
438
+ # as its own rule -- and only when something is actually configured: an
439
+ # unconfigured rule would restate the payload's default, an always-loaded
440
+ # no-op that costs context on every turn.
441
+ label = "~/.cursor/rules/leos-agent-routing.mdc"
442
+ dest = home / ".cursor" / "rules" / "leos-agent-routing.mdc"
443
+ configured = bool(
444
+ routing.profile(config, "cursor", "runner") or routing.profile(config, "cursor", "executor")
345
445
  )
346
446
 
447
+ def cursor_rule_target(l=label):
448
+ if args.uninstall or configured:
449
+ return install_file_copy(None, dest, args, l, payload=cursor_routing_rule(harness, config))
450
+ # Unconfigured install: write nothing, and take back a stale rule a
451
+ # previous config left behind -- but only one that is provably ours.
452
+ if not dest.is_file():
453
+ return Result(l, "skipped", "no routing configured for cursor")
454
+ if PROVENANCE not in dest.read_text(encoding="utf-8"):
455
+ return Result(l, "skipped", "no routing configured; leaving the unrelated file at this path")
456
+ if args.writes:
457
+ dest.unlink()
458
+ return Result(l, "removed", "no routing configured; stale rule removed")
459
+
460
+ targets.append((label, cursor_rule_target))
461
+
347
462
  elif harness == "hermes":
348
463
  # Never create SOUL.md: Hermes writes its own starter identity file on
349
464
  # first run, and pre-empting that would fight the bootstrap.
@@ -374,6 +489,9 @@ def run(harness, root, args):
374
489
  args,
375
490
  skill_label,
376
491
  owned_parent=True,
492
+ payload=opencode_payload(
493
+ root / "skills" / "install" / "SKILL.md", root, rename_install=True
494
+ ),
377
495
  ),
378
496
  )
379
497
  )
@@ -394,6 +512,7 @@ def run(harness, root, args):
394
512
  args,
395
513
  l,
396
514
  owned_parent=True,
515
+ payload=opencode_payload(s, root),
397
516
  ),
398
517
  )
399
518
  )
@@ -407,6 +526,7 @@ def run(harness, root, args):
407
526
  args,
408
527
  l,
409
528
  owned_parent=True,
529
+ payload=opencode_payload(root / "skills" / n / "SKILL.md", root),
410
530
  ),
411
531
  )
412
532
  )
@@ -420,6 +540,7 @@ def run(harness, root, args):
420
540
  cfg / "commands" / f"{n}.md",
421
541
  args,
422
542
  l,
543
+ payload=opencode_payload(root / "commands" / f"{n}.md", root),
423
544
  ),
424
545
  )
425
546
  )
@@ -8,6 +8,7 @@ regressions remain visible without a tokenizer or network access.
8
8
  """
9
9
 
10
10
  import argparse
11
+ import importlib.util
11
12
  import json
12
13
  import re
13
14
  import sys
@@ -19,9 +20,19 @@ ROOT = Path(__file__).resolve().parent.parent
19
20
  # the before/after output in the change that raises it.
20
21
  LIMITS = {
21
22
  "global_policy_bytes": 4_500,
23
+ # What an unconfigured machine actually installs. Rendering the routing region
24
+ # per harness dropped this below the old whole-file figure of 4497, and it must
25
+ # stay there: the model config exists to save money, so it may not cost
26
+ # always-loaded bytes to have. A configured harness exceeds this only by the
27
+ # length of the model names chosen, which is bounded and deliberate.
28
+ "rendered_policy_bytes": 4_497,
22
29
  "codex_implicit_skill_metadata_bytes": 600,
23
30
  "claude_implicit_skill_metadata_bytes": 800,
24
31
  "codex_agent_description_bytes": 550,
32
+ "claude_agent_description_bytes": 550,
33
+ # Command descriptions are listed alongside skills in Claude Code and Cursor,
34
+ # so they are always-loaded context on the same terms as skill metadata.
35
+ "command_description_bytes": 400,
25
36
  "review_dispatch_bytes": 3_500,
26
37
  }
27
38
 
@@ -69,6 +80,20 @@ def agent_description(path):
69
80
  return match.group(1)
70
81
 
71
82
 
83
+ def rendered_policy():
84
+ """The installed payload body per harness, with no routing config present.
85
+
86
+ This is what a session actually loads -- rules/preferences.md on disk keeps a
87
+ harness-neutral default in its routing region, and the installer narrows it to
88
+ one harness. Measured with the config forced empty so the number is a property
89
+ of the repository, not of whoever runs it.
90
+ """
91
+ spec = importlib.util.spec_from_file_location("leo_install_measure", ROOT / "scripts" / "leo-install.py")
92
+ installer = importlib.util.module_from_spec(spec)
93
+ spec.loader.exec_module(installer)
94
+ return {h: byte_len(installer.payload_body(ROOT, h, {})) for h in installer.HARNESSES}
95
+
96
+
72
97
  def measurements():
73
98
  portable = sorted((ROOT / "skills").glob("*/SKILL.md"))
74
99
  claude_only = sorted((ROOT / "skills-claude").glob("*/SKILL.md"))
@@ -77,11 +102,26 @@ def measurements():
77
102
  review_fm, review_body = frontmatter(ROOT / "skills" / "review-pr" / "SKILL.md")
78
103
  del review_fm
79
104
  agent_paths = sorted((ROOT / "payload" / "codex-agents").glob("*.toml"))
105
+ # Claude Code lists every plugin agent's name and description in the parent's
106
+ # always-loaded agent roster, so they are part of the static footprint too.
107
+ claude_agent_paths = sorted((ROOT / "agents").glob("*.md"))
108
+ claude_agent_bytes = 0
109
+ for path in claude_agent_paths:
110
+ fm, _ = frontmatter(path)
111
+ claude_agent_bytes += byte_len(field(fm, "name")) + byte_len(field(fm, "description"))
112
+ command_paths = sorted((ROOT / "commands").glob("*.md")) + sorted((ROOT / "commands-claude").glob("*.md"))
113
+ command_bytes = 0
114
+ for path in command_paths:
115
+ fm, _ = frontmatter(path)
116
+ command_bytes += byte_len(field(fm, "description"))
80
117
  return {
81
118
  "global_policy_bytes": byte_len(policy_body.strip()),
119
+ "rendered_policy_bytes": max(rendered_policy().values()),
82
120
  "codex_implicit_skill_metadata_bytes": skill_metadata_bytes(portable, codex_implicit),
83
121
  "claude_implicit_skill_metadata_bytes": skill_metadata_bytes(portable + claude_only, claude_implicit),
84
122
  "codex_agent_description_bytes": sum(byte_len(agent_description(path)) for path in agent_paths),
123
+ "claude_agent_description_bytes": claude_agent_bytes,
124
+ "command_description_bytes": command_bytes,
85
125
  "review_dispatch_bytes": byte_len(review_body.strip()),
86
126
  }
87
127
 
@@ -99,6 +139,9 @@ def main(argv=None):
99
139
  print("Static prompt footprint (bytes; tokens are roughly bytes / 4 for this prose)")
100
140
  for name, value in values.items():
101
141
  print(f" {name:38} {value:5} limit {LIMITS[name]:5}")
142
+ print(" rendered_policy_bytes is the worst case across harnesses; each one installs:")
143
+ for harness, value in sorted(rendered_policy().items()):
144
+ print(f" {harness:38} {value:5}")
102
145
  print("This excludes conversation history, tool output, cache effects, and subagent work.")
103
146
 
104
147
  over = {name: (value, LIMITS[name]) for name, value in values.items() if value > LIMITS[name]}