Daily MaverickHOUSEHOLD SQUEEZE: Fuel levy cut offers brief relief as food basket outpaces SA’s unchanged SRD grantPunchOgun police command strengthens GBV response with trainingEl ComercioEstados Unidos revela su hoja de ruta para Venezuela: reformas, vacancia de Delcy Rodríguez y eleccionesInquirer EntertainmentLyca Gairanod resumes studies after song ‘Puede Nang Mangarap’ resurgenceInquirerArmy kills 2 NPA rebels, seizes arms in separate Eastern Visayas opsBollywood HungamaPVR INOX sees record Q2 as Indian box office crosses Rs 4,000 crore for FIRST time in a quarter!The Jerusalem PostBen-Gvir calls for arrest of Arab party heads amid Qatar-linked election interference claimsCollider‘Ready Player One’ Meets ‘Dexter’ in Gerard Butler’s Brutal Sci-Fi Thriller Officially Streaming for FreeZDF heuteAktuelle Pressemitteilungen des ZDFIl Fatto QuotidianoRocco Siffredi con la moglie Rózsa Tassi, Benji e Fede, Elisabetta Gregoraci con la sorella Marzia: ecco il cast di “Pechino Express 2027” e le nuove rotteBusiness AMGoudprijs daalt in aanloop naar Amerikaanse inflatiecijfersNMESlipknot’s Eloy Casagrande on new album: “We are making real music, we are not thinking about business”
The Daily Newsstand · Free, Always
Thursday, October 29, 2026

Агент повесил Bash одной кавычкой. Как детерминированный FSM спасает CLI-рантаймы

Translate

Представьте: ваш автономный агент генерирует команду для терминала, но ошибается на один символ. Шелл уходит в бесконечное ожидание, а процесс агента намертво зависает. Сегодня разберём, почему регулярки не спасают от этой боли, и покажем, как детерминированный конечный автомат (FSM) на хуке PreToolUse снимает проблему на уровне инфраструктуры.

Вы просите LLM-агента выполнить команду в терминале. Агент генерирует строку, но ошибается — теряет закрывающую кавычку. Если вы используете Bash или PowerShell, это не просто «опечатка в тексте». Шелл решает, что ввод многострочный, печатает приглашение >> и замирает в ожидании закрывающей кавычки из stdin. Если агент читает процесс синхронно через пайп (stdout/stderr), он встаёт в глухой deadlock: процесс не завершается, EOF не наступает, таймаут сессии выгорает, агент падает.

Первое, что приходит в голову — поставить регулярку на проверку чётности кавычек (count % 2 == 0). Но регулярка ломается о парадокс изоморфности. Одна и та же строка dir “C:” абсолютно безопасна в PowerShell (где \ — обычный разделитель пути) и гарантированно вешает Bash (где \ экранирует закрывающую кавычку). Наивный валидатор либо пропустит зависание в Linux, либо заблокирует легитимный Windows-путь. Инженер, увидев такой false positive, просто отключит защиту.

Ниже — архитектура Quote Guard: как мы выбросили регулярные выражения и построили FSM на хуке PreToolUse, который перехватывает управление до передачи байтов в ОС. Разберём лексический автомат с тремя изолированными грамматиками, механизм «Двойного замка» для неизвестных инструментов, историю о том, как закрыли дыру с heredoc (которая молча пропускала незакрытые кавычки), и почему 122 состязательных теста на Python и Node.js — единственная гарантия, что ваш предохранитель не сломает прод.

Матрица изоморфности: почему нельзя угадать шелл

Для наивного парсера эти строки структурно изоморфны. Попытка угадать грамматику по ОС хоста обречена: в контейнерах Windows крутится Bash, в WSL крутится PowerShell, а агент запускает скрипты кросс-платформенно.

Возьмём dir “C:”. В PowerShell обратный слэш — обычный разделитель пути, строка безопасна: шелл открывает кавычку, видит C:, закрывает её и готов к исполнению. В Bash тот же байт \ экранирует следующую за ним кавычку, и она перестаёт быть закрывающей. Приглашение >> ждёт продолжения ввода, которое никогда не придёт. Второй случай — echo "a`“b”. В PowerShell бэктик — официальный символ экранирования, и конструкция читается как «a», затем экранированная кавычка, затем «b». В Bash бэктик запускает command substitution: шелл ищет закрывающий бэктик, не находит и снова уходит в многострочный режим.

