feat(tui): add agent interaction workflows

This commit is contained in:
Xubin Ren
2026-08-17 20:56:10 +08:00
parent e77eed76c9
commit 4391bbf4da
28 changed files with 1275 additions and 87 deletions
+27
View File
@@ -1819,6 +1819,33 @@ async def test_system_subagent_followup_is_persisted_before_prompt_assembly(tmp_
]
@pytest.mark.asyncio
async def test_turn_usage_is_persisted_with_the_saved_session(tmp_path: Path) -> None:
loop = _make_full_loop(tmp_path)
loop.consolidator.maybe_consolidate_by_tokens = AsyncMock(return_value=False) # type: ignore[method-assign]
async def fake_run_agent_loop(initial_messages, **_kwargs):
loop._last_usage = {"prompt_tokens": 64, "completion_tokens": 9}
return (
"done",
[],
[*initial_messages, {"role": "assistant", "content": "done"}],
"stop",
False,
)
loop._run_agent_loop = fake_run_agent_loop # type: ignore[method-assign]
await loop._process_message(
InboundMessage(channel="cli", sender_id="user", chat_id="usage", content="hello")
)
loop.sessions.invalidate("cli:usage")
assert loop.sessions.get_or_create("cli:usage").metadata["_last_usage"] == {
"prompt_tokens": 64,
"completion_tokens": 9,
}
@pytest.mark.asyncio
async def test_system_subagent_followup_does_not_log_content(tmp_path: Path) -> None:
loop = _make_full_loop(tmp_path)
+3
View File
@@ -99,6 +99,7 @@ async def test_runtime_event_publisher_consumes_turn_metadata_on_complete() -> N
bus.subscribe(seen.append)
publisher.record_turn_runtime("cli:direct", "runtime")
publisher.record_turn_latency("cli:direct", 123)
publisher.record_turn_usage("cli:direct", {"prompt_tokens": 40, "completion_tokens": 2})
await publisher.turn_completed(
channel="cli",
@@ -119,9 +120,11 @@ async def test_runtime_event_publisher_consumes_turn_metadata_on_complete() -> N
assert first.context.metadata == {"source": "test"}
assert first.latency_ms == 123
assert first.runtime == "runtime"
assert first.usage == {"prompt_tokens": 40, "completion_tokens": 2}
assert isinstance(second, TurnCompleted)
assert second.latency_ms is None
assert second.runtime is None
assert second.usage == {}
@pytest.mark.asyncio
+20
View File
@@ -40,6 +40,7 @@ def test_session_context_separates_archive_progress_from_replay() -> None:
"estimated_session_tokens": replay_tokens + summary_tokens,
"archived_summary": "The archived conversation settled the old question.",
"archived_summary_at": "2026-08-13T10:00:00Z",
"last_usage": None,
}
@@ -54,3 +55,22 @@ def test_session_context_tolerates_untrusted_summary_metadata() -> None:
assert payload["archived_summary"] is None
assert payload["archived_summary_at"] is None
def test_session_context_sanitizes_usage_metadata() -> None:
session = Session(
key="websocket:context",
metadata={
"_last_usage": {
"prompt_tokens": 120,
"completion_tokens": 8,
"negative": -1,
"boolean": True,
"text": "invalid",
}
},
)
payload = session_context_payload(session)
assert payload["last_usage"] == {"prompt_tokens": 120, "completion_tokens": 8}