OrcaReplay — запись и повтор запусков AI-кодинг-агентов

· 2 мин чтения
ai-agents debugging observability replay cli
📂 Исходный код на GitHub

Открытый инструмент для отладки кодинг-агентов: локальный прокси и ещё пять слоёв записывают все запросы к модели, вызовы инструментов, команды shell и изменения файлов в одну трассу. Трассу можно повторить точно и офлайн, открыть в одном HTML-файле, нарисовать как граф причин и сравнить модели на одной и той же задаче.

OrcaReplay — запись и повтор запусков AI-кодинг-агентов

Агент сломал код в два часа ночи. Утром надо разобраться: что он делал, почему упал, какой вызов удалил файл. Обычно это превращается в археологию — листаешь лог терминала, перезапускаешь, получаешь другую ошибку, добавляешь print в чужую обвязку агента, то есть в тот код, который его запускает и кормит промптами.

OrcaReplay отвечает на это иначе. Он записывает прогон агента целиком в обычный файл, а потом отдаёт этот файл вам обратно. Прогон можно повторить точно, офлайн и без единого вызова модели. А можно наоборот: взять запись с четвёртого шага, сменить модель и посмотреть, кто с задачей справится.

Что это

Один npm-пакет orcareplay, который ставит в PATH одну команду — orca. Отдельного инструмента под тем же именем не существует, и в вашего агента ничего не встраивается. Нужен Node 20 или новее. Код под Apache-2.0, формат трассы под CC BY 4.0 — чтобы его можно было переписать на другом языке.

Три команды

npm i -g orcareplay

orca record claude              # ваш агент без изменений делает свою работу
orca replay last                # тот же прогон заново: без сети, без токенов
orca replay last --from 4 --model claude-haiku-4-5 --ui

Третья строка даёт больше всего: те же файлы, тот же начальный кусок переписки, но с четвёртого шага работает другая модель. Меняется ровно одна переменная, поэтому вывод что-то значит.

Если агента, ключа и сети пока нет, есть orca quickstart. Команда создаёт маленький проект с настоящим багом и запись того, как агент этот баг чинит, а потом повторяет запись поверх проекта, не вызывая модель: до повтора два теста падают, после — четыре проходят, потрачено ничего.

Как это работает

API моделей не хранят состояние. На каждом ходу агент отправляет всю переписку целиком, включая результаты прошлого хода. Значит, прокси перед моделью видит весь цикл: каждый запрос, каждый поток ответа, каждый вызов инструмента и каждый результат.

OrcaReplay построен на этом свойстве. Он не патчит агента. Он поднимает локальный прокси, ставит две переменные окружения и уходит в сторону.

При повторе прокси не пересылает ничего, чего нет в трассе. Модель не вызывается, токены не тратятся, строка в выводе читается как egress=blocked. Повтор при этом по-настоящему выполняет записанные вызовы инструментов. Важно: повтор — не песочница. Гарантия не распространяется на инструменты, которые сами открывают соединение: команда curl внутри shell, MCP-сервер, который что-то тянет. Если нужна песочница — запускайте повтор внутри неё.

Шесть слоёв записи

Протокол не показывает всего. В нём нет кода возврата команды, реальной длительности, разницы между потоками вывода и упоминания файла, который записан без спроса. Поэтому запись идёт в шесть слоёв:

  • прокси с переменной адреса API — весь разговор с моделью;
  • подмена команд через PATH — код возврата, длительность, разделение stdout и stderr;
  • перехват JSON-RPC — вызовы MCP, с переписыванием конфига;
  • теневой git-индекс — состояние рабочей папки на каждом ходу;
  • перехват fetch — для агентов, у которых адрес API вшит в исходник;
  • слой структуры агента — какие субагенты запускались, кто кому передал управление, сработала ли защита.

Всё это попадает в одну шкалу времени, упорядоченную по тому, когда события на самом деле случились.

Точный повтор, ответвление и сравнение — одна и та же штука

Это не три разных механизма. Это один прокси с курсором — точкой в записанном потоке, где прокси перестаёт отвечать с диска и начинает отвечать из сети.

команда где стоит курсор что получите
orca replay last в конце весь прогон заново, сеть перекрыта — без токенов и разброса
orca replay last --from 4 --model X на чекпойнте 4 ходы до 4-го те же, дальше работает другая модель
orca compare last --from 4 --models a,b на чекпойнте 4, несколько раз одна таблица, одна переменная — модель

