feat(#2): consume GET /sessions/{id}/tools — Tools-pane inventory hydrate

v1 coverage-audit Tier-2 quick win. The owner-scoped tool-inventory
endpoint (#183) had no caller; wire it into the TUI Tools pane.

- sessions.py: get_session_tools (GET /sessions/{id}/tools) — owner-
  scoped (consumer key, no admin scope), 200 -> parsed dict verbatim,
  non-200 -> SessionApiFailed. Mirrors get_persona_state / get_me.
- tui.py: _format_tool_inventory helper + _hydrate_session_tools
  best-effort worker (mirrors _hydrate_persona), wired unconditionally
  in on_mount. Writes the merged {agent_id, builtin_tools,
  bifrost_tools} inventory the LLM saw at turn-fire into the Tools
  pane + audits; never crashes on failure.
- Covers the design-brief 5 "Tools widget" via the reachable owner
  endpoint (the admin variant stays a gap only for cross-user debug).
- Contract #2 amended (FN) + validated. TDD: 3 wrapper tests + 1
  format-helper unit + 2 hydrate integration tests. Coverage: REST
  10/40. Suite 544 green; touched code ruff-clean.
This commit is contained in:
vh
2026-06-30 22:50:12 -07:00
parent 0205b81319
commit e62208d8e3
9 changed files with 229 additions and 13 deletions
+68
View File
@@ -3167,3 +3167,71 @@ class TestBareSessionPicker:
assert rc == 20
assert "[session_api_failed]" in capsys.readouterr().err
assert not opened
class TestSessionToolsHydration:
"""get_session_tools + the #183 Tools-pane inventory hydrate (GET /sessions/{id}/tools)."""
def test_format_tool_inventory(self) -> None:
"""format_tool_inventory [unit]: header + builtin + bifrost lines."""
from ratatoskr.tui import _format_tool_inventory
lines = _format_tool_inventory(
{
"agent_id": "alice:wizard",
"builtin_tools": [],
"bifrost_tools": [{"name": "bifrost.x"}, {"name": "bifrost.y"}],
}
)
joined = "\n".join(lines)
assert "agent=alice:wizard" in joined
assert "builtin=0 bifrost=2" in joined
assert "builtin: (none)" in joined
assert "bifrost.x, bifrost.y" in joined
async def test_hydrate_writes_inventory_and_audits(
self, monkeypatch: pytest.MonkeyPatch
) -> None:
"""hydrate_writes_inventory [scenario,tracer]: 200 → inventory in Tools pane + audit."""
import ratatoskr.tui as tui_mod
writes = _spy_writes(monkeypatch)
async def fake_tools(client, session_id):
return {
"agent_id": "alice:wizard",
"builtin_tools": [],
"bifrost_tools": [{"name": "bifrost.set_field"}],
}
monkeypatch.setattr(tui_mod, "get_session_tools", fake_tools)
app = _resolved_app(_args_existing(session_id="s-tools-01"))
async with app.run_test() as pilot:
await pilot.pause()
await app._hydrate_session_tools()
await pilot.pause()
joined = " ".join(_text_of(w) for w in writes)
assert "session tool inventory" in joined
assert "bifrost.set_field" in joined
assert "session_tools_hydrated" in joined # audit line landed
async def test_hydrate_failure_audits_no_crash(
self, monkeypatch: pytest.MonkeyPatch
) -> None:
"""hydrate_failure [error]: get_session_tools raises → failure audit; no crash."""
import ratatoskr.tui as tui_mod
from ratatoskr.sessions import SessionApiFailed
writes = _spy_writes(monkeypatch)
async def boom(client, session_id):
raise SessionApiFailed(status=404, body=b"session_not_found")
monkeypatch.setattr(tui_mod, "get_session_tools", boom)
app = _resolved_app(_args_existing(session_id="s-tools-02"))
async with app.run_test() as pilot:
await pilot.pause()
await app._hydrate_session_tools()
await pilot.pause()
joined = " ".join(_text_of(w) for w in writes)
assert "session_tools_hydration_failed" in joined