feat(vocero): add linked content and TALKFIRST greetings (#2)

Opening a report or podcast link now establishes an authenticated starting reference for the conversation. Users can select other accessible content or compare reports while retaining the original link, and TALKFIRST greetings can name the verified report or podcast.

Release notes:

- Added authenticated report and episode link resolution with bounded lookups and unavailable-content handling
- Added explicit conversation selection without restricting searches or changing the reference during comparisons
- Added contextual TALKFIRST greetings through both voice-proxy and HTTP reasoning paths
- Preserved backend permission checks, conversation isolation, cancellation, and the existing speech protocol

Reviewed-on: AquaPortal/s2s#2
This commit is contained in:
2026-10-08 09:34:16 -04:00
parent 96895996d9
commit 4b0108ecf0
21 changed files with 946 additions and 66 deletions
+9 -7
View File
@@ -113,6 +113,7 @@ def test_backend_login_validates_exact_contract_without_leaking_credentials(enab
@pytest.mark.parametrize("use_sse", [False, True])
def test_sdk_discovery_and_execution_preserve_nested_schema_results_and_per_user_headers(enabled, monkeypatch, use_sse):
calls = []
original = httpx.AsyncClient
def upstream(request):
calls.append(request)
@@ -141,6 +142,8 @@ def test_sdk_discovery_and_execution_preserve_nested_schema_results_and_per_user
else:
assert method == "tools/call"
assert message["params"]["arguments"] == {"request": {"report_id": "r1"}}
user = request.headers["authorization"].removeprefix("Bearer ")
assert request.headers["x-vocero-podcast-id"] == f"report:{user}"
result = {
"content": [],
"structuredContent": {"result": {"status": "found", "caller": request.headers["authorization"]}},
@@ -153,11 +156,7 @@ def test_sdk_discovery_and_execution_preserve_nested_schema_results_and_per_user
return httpx.Response(200, json=body)
monkeypatch.setattr(
mcp,
"http_client",
lambda token: httpx.AsyncClient(
transport=httpx.MockTransport(upstream), headers={"Authorization": f"Bearer {token}"}
),
mcp.httpx, "AsyncClient", lambda **kwargs: original(transport=httpx.MockTransport(upstream), **kwargs)
)
async def scenario():
@@ -165,7 +164,10 @@ def test_sdk_discovery_and_execution_preserve_nested_schema_results_and_per_user
assert [tool.name for tool in catalog] == ["get_report"]
assert catalog[0].parameters["properties"]["request"] == {"type": "object"}
outputs = await asyncio.gather(
*(mcp.execute(user, "get_report", {"request": {"report_id": "r1"}}) for user in ("alice", "bob"))
*(
mcp.execute(user, "get_report", {"request": {"report_id": "r1"}}, podcast_id=f"report:{user}")
for user in ("alice", "bob")
)
)
assert [output["data"]["result"]["caller"] for output in outputs] == ["Bearer alice", "Bearer bob"]
@@ -179,7 +181,7 @@ def test_mcp_transport_errors_are_safe_and_preserve_authentication_status(enable
monkeypatch.setattr(
mcp,
"http_client",
lambda token: httpx.AsyncClient(
lambda token, podcast_id=None: httpx.AsyncClient(
transport=httpx.MockTransport(lambda req: httpx.Response(status, text="secret-upstream-body"))
),
)