0.167.20

Fixed

  • FileStates пересоздавался каждый ход — read-before-edit и дедупликация чтений не работали между ходами ( — P1). handleInbound создавал tools.NewFileStates() на каждый ход: всё накопленное терялось на границе хода. Агент, прочитавший файл в ходу N и правящий его в ходу N+1, получал ложное предупреждение «You are editing X without reading it first» (модель реагировала лишним чтением), дедупликация повторных чтений с хешированием не пережидала ход, обнаружение внешних изменений (Mtime/ContentHash) между ходами было невозможно. Фикс: FileStates живёт на SessionActor (создаётся в newSessionActor, кладётся в ctx каждого хода), переживает границы ходов в пределах сессии и умирает с актором (idle-timeout — состояние теряется корректно). Сброс — на границах сессии: новый FileStates.Reset() при смене topic_break/interrupted-маркера (trimAndMaybeResetAtTopicBreak на входе в ход — после обрезки истории агент логически начинает заново) и при /clear — роутингом через новый msgResetFileStates в канал актора (сброс выполняется акторной горутиной между ходами, а не с горутины bus’а посреди tool-call — без torn-state window; ocr review). Ask_user refetch-ветка использует чистую trimWindowAfterLastTopicBreak (без reset — маркер, добавленный в БД параллельно, не затирает read-state посреди хода). fileLocks Reset не чистит (lookup-then-unlock в UnlockFile — очистка создала бы второй мьютекс на путь при живом держателе, ломая mutual exclusion); RecordRead при недоступном path теперь пишет Debug-лог (вечноживущее состояние больше не маскирует провал молчанием). Helper agentKey() дедуплицирует построение ключа роутера (handleStop/handleClear). Тесты: FileStates — RecordRead→IsUnchanged, внешнее изменение → false, RecordWrite снимает read-state (CheckReadBeforeEdit снова предупреждает), Reset (read-state пуст, fileLocks живы и работают), FIFO-вытеснение; актор — общий инстанс между ходами, reset при новом маркере / отсутствие reset при том же, чистая refetch-обрезка без mid-turn reset, новый актор стартует с пустым состоянием, /clear ставит msgResetFileStates в канал.