Skip to content

Поддержать сессии с hook-плагинами в active_chain (OpenIsland) - #1

Open
blackbalancef wants to merge 1 commit into
Glym143:mainfrom
blackbalancef:fix/active-chain-hook-plugins
Open

Поддержать сессии с hook-плагинами в active_chain (OpenIsland)#1
blackbalancef wants to merge 1 commit into
Glym143:mainfrom
blackbalancef:fix/active-chain-hook-plugins

Conversation

@blackbalancef

Copy link
Copy Markdown

Проблема

В сессиях, где включены хуки от внешних утилит (например, OpenIsland — Dynamic Island для macOS), Claude Code на каждое событие (PreToolUse, PostToolUse, Stop, SessionStart…) пишет в .jsonl отдельный узел type=attachment с подтипом hook_success. У него собственный uuid и parentUuid, указывающий на предыдущее сообщение. В результате каждое user/assistant получает attachment-потомка и перестаёт быть листом.

active_chain() искал листы только среди {user, assistant}:

leaves = [o for o in objs
          if o.get("uuid") and o["uuid"] not in children
          and o.get("type") in ("user", "assistant")]

На таких сессиях множество листьев пустое → sys.exit("Не найдено ни одного листа диалога — файл пуст или повреждён."). Пользователь не может срезать контекст, хотя файл целый.

Воспроизведение

В .jsonl сессии с активным OpenIsland видно:

attachment subtypes:
  111  hook_success
    4  task_reminder
    1  deferred_tools_delta
    ...

hook_success by hookName:
  35  PreToolUse:Bash
  34  PostToolUse:Bash
   7  Stop
   ...

user:      total=62, has any child=62, has hook_success child=51
assistant: total=101, has any child=101, has hook_success child=45

Все 62/62 user-сообщений имели потомка — ни одного листа в {user,assistant}.

Решение

Лист — любой uuid без потомков. attachment-листы становятся допустимым стартом, а цепочка назад по parentUuid корректно проходит через user/assistant.

-    leaves = [o for o in objs
-              if o.get("uuid") and o["uuid"] not in children
-              and o.get("type") in ("user", "assistant")]
+    leaves = [o for o in objs if o.get("uuid") and o["uuid"] not in children]

Что не меняется:

  • msg_count всё так же считает только user/assistant для отображения.
  • next_user_cut всё так же выравнивает рез по user-границе.
  • На сессиях без hook-плагинов лист и так был user/assistant — поведение идентично.

Тестирование

  1. Песочница — копия живой сессии (354 объекта, 113 attachment'ов, 0 листов в u/a).

    • До фикса: Не найдено ни одного листа диалога → exit.
    • После фикса: активная ветка 244 узла ≈ 35 486 токенов, рез на 30k → удалено 244 записи цепочки, новый корень — user, broken=False.
  2. Живая сессия (с открытым процессом claude и OpenIsland-хуками):

    • Файл: 828KB → 487KB (−41%).
    • Реальный контекст после --resume: 157k → 96k (−61k освобождено).
    • Дописанные после реза сообщения не стали orphan'ами (parentUuid указывает на сохранённый хвост).

Не покрыто этим PR

  • Orphan-узлы из параллельных веток (например, после /compact или серии --resume) остаются в файле после реза. Они не ломают --resume (Claude Code строит активную ветку от листа назад), но засоряют файл. Уборка orphan'ов — отдельная задача.

Ограничения

  • В очень редкой ситуации, когда в файле вообще нет user/assistant (только attachment'ы), next_user_cut вернёт None и ccclean честно сообщит «не нашёл границу — попробуй меньший объём». До фикса в таком случае был бы выход на этапе листа, после — на этапе границы. Поведение по сути то же — отказ резать пустой диалог.

Утилиты вроде OpenIsland регистрируют хуки на PreToolUse/PostToolUse
и пр. Claude Code пишет результат каждого срабатывания как attachment-
узел (subType=hook_success) с собственным uuid и parentUuid, привязанным
к предыдущему user/assistant. В таких сессиях каждое сообщение перестаёт
быть листом — у него есть attachment-потомок.

active_chain раньше искал листы только среди {user,assistant} и на таких
сессиях не находил ни одного, выходя с "Не найдено ни одного листа".

Решение: лист — любой uuid без потомков. От attachment-листа цепочка
назад по parentUuid собирается корректно через user/assistant, msg_count
по-прежнему считает только user/assistant, next_user_cut выравнивает рез
по user-границе. На сессиях без hook-плагинов поведение не меняется.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
@blackbalancef
blackbalancef force-pushed the fix/active-chain-hook-plugins branch from a60ee46 to eb67172 Compare June 10, 2026 14:38
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant