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(новое опциональное поле, сериализуется в persistedtool_iterationrows и entries JSONtool_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 — wirecall_idвtool_start/tool_end+ legacy-фрейм без id, resumeactive_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 платформ) — зелёные.