-
feat(tier3): ratatoskr.tier3 module + CLI (v0.7.0)
released this
2026-05-24 20:31:10 -07:00 | 251 commits to main since this releaseIssue #15. Worldtree Phase 2.0 ships Tier 3 (consumer-defined)
agents at<user_id>:<agent_name>; ratatoskr now exposes their
lifecycle via a dedicated module + CLI tool. The picker handles
the colon-containing agent_id generically (per issue #8 out-of-
scope clause); session creation works unchanged. What was missing
was a way to DEFINE / PATCH / DELETE these agents from ratatoskr
itself — operators previously had to curl the API directly.Public surface (ratatoskr.tier3)
Tier3AgentInfo (frozen dataclass)
define_agent (client, *, agent_name, system_prompt, model) → Info
patch_agent (client, agent_id, *, system_prompt?, model?) → Info
delete_agent (client, agent_id) → NoneTier3QuotaExceeded — 429 agent_quota_exceeded (50-agent cap)
Tier3UserIdUnsupported — 403 tier3_user_id_unsupported
Tier3FieldNotMutable — 422 field_not_mutable (PATCH)
Tier3LayerDeferred — 422 layer_deferred (define, defense-only)
Tier3AgentNotFound — 404
SessionApiFailed (reused) — all other non-2xxCaller-owned httpx.AsyncClient posture (same as ratatoskr.sessions).
Module is standalone — does NOT import sessions/sse_client/tui/cli
beyond reusing the USER_AGENT constant from cli.CLI (python -m ratatoskr.tier3 )
define --name --system-prompt --model
patch <agent_id> [--system-prompt ] [--model ]
delete <agent_id>Auth resolution mirrors ratatoskr.cli verbatim — --api-key flag >
$WORLDTREE_API_KEY > exit 11. Server URL via --server >
$WORLDTREE_API_URL > http://localhost:8000. Exit codes follow the
cli.py matrix: 0 / 10 (usage) / 11 (auth) / 20 (api-failure) / 21
(network).Real-world finding from live smoke
Tier-3 agents do NOT appear in
GET /agents— the public list
filters them out. The picker won't surface tier-3 agents; operators
bypass it viaratatoskr --send "..." --new --agent ratatoskr:<n>
directly. This contradicts the contract's acceptance assumption
("the new tier-3 agent should appear in the list") — caught at
smoke time. The picker integration was hopeful; the real shape is
"you know your tier-3 agent_id because you defined it." Adding a
ratatoskr-sidetier3 listsubcommand would need a Worldtree
endpoint that doesn't exist today; surfacing to worldtree-dev as a
followup.Live lifecycle smoke (personal Worldtree v0.16.2)
$ python -m ratatoskr.tier3 define --name smoke-tier3
--system-prompt "..." --model qwen3.6-35-a3b
→ defined ratatoskr:smoke-tier3 (qwen3.6-35-a3b)$ ratatoskr --send "hello via tier-3" --new --agent ratatoskr:smoke-tier3
→ [done] turn_id=286 model=qwen3.6-35-a3b duration=14.2s
usage 44 in → 390 out (434 total, 0 cached)$ python -m ratatoskr.tier3 delete ratatoskr:smoke-tier3
→ deleted ratatoskr:smoke-tier3$ python -m ratatoskr.tier3 delete ratatoskr:smoke-tier3
→ [agent_not_found] ratatoskr:smoke-tier3 (exit 20)The colon-containing agent_id flowed transparently through
ratatoskr.sessions.create_session, the SSE stream's text +
worker_phase + done events all rendered correctly, and the
ratatoskr.sessions module needed zero changes.Contract
docs/contracts/issues/15.contract.md — new module spec; drift-check
clean. Acceptance criterion about "appears in GET /agents" should be
amended in a follow-up to reflect the empirical finding.Tests
+26 tests (264 total GREEN, was 238). Covers all error paths via
respx mocking — quota, user_id, layer_deferred, field_not_mutable,
404, 5xx — plus CLI happy + error paths. ruff clean.Minor bump (v0.6.5 → v0.7.0) per SemVer etiquette: new public
module + CLI surface; new caller-visible behavior.Downloads