---
title: Просмотр сессий
sidebar:
  order: 10
---

`ocr viewer` — небольшой встроенный HTTP-сервер, который отображает прошлые
сеансы ревью в удобном для браузера интерфейсе. Внешние зависимости не нужны:
сеансы читаются непосредственно из файлов JSONL, записываемых OCR на диск во
время каждого ревью.

## Запуск

```bash
ocr viewer                  # binds localhost:5483
ocr viewer --addr :3000     # bind to all interfaces on port 3000
ocr viewer --addr 0.0.0.0:8080   # bind on all interfaces
```

Адрес по умолчанию — `localhost:5483`. Сервер работает на переднем плане;
`Ctrl+C` останавливает его. При каждом запросе сеансы лениво считываются из
`~/.opencodereview/sessions/`, поэтому ревью, запущенное в другом терминале,
появится сразу после создания его файла JSONL.

> **Защита от DNS rebinding.** Средство просмотра сверяет заголовок `Host` со
> списком разрешённых адресов loopback (`localhost`, `127.0.0.1`, `::1`).
> Конкретный хост привязки (например, `--addr 192.168.1.10:5483`) добавляется
> автоматически, а **wildcard-привязки** (`:3000`, `0.0.0.0`, `::`) — нет.
> Поэтому при обращении к интерфейсу по IP-адресу или имени хоста в локальной
> сети возвращается `forbidden host`. Чтобы открыть wildcard-привязку, задайте
> в `OCR_VIEWER_ALLOWED_HOSTS` разделённый запятыми список разрешённых имён
> хостов (например, `OCR_VIEWER_ALLOWED_HOSTS=box.local,192.168.1.10`).

## Три страницы

Средство просмотра предоставляет три URL:

| URL | Что отображается |
|---|---|
| `/` | Список всех репозиториев, для которых на диске есть сеансы. |
| `/r/{repo}` | Список сеансов одного репозитория, начиная с самых новых. |
| `/r/{repo}/{sessionID}` | Полные сведения об одном сеансе. |

