Files
CodePilot/docs/handover/agent-tooling-todo-bridge.md
7418andClaude Opus 4.6 6209c865fd refactor: 整理 docs 目录结构
- docs/handover/ — 交接文档(架构、设计决策)
- docs/research/ — 调研文档(技术方案、可行性分析)
- 各目录添加 README.md 索引,要求 AI 增删文件时更新索引
- CLAUDE.md 更新文档路径引用

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-01 09:41:14 +08:00

93 lines
4.1 KiB
Markdown

# Agent Tooling & TodoWrite Bridge
## Architecture Overview
```
SDK TodoWrite tool_use
→ PostToolUse hook (claude-client.ts)
→ Emits tool_result SSE (deduped)
→ Emits task_update SSE { session_id, todos[] }
→ collectStreamResponse (route.ts)
→ syncSdkTasks() persists to DB
→ Frontend SSE consumer (useSSEStream.ts)
→ onTaskUpdate dispatches 'tasks-updated' event
→ TaskList re-fetches from /api/tasks
```
## TodoWrite Field Mapping
| SDK TodoWriteInput field | Local tasks table column | Notes |
|--------------------------|-------------------------|-------|
| (array index) | `id` (prefixed `sdk-{sessionId}-{index}`) | Full sessionId avoids collision |
| `content` | `title` | Main display text |
| `status` | `status` | Mapped via `mapStatus()` |
| `activeForm` | `description` | Present-continuous form label |
| (array index) | `sort_order` | Preserves original order |
### Status Mapping
| SDK status | Local TaskStatus |
|---------------|------------------|
| `pending` | `pending` |
| `in_progress` | `in_progress` |
| `completed` | `completed` |
| Other | `pending` |
## Idempotency
`syncSdkTasks()` uses a **replace-all** strategy within a transaction:
1. `DELETE FROM tasks WHERE session_id = ? AND source = 'sdk'`
2. `INSERT` all todos from the latest TodoWrite payload
This is naturally idempotent — calling it multiple times with the same data produces identical results. User-created tasks (`source = 'user'`) are never affected.
## tool_result Two-Layer Last-Wins Dedup
Both `PostToolUse` hook and `user` message handler in the SDK can emit the same `tool_result`.
PostToolUse fires first (for immediate UI feedback); the user handler's result may be more
complete/canonical. Strategy: **both sources emit freely; consumption layers use "last wins"
(replace, not skip)** so the most complete result is always kept.
### Layer 1: route.ts (DB persistence)
- `seenToolResultIds` Set in `collectStreamResponse()` — when a duplicate `tool_use_id`
arrives, the existing `contentBlock` entry is **replaced** with the newer result.
### Layer 2: ChatView.tsx (UI state)
- `onToolResult` finds existing entry by `tool_use_id` and **replaces** it instead of
appending a duplicate.
## SSE Event Types
| Event | Source | Purpose |
|-------------------|-------------------|---------|
| `text` | stream_event | Streaming text delta |
| `tool_use` | assistant message | Tool invocation |
| `tool_result` | PostToolUse / user | Tool execution result (deduped) |
| `tool_output` | stderr callback | Raw tool output |
| `tool_timeout` | tool_progress | Tool execution timeout |
| `task_update` | PostToolUse | TodoWrite sync trigger |
| `status` | system message | Session init, notifications |
| `result` | result message | Final result with usage stats |
| `permission_request` | canUseTool | Permission approval needed |
| `mode_changed` | system status | SDK mode change |
| `error` | catch block | Error occurred |
| `done` | stream end | Stream complete |
## Troubleshooting
### Tasks not appearing after TodoWrite
1. Check server console for `[claude-client]` logs — ensure PostToolUse fires
2. Verify `task_update` SSE event in browser DevTools Network tab
3. Check `syncSdkTasks()` runs in `collectStreamResponse` (server-side tee'd stream)
4. Verify `tasks-updated` CustomEvent fires in browser console
### Duplicate tool results
1. Check `emittedToolResultIds` Set size in server logs
2. Compare SSE event count before/after (should be halved for tools)
3. Query `messages` table: `SELECT content FROM messages WHERE content LIKE '%tool_result%'` — each `tool_use_id` should appear once
### SDK upgrade issues
- Current: v0.2.62 (upgraded from v0.2.33)
- Rollback: `npm install @anthropic-ai/claude-agent-sdk@0.2.33`
- Key integration points: `canUseTool`, `PostToolUse`, `Notification`, `tool_progress`, `system`, `result`