v6.0.0 made caller mandatory on every invocation of run_automation_prompt. That was the right call for scripts living inside the repo — each one deserves a deliberate tier that a reviewer can see. It was the wrong call for scripts that live in the operator's ~/.nexo/scripts/ tree: they cannot edit the public repo, so every one of them silently collapsed to the generic agent_run/generic tier and lost its per-script identity. v6.0.2 closes that gap without opening a backdoor to the registry.
The new contract
Any caller whose id starts with the reserved prefix personal/ now bypasses the registry entirely. The resolver evaluates in this order and returns the first valid hit:
explicit_tier(new keyword-only argument on the resolver;tier="..."kwarg on the public wrappers).user_defaultpassed explicitly (rare; mostly internal).preferences.default_resonanceread frombrain/calibration.jsonon disk.DEFAULT_RESONANCE—"alto"since v5.9.0.
Invalid tier values (typos, unknown strings) are treated as "no hint" and the resolver falls through to the next step. There is no silent crash, and there is no silent promotion — a personal/ caller without any of the four signals always ends at alto.
The wrappers picked up tier everywhere
The new kwarg propagates through every public surface a personal script might touch:
run_automation_prompt(..., tier="alto")run_automation_interactive(..., tier="alto")nexo_helper.run_automation_text(..., caller="personal/foo", tier="alto")nexo_helper.run_automation_json(..., caller="personal/foo", tier="alto")nexo-agent-run.py --caller personal/foo --tier alto
If a script needs a direct effort override (the xhigh / max buckets without the tier semantics) it can still pass reasoning_effort= as before — that argument still wins, because the whole tier system is a convenience over the raw effort string.
Backcompat
| Caller shape | v6.0.0 behaviour | v6.0.2 behaviour |
|---|---|---|
nexo_chat (USER_FACING) | user_default or DEFAULT | unchanged |
deep-sleep/extract (SYSTEM_OWNED) | pinned tier | unchanged |
unknown/caller | UnregisteredCallerError | unchanged |
personal/anything | UnregisteredCallerError | resolves via new precedence |
The guide
Because the new prefix exists specifically so a NEXO session can help an operator author a new personal script, v6.0.2 ships docs/personal-scripts-guide.md. It covers:
- When to use the prefix (and when NOT to — core callers still register).
- How to pick a tier based on reasoning demand, not cost.
- Anti-patterns: hardcoded model strings, missing prefix, leaking
agent_run/generic. - How to test against a scratch
NEXO_HOME=/tmp/nexo-test-$(date +%s). - Where to look when the guide goes out of date (
src/resonance_map.py::resolve_tier_for_caller).
Tests
Three new pytest modules cover every path: test_personal_caller_prefix.py (the four-step precedence, typo rejection, registered-caller backcompat, and the resolve_model_and_effort passthrough), test_run_automation_prompt_tier_kwarg.py (tier resolution at the agent_runner layer and signature guard), and test_nexo_agent_run_tier_flag.py (CLI flag propagation). Full suite: 1079 passed, 1 skipped.