1.7.10

Fixed (#412)

  • Live-панель инструментов больше не помечает завершёнными сразу несколько карточек при параллельных вызовах одного инструмента. tool_end обновлял все running-записи с совпадающим именем (t.tool === data.tool), а wire-кадры tool_start/tool_end не несли идентификатора вызова — один фрейм завершения закрывал обе одноимённые карточки (визуальная история выполнения недостоверна до tool_batch_end). Исправление сквозной корреляцией tool_call_id: (1) Runner — ToolEvent рождается со своим CallID/Arguments в runTool (tc.ID в скоупе); прежняя индексная спарка ToolEventWithCall в Run клеила id соседнего вызова — параллельный батч аппендит события в порядке завершения горутин, а не в порядке resp.ToolCalls (хелпер удалён как единственный потребитель). (2) streamHook — BeforeExecuteTools кладёт tc.ID в IterationEntry.CallID (новое опциональное поле, сериализуется в persisted tool_iteration rows и entries JSON tool_batch_end) и публикует _tool_call_id в _tool_start; OnToolComplete матчит запись сперва по id, name-fallback — только на записи без id (true-legacy): дубликат tool_end (id найден, запись закрыта) и чужой id (поздний кадр прошлого хода после сброса iterEntries) не закрывают соседний параллельный вызов; _tool_end несёт _tool_call_id. (3) WS-канал — tool_start/tool_end ретранслируют _tool_call_id в JSON-поле call_id (WSOutboundEvent.CallID уже существовал для ask_user, omitempty бережёт legacy-фреймы старого клиента). (4) Resume-parity — ActiveToolInfo (agent_status.active_tools) несёт call_id, AddActiveTool/RemoveActiveTool(name, callID) снимают точный слот: reconnect mid-ход восстанавливает карточки с id, последующий tool_end закрывает свою. (5) WebUI-стор — ToolIterationEntry.call_id?; tool_end закрывает ровно одну карточку (матч по id, fallback — первый running без id, зеркально серверу: live-панель не расходится с persisted entries на tool_batch_end); дроп без матча логируется console.warn; resume-рибилд конвертирует unix-секунды started_at снапшота в ISO (карточка «Начало»/длительность группы живёт и до прилёта tool_end) с Number.isFinite-гвардом. task_id/is_async/error-статус ложатся только на свою карточку.

Review (#412)

  • ocr review — 2 раунда; исправлены: дубликат/чужой tool_end закрывал чужой параллельный вызов через name-fallback (строгая семантика «fallback только на без-id записи» на сервере, акторе и клиенте — зеркально друг другу), асимметрия RemoveActiveTool/OnToolComplete при промахе id (activeTools не снимался, iterEntries закрывался), resume-рибилд терял started_at (unix-сек → ISO, Number.isFinite-гард против RangeError в toISOString), двойной проход по массиву в tool_end (slice + точечная замена), тихий дроп tool_end без матча (диагностика console.warn в стиле файла). Отклонены с обоснованием: LIFO вместо FIFO для legacy-fallback (расходился бы с серверным persisted-порядком — скачок статусов на tool_batch_end), «комментарии только на английском» (кодовая база комментирована по-русски — ложная посылка), call_id: undefined вместо '' (матчинг через !t.call_id не различает), отказ от ToolEvent.Arguments (поле нужно hook-потребителям: SkillRunStepsHook, spawn-детект; ранее заполнялось кривой индексной спаркой). Попутно вычищены все легаси-SA5011 в internal/agent (~30 сайтов в 13 тест-файлах, паттерн nil-check + t.Fatal + deref — staticcheck не считает t.Fatal терминирующим; фиксы: value-копия/positive-ветка/t.Fatal+return) — pre-commit golangci-hook по пакету теперь проходит без легаси-шума. Тесты: Go — wire call_id в tool_start/tool_end + legacy-фрейм без id, resume active_tools с call_id, streamHook (два одноимённых вызова, обратный порядок завершения + error, дубликат/чужой id — no-op, id-событие над без-id записями, legacy FIFO, persisted entries с call_id), runTool/параллельный батч несёт свои id+аргументы, RemoveActiveTool (точный слот/чужой id/дубликат/legacy-слот); vitest — параллельные одноимённые карточки (свой tool_end закрывает свою), обратный порядок + error, async task_id на своей карточке, legacy FIFO оба фрейма без id, чужой id и дубликат — no-op, resume с call_id+started_at. Проверки: go vet, go test ./... -count=1 (0 падений), golangci-lint (0 замечаний — было 20+ легаси), npm run check (0), npm test (918), make build-cross (6 платформ) — зелёные.