# Agent UI Schema and Event Wiring Implementation Plan > **For Claude:** REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task. **Goal:** 打通 agent 工具结果在实时事件与历史回放的一致渲染链路:SSE 实时带 UI,落库 content 存摘要,完整 UI schema 存 storage 并通过 metadata 回填。 **Architecture:** 后端在 `TOOL_CALL_RESULT` 持久化链路中引入“摘要 + 全量分离”策略:摘要写 `messages.content`,全量 payload 写对象存储,metadata 仅存索引路径;history 读取时按 metadata 反查 storage 回填 `ui`。前端复用现有 AG-UI 事件模型,实现 runs/events/history 三路统一映射到 `ChatListItem`,并补齐 step 事件渲染与 history 多模态渲染。 **Tech Stack:** FastAPI, SQLAlchemy, AgentScope runtime/events, Supabase Storage, Flutter (Bloc/Cubit), Dart models/tests, AG-UI events --- ### Task 1: Add Tool Summary Rule Engine (Backend) **Files:** - Create: `backend/src/core/agentscope/events/tool_result_summary.py` - Test: `backend/tests/unit/core/agentscope/events/test_tool_result_summary.py` **Step 1: Write the failing test** ```python from core.agentscope.events.tool_result_summary import build_tool_content_summary def test_calendar_write_summary() -> None: text = build_tool_content_summary( tool_name="calendar_write", args={"title": "项目评审"}, result={"start_time": "明天 10:00"}, error=None, ) assert text.startswith("已创建日程") ``` **Step 2: Run test to verify it fails** Run: `uv run pytest backend/tests/unit/core/agentscope/events/test_tool_result_summary.py -q` Expected: FAIL with import/module/function missing. **Step 3: Write minimal implementation** ```python def build_tool_content_summary(*, tool_name: str, args, result, error) -> str: if error: return f"{tool_name} 执行失败" if tool_name == "calendar_write": return "已创建日程" return f"{tool_name} 执行完成" ``` **Step 4: Run test to verify it passes** Run: `uv run pytest backend/tests/unit/core/agentscope/events/test_tool_result_summary.py -q` Expected: PASS. **Step 5: Extend tests for all rules and refactor** Add cases for `calendar_read/calendar_delete/calendar_share/user_resolve/error/fallback/truncation` and implement full rule table. **Step 6: Commit** ```bash git add backend/src/core/agentscope/events/tool_result_summary.py backend/tests/unit/core/agentscope/events/test_tool_result_summary.py git commit -m "feat: add deterministic tool result summary engine" ``` ### Task 2: Persist Full Tool Payload to Storage and Keep Content Lightweight **Files:** - Modify: `backend/src/core/agentscope/events/store.py` - Test: `backend/tests/unit/core/agentscope/events/test_store.py` **Step 1: Write the failing tests** Add tests asserting: - `TOOL_CALL_RESULT` persists summary to `content`. - metadata includes `storage_bucket/storage_path/tool_call_id`. - uploaded payload includes full `ui/args/result/error`. **Step 2: Run targeted tests (RED)** Run: `uv run pytest backend/tests/unit/core/agentscope/events/test_store.py -q` Expected: FAIL on new assertions. **Step 3: Implement minimal storage write path** In `_persist_tool_call_result`: - build `full_payload` from event fields. - call summary engine for `content`. - upload payload via tool result storage (inject dependency if needed). - store only path/index in metadata. **Step 4: Run tests (GREEN)** Run: `uv run pytest backend/tests/unit/core/agentscope/events/test_store.py -q` Expected: PASS. **Step 5: Add fallback test and implementation** Add case where storage upload fails but tool message still persists with summary and no crash. **Step 6: Commit** ```bash git add backend/src/core/agentscope/events/store.py backend/tests/unit/core/agentscope/events/test_store.py git commit -m "feat: store tool payload in object storage with metadata index" ``` ### Task 3: Hydrate History Tool UI from Metadata Storage Path **Files:** - Modify: `backend/src/v1/agent/repository.py` - Test: `backend/tests/unit/v1/agent/test_repository.py` **Step 1: Write failing tests** Add/adjust assertions: - history tool payload resolves `ui` from storage payload. - when storage missing, fallback to `messages.content` summary. **Step 2: Run tests (RED)** Run: `uv run pytest backend/tests/unit/v1/agent/test_repository.py -q` Expected: FAIL on `ui` hydration and fallback assertions. **Step 3: Implement minimal hydration logic** In `_to_snapshot_message` for tool role: - read storage via `metadata.storage_bucket/storage_path`. - map hydrated payload fields to snapshot (`ui`, `content`, `toolCallId`). - keep safe fallback when storage read fails. **Step 4: Run tests (GREEN)** Run: `uv run pytest backend/tests/unit/v1/agent/test_repository.py -q` Expected: PASS. **Step 5: Commit** ```bash git add backend/src/v1/agent/repository.py backend/tests/unit/v1/agent/test_repository.py git commit -m "fix: hydrate tool ui from metadata storage in history snapshots" ``` ### Task 4: Keep SSE TOOL_CALL_RESULT Compatible with Existing Frontend Parsing **Files:** - Modify: `backend/src/core/agentscope/runtime/agent_route_runtime.py` - Test: `backend/tests/unit/core/agentscope/runtime/test_agent_route_runtime.py` **Step 1: Write failing test** Add assertion that emitted `TOOL_CALL_RESULT` data contains expected renderable fields (`callId/toolName/result/error` and `ui` path from result payload). **Step 2: Run tests (RED)** Run: `uv run pytest backend/tests/unit/core/agentscope/runtime/test_agent_route_runtime.py -q` Expected: FAIL on missing/incorrect payload fields. **Step 3: Implement minimal payload normalization** Normalize tool result event payload so frontend can keep current parsing without contract breaks. **Step 4: Run tests (GREEN)** Run: `uv run pytest backend/tests/unit/core/agentscope/runtime/test_agent_route_runtime.py -q` Expected: PASS. **Step 5: Commit** ```bash git add backend/src/core/agentscope/runtime/agent_route_runtime.py backend/tests/unit/core/agentscope/runtime/test_agent_route_runtime.py git commit -m "fix: preserve frontend-compatible tool result event payload" ``` ### Task 5: Wire Frontend History + Events to Unified Rendering Path **Files:** - Modify: `apps/lib/features/chat/data/services/ag_ui_service.dart` - Modify: `apps/lib/features/chat/presentation/bloc/chat_bloc.dart` - Modify: `apps/lib/features/chat/data/models/tool_result.dart` - Modify: `apps/lib/features/home/ui/screens/home_screen.dart` - Test: `apps/test/features/chat/ag_ui_service_test.dart` - Create/Modify: `apps/test/features/chat/chat_bloc_test.dart` **Step 1: Write failing tests** Add tests asserting: - history tool message with `ui` becomes `ToolResultItem`. - SSE `TOOL_CALL_RESULT` with `ui` renders same item shape. - attachments in history user message are mapped for multimodal rendering. **Step 2: Run tests (RED)** Run: `cd apps && flutter test test/features/chat/ag_ui_service_test.dart` Expected: FAIL on new mapping assertions. **Step 3: Implement minimal mapping changes** - In service/bloc, unify history and event mapping into same conversion path. - Keep existing `UiSchemaRenderer` input format untouched. - Ensure fallback to content text when `ui` missing. **Step 4: Run tests (GREEN)** Run: `cd apps && flutter test test/features/chat/ag_ui_service_test.dart` Expected: PASS. **Step 5: Commit** ```bash git add apps/lib/features/chat/data/services/ag_ui_service.dart apps/lib/features/chat/presentation/bloc/chat_bloc.dart apps/lib/features/chat/data/models/tool_result.dart apps/lib/features/home/ui/screens/home_screen.dart apps/test/features/chat/ag_ui_service_test.dart apps/test/features/chat/chat_bloc_test.dart git commit -m "feat: unify realtime and history tool card rendering" ``` ### Task 6: Add Step Event Rendering for Intent/Execution/Report **Files:** - Modify: `apps/lib/features/chat/presentation/bloc/chat_bloc.dart` - Modify: `apps/lib/features/home/ui/screens/home_screen.dart` - Test: `apps/test/features/chat/chat_bloc_test.dart` **Step 1: Write failing test** Add test verifying `STEP_STARTED/STEP_FINISHED` transitions produce visible stage state. **Step 2: Run tests (RED)** Run: `cd apps && flutter test test/features/chat/chat_bloc_test.dart` Expected: FAIL on missing stage state. **Step 3: Implement minimal state and UI** - Track current stage enum in `ChatState`. - Render compact stage progress row in chat screen. **Step 4: Run tests (GREEN)** Run: `cd apps && flutter test test/features/chat/chat_bloc_test.dart` Expected: PASS. **Step 5: Commit** ```bash git add apps/lib/features/chat/presentation/bloc/chat_bloc.dart apps/lib/features/home/ui/screens/home_screen.dart apps/test/features/chat/chat_bloc_test.dart git commit -m "feat: render agent step progress from AG-UI events" ``` ### Task 7: Verification Gate (Backend + Frontend) **Files:** - Modify (if needed): `docs/plans/2026-03-11-agent-multimodal-smoke-runbook.md` **Step 1: Run backend targeted tests** Run: `uv run pytest backend/tests/unit/core/agentscope/events/test_tool_result_summary.py backend/tests/unit/core/agentscope/events/test_store.py backend/tests/unit/v1/agent/test_repository.py backend/tests/unit/core/agentscope/runtime/test_agent_route_runtime.py -q` Expected: PASS. **Step 2: Run frontend targeted tests** Run: `cd apps && flutter test test/features/chat/ag_ui_service_test.dart test/features/chat/chat_bloc_test.dart` Expected: PASS. **Step 3: Run backend quality checks** Run: `uv run ruff check backend/src backend/tests` Expected: PASS. **Step 4: Run backend type checks** Run: `uv run basedpyright` Expected: 0 errors. **Step 5: Update runbook evidence** Record changed contract, test evidence, and known follow-ups. **Step 6: Commit** ```bash git add docs/plans/2026-03-11-agent-multimodal-smoke-runbook.md git commit -m "docs: record tool ui schema storage and rendering verification" ```