Чекпойнт не записывается, а вычисляется: это любое место, где переписка закрыта и сделан снимок рабочей папки. Поэтому каждое ответвление стартует из состояния, которое точно существовало.

Как выглядит охота за багом

Агент должен был починить падающий тест авторизации. Он вышел с кодом 0, тест всё ещё падает. Первое, на что стоит посмотреть, — что агент на самом деле делал:

$ orca show last
run_6473f858b59e  generic-openai@0.1.0  14 events  exit 0

SEQ  KIND   WHAT                                            DETAIL
4    TOOL   edit_file                                       {"path":"auth.ts",…}
6    FILE   auth.ts                                         modified +1 -3
11   SHELL  ["sh","-c","node --check nonexistent-file.ts"]  /tmp/hunt
12   SHELL  shell result                                    exit 1 · 43ms
13   RUN    run ended                                       exit 0

info usage input=201 output=25 cost=$0.004890

Три факта, которых не даёт ни переписка модели, ни код возврата запуска: файл действительно изменился (ход 6, +1 -3), проверка, которую запустил агент, провалилась (ход 12, exit 1), и он всё равно закончил работу. Прогон вышел с кодом 0, потому что агент вышел с кодом 0.

Порядок событий даёт orca show. Что породило что — команда orca graph:

$ orca graph last
FROM              TO               KIND      WHY
3 model.response  4 tool.call      recorded  tool_use block in the response
4 tool.call       6 fs.change      inferred  changed path appears in tool input, same turn
11 shell.exec     12 shell.result  recorded  shell result answers its exec

  1 inferred — derived from this trace, not recorded in it

Рёбра бывают двух видов, и разница важна. Записанное ребро появилось в момент прогона, потому что блок tool_use физически лежит внутри ответа, который его породил. Выведенное ребро orca вычисляет прямо сейчас по правилу, которое называет рядом: снимок файлов делается раз на ход, а не раз на вызов инструмента, поэтому приписать изменение файла к конкретному вызову — хорошее предположение, а не факт. Выведенные рёбра никогда не записываются обратно в трассу.

Одна задача, разные модели

orca compare отвевляет одну запись на несколько моделей с одного чекпойнта, с одними и теми же файлами и одной и той же перепиской, и проверяет каждую командой, которую вы задали:

$ orca compare last --from 5 --models claude-opus-5,claude-haiku-4-5 --verify "npm test"
MODEL             VERDICT  TOKENS  COST       WALL  RUN
claude-opus-5     pass     201/25  $0.004890  0.3s  run_1457b35062ba
claude-haiku-4-5  pass     201/25  $0.000326  0.3s  run_b8ee08479fb6

Обе модели прошли. Одна дешевле в 15 раз.

Когда агента нельзя перенаправить

Подмена адреса API ловит любую обвязку, которая читает переменную с адресом. А Codex CLI, вошедший в аккаунт по подписке ChatGPT, так не записывается: привязаны не адрес, а учётные данные. Токен подписки принимает только бэкенд ChatGPT, а api.openai.com — нет. Если подменить адрес, Codex попросит обычный API-ключ, и вы будете записывать уже другую сессию.

Ответ — --tls-intercept. Он выпускает свой корневой сертификат на один запуск. Сертификат доверяет только тот агент, которого запустил orca, через окружение этого дочернего процесса. В системное хранилище и в браузер он не попадает и удаляется в конце прогона. Orca не предлагает его никуда установить. Хосты вне списка пробрасываются непрочитанными: в трассу попадает только адрес, порт и число байт.

Записи на машине и приватность

Всё лежит в .orca/runs/ внутри того проекта, в котором вы записывали. Глобального хранилища нет, поэтому запись уезжает вместе с рабочей копией. Один каталог прогона — самодостаточная вещь:

.orca/
  runs/run_d0a2ee7ce615/
    manifest.json     # кто, когда, какой адаптер, коммит, счётчики, контрольная сумма
    events.jsonl      # шкала времени, по одному JSON-объекту на строку, только добавление
    blobs/            # содержимое крупнее 4 КБ, адресное и без дублей
    fs/               # теневой git-индекс: рабочая папка на каждом ходу
    redactions.json   # что убрано: правило и количество, никогда значение

