• v0.7.0 c086ae2b32

    feat(tier3): ratatoskr.tier3 module + CLI (v0.7.0)

    vh released this 2026-05-24 20:31:10 -07:00 | 251 commits to main since this release

    Issue #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) → None

    Tier3QuotaExceeded — 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-2xx

    Caller-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 via ratatoskr --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-side tier3 list subcommand 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