leos-agent 10.7.0 → 10.7.1
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.
- package/README.md +12 -9
- package/package.json +1 -1
- package/scripts/dispatch_guard.py +12 -1
- package/scripts/dispatch_log.py +18 -1
- package/scripts/usage_scan.py +11 -1
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# leos-agent
|
|
2
2
|
|
|
3
|
-
Leo's portable agent operating policy, version **10.7.
|
|
3
|
+
Leo's portable agent operating policy, version **10.7.1**, installable on Claude
|
|
4
4
|
Code, Codex, Cursor, Hermes, Pi, and OpenCode through each harness's own plugin
|
|
5
5
|
system.
|
|
6
6
|
|
|
@@ -119,7 +119,7 @@ their global instruction file.
|
|
|
119
119
|
The payload has always said that a subagent dispatch must name a model. Prose
|
|
120
120
|
alone did not hold: a forgotten dispatch inherits the parent's expensive model
|
|
121
121
|
and pays a cold cache write per child, which is the single most expensive shape
|
|
122
|
-
this policy has. From 10.7.
|
|
122
|
+
this policy has. From 10.7.1 that half of the rule is enforced by a hook instead,
|
|
123
123
|
and the prose it replaced came out of the always-loaded payload — enforcement in
|
|
124
124
|
code costs **zero** context per turn, so the guard paid for itself in bytes
|
|
125
125
|
before saving a cent.
|
|
@@ -134,6 +134,9 @@ can name one. Three ways to comply, all of them one word:
|
|
|
134
134
|
| a generic agent, no model | `subagent_type: "leo-executor"` | an approved plan, a well-specified change |
|
|
135
135
|
| a generic agent, no model | `model: "<name>"` | investigation and debugging — naming it *is* the stated reason |
|
|
136
136
|
|
|
137
|
+
A plugin install namespaces the type — dispatch `leos-agent:leo-runner` rather
|
|
138
|
+
than `leo-runner`; the guard accepts either.
|
|
139
|
+
|
|
137
140
|
It never picks a model for you. It cannot force cheap work onto an expensive
|
|
138
141
|
problem, so it cannot cause a quality regression — only an explicit choice. It
|
|
139
142
|
is also deliberately narrow: a false block costs one re-dispatch, while a caught
|
|
@@ -158,7 +161,7 @@ LEOS_AGENT_DISPATCH_LOG_PROMPTS=1 debug only: keep 200 chars of brief text in t
|
|
|
158
161
|
Hermes and OpenCode are wired with the same policy but their dispatch argument
|
|
159
162
|
shapes are unconfirmed; there the guard is best-effort and no-ops rather than
|
|
160
163
|
misfires. Pi has no hook surface and gets nothing. **Codex hash-pins hooks**, so
|
|
161
|
-
upgrading to 10.7.
|
|
164
|
+
upgrading to 10.7.1 — and every later edit to the guard — needs re-approval
|
|
162
165
|
through `/hooks` there. Until you do, Codex silently enforces nothing; zero Codex
|
|
163
166
|
rows in the report is the symptom.
|
|
164
167
|
|
|
@@ -187,7 +190,7 @@ gets it through its global instruction file, written by
|
|
|
187
190
|
[`scripts/leo-install.py`](scripts/leo-install.py) into a marker block:
|
|
188
191
|
|
|
189
192
|
```
|
|
190
|
-
<leos-agent version="10.7.
|
|
193
|
+
<leos-agent version="10.7.1">
|
|
191
194
|
...the payload...
|
|
192
195
|
</leos-agent>
|
|
193
196
|
```
|
|
@@ -256,7 +259,7 @@ that it is already installed and changes nothing.
|
|
|
256
259
|
Run the installer's uninstall first, while the script is still on disk:
|
|
257
260
|
|
|
258
261
|
```bash
|
|
259
|
-
python3 ~/.claude/plugins/cache/leos-agent/leos-agent/10.7.
|
|
262
|
+
python3 ~/.claude/plugins/cache/leos-agent/leos-agent/10.7.1/scripts/leo-install.py claude --uninstall
|
|
260
263
|
```
|
|
261
264
|
|
|
262
265
|
```bash
|
|
@@ -287,7 +290,7 @@ Then run the `install` skill in a Codex session (`$leos-agent`, then `install`),
|
|
|
287
290
|
run the script directly:
|
|
288
291
|
|
|
289
292
|
```bash
|
|
290
|
-
python3 ~/.codex/plugins/cache/leos-agent/leos-agent/10.7.
|
|
293
|
+
python3 ~/.codex/plugins/cache/leos-agent/leos-agent/10.7.1/scripts/leo-install.py codex
|
|
291
294
|
```
|
|
292
295
|
|
|
293
296
|
This writes `~/.codex/AGENTS.md` and installs two economical agents:
|
|
@@ -312,7 +315,7 @@ threads only. Re-adding an already-installed plugin is idempotent.
|
|
|
312
315
|
**Uninstall**
|
|
313
316
|
|
|
314
317
|
```bash
|
|
315
|
-
python3 ~/.codex/plugins/cache/leos-agent/leos-agent/10.7.
|
|
318
|
+
python3 ~/.codex/plugins/cache/leos-agent/leos-agent/10.7.1/scripts/leo-install.py codex --uninstall
|
|
316
319
|
```
|
|
317
320
|
|
|
318
321
|
```bash
|
|
@@ -461,7 +464,7 @@ Pinned refs are reconciled, never silently advanced — to move to a new tag,
|
|
|
461
464
|
install it explicitly:
|
|
462
465
|
|
|
463
466
|
```bash
|
|
464
|
-
pi install git:github.com/foxhatleo/leos-agent@v10.7.
|
|
467
|
+
pi install git:github.com/foxhatleo/leos-agent@v10.7.1
|
|
465
468
|
```
|
|
466
469
|
|
|
467
470
|
Re-run `/skill:install` afterwards.
|
|
@@ -681,7 +684,7 @@ claude plugin uninstall leos-agent@leos-agent && claude plugin install leos-agen
|
|
|
681
684
|
```
|
|
682
685
|
|
|
683
686
|
or replace the cachebuster suffix in the Codex manifest with one in the form
|
|
684
|
-
`10.7.
|
|
687
|
+
`10.7.1+codex.local-YYYYMMDD-HHMMSS` and re-add. Either way, plugin changes only
|
|
685
688
|
reach a **new** session or thread.
|
|
686
689
|
|
|
687
690
|
`--check` exits non-zero when a file is out of date, and `--force` replaces a
|
package/package.json
CHANGED
|
@@ -192,11 +192,21 @@ def routable(name):
|
|
|
192
192
|
return False
|
|
193
193
|
|
|
194
194
|
|
|
195
|
+
def _is_tier(agent):
|
|
196
|
+
try:
|
|
197
|
+
from dispatch_log import is_tier
|
|
198
|
+
return is_tier(agent)
|
|
199
|
+
except Exception:
|
|
200
|
+
# Never let a missing sibling module turn a compliant dispatch into a
|
|
201
|
+
# refusal: fall back to allowing anything that looks like a tier.
|
|
202
|
+
return bool(agent) and agent.rsplit(":", 1)[-1].startswith("leo-")
|
|
203
|
+
|
|
204
|
+
|
|
195
205
|
def decide(dispatch, name, is_routable):
|
|
196
206
|
"""(action, reason). The entire policy, and deliberately four lines of it."""
|
|
197
207
|
if dispatch is None:
|
|
198
208
|
return ALLOW, "not-a-dispatch"
|
|
199
|
-
if dispatch.agent
|
|
209
|
+
if _is_tier(dispatch.agent):
|
|
200
210
|
return ALLOW, "leo-tier" # the agent definition carries the model
|
|
201
211
|
if dispatch.model:
|
|
202
212
|
return ALLOW, "explicit-model" # a choice was typed; cheap or not
|
|
@@ -212,6 +222,7 @@ def render_block(dispatch):
|
|
|
212
222
|
"silently inherit the parent's. Re-dispatch with one of:\n"
|
|
213
223
|
' subagent_type "leo-runner" reading, search, tests, logs, codemods, fan-out\n'
|
|
214
224
|
' subagent_type "leo-executor" an approved plan or a specified change\n'
|
|
225
|
+
' (a plugin install namespaces these: "leos-agent:leo-runner")\n'
|
|
215
226
|
' model: "<name>" investigation/debugging - naming it IS the reason\n'
|
|
216
227
|
"Set LEOS_AGENT_DISPATCH_GUARD=off to disable, =warn to log only."
|
|
217
228
|
) % dispatch.agent
|
package/scripts/dispatch_log.py
CHANGED
|
@@ -47,6 +47,23 @@ MAX_BYTES = 1 << 20
|
|
|
47
47
|
RECORD_VERSION = 1
|
|
48
48
|
|
|
49
49
|
|
|
50
|
+
TIER_PREFIX = "leo-"
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def is_tier(agent):
|
|
54
|
+
"""Is this one of the economical-tier agents?
|
|
55
|
+
|
|
56
|
+
Match the bare name after any namespace. A plugin install namespaces the
|
|
57
|
+
type -- Claude Code dispatches `leos-agent:leo-runner`, not `leo-runner` --
|
|
58
|
+
and a raw prefix test rejects that, which blocked the exact path the refusal
|
|
59
|
+
message recommends. That is the worst false positive this guard can have, so
|
|
60
|
+
the one place that decides it is shared rather than repeated.
|
|
61
|
+
"""
|
|
62
|
+
if not agent:
|
|
63
|
+
return False
|
|
64
|
+
return agent.rsplit(":", 1)[-1].rsplit("/", 1)[-1].startswith(TIER_PREFIX)
|
|
65
|
+
|
|
66
|
+
|
|
50
67
|
def path():
|
|
51
68
|
return os.path.join(_data_root(), LOG_NAME)
|
|
52
69
|
|
|
@@ -149,7 +166,7 @@ def summarise(entries):
|
|
|
149
166
|
tiers = collections.Counter()
|
|
150
167
|
for entry in entries:
|
|
151
168
|
agent = entry.get("agent") or ""
|
|
152
|
-
if agent
|
|
169
|
+
if is_tier(agent):
|
|
153
170
|
tiers[agent] += 1
|
|
154
171
|
elif entry.get("model"):
|
|
155
172
|
tiers["explicit model"] += 1
|
package/scripts/usage_scan.py
CHANGED
|
@@ -336,6 +336,16 @@ def scan_guard():
|
|
|
336
336
|
return {"error": "%s: %s" % (type(exc).__name__, exc)}
|
|
337
337
|
|
|
338
338
|
|
|
339
|
+
def _is_tier(agent):
|
|
340
|
+
"""Shared with the guard, so the report cannot disagree with enforcement
|
|
341
|
+
about what counts as a tier -- namespaced plugin types included."""
|
|
342
|
+
try:
|
|
343
|
+
import dispatch_log
|
|
344
|
+
return dispatch_log.is_tier(agent)
|
|
345
|
+
except Exception:
|
|
346
|
+
return bool(agent) and agent.rsplit(":", 1)[-1].startswith("leo-")
|
|
347
|
+
|
|
348
|
+
|
|
339
349
|
def routing_compliance(dispatches):
|
|
340
350
|
"""Problem (c), quantified: how many dispatches named a tier, and how many
|
|
341
351
|
let the parent's model ride along."""
|
|
@@ -343,7 +353,7 @@ def routing_compliance(dispatches):
|
|
|
343
353
|
inherited_bytes = 0
|
|
344
354
|
for d in dispatches:
|
|
345
355
|
agent = d.get("agent") or "-"
|
|
346
|
-
if agent
|
|
356
|
+
if _is_tier(agent):
|
|
347
357
|
tiers[agent] += 1
|
|
348
358
|
elif d.get("model"):
|
|
349
359
|
tiers["explicit model"] += 1
|