throughline 0.12.9 → 0.14.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -10,6 +10,48 @@ shipped to npm but were not individually tagged on GitHub.
10
10
 
11
11
  ## [Unreleased]
12
12
 
13
+ ## [0.14.0] — 2026-10-05
14
+
15
+ ### 変更
16
+
17
+ - Claude Codeの自動継続を作り直す([ADR 0033](docs/adr/0033-claude-auto-handoff-new-session.md))。0.13.0の方式(自動圧縮を通し、圧縮の直後に同じ会話へ記憶を注入する)は取り下げた。Throughlineは自動圧縮を置き換えるもので、圧縮が走る前に旧い会話を止め、記憶を持った新しい会話で作業を続ける。Codexの自動継続と同じ並びにした。
18
+ 1. 自動圧縮の直前に、`PreCompact`フックが圧縮を止め、`/tl`と同じ印を残す。
19
+ 2. `PreToolUse`フックが、旧い会話の次の道具を実行させずに止める。旧い会話は空にしない。
20
+ 3. 同じprojectに、`claude --bg`で指示を待つ新しい会話を立てる。モデル・推論強度・権限は旧い会話から引き継ぐ。
21
+ 4. 新しい会話の受け口へ、配送ライブラリ(`aiterm-steer-delivery` 0.1.13の`sendClaudeInbox`)で継続の指示を1通送る。届いた時に、止めた時点の依頼と直近の会話の原文を注入する。
22
+ - `throughline auto-handoff enable --host claude`が`PreCompact`と`PreToolUse`のフックを登録し、`disable`が外す。`throughline install`は登録しない。0.13.0の`install`が置いた`PreCompact`フックは、自動継続が無効な端末の`install`で外す。
23
+ - 続きは裏の会話で動く。`claude agents`の一覧と`claude attach <id>`で見られる。Claude Desktopの画面には出ず、後継のターンが終わって入力待ちになった後に開ける。
24
+ - 別の会話から届いた発言(`Another Claude session sent a message:`)は、Claude Codeが後ろに付ける定型の注意書きを落として保存する。
25
+ - 依存の`aiterm-steer-delivery`を0.1.9から0.1.13に上げる。
26
+
27
+ ### 0.13.0から変わらないもの
28
+
29
+ - 圧縮の要約行(`isCompactSummary`)をユーザー発言として保存しない修理は、そのまま有効。
30
+
31
+ ### 確認した範囲
32
+
33
+ - LinuxのClaude Code 2.1.289。対話画面の会話から始めて後継へ3回連続で引き継ぎ、最後の後継が作業を完了した。4つの会話のどれにも圧縮の記録は無く、止めた道具は実行されていなかった。
34
+ - macOS・Windows、Claude Desktopから始まる会話、subagentが動いている最中の引き継ぎは確かめていない。
35
+
36
+ ## [0.13.0] — 2026-10-04
37
+
38
+ ### 追加
39
+
40
+ - Claude Codeの自動継続([ADR 0032](docs/adr/0032-claude-compact-continuation.md))。`throughline auto-handoff enable --host claude [--project <path>]`で有効にすると、自動圧縮の直後に、圧縮前の記録を同じ会話へ注入する。注入するのは、作業途中のユーザー依頼と圧縮直前の発言、直近の会話の原文(上限9,500字)。モデルは追加の入力なしで作業を続ける。既定は無効。手動`/compact`は対象外。
41
+ - Claude Codeは、hookから作業を止めることも、新しい会話へ指示を送ることもできない。Codexのように新しいタスクへは切り替えず、hostが続ける同じ会話へ記憶を渡す。
42
+ - `throughline install`は、Claudeの`PreCompact`フック(`throughline pre-compact`)を登録する。自動継続が無効の時は、判定を記録して抜ける。このフックは圧縮を止めない。
43
+ - `auto-handoff`に`--host claude`を足す。使えるのは`enable`・`disable`・`status`。`--host`を省いた時は今までどおりCodex。
44
+
45
+ ### 修正
46
+
47
+ - 圧縮をまたいだClaudeのターンが、複数のターンに割れて保存されていた不具合を直す。Claude Codeは圧縮の直後に、要約を本文に持つ`user`行(`isCompactSummary: true`)をtranscriptへ書く。0.12.9以前はこの行をユーザーの発言として数え、要約をユーザー発言としてL2へ保存していた。3回圧縮した実機のターンは4ターンとして保存された。圧縮より前のtool入出力はL3に入らなかった。
48
+ - 修正後は、元の依頼と最終回答の1ターンとして保存し、圧縮より前のtool入出力も同じターンのL3へ入れる。Observer feedの`turn_start`は元の依頼のものになる(今までは要約行のターンとして`unknown`)。修正より前に保存した行は変えない。
49
+
50
+ ### 確認した範囲
51
+
52
+ - LinuxのClaude Code 2.1.289(Haiku 4.5)。`claude -p`と対話画面のそれぞれで、自動圧縮2回をまたいで作業が完了し、圧縮のたびに注入が届いた。9,099字の注入はfile化されずに届いた。
53
+ - macOS・Windows、VS Code拡張・Desktop、subagentの中の圧縮は確かめていない。
54
+
13
55
  ## [0.12.9] — 2026-10-04
14
56
 
15
57
  ### 修正
@@ -1634,7 +1676,9 @@ two attempts, instrument first instead of patching again.
1634
1676
 
1635
1677
  ---
1636
1678
 
1637
- [Unreleased]: https://github.com/kitepon/Throughline/compare/v0.12.9...HEAD
1679
+ [Unreleased]: https://github.com/kitepon/Throughline/compare/v0.14.0...HEAD
1680
+ [0.14.0]: https://github.com/kitepon/Throughline/compare/v0.13.0...v0.14.0
1681
+ [0.13.0]: https://github.com/kitepon/Throughline/compare/v0.12.9...v0.13.0
1638
1682
  [0.12.9]: https://github.com/kitepon/Throughline/compare/v0.12.8...v0.12.9
1639
1683
  [0.12.8]: https://github.com/kitepon/Throughline/compare/v0.12.7...v0.12.8
1640
1684
  [0.12.7]: https://github.com/kitepon/Throughline/compare/v0.12.6...v0.12.7
package/README.ja.md CHANGED
@@ -44,6 +44,33 @@ throughline auto-handoff resume --operation <handoff-id> --json
44
44
 
