Outcome: Vani's converse agent now exposes the 16 approved tool descriptions to the model. Each stub returns one explicit, non-empty result:
{"status": "UNAVAILABLE", "reason": "This capability is not built."}
The prompt requires Vani to tell counsel that the capability is not built. An SDK-native
output validator rejects a finding-shaped reply that omits “not built” and asks the model
to correct it. Because validation happens after generation, only generations following an
UNAVAILABLE tool result withhold answer-text deltas; the validated correction
is then emitted. Ordinary turns retain immediate streaming, and provider/thinking streaming
continues on the narrow unavailable path.
resolve_matter_document remains the
one live deferred broker read. current_matter is not registered: it remains the
broker's deterministic /current variable read under pramaan-docs ADR-166.
No tool accepts a channel parameter. No tool returns a rendering or mints a link.counsel asks for a planned capability → Vani selects the matching registered tool → local stub returns UNAVAILABLE (never []) → Vani says: I cannot do that because it is not built yet counsel asks to fetch a matter document → resolve_matter_document remains deferred → broker reads under counsel authz/RLS → existing candidate presentation path continues unchanged
request_frame_update · search_evidence ·
find_exact · find_contradictions ·
next_hearing · send_record_link ·
record_card · record_insights ·
citation_audit · verify_citation ·
find_authority · court_weight ·
proposition_match · citation_history ·
case_status · cause_list.
Every description is read directly from its existing
ToolSpec.purpose. The stubs take no arguments because the specs declare no
implementation parameters and this Story must not invent them.
| Question | Decision |
|---|---|
ToolReturn or a bare value? |
Bare typed dictionary. ToolReturn separates a return value from extra
model content or hidden metadata; these stubs have exactly one canonical model-visible
status and no extra content. |
| Deferred tool loading? | Not adopted. On Vani's current routes the SDK's local search fallback changes tool definitions after discovery and invalidates the cached prefix. This Story explicitly requires the model to see all approved descriptions, so the 16 schemas remain eager. |
The test serializes the exact Pydantic AI definitions into the OpenAI-wire
tools shape and counts with o200k_base. Compared with the existing
resolve-only baseline, the 16 schemas add 2,497 tokens and
11,418 UTF-8 bytes. This is material and intentionally visible.
| Requirement | Test |
|---|---|
| Exact 16 names, exact purposes, zero invented parameters | test_converse_registers_the_exact_specced_tools_and_measured_schema_delta |
Every call returns non-empty UNAVAILABLE; a fabricated negative is
rejected; Vani says “not built” |
test_every_stub_returns_unavailable_and_vani_says_it_is_not_built |
The rejected false finding never reaches any turn.delta |
test_rejected_unavailable_finding_never_reaches_the_delta_stream |
| Lab and manifest distinguish live, unavailable, and excluded | test_identity_and_manifest_distinguish_live_unavailable_and_excluded |
| Existing resolver unchanged | agents/mira/tests/test_doc_resolution.py |
Focused Story + resolver + converse tests → 34 passed Vani package suite → 268 passed uv run ruff check . → All checks passed uv run pytest -q → 444 passed, 3 dependency warnings