Права на файлы — 0600, у пишущей программы нет собственных сетевых соединений. Секреты вычищаются прямо на пути записи: переменные окружения по умолчанию не записываются, заголовки авторизации не пишутся никогда, ключи известных форм и строки с высокой энтропией заменяются на устойчивые заглушки.

Авторы сами предупреждают: это меры снижения риска, а не гарантия. Считайте трассу чувствительной — примерно как история shell плюс дамп памяти. Убрать что-то задним числом можно командой orca scrub. Файловые снимки она переписать не может: объекты git адресованы хешем своего содержимого, и правка одного меняет его имя. Поэтому scrub ищет строку в хранилище снимков и честно говорит, что нашёл, а ключ --drop-fs удаляет хранилище целиком — ценой возможности ответвить прогон.

Ключ от шлюза в трассу не попадает. Он цепляется только к исходящему запросу, а записывается то, что пришло входящим, с авторизацией срезанной.

Чем это отличается от инструментов наблюдаемости

Инструменты наблюдаемости OrcaReplay
Сказать, сколько стоил прогон ✅ ✅
Сказать, какой вызов удалил файл иногда ✅
Запустить агента снова и получить тот же ответ ❌ ✅ из записи, до байта
Сменить модель и продолжить с 4-го шага ❌ ✅
Требовать правки вашего агента обычно обёртка SDK ❌ две переменные окружения
Работать после закрытия терминала ❌ ✅ это файл
Видеть то, что дальше API модели: коды возврата shell, записи файлов ❌ ✅ каждый ход

Кто поддерживается

Решают два вопроса: можно ли обвязку направить на прокси и понимает ли orca формат обмена, который она говорит.

Готовые адаптеры есть у Claude Code (ANTHROPIC_BASE_URL), Codex CLI, opencode, grok-cli, OpenClaw, goose, OpenAI Agents SDK и Vercel AI SDK. Фреймворки запускаются через общий адаптер: LangGraph, CrewAI, Aider, OpenHands, Haystack, browser-use, IndexRAG и RAG-пайплайны на LlamaIndex, GraphRAG, LightRAG. Все они проверяются в CI на реальном фреймворке с заглушкой вместо API: запись, убийство источника, офлайн-повтор.

Агент, который не читает ни одной переменной с адресом, тоже записывается — командой orca record exec --tls-intercept -- python bot.py. А агент, которого вообще нет на вашей машине, — командой orca attach: она держит прокси открытым и печатает блок переменных для вставки на другой стороне.

Свой адаптер — это примерно двадцать строк, если ваша обвязка читает переменную с адресом.

Для агента, скрипта и CI

Трасса — это файл, а обычные инструменты наблюдаемости с файлами не работают. Самая полезная задача про упавший прогон — такая, которую может задать агент: повтори мой последний прогон и скажи, где разошлось. Все команды отдают данные: --json печатает один JSON-документ в stdout, диагностику — в stderr. Как инструменты для агента, orca mcp отдаёт хранилище трасс по stdio шестью инструментами, среди них orca_replay и orca_compare.

Из кода — класс Orca:

import { Orca } from 'orcareplay';

const orca = new Orca({ cwd: process.cwd() });
const { unmatched, divergences } = await orca.replay('last');

Состояние и лицензия

Проект ранний. Формат трассы версии 0 — работающий скелет трёх команд: record, replay, compare. Всё перечисленное в README проверяется 1 393 тестами, проверкой соответствия формата трассы и проверкой нейтральности плагинного API, на Node 20 и 22.

Что ломалось на живом агенте, собрано в отдельном документе: запись реального исправления реального бага в Claude Code, офлайн-повтор, ответвление с чекпойнта и экспорт. Это сломало четыре вещи, которые не могли сломаться в тесте. Общий архитектурный документ и список интеграций ссылаются на конкретные файлы и числа.

Кто делает продукт, видно сразу: команда OrcaRouter, у которой OrcaRouter и есть модельный шлюз. Orca предлагает его как значение по умолчанию в orca setup и подписывает им экспортированные карточки. На модели это не влияет: любой путь к модели остаётся обычным URL, который можно указать куда угодно.

Источник: https://github.com/Continuum-AI-Corp/OrcaReplay