45
45
  [実測と検証範囲](https://github.com/kitepon/Throughline/blob/main/evidence/2026-10-03-codex-auto-continuation.md)を参照してください。
46
46
 
47
+ ## Claude Codeの自動継続
48
+
49
+ ```bash
50
+ throughline auto-handoff enable --host claude --project /absolute/project/path
51
+ throughline auto-handoff status --host claude --json
52
+ throughline auto-handoff disable --host claude
53
+ ```
54
+
55
+ 既定では無効です。`enable`は必要な`PreCompact`と`PreToolUse`のフックを登録し、`disable`が外します。`--project`を省略すると全projectを対象にします。Codexの設定とは別で、互いに影響しません。
56
+
57
+ 自動圧縮を置き換えます。圧縮が走る前に旧い会話を止め、記憶を持った新しい会話で作業を続けます([ADR 0033](docs/adr/0033-claude-auto-handoff-new-session.md))。
58
+
59
+ 1. 自動圧縮の直前に、圧縮を止めます。`/tl`と同じ印を残します。
60
+ 2. 旧い会話の次の道具を、実行させずに止めます。旧い会話は止めるだけで、空にしません。
61
+ 3. 同じprojectに、新しいClaudeの会話を裏で立てます(`claude --bg`)。モデル・推論強度・権限は旧い会話から引き継ぎます。
62
+ 4. 新しい会話へ継続の指示を1通送ります。届いた時に、止めた時点の依頼と直近の会話の原文を注入します(上限9,500字)。
63
+
64
+ 続きは裏の会話で動きます。`claude agents`の一覧と`claude attach <id>`で見られます。Claude Desktopの画面には出ず、後継のターンが終わって入力待ちになった後にDesktopで開けます。手動`/compact`、subagentの中の圧縮、`claude -p`などscriptから起動した会話は対象に含めません。
65
+
66
+ ```bash
67
+ throughline auto-handoff status --host claude --operation <handoff-id> --json
68
+ ```
69
+
70
+ 後継の立ち上げや配送に失敗した時は、固定の理由を記録して止まります。結果が不明な指示は再送しません。圧縮を止めた後にモデルが道具を呼ばずにターンを終えた時は、後継を立てません。印は残るので、1時間以内に開いた新しい会話が記憶を引き継ぎます。
71
+
72
+ 有効にした端末では、Claudeの道具の呼び出しのたびに`PreToolUse`フックが1回走ります。LinuxのClaude Code 2.1.289で実機確認済みです。macOS・Windows、Claude Desktopから始まる会話は未検証です。[実測と検証範囲](https://github.com/kitepon/Throughline/blob/main/evidence/2026-10-05-claude-auto-handoff.md)を参照してください。
73
+
47
74
  ## 所有境界
48
75
 
49
76
  本repositoryは導入、設定、状態、schemaとmigration、診断、復旧、更新、release判断を
package/README.md CHANGED
@@ -43,6 +43,33 @@ throughline auto-handoff resume --operation <handoff-id> --json
43
43
 
44
44
  [実測と検証範囲](https://github.com/kitepon/Throughline/blob/main/evidence/2026-10-03-codex-auto-continuation.md)を参照してください。
45
45
 
46
+ ## Claude Codeの自動継続
47
+
48
+ ```bash
49
+ throughline auto-handoff enable --host claude --project /absolute/project/path
50
+ throughline auto-handoff status --host claude --json
51
+ throughline auto-handoff disable --host claude
52
+ ```
53
+
54
+ 既定では無効です。`enable`は必要な`PreCompact`と`PreToolUse`のフックを登録し、`disable`が外します。`--project`を省略すると全projectを対象にします。Codexの設定とは別で、互いに影響しません。
55
+
56
+ 自動圧縮を置き換えます。圧縮が走る前に旧い会話を止め、記憶を持った新しい会話で作業を続けます([ADR 0033](docs/adr/0033-claude-auto-handoff-new-session.md))。
57
+
58
+ 1. 自動圧縮の直前に、圧縮を止めます。`/tl`と同じ印を残します。
59
+ 2. 旧い会話の次の道具を、実行させずに止めます。旧い会話は止めるだけで、空にしません。
60
+ 3. 同じprojectに、新しいClaudeの会話を裏で立てます(`claude --bg`)。モデル・推論強度・権限は旧い会話から引き継ぎます。
61
+ 4. 新しい会話へ継続の指示を1通送ります。届いた時に、止めた時点の依頼と直近の会話の原文を注入します(上限9,500字)。
62
+
63
+ 続きは裏の会話で動きます。`claude agents`の一覧と`claude attach <id>`で見られます。Claude Desktopの画面には出ず、後継のターンが終わって入力待ちになった後にDesktopで開けます。手動`/compact`、subagentの中の圧縮、`claude -p`などscriptから起動した会話は対象に含めません。
64
+
65
+ ```bash
66
+ throughline auto-handoff status --host claude --operation <handoff-id> --json
67
+ ```
68
+
69
+ 後継の立ち上げや配送に失敗した時は、固定の理由を記録して止まります。結果が不明な指示は再送しません。圧縮を止めた後にモデルが道具を呼ばずにターンを終えた時は、後継を立てません。印は残るので、1時間以内に開いた新しい会話が記憶を引き継ぎます。
70
+
71
+ 有効にした端末では、Claudeの道具の呼び出しのたびに`PreToolUse`フックが1回走ります。LinuxのClaude Code 2.1.289で実機確認済みです。macOS・Windows、Claude Desktopから始まる会話は未検証です。[実測と検証範囲](https://github.com/kitepon/Throughline/blob/main/evidence/2026-10-05-claude-auto-handoff.md)を参照してください。
72
+
46
73
  ## Ownership boundary
47
74
 
48
75
  This repository owns installation, configuration, state, schema and migrations,
@@ -8,6 +8,8 @@
8
8
  * throughline uninstall # hook を削除
9
9
  * throughline process-turn # Stop hook (Claude Code から呼ばれる)
10
10
  * throughline session-start # SessionStart hook (Claude Code から呼ばれる)
11
+ * throughline pre-compact # PreCompact hook (Claude Code から呼ばれる。自動継続: 自動圧縮を止める)
12
+ * throughline pre-tool-use # PreToolUse hook (Claude Code から呼ばれる。自動継続: 旧い会話を止める)
11
13
  * throughline detail <時刻> # L2+L3 詳細取得 (Claude が Bash 経由で呼ぶ想定)
12
14
  * throughline recall --l2|--l1 # 注入案内から辿る pull 用 read-only 記憶取得
13
15
  * throughline handoff-preview # Codex-facing throughline_handoff JSON preview
@@ -52,7 +54,7 @@ const [, , cmd, ...rest] = process.argv;
52
54
 
53
55
  // hook入口では、送信を明示して有効にした端末だけ、未受領のruntime errorを裏で送るprocessを起こす。
54
56
  // 既定(送信設定なし)では小さなファイル1つを見るだけで、通信もprocess起動もしない。
55
- if (['process-turn', 'session-start', 'prompt-submit', 'codex-hook'].includes(cmd)) {
57
+ if (['process-turn', 'session-start', 'prompt-submit', 'pre-compact', 'codex-hook'].includes(cmd)) {
56
58
  try {
57
59
  (await import('../src/runtime-error-report-trigger.mjs')).triggerRuntimeErrorReportBestEffort();
58
60
  } catch {
@@ -80,6 +82,12 @@ switch (cmd) {
80
82
  case 'prompt-submit':
81
83
  await (await import('../src/prompt-submit.mjs')).run();
82
84
  break;
85
+ case 'pre-compact':
86
+ await (await import('../src/claude-pre-compact.mjs')).run();
87
+ break;
88
+ case 'pre-tool-use':
89
+ await (await import('../src/claude-pre-tool-use.mjs')).run();
90
+ break;
83
91
  case 'monitor':
84
92
  (await import('../src/token-monitor.mjs')).main();
85
93
  break;
@@ -233,6 +241,8 @@ switch (cmd) {
233
241
  'session-start': 'HOOK_SESSION_START_FAILED',
234
242
  'prompt-submit': 'HOOK_PROMPT_SUBMIT_FAILED',
235
243
  'process-turn': 'HOOK_PROCESS_TURN_FAILED',
244
+ 'pre-compact': 'HOOK_PRE_COMPACT_FAILED',
245
+ 'pre-tool-use': 'HOOK_PRE_TOOL_USE_FAILED',
236
246
  }[cmd];
237
247
  if (code) {
238
248
  const { recordRuntimeErrorBestEffort } = await import('../src/runtime-error-store.mjs');
@@ -401,6 +411,9 @@ Usage:
401
411
  同じ引き継ぎを再開(結果不明は再送しない)
402
412
  throughline auto-handoff detail --operation <id> --origin <session> --turn <n>
403
413
  凍結したL2/L3を取得
414
+ throughline auto-handoff enable|disable|status --host claude [--project <path>] [--json]
415
+ Claude Codeの自動継続(自動圧縮を止め、記憶を持った
416
+ 新しい会話で続ける)を有効化・無効化・確認
404
417
  throughline doctor Check environment
405
418
  throughline doctor --trim Show trim host boundary diagnostics
406
419
  throughline doctor --codex Show Codex primary diagnostics
@@ -411,6 +424,8 @@ Hook subcommands (called by Claude Code / Codex):
411
424
  throughline session-start SessionStart hook
412
425
  throughline process-turn Stop hook
413
426
  throughline prompt-submit UserPromptSubmit hook (/tl & /clear baton writer)
427
+ throughline pre-compact PreCompact hook (Claude自動継続: 自動圧縮を止めて印を残す)
428
+ throughline pre-tool-use PreToolUse hook (Claude自動継続: 旧い会話を止めて後継を立てる)
414
429
  throughline codex-hook user-prompt-submit Codex current-session refresh prompt hook
415
430
  throughline codex-hook post-tool-use Codex current-session refresh tool-loop hook
416
431
  throughline codex-hook stop Codex Stop hook
@@ -11,6 +11,7 @@
11
11
  | `/tl` | `UserPromptSubmit` が現在の session id を baton として保存する。次の実セッションがその前任を確定的に引き継ぐ |
12
12
  | VS Code の `/clear` | 組み込みコマンドは `UserPromptSubmit` に届かない。`SessionStart source='clear'` を使う auto path が、同じ project の直近前任を凍結して引き継ぐ |
13
13
  | Claude Desktop の `/clear` | `UserPromptSubmit` に届かず、`SessionStart source='clear'` も来ないため自動継承しない。続ける場合は `/clear` の前に `/tl` を実行する |
14
+ | Claude Code の自動圧縮 | `auto-handoff enable --host claude` で有効にした時だけ、`PreCompact`(`trigger: auto`)が圧縮を止めて `/tl` と同じ印を残し、`PreToolUse` が旧い会話を止める。Throughline が `claude --bg` で新しい会話を立て、継続の指示を1通送る。その指示の `UserPromptSubmit` が印を消費して記憶を注入する。既定は無効。手動 `/compact` は対象外。正本は [ADR 0033](adr/0033-claude-auto-handoff-new-session.md) |
14
15
 
15
16
  `/clear` が `UserPromptSubmit` に届かないことは、VS Code と Desktop の対照実測で
16
17
  確定している。`prompt-submit` に残る `/clear` 分岐は host が将来その文字列を渡した場合の
@@ -43,7 +44,7 @@ baton path と auto path は同じ `buildBudgetedResumeContext` を使う。注
43
44
 
44
45
  ## host 別境界
45
46
 
46
- - Claude Code: VS Code `/clear` は auto path、Desktop `/clear` は `/tl` 併用。
47
+ - Claude Code: VS Code `/clear` は auto path、Desktop `/clear` は `/tl` 併用。自動圧縮は、有効にした時だけ、圧縮を止めて新しい会話へ引き継ぐ(ADR 0033)。
47
48
  - Grok: `/tl` の baton 成功後に `throughline grok-continue --session <id>` で後継席を
48
49
  起動する。`/clear` では起動しない。正本は [ADR 0021](adr/0021-grok-host-capture.md)。
49
50
  - Cursor: `/tl` は baton を残すが後継会話を自動起動しない。次の会話が baton を消費する。
@@ -28,6 +28,9 @@
28
28
  最初の成立検証はCodex Desktopで行う。OSごとの仕組みと受入条件は共通にし、
29
29
  差分は既存の `src/os/` と配送ライブラリの環境適合へ閉じ込める。
30
30
  VS Code・CLIとClaude/Grok/Cursorの対応はhostごとに同じ条件で検証する。
31
+ Claude Codeは同じ並び(圧縮を止める、旧い会話を止める、新しい会話を立てる、継続の指示を1通送る)を、
32
+ Claude Codeの入口(hook、`claude --bg`、会話ごとの受け口)で行う([ADR 0033](adr/0033-claude-auto-handoff-new-session.md))。
33
+ この節の処理順と進行状態の細目はCodexだけに適用する。
31
34
  一つのhostの成功で他のhostも対応済みとはしない。
32
35
 
33
36
  ### 根拠と検証範囲
@@ -0,0 +1,90 @@
1
+ # ADR 0032: Claude Code の自動継続は、自動圧縮の直後に同じ会話へ記憶を注入する
2
+
3
+ 日付: 2026-10-04
4
+
5
+ 状態: **決定1〜4・6〜8(同じ会話への注入)は [ADR 0033](0033-claude-auto-handoff-new-session.md) で置き換えた。** オーナーが却下した(自動圧縮が走った時点で負け)。決定5(圧縮の要約行の扱い)は有効。以下は 0.13.0 の時点の記録として残す。
6
+
7
+ ## Context
8
+
9
+ Codex Desktop の自動継続(0.11.0)は、自動圧縮の前に旧タスクを止め、記憶を注入した新しいタスクで
10
+ 作業を続ける。オーナーの指示(2026-10-04)で、同じことを Claude Code でも行う。
11
+
12
+ Claude Code 2.1.289 で、Codex と同じ方式が取れるかを公式の hook 仕様と実機で確かめた。
13
+
14
+ | 必要な操作 | Codex Desktop | Claude Code 2.1.289 |
15
+ |---|---|---|
16
+ | 圧縮の前に作業を止める | `PreCompact` が `continue: false` を返す | `PreCompact` の `continue` は捨てられる。できるのは圧縮を止めること(exit code 2、`decision: "block"`)だけで、止めると会話は圧縮されないまま続き、文脈上限に達した後は元の error で失敗する |
17
+ | 新しい会話を作って記憶を入れる | app-server の `thread/start` と `thread/inject_items` | 公開された入口が無い |
18
+ | 新しい会話へ指示を送って始めさせる | 公式 queue への配送 | `claude-cli://` は入力欄へ下書きを入れるだけで送信しない。`claude --bg` は別の background session を作り、編集の前に git worktree へ移る。元の画面の会話は入れ替わらない |
19
+
20
+ 一方、Claude Code は自動圧縮の後、同じ会話で作業をそのまま続ける。圧縮の直後に
21
+ `SessionStart`(`source: "compact"`)の hook を走らせ、その出力を圧縮後の文脈へ足すと公式文書に書かれている。
22
+
23
+ 実機の結果(Linux、`claude -p`、Haiku 4.5、`CLAUDE_CODE_AUTO_COMPACT_WINDOW=100000`):
24
+
25
+ - `PreCompact` は `trigger: "auto"` で届く。payload は `session_id`・`transcript_path`・`cwd`・`trigger`・`custom_instructions`。
26
+ - 圧縮の後、同じ `session_id` の `SessionStart(source=compact)` が届く。stdout は `<system-reminder>` として
27
+ モデルへ届き、モデルは追加の入力なしで作業を続けた。9,099 字の出力は file 化されずにそのまま届いた。
28
+ - transcript は圧縮より前の行を残したまま、`system`(`subtype: "compact_boundary"`、`compactMetadata.trigger`)と、
29
+ 要約を本文に持つ `user` 行(`isCompactSummary: true`)を足す。圧縮境界の行は `SessionStart` hook が
30
+ 走った時刻より後の時刻を持つ。
31
+ - 1回目の圧縮の前に `PreCompact` が5秒の間隔で2回届いたことがある。
32
+ - 依頼を送った直後(最初の応答の前)に自動圧縮が起きることがある。その回では、`SessionStart(source=compact)` の
33
+ 時点で、送った依頼の行は transcript に書かれていた。行の `promptId` は hook payload の `prompt_id` と同じ値。
34
+
35
+ あわせて、既存の不具合が見つかった。`readTranscript` は要約の `user` 行を人の発言として数えていた。
36
+ 圧縮をまたいだ1ターンは「元の依頼 + 途中の発言」と「要約 + 圧縮後の発言」に割れ、要約が user 発言として
37
+ bodies に入り、圧縮より前の tool 入出力は L3 に入らなかった。3回圧縮した実機のターンは4ターンとして保存された。
38
+
39
+ ## Decision
40
+
41
+ Claude では会話を切り替えず、host が続ける同じ会話へ記憶を渡す。
42
+
43
+ 1. **印**: `PreCompact` hook(`throughline pre-compact`、matcher 無し)が、`trigger: "auto"` で自動継続が有効な
44
+ project の時だけ、`~/.throughline/claude-auto-handoff/<session_id>.json` に印を残す。手動の `/compact` と
45
+ 無効の時は、同じ会話の古い印を消す。subagent の中の圧縮(payload に `agent_id`)は印に触らない。
46
+ hook は圧縮を止めない。stdout へ何も書かず、失敗しても exit code 2 を返さない。
47
+ 2. **注入**: `SessionStart(source=compact)` が印を取り出して消し、次を stdout へ書く(上限 9,500 字、ADR 0014)。
48
+ - ヘッダ: 自動圧縮が起きたこと、下記が圧縮前の原文であること、作業の途中なので入力を待たずに続けること、
49
+ 完了済みの操作を繰り返さないこと。宣言文は求めない。
50
+ - 現在地: 作業途中のターンの user 依頼(4,000 字まで)と、圧縮直前の assistant 発言(1,500 字まで)。
51
+ transcript から読む。上限を超える時は先頭 3/4 と末尾 1/4 を残し、間を抜いた字数を書く。
52
+ - 案内と L2: `/clear` の引き継ぎ(ADR 0016)と同じ。完了済みのターンを新しい順に、丸ごと入る分だけ。
53
+ 3. **作業途中のターンは保存しない**: 注入の前に完了済みのターンを bodies へ回収するが、最後の user 発言の群は除く。
54
+ 途中の発言を代表として保存すると、群レベルの重複除去で最終回答が入らなくなる。保存は今までどおり Stop で行う。
55
+ transcript は hook より遅れて書かれることがある。hook payload の `prompt_id` と、最後の user 行の `promptId` が
56
+ 両方あって食い違う時は、今の依頼がまだ書かれていない。その時は最後の群を現在地に載せず、回収もせず、
57
+ 現在地には「記録からまだ読めない」とだけ書く。
58
+ 4. **trigger は `PreCompact` の payload だけから決める**: `SessionStart` の時点の transcript から推測しない
59
+ (圧縮境界の行は hook より後に書かれる)。手動の `/compact` は対象にしない(Codex と同じ)。
60
+ 5. **要約行**: `isCompactSummary: true` の行は、ターンの始まりにも本文にもしない。`turn_number`(transcript 内の
61
+ 本文行の通し番号)は詰めず、要約行の分も進める。詰めると、要約行を数えていた頃に保存した行と番号が食い違う。
62
+ L3 の範囲も要約行で切らず、元の依頼から取る。
63
+ 6. **有効化**: 既定は無効。`throughline auto-handoff enable --host claude [--project <path>]` が
64
+ `~/.throughline/claude-auto-handoff.json` を書く。`--host` を省いた時は今までどおり Codex。
65
+ Codex の設定とは別のファイルで、互いに影響しない。有効判定は会話を起動した project(`CLAUDE_PROJECT_DIR`)で行う。
66
+ `resume`・`detail`・`worker` と `--operation` は Claude では使えない(引き継ぎ操作の記録を持たない)。
67
+ 7. **hook の登録**: `throughline install` が Claude の `PreCompact` hook を登録する。`enable` は登録が無ければ足す。
68
+ 無効の時、hook は判定を記録して抜ける。
69
+ 8. **失敗**: `PreCompact` の失敗は `HOOK_PRE_COMPACT_FAILED`(severity `warn`。圧縮と作業は続く)、
70
+ 注入の失敗は既存の `HOOK_SESSION_START_FAILED`。どちらも理由を `hook-failures.log` に残す。
71
+ 判定は `inheritance-decision.log` に `phase: "pre-compact"` と `phase: "compact-continuation"` で残す。
72
+
73
+ ## Consequences
74
+
75
+ - Claude Code の圧縮は走る。Codex のように圧縮の本文生成を省けない。圧縮後の文脈は、host の要約と
76
+ Throughline の記録の両方を持つ。食い違う時は記録を正とするよう、ヘッダで指示する。
77
+ - schema は変えない(v12 のまま)。新しい会話も、配送も、常駐 process も作らない。`aiterm-steer-delivery` は使わない。
78
+ - Claude の `/clear` と `/tl` の引き継ぎ(ADR 0014・0016)は変えない。`SessionStart(source=compact)` の
79
+ pending intent の登録も今までどおり行う。
80
+ - 作業途中のターンの tool 入出力は、Stop まで `throughline detail` で取れない。
81
+ - 圧縮をまたいだターンは、元の依頼と最終回答の1ターンとして保存される。Observer feed にも1ターンで出て、
82
+ `turn_start` は元の依頼のものになる。今までは要約行のターンとして `unknown` で出ていた。
83
+ - 修正より前に保存した行はそのまま残る。同じ断片を持つ群は入れ直さない。
84
+ - `throughline install`(`self-update` を含む)は `~/.claude/settings.json` に `PreCompact` を1件足す。
85
+
86
+ ## 確認した範囲
87
+
88
+ - Linux の Claude Code 2.1.289(`claude -p` と対話画面)、Haiku 4.5。自動圧縮2回をまたいで作業が完了し、
89
+ 圧縮のたびに注入が届き、圧縮されたターンが1ターンとして保存された。
90
+ - macOS・Windows、VS Code 拡張・Desktop、subagent の中の圧縮は確かめていない。
@@ -0,0 +1,86 @@
1
+ # ADR 0033: Claude Code の自動継続は、自動圧縮を止めて新しい会話へ引き継ぐ
2
+
3
+ 日付: 2026-10-05
4
+
5
+ ## Context
6
+
7
+ ADR 0032(0.13.0)は、Claude Code の自動圧縮を通し、圧縮の直後に同じ会話へ記憶を注入した。
8
+ オーナーはこの方式を却下した(2026-10-04)。Throughline は自動圧縮を置き換えるもので、
9
+ 自動圧縮が走った時点で負けである。Codex の自動継続(圧縮の前に止めて、新しいタスクへ引き継ぐ)と
10
+ 同じ並びを、Claude Code でも実現する。
11
+
12
+ Codex 版の各段に当たるものを、Claude Code 2.1.289 の公式文書と実機で1段ずつ確かめた。
13
+
14
+ | Codex 版の段 | Claude Code で当たるもの | 確認 |
15
+ |---|---|---|
16
+ | 圧縮の前に旧タスクを止める | `PreCompact` の exit code 2 で圧縮を止める。`PreToolUse` が `permissionDecision: "deny"` と `continue: false` を返すと、その道具は実行されずにターンが止まる | 実機 |
17
+ | 新しいタスクを作る | `claude --bg`。指示を付けずに起動すると、指示を待つ会話が立つ(`idle — send a prompt to start`) | 実機 |
18
+ | 記憶を入れる | 最初の指示の `UserPromptSubmit` hook の出力。`/tl` の印(baton)の引き継ぎと同じ | 実機 |
19
+ | 「続けて」を送る | 会話ごとの受け口(inbox socket)。配送ライブラリ `aiterm-steer-delivery` 0.1.13 の `sendClaudeInbox` | 実機 |
20
+
21
+ `PreCompact` の `continue` は捨てられるので、圧縮を止める hook と作業を止める hook は別になる。
22
+ `PreToolUse` が `continue: false` だけを返すと、その道具は実行されてから止まる。`deny` を一緒に返すと実行されない。
23
+
24
+ Claude Desktop の新しい会話は、画面を開いただけでは process が起動しない(最初の指示が送られた時に起動する)。
25
+ `claude://code/new` の `q` は入力欄への事前入力で、送信しない。受け口が無いので、Desktop の下書きへは配送できない
26
+ (配送ライブラリの持ち主も確認)。後継は Throughline が起動する会話にする(オーナーの判断、2026-10-04)。
27
+
28
+ ## Decision
29
+
30
+ 1. **圧縮を止める**: `PreCompact` hook(`throughline pre-compact`、matcher 無し)が、`trigger: "auto"` で自動継続が
31
+ 有効な project の時だけ、`/tl` と同じ印(baton)と引き継ぎの記録を残し、exit code 2 で圧縮を止める。
32
+ 手動の `/compact`、無効な project、subagent の中の圧縮(payload に `agent_id`)、script から起動した会話
33
+ (`CLAUDE_CODE_ENTRYPOINT` が `sdk-` で始まる)では止めない。
34
+ 2. **旧い会話を止める**: `PreToolUse` hook(`throughline pre-tool-use`、全ての道具)が、記録のある会話の次の道具を、
35
+ 実行させずに止める。旧い会話は止めるだけで、空にしない。同じ応答に並んだ道具も全て止め、後継の立ち上げは1回だけ行う。
36
+ 記録が無い会話では、記録のファイルの有無だけを見て終わる。subagent の中の道具は止めない。
37
+ 3. **新しい会話を立てる**: 切り離した worker が、止めた時点の依頼・モデル・推論強度・権限を記録へ写し、
38
+ 同じ project で `claude --bg` を指示なしで起動する。起動時に `--model`・`--effort`・`--permission-mode` を渡し、
39
+ `--settings '{"worktree":{"bgIsolation":"none"}}'` で同じ作業ツリーを編集させる。
40
+ 依頼と直前の発言は、止めた道具の呼び出しが transcript に書かれるのを待ってから読む(hook の時点では直前の発言が
41
+ まだ書かれていないことがある)。
42
+ 4. **継続の指示を1通送る**: 後継の `SessionStart` hook が、同じ project に立ち上げ中の引き継ぎがある時だけ、
43
+ 自分の受け口(`CLAUDE_CODE_MESSAGING_SOCKET` と token)を控える。worker はそれを読んで消し、
44
+ `sendClaudeInbox` で継続の指示を1通送る。配送は公開 API だけを使い、Throughline の中に送信の実装を持たない。
45
+ 届いた時の `UserPromptSubmit` が baton を消費して前任を合流させ、記憶を注入する。
46
+ 注入は ADR 0016 と同じ予算(9,500 字)で、ヘッダに作業ディレクトリ、現在地に止めた時点の依頼(4,000 字まで)と
47
+ 直前の発言(1,500 字まで)、続けて案内と直近の L2 を載せる。宣言文は求めない。
48
+ 5. **受領**: 継続の指示は引き継ぎ ID を含む。後継の最初の指示がその ID を含む時だけ、受領を記録する。
49
+ `sendClaudeInbox` の結果が `accepted` なら `sent`、`not_sent` なら `failed`、`unknown` なら `unknown` にする。
50
+ 結果が不明な時は再送しない。後継が作業を進めたことまでは確かめない(オーナーの判断: 不要)。
51
+ 6. **連続の引き継ぎ**: 後継も同じ hook で止まり、次の後継へ引き継ぐ。後継の最後の user 発言は前の引き継ぎの
52
+ 継続の指示なので、現在地には前の引き継ぎが運んだ元の依頼を引き続き載せる。
53
+ 7. **道具を呼ばずに終わったターン**: 圧縮を止めた後、モデルが道具を呼ばずにターンを終えたら、Stop hook が記録を取り下げる。
54
+ 印は残るので、1時間以内に開いた新しい会話が記憶を引き継ぐ。
55
+ 8. **引き継ぎ済みの会話**: 後継を立てた会話は、その後も圧縮と道具を止め、後継を案内する。同じ会話から2つ目の後継は立てない。
56
+ 立ち上げに失敗した記録(`failed`)は、次の道具でもう一度立ち上げる。
57
+ 9. **有効化**: 既定は無効。`throughline auto-handoff enable --host claude [--project <path>]` が設定を書き、
58
+ `PreCompact` と `PreToolUse` の hook を登録する。`disable` が2つの hook を外す。`install` は登録しない
59
+ (`PreToolUse` は道具の呼び出しのたびに走るので、使わない端末に置かない)。有効な端末の `install` は2つの hook を保ち、
60
+ 無効な端末の `install` は 0.13.0 が置いた `PreCompact` を外す。
61
+ 10. **状態**: `~/.throughline/claude-auto-handoff/` のファイルに持つ。schema は変えない(v12)。記録は旧い会話の
62
+ session id を鍵にし、24時間を過ぎたものは次の引き継ぎの時に掃除する。token は記録に残さない。
63
+ 11. **失敗**: hook 自身の失敗では圧縮も道具も止めない(exit code 1)。`HOOK_PRE_COMPACT_FAILED`・`HOOK_PRE_TOOL_USE_FAILED`
64
+ (どちらも `warn`)を記録する。worker の失敗は記録の `error_code` に固定の理由で残す。
65
+ 12. ADR 0032 の決定のうち、圧縮の要約行(`isCompactSummary`)の扱い(決定5)は有効。同じ会話への注入(決定1〜4、6〜8)は
66
+ この ADR で置き換える。
67
+
68
+ ## Consequences
69
+
70
+ - 有効な project では、Claude Code の自動圧縮は走らない。旧い会話は、止めた道具の hook の文を出して止まる。
71
+ - 続きは裏の会話(agent view)で動く。`claude agents` の一覧と `claude attach <id>` で見られる。Claude Desktop の画面には
72
+ 出ず、後継のターンが終わって入力待ちになった後に Desktop で開ける。
73
+ - `PreToolUse` hook は、有効な端末の全ての Claude の会話で、道具の呼び出しのたびに1回走る。
74
+ - 止めたターンは Stop hook を通らない。本文は後継の最初の指示の時に回収するが、返事の本文が1つも無いまま止めた
75
+ ターンは bodies に入らず、依頼は現在地だけに載る。止めたターンの tool 入出力は L3 に入らない。
76
+ - 圧縮が「文脈上限の error から回復するため」に起きた時は、止めると元の error でその要求が失敗する(公式文書)。
77
+ この場合の後継の立ち上げは、次の道具の呼び出しまで起きない。
78
+ - 別の会話から届いた発言(`Another Claude session sent a message:`)は、Claude Code が後ろに付ける定型の注意書きを
79
+ 落として保存する。
80
+ - 依存の `aiterm-steer-delivery` を 0.1.9 から 0.1.13 に上げる。Codex の配送の入口は変わらない。
81
+
82
+ ## 確認した範囲
83
+
84
+ - Linux の Claude Code 2.1.289。対話画面の会話から始めて、後継へ3回連続で引き継ぎ、最後の後継が作業を完了した。
85
+ 4つの会話のどれにも圧縮の記録は無く、止めた道具は実行されていなかった。
86
+ - macOS・Windows、Claude Desktop から始まる会話、subagent が動いている最中の引き継ぎは確かめていない。
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "throughline",
3
- "version": "0.12.9",
3
+ "version": "0.14.0",
4
4
  "type": "module",
5
5
  "description": "Persistent memory hooks for Claude Code, Codex, Grok, and Cursor (/clear-safe context compression)",
6
6
  "keywords": [
@@ -55,6 +55,6 @@
55
55
  "unified": "^11.0.5"
56
56
  },
57
57
  "dependencies": {
58
- "aiterm-steer-delivery": "0.1.9"
58
+ "aiterm-steer-delivery": "0.1.13"
59
59
  }
60
60
  }
@@ -0,0 +1,81 @@
1
+ # Claude Code: 圧縮まわりの hook(一次ソース抜粋)
2
+
3
+ 取得: 2026-10-04、`https://code.claude.com/docs/en/hooks.md`・`context-window.md`・`deep-links.md`・`agent-view.md`・`env-vars.md`。
4
+ 実機は Claude Code 2.1.289。設計は [ADR 0033](../../../docs/adr/0033-claude-auto-handoff-new-session.md)(0.13.0 の時点は ADR 0032)。
5
+
6
+ ## PreCompact
7
+
8
+ > Exit with code 2 to block compaction. For a manual `/compact`, the stderr message is shown to the user. You can also block by returning JSON with `"decision": "block"`.
9
+ >
10
+ > Blocking automatic compaction has different effects depending on when it fires. If compaction was triggered proactively before the context limit, Claude Code skips it and the conversation continues uncompacted. If compaction was triggered to recover from a context-limit error already returned by the API, the underlying error surfaces and the current request fails.
11
+ >
12
+ > Claude Code discards a PreCompact hook's `systemMessage` and `continue` fields.
13
+
14
+ - matcher は `manual` / `auto`。入力は共通項目に加えて `trigger` と `custom_instructions`(auto では `null`)。
15
+ - → 圧縮の前に作業を止める口は無い。止められるのは圧縮だけ。
16
+
17
+ ## SessionStart(source=compact)
18
+
19
+ > | `compact` | Auto or manual compaction |
20
+
21
+ > | [SessionStart hooks](/docs/en/hooks-guide#re-inject-context-after-compaction) that match the `compact` source | Claude Code runs them and adds their output to the compacted context |
22
+
23
+ - 入力の `source` は `compact`。auto と manual の区別は入力に無い。
24
+ - → 区別は `PreCompact` の `trigger` で取る。
25
+
26
+ ## PostCompact
27
+
28
+ > PostCompact hooks have no decision control. They can't affect the compaction result but can perform follow-up tasks.
29
+
30
+ - 入力に `compact_summary`(生成された要約)。Throughline は使わない。
31
+
32
+ ## subagent
33
+
34
+ > | `agent_id` | Unique identifier for the subagent. Present only when the hook fires inside a subagent call. Use this to distinguish subagent hook calls from main-thread calls. |
35
+
36
+ ## 作業を止める(PreToolUse)
37
+
38
+ - `PreCompact` の `continue` は捨てられるので、圧縮を止めた後の作業は別の hook で止める。
39
+ - 実機: `PreToolUse` が `{"continue": false, "stopReason": "…"}` だけを返すと、その道具は実行されてからターンが止まる
40
+ (transcript に `hook_stopped_continuation`)。`hookSpecificOutput.permissionDecision: "deny"` を一緒に返すと、
41
+ 道具は実行されずに止まる。同じ応答に並んだ道具には、それぞれ hook が走る。
42
+ - hook で止めたターンでは `Stop` hook は走らない。
43
+
44
+ ## 新しい会話を始める口
45
+
46
+ > A deep link never executes anything on its own. The link only chooses a directory and fills the prompt box.
47
+
48
+ > When you dispatch a background session from agent view or start one with `claude --bg`, the session starts in your working directory. Before editing files, Claude moves the session into an isolated git worktree under `.claude/worktrees/`
49
+
50
+ - `claude-cli://open?q=…` と Claude Desktop の `claude://code/new?q=…&folder=…` は、入力欄へ下書きを入れるだけ。
51
+ Desktop の新しい会話は、最初の指示が送られるまで process が起動しない(実機: hook も受け口も現れない)。
52
+ - `claude --bg "<prompt>"` は別の background session を立てて、すぐ戻る。指示を付けずに起動すると
53
+ `backgrounded · <id> · <name> (idle — send a prompt to start)` と出て、指示を待つ会話が立つ(実機。SessionStart は走る)。
54
+ `--model`・`--effort`・`--permission-mode`・`--name`・`--settings` を受ける。`--session-id` は無視される
55
+ (短い ID は session id の先頭8桁)。別の background session の中から起動すると、出力の ID に色の制御文字が付く。
56
+ - 動いている background session は Desktop へ移せない(`claude --desktop --resume`)。ターンが終わった後は開ける。
57
+
58
+ ## 既存の会話へ外から送る口(inbox socket)
59
+
60
+ > Read this section when a session you expect isn't in the agent list, when you want a script or hook to post into a session, or when a sandboxed command can't reach the socket.
61
+
62
+ - 会話ごとに Unix domain socket(Windows は named pipe)。path は hook と Bash へ `CLAUDE_CODE_MESSAGING_SOCKET`、
63
+ 会話ごとの token は `CLAUDE_CODE_MESSAGING_TOKEN`。起動時に `--messaging-socket-path` でも指定できる。
64
+ - 送る形は Claude Code 本体の中の例: 1行の JSON `{"type":"user","message":{"role":"user","content":"…"}}`。
65
+ 先頭に `{"type":"auth","token":"…"}`(macOS・Linux は任意、Windows は必須)。
66
+ - 実機: 入力待ちの会話へ送るとターンが始まり、`UserPromptSubmit` hook が `prompt` にその文を持って走る。
67
+ transcript では `origin: {kind: "peer"}`、本文の前後に Claude Code が送り主の行と定型の注意書きを付ける。
68
+ - 製品の配送は `aiterm-steer-delivery` 0.1.13 の `sendClaudeInbox` を使う(受領の確認は呼び出し側の観測)。
69
+
70
+ ## 自動圧縮の窓
71
+
72
+ > | `CLAUDE_CODE_AUTO_COMPACT_WINDOW` | Set the auto-compact window in tokens, from `100000` to `1000000`. … |
73
+
74
+ - 実機試験だけで使う。製品は依存しない。
75
+
76
+ ## 実機で見た transcript(2.1.289)
77
+
78
+ - 圧縮の後に `{"type":"system","subtype":"compact_boundary","compactMetadata":{"trigger":"auto","preTokens":…,"postTokens":…}}`。
79
+ - 続けて `{"type":"user","isCompactSummary":true,"isVisibleInTranscriptOnly":true,"message":{"role":"user","content":"This session is being continued from a previous conversation that ran out of context. …"}}`。
80
+ - `SessionStart:compact` の hook 出力は `attachment`(`type: "hook_success"`)として残り、モデルには `<system-reminder>` で届く。
81
+ - 圧縮より前の行は消えない。
package/rag/INDEX.md CHANGED
@@ -27,6 +27,10 @@ rag/
27
27
  - [fork rolloutのmetadata実測](01-hooks/codex-fork-rollout-metadata.md) — 子の先頭metadataの後に親のmetadataと履歴がコピーされる並びを実機で確認。自動継続の誤ったthread不一致判定を最小fixtureと実rolloutで再現・修正した。
28
28
  - [PreCompact・記憶注入・配送の一次ソース抜粋](01-hooks/raw/codex-auto-handoff-extract.md) — 公式仕様と配送ライブラリの固定source。[Desktop実測](https://github.com/kitepon/Throughline/blob/main/evidence/2026-10-03-codex-auto-continuation.md)でauto停止・新規idle taskの自動開始・配送process終了後の継続が成立した。cold readの途中状態と表示後の権限更新を観測し、[設計](../docs/05_codex_first_roadmap.md#自動新規タスク継続の設計案)へ反映した。
29
29
 
30
+ ## Claude自動継続の根拠
31
+
32
+ - [圧縮・会話の起動・受け口の一次ソース抜粋](01-hooks/raw/claude-compact-hooks-extract.md) — `PreCompact`は圧縮を止められる(exit code 2)が`continue`は捨てられる。`PreToolUse`の`deny`+`continue:false`で道具を実行させずにターンを止められる。`claude --bg`は指示なしで起動すると指示待ちの会話が立つ。会話ごとの受け口(inbox socket)へ外から1通送ると、入力待ちの会話でターンが始まる。[実測](https://github.com/kitepon/Throughline/blob/main/evidence/2026-10-05-claude-auto-handoff.md)で、対話の会話から後継へ3回連続で引き継ぎ、圧縮は1回も走らなかった。Finding 6・第三仮説(PreCompactでの再投影)に対する現行の答えは[ADR 0033](../docs/adr/0033-claude-auto-handoff-new-session.md)。
33
+
30
34
  ## Question this RAG was built to answer
31
35
 
32
36
  > Throughline は「コンテキスト削減しつつ過去の記憶を一切失わない」と定義されている。記憶を引き継いでいてもモデルがそれを自分の作業履歴として体感していないなら、その記憶は無意味なコンテキストである。
@@ -0,0 +1,38 @@
1
+ import { readFileSync, mkdirSync, writeFileSync, renameSync } from 'node:fs';
2
+ import { homedir } from 'node:os';
3
+ import { join, dirname, isAbsolute } from 'node:path';
4
+ import { randomUUID } from 'node:crypto';
5
+ import { normalizeProjectPathForCompare, sameProjectPath } from './project-path.mjs';
6
+
7
+ // Claude の自動継続の設定。Codex の設定(codex-auto-handoff.json)とは別のファイルで、互いに影響しない。
8
+ export const CLAUDE_AUTO_HANDOFF_CONFIG_SCHEMA = 'throughline.claude-auto-handoff.v1';
9
+ export const claudeAutoHandoffConfigPath = () => join(homedir(), '.throughline', 'claude-auto-handoff.json');
10
+
11
+ export function readClaudeAutoHandoffConfig(path = claudeAutoHandoffConfigPath()) {
12
+ let text;
13
+ try { text = readFileSync(path, 'utf8'); }
14
+ catch (error) {
15
+ if (error.code === 'ENOENT') return { schema: CLAUDE_AUTO_HANDOFF_CONFIG_SCHEMA, enabled: false, projects: [] };
16
+ throw error;
17
+ }
18
+ const config = JSON.parse(text);
19
+ if (config.schema !== CLAUDE_AUTO_HANDOFF_CONFIG_SCHEMA || typeof config.enabled !== 'boolean' ||
20
+ !Array.isArray(config.projects) ||
21
+ config.projects.some(p => typeof p !== 'string' || !isAbsolute(p))) throw new Error('auto_handoff_config_invalid');
22
+ return config;
23
+ }
24
+
25
+ export function writeClaudeAutoHandoffConfig({ enabled, projects = [] }, path = claudeAutoHandoffConfigPath()) {
26
+ if (typeof enabled !== 'boolean') throw new TypeError('auto_handoff_config_invalid');
27
+ const config = { schema: CLAUDE_AUTO_HANDOFF_CONFIG_SCHEMA, enabled,
28
+ projects: projects.map(normalizeProjectPathForCompare) };
29
+ mkdirSync(dirname(path), { recursive: true, mode: 0o700 });
30
+ const temporary = `${path}.${randomUUID()}.tmp`;
31
+ writeFileSync(temporary, `${JSON.stringify(config)}\n`, { mode: 0o600, flag: 'wx' });
32
+ renameSync(temporary, path);
33
+ return config;
34
+ }
35
+
36
+ export function claudeAutoHandoffEnabledFor(config, projectPath) {
37
+ return config.enabled && (config.projects.length === 0 || config.projects.some(p => sameProjectPath(p, projectPath)));
38
+ }