`{repo}` — строка с закодированным путём (разделители `/` и `\` заменяются на
`-`, а двоеточия — на `_`; то же кодирование используется для имён каталогов
на диске). Обычно вводить её не требуется: достаточно переходить по ссылкам.

### `/` — список репозиториев

Для каждого репозитория, содержащего хотя бы один сеанс, отображаются путь,
общее количество сеансов и время последней активности.

### `/r/{repo}` — список сеансов репозитория

Для каждого сеанса отображаются ID (UUID), имя ветки (если OCR смог его
определить), режим ревью, модель, количество файлов, длительность и время
начала.

### `/r/{repo}/{sessionID}` — сведения о сеансе

Страница сведений наиболее информативна. Она содержит:

1. **Заголовок** — диапазон diff, модель, ветка, общее число токенов и
   длительность запуска.
2. **Группу файлов** — отдельный блок для каждого проверенного файла. Внутри
   каждого файла расположены пять дорожек «типов задач»:

| Тип задачи | Когда отображается |
|---|---|
| `plan_task` | Выполнялся этап планирования (файл ≥ `PLAN_MODE_LINE_THRESHOLD`). |
| `main_task` | Для каждого файла. Основной цикл ревью. |
| `review_filter_task` | Для файла выполнялся этап фильтрации комментариев после ревью. |
| `memory_compression_task` | Зона active+compress превысила 60 % / 80 % бюджета. |
| `re_location_task` | Не удалось привязать `code_comment`, поэтому выполнялся резервный поиск позиции. |

Каждая дорожка представляет собой горизонтальную полосу **карточек задач** —
по одной на каждый цикл запроса к LLM. Цвет карточки зависит от типа задачи,
поэтому сразу видно, какие этапы заняли основную часть запуска.

## Содержимое карточки задачи

Щёлкните карточку задачи, чтобы развернуть её. Каждая карточка содержит:

- **строку заголовка** — номер запроса, значок модели, значок токенов
  (`P:` — промпт / `C:` — завершение, а при наличии `CR:` / `CW:` — чтение /
  запись кеша), значок длительности и значок ошибки, если цикл завершился со
  сбоем;
- **Response** — исходный ответ ассистента, включая все блоки рассуждений /
  `thinking`;
- **Tool calls** — каждый вызов инструмента с аргументами и возвращённым
  результатом (можно свернуть).

Полный список сообщений, отправленных модели, и определения доступных
инструментов **не** отображаются в интерфейсе карточки. При необходимости
просмотрите расшифровку JSONL напрямую: поле `messages` каждой записи
`llm_request`.

## Сценарии использования

Средство просмотра рассчитано на три рабочих процесса:

### «Почему модель это сказала?»

Откройте комментарий в терминальном выводе, найдите файл в средстве просмотра и
пройдите по его дорожке `main_task`. Раунд, создавший комментарий, находится в
карточке, чьи **вызовы инструментов** содержат нужный `code_comment`. Раздел
Response этой карточки показывает рассуждения модели. Чтобы увидеть точный
промпт и отправленный модели контекст, откройте в расшифровке JSONL запись
`llm_request` с соответствующим номером запроса (её поле `messages`).

### «Почему для этого файла нет замечаний?»

Файл **без комментариев** считается успешно проверенным, только если модель
*намеренно* вызвала `task_done`. Если на дорожке есть вызовы инструментов, но
нет `code_comment`, это намеренно завершённое ревью без замечаний. Если дорожка
заканчивается карточкой ошибки, за отсутствием замечаний скрывается сбой —
сообщите о нём как о предупреждении.

### «Что сохранилось или исчезло при сжатии?»

Дорожка `memory_compression_task` показывает каждый раунд сжатия. Внутри раздел
Response содержит получившуюся сводку, а отрисованный XML зоны сжатия,
переданный модели, находится в записи `llm_request` этого раунда, в её поле
`messages`, в расшифровке JSONL. Это полезно при разборе жалобы «модель забыла
предыдущий контекст»: можно определить, были ли нужные сведения отброшены при
сжатии.

## Структура хранения на диске

Средство просмотра читает данные из:

```
~/.opencodereview/sessions/
└── <path-encoded-repo-path>/
    └── <session-id>.jsonl
```

Каждая строка файла JSONL представляет одно событие:

```json
{"type": "llm_request", "filePath": "src/foo.go", "taskType": "main_task", "request_no": 1, "messages": [{"role": "user", "content": "Review this diff…"}], "timestamp": "2026-06-02T10:15:23Z"}
{"type": "llm_response", "filePath": "src/foo.go", "taskType": "main_task", "model": "claude-sonnet-4-6", "content": "Found 2 issues…", "duration_ms": 8421, "usage": {"prompt_tokens": 12450, "completion_tokens": 320}}
{"type": "tool_call", "filePath": "src/foo.go", "tool_name": "file_read", "arguments": "{\"file_path\":\"src/foo.go\",\"start_line\":1,\"end_line\":50}", "result": "File: src/foo.go (Total lines: 220)\nIS_TRUNCATED: false\nLINE_RANGE: 1-50\n1|package foo…", "ok": true, "duration_ms": 14}
```

Строки только добавляются в конец файла. Неполный JSONL означает, что сеанс
был прерван во время выполнения; средство просмотра отображает имеющиеся
данные.

Чтобы освободить место на диске, удалите файлы сеансов целиком. При следующем
запросе средство просмотра заново создаст индекс.

## Конфиденциальность

Расшифровки JSONL содержат **всё**, что отправлялось в LLM и возвращалось ею,
включая код из diff. Они хранятся только на вашем компьютере в каталоге
`~/.opencodereview/`. OCR никуда их не загружает.

Если в ревью присутствует код, который не следует хранить длительное время:

- периодически удаляйте файлы сеансов; или
- направьте вывод `--audience agent --format json` во временный канал в CI и
  запускайте OCR с временным `HOME`, чтобы JSONL не сохранялся.

Экспортёр OpenTelemetry работает отдельно. О том, как не включать содержимое
промптов в экспортируемые трассировки, см. в разделе
[Телеметрия](../telemetry/).

## Когда средство просмотра не подходит

- Для программной постобработки (CI, панели мониторинга) используйте
  `ocr review --format json --audience agent`. Средство просмотра предназначено
  для людей, а не машин.
- Для поиска по множеству сеансов используйте `jq` непосредственно с файлами
  JSONL. Поля поиска в интерфейсе пока нет.

## См. также

- [Архитектура](../architecture/) — как на самом деле работают эти пять типов
  задач.
- [Инструменты](../tools/) — вызовы инструментов, отображаемые в карточках
  `main_task`.
