fix: address tool_result dedup, SDK task ID collision, and doc accuracy
P1: tool_result dedup changed from "first wins" to "last wins"
- claude-client.ts: remove emittedToolResultIds Set, both PostToolUse and
user message handler emit freely. PostToolUse provides immediate UI feedback;
user handler's potentially more complete result arrives second.
- route.ts: collectStreamResponse replaces existing contentBlock entry when
duplicate tool_use_id arrives (last wins for DB persistence).
- ChatView.tsx: onToolResult replaces existing state entry by tool_use_id
instead of skipping, ensuring most complete result is displayed.
P2: SDK task ID collision risk eliminated
- db.ts: syncSdkTasks now uses full sessionId in task ID
(`sdk-{fullSessionId}-{index}`) instead of truncated 8-char prefix,
eliminating cross-session primary key collisions.
P2: Todo ordering stabilized
- db.ts: add sort_order column migration, syncSdkTasks writes array index
as sort_order. getTasksBySession sorts by sort_order ASC, created_at ASC
so tasks with identical timestamps maintain insertion order.
P3: Documentation field mapping corrected
- agent-tooling-todo-bridge.md: fix field mapping table — actual implementation
maps activeForm (not priority) to description column. Updated dedup strategy
docs from "three-layer skip" to "two-layer last-wins replace".
Lint: fix prefer-const for finalPrompt in claude-client.ts.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
1 parent
c13ff54638
commit
0c379e5607
5 files changed
+64
-54
No files matched your search
@@ -16,12 +16,13 @@ SDK TodoWrite tool_use
|
||||
|
||||
## TodoWrite Field Mapping
|
||||
|
||||
| SDK TodoWrite field | Local tasks table column | Notes |
|
||||
|---------------------|-------------------------|-------|
|
||||
| `id` | `id` (prefixed `sdk-`) | Prefixed to avoid collision with user tasks |
|
||||
| `content` | `title` | Main display text |
|
||||
| `status` | `status` | Mapped via `mapStatus()` |
|
||||
| `priority` | `description` | Optional context |
|
||||
| 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
|
||||
|
||||
@@ -40,19 +41,20 @@ SDK TodoWrite tool_use
|
||||
|
||||
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 Three-Layer Dedup
|
||||
## tool_result Two-Layer Last-Wins Dedup
|
||||
|
||||
Both `PostToolUse` hook and `user` message handler in the SDK can emit the same `tool_result`. Three layers prevent duplicates:
|
||||
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: claude-client.ts (SSE emission)
|
||||
- `emittedToolResultIds` Set — checks before enqueueing any `tool_result` SSE event
|
||||
- Both PostToolUse and user message handler check this Set
|
||||
### 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: route.ts (DB persistence)
|
||||
- `seenToolResultIds` Set in `collectStreamResponse()` — skips duplicate `tool_use_id` before writing to `contentBlocks`
|
||||
|
||||
### Layer 3: ChatView.tsx (UI state)
|
||||
- `onToolResult` checks `prev.some(r => r.tool_use_id === id)` before adding to state
|
||||
### 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
|
||||
|
||||
|
||||
Reference in new issue
Block a user