Обычный валидатор либо пропустит зависание Bash на обратном слэше, либо заблокирует безопасный путь C:\ в Windows. Защитный контур обязан обладать контекстной зоркостью.

Решение: FSM и «Двойной замок» (Strict Intersect)

Мы перевели Quote Guard с регулярных выражений на детерминированный конечный автомат (FSM) с тремя изолированными грамматиками: posix, powershell и cmd. Лексер последовательно переключает состояния (NORMAL, IN_DOUBLE_QUOTES, IN_SINGLE_QUOTES, ESCAPE) в строгом соответствии с правилами целевой среды.

Но как гарантировать безопасность, если команда исходит от стороннего плагина или кастомного агента, чья среда не объявлена явно? Для этого работает Strict Intersect. Когда инструмент не идентифицирован, лексер синхронно валидирует команду сразу по двум полярным грамматикам — posix и powershell. Если строка безопасна в обоих мирах (safe + safe), она пропускается в рантайм; если ведёт к дедлоку в обоих (unsafe + unsafe) — блокируется наглухо. Расхождение оценок и есть единственный по-настоящему интересный случай: он возникает на dir “C:”, где строка безопасна в PowerShell, но вешает POSIX. Здесь автомат прерывает исполнение с кодом Exit 2 и формирует машиночитаемую инструкцию для LLM:

[quote-guard BLOCKED]: Неизвестный инструмент 'MyTool'.
Строка безопасна в PowerShell, но вешает POSIX.
Лечение: задайте QUOTE_GUARD_MAP=MyTool=powershell

Модель, получив структурированный отказ вместо зависания, считывает подсказку и со второго шага отправляет синтаксически выверенный вызов. Это тот самый «компилятор, который лечит модель» — по крайней мере, в нашем случае.

Кейс с Heredoc: история одного fail-open

Этот раздел — про реальный инженерный факап, который мы поймали на тестах.

В версии v3.0 хук видел маркер <<EOF, решал «а, это heredoc, внутри него кавычки сырые» и пропускал команду целиком. Логика казалась понятной: раз heredoc читается шеллом до маркера-терминатора, кавычки внутри якобы не участвуют в балансе. Но на тестах поймали fail-open. Модель сгенерировала cat <<EOF … EOF \n echo "unclosed. Хук увидел маркер, пропустил всё целиком — и незакрытая кавычка после блока повесила шелл. То есть пробел в модели языка превратился в открытые ворота.

В v3.1 закрыли: написали маскирование тела heredoc (mask_heredocs). Тело затирается пробелами с сохранением смещений, чтобы не сбивать парсер, а всё, что до и после, проверяется FSM. Если сам маркер EOF не закрыт, хук рапортует state=HEREDOC и блокирует запуск. Теперь cat <<EOF со скриптом внутри пропускается, а EOF \n echo "broken — блокируется.

Тестирование: 122 проверки

Проект проверяется на едином кросс-языковом фикстуре (cases.jsonl) в 31 грамматическом случае:

  • 46 проверок на Python-реализацию лексера;

  • 46 проверок на параллельную Node.js-реализацию (контроль побайтового изоморфизма логики);

  • 25 проверок на интеграционную механику хука PreToolUse;

  • 5 проверок self-test детектора дублирования.

Итого: 122 проверки, все PASS.[^1]

Это не тривиальные «юнит-тесты», а состязательные (adversarial) и негативные тесты. В матрицу намеренно зашиты векторы атак, попытки побега из кавычек, полиморфные вложенности, многострочные инъекции и попытки обхода хука.

Итог

Хук опущен на уровень инфраструктуры хоста: агент не может отправить команду в операционную систему, минуя точку перехвата PreToolUse, — при условии, что хук установлен на хосте и переменные окружения не подменены. Вероятностный хаос генерации изолирован детерминированным шлюзом.

[^1]: Актуально на 27.10.2026. Полные цифры тестов, история версий (v3.0→v3.1) и документация — в репозитории: https://github.com/skywhiteangels-cell/llm-token-architect

View the original on Хабр →

KioskNews shows a cleaned-up reading view extracted from the publisher’s page — the original always lives on their site, not ours.