9fade55901
Worldtree's GET /agents doesn't return consumer-defined (tier-3)
agents — the public list excludes them by design. Confirmed live in
v0.7.0's smoke. Without server-side knowledge, ratatoskr's picker
couldn't show tier-3 agents the operator had defined; the workflow
was "remember the agent_id, pass --agent ratatoskr:<name>
explicitly." Friction grows with every tier-3 agent.
## Fix: client-side index, merged at picker time
New module `ratatoskr.local_agents` maintains a JSON-backed index at
$XDG_CONFIG_HOME/ratatoskr/local_agents.json (override via
$RATATOSKR_LOCAL_AGENTS). `tier3` CLI define / patch / delete update
the index as side-effects. `tui._resolve_then_run` loads the index
after `list_agents(client)` and appends entries not already in the
remote list (dedup by agent_id; remote wins on conflict).
Library-level `tier3.define_agent` / `patch_agent` / `delete_agent`
stay pure — local persistence lives in the CLI layer (`_run_define`
etc.), not in the library functions. Tests of the library don't
touch the filesystem.
## Public surface
ratatoskr.local_agents:
LocalAgentEntry (frozen dataclass)
load_local_agents() -> list[LocalAgentEntry]
add_local_agent(entry)
update_local_agent(entry) # same semantics as add (agent_id key)
remove_local_agent(agent_id)
make_description(system_prompt) -> str # synthetic picker label
Failure modes are lenient: missing file → empty index; corrupt JSON
or schema mismatch → empty index (no crash). The picker continues
to show foundational agents either way; tier-3 surface degrades to
the pre-v0.8.0 workflow.
## Picker integration
Local entries convert to ratatoskr.sessions.AgentInfo with synthetic
fields:
name = agent_name (from LocalAgentEntry)
description = "(tier 3) <first non-empty line of system prompt>"
version, capabilities, supported_models, persona_traits, ui_hints
= None / [] / [] / {} / {}
If Worldtree later starts returning tier-3 in GET /agents, this
module's role narrows to redundant local cache; can be removed
cleanly since the dedup-by-agent-id keeps remote-wins behavior.
## Tests
286/286 GREEN (was 265, +21: 20 local_agents + 1 picker-merge
integration). Ruff clean. Tests isolate the index via
$RATATOSKR_LOCAL_AGENTS pointed at pytest's tmp_path — no pollution
of operator's real ~/.config/ratatoskr/.
## Manual smoke
Sindra-like define against personal Worldtree:
python -m ratatoskr.tier3 define --name foo --system-prompt "..." --model X
cat ~/.config/ratatoskr/local_agents.json
# ratatoskr --new picker now shows ratatoskr:foo alongside mimir et al.
Cross-machine: the file is per-host. Operator can sync via dotfiles
if needed; out of scope for this commit.
Minor bump (v0.7.1 → v0.8.0) — new public module + new picker
behavior (more agents shown). No caller-side breaking changes.
66 lines
1.8 KiB
TOML
66 lines
1.8 KiB
TOML
[build-system]
|
|
requires = ["hatchling"]
|
|
build-backend = "hatchling.build"
|
|
|
|
[project]
|
|
name = "ratatoskr"
|
|
version = "0.8.0"
|
|
description = "Worldtree Conversation API debug TUI — multi-pane observability dashboard"
|
|
readme = "README.md"
|
|
requires-python = ">=3.12"
|
|
license = { file = "LICENSE" }
|
|
authors = [{ name = "Vuong Hoang" }]
|
|
keywords = ["worldtree", "tui", "debug", "sse", "textual"]
|
|
|
|
# Network + SSE consumer + TUI framework.
|
|
# See docs/design-brief.md §1 (Textual), §3 (httpx-sse).
|
|
dependencies = [
|
|
"httpx>=0.27",
|
|
"httpx-sse>=0.4",
|
|
"textual>=0.85",
|
|
]
|
|
|
|
[project.optional-dependencies]
|
|
dev = [
|
|
"pytest>=8",
|
|
"pytest-asyncio>=0.24",
|
|
"respx>=0.21", # httpx mocking for SSE-recorded snapshot tests
|
|
"ruff>=0.6",
|
|
"mypy>=1.11",
|
|
"textual-dev>=1.5", # textual console + live reload during dev
|
|
"pyyaml>=6", # used by docs/contracts/contract_parser.py and scripts/contract_drift_check.py
|
|
]
|
|
|
|
[project.scripts]
|
|
ratatoskr = "ratatoskr.cli:main"
|
|
|
|
[project.urls]
|
|
Repository = "https://gitea.phasefinal.com/vh/ratatoskr"
|
|
"Design Brief" = "https://gitea.phasefinal.com/vh/brokkr-smithy/src/branch/main/docs/ratatoskr-design-brief.md"
|
|
|
|
# Worldtree spec pin — see docs/SPEC-PIN.md for the full bump procedure.
|
|
# Ratatoskr is built against Worldtree at this commit; the vendored
|
|
# spec snapshot in docs/ reflects that SHA.
|
|
[tool.ratatoskr.spec-pin]
|
|
worldtree-spec-rev = "55101e909abcd2219833266b6f905c5bc956e0f0"
|
|
worldtree-version = "v0.19.0"
|
|
pinned-on = "2026-05-20"
|
|
|
|
[tool.hatch.build.targets.wheel]
|
|
packages = ["src/ratatoskr"]
|
|
|
|
[tool.pytest.ini_options]
|
|
asyncio_mode = "auto"
|
|
testpaths = ["tests"]
|
|
|
|
[tool.ruff]
|
|
line-length = 100
|
|
target-version = "py312"
|
|
|
|
[tool.ruff.lint]
|
|
select = ["E", "F", "I", "B", "UP", "RUF"]
|
|
|
|
[tool.mypy]
|
|
python_version = "3.12"
|
|
strict = true
|