throughline 0.3.23 → 0.3.25
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/.claude/commands/tl-trim.md +42 -0
- package/.codex-sidecar.yml +62 -0
- package/CHANGELOG.md +583 -0
- package/README.ja.md +42 -5
- package/README.md +400 -23
- package/bin/throughline.mjs +168 -4
- package/codex/skills/throughline/SKILL.md +157 -0
- package/codex/skills/throughline/agents/openai.yaml +7 -0
- package/docs/INHERITANCE_ON_CLEAR_ONLY.md +146 -0
- package/docs/L1_L2_L3_REDESIGN.md +415 -0
- package/docs/PUBLIC_RELEASE_PLAN.md +184 -0
- package/docs/THROUGHLINE_CODEX_DUAL_SUPPORT.md +249 -0
- package/docs/THROUGHLINE_CODEX_FIRST_ROADMAP.md +555 -0
- package/docs/THROUGHLINE_CODEX_MONITOR_IMPLEMENTATION_PLAN.md +220 -0
- package/docs/THROUGHLINE_CODEX_TRIM_IMPLEMENTATION_PLAN.md +528 -0
- package/docs/THROUGHLINE_CODEX_TRIM_ROLLBACK_FIX_PLAN.md +672 -0
- package/docs/archive/CONCEPT.md +476 -0
- package/docs/archive/EXPERIMENT.md +371 -0
- package/docs/archive/README.md +22 -0
- package/docs/archive/SESSION_LINKING_DESIGN.md +231 -0
- package/docs/archive/THROUGHLINE_NEXT_STEPS.md +134 -0
- package/docs/throughline-codex-trim-rollback-incident-report.md +306 -0
- package/docs/throughline-handoff-context.example.json +57 -0
- package/docs/throughline-rollback-context-trim-insight.md +455 -0
- package/package.json +6 -2
- package/src/cli/codex-capture.mjs +95 -0
- package/src/cli/codex-handoff-model-smoke.mjs +292 -0
- package/src/cli/codex-handoff-model-smoke.test.mjs +262 -0
- package/src/cli/codex-handoff-smoke.mjs +163 -0
- package/src/cli/codex-handoff-smoke.test.mjs +149 -0
- package/src/cli/codex-handoff-start.mjs +291 -0
- package/src/cli/codex-handoff-start.test.mjs +194 -0
- package/src/cli/codex-hook.mjs +276 -0
- package/src/cli/codex-hook.test.mjs +293 -0
- package/src/cli/codex-host-primitive-audit.mjs +110 -0
- package/src/cli/codex-host-primitive-audit.test.mjs +75 -0
- package/src/cli/codex-restore-smoke.mjs +357 -0
- package/src/cli/codex-restore-source-audit.mjs +304 -0
- package/src/cli/codex-resume.mjs +138 -0
- package/src/cli/codex-rollback-model-visible-smoke.mjs +373 -0
- package/src/cli/codex-rollback-model-visible-smoke.test.mjs +255 -0
- package/src/cli/codex-sidecar-diagnostics.mjs +48 -0
- package/src/cli/codex-sidecar-dry-run.mjs +85 -0
- package/src/cli/codex-summarize.mjs +224 -0
- package/src/cli/codex-threads.mjs +89 -0
- package/src/cli/codex-visibility-smoke.mjs +196 -0
- package/src/cli/codex-vscode-restore-smoke.mjs +226 -0
- package/src/cli/codex-vscode-rollback-smoke.mjs +114 -0
- package/src/cli/doctor.mjs +503 -1
- package/src/cli/doctor.test.mjs +542 -3
- package/src/cli/handoff-preview.mjs +78 -0
- package/src/cli/help.test.mjs +64 -0
- package/src/cli/install.mjs +227 -4
- package/src/cli/install.test.mjs +207 -4
- package/src/cli/trim.mjs +564 -0
- package/src/codex-app-server.mjs +1816 -0
- package/src/codex-app-server.test.mjs +512 -0
- package/src/codex-auto-refresh.mjs +194 -0
- package/src/codex-auto-refresh.test.mjs +182 -0
- package/src/codex-capture.mjs +235 -0
- package/src/codex-capture.test.mjs +393 -0
- package/src/codex-handoff-model-smoke.mjs +114 -0
- package/src/codex-handoff-model-smoke.test.mjs +89 -0
- package/src/codex-handoff-smoke.mjs +124 -0
- package/src/codex-handoff-smoke.test.mjs +103 -0
- package/src/codex-handoff.mjs +331 -0
- package/src/codex-handoff.test.mjs +220 -0
- package/src/codex-host-primitive-audit.mjs +374 -0
- package/src/codex-host-primitive-audit.test.mjs +208 -0
- package/src/codex-restore-smoke.test.mjs +639 -0
- package/src/codex-restore-source-audit.mjs +1348 -0
- package/src/codex-restore-source-audit.test.mjs +623 -0
- package/src/codex-resume.test.mjs +242 -0
- package/src/codex-rollout-memory.mjs +711 -0
- package/src/codex-rollout-memory.test.mjs +610 -0
- package/src/codex-sidecar-cli.test.mjs +75 -0
- package/src/codex-sidecar.mjs +246 -0
- package/src/codex-sidecar.test.mjs +172 -0
- package/src/codex-summarize.test.mjs +143 -0
- package/src/codex-thread-identity.mjs +23 -0
- package/src/codex-thread-index.mjs +173 -0
- package/src/codex-thread-index.test.mjs +164 -0
- package/src/codex-usage.mjs +110 -0
- package/src/codex-usage.test.mjs +140 -0
- package/src/codex-visibility-smoke.test.mjs +222 -0
- package/src/codex-vscode-restore-smoke.mjs +206 -0
- package/src/codex-vscode-restore-smoke.test.mjs +325 -0
- package/src/codex-vscode-rollback-smoke.mjs +90 -0
- package/src/codex-vscode-rollback-smoke.test.mjs +290 -0
- package/src/db-schema.test.mjs +97 -0
- package/src/haiku-summarizer.mjs +267 -26
- package/src/haiku-summarizer.test.mjs +282 -0
- package/src/handoff-preview.test.mjs +108 -0
- package/src/handoff-record.mjs +294 -0
- package/src/handoff-record.test.mjs +226 -0
- package/src/hook-entrypoints.test.mjs +326 -0
- package/src/package-files.test.mjs +19 -0
- package/src/prompt-submit.mjs +9 -6
- package/src/resume-context.mjs +44 -140
- package/src/resume-context.test.mjs +172 -0
- package/src/session-start.mjs +8 -5
- package/src/state-file.mjs +50 -6
- package/src/state-file.test.mjs +50 -0
- package/src/token-monitor.mjs +14 -10
- package/src/token-monitor.test.mjs +27 -0
- package/src/trim-cli.test.mjs +1584 -0
- package/src/trim-model.mjs +584 -0
- package/src/trim-model.test.mjs +568 -0
- package/src/turn-processor.mjs +17 -10
- package/src/vscode-task.mjs +94 -6
- package/src/vscode-task.test.mjs +186 -6
|
@@ -0,0 +1,455 @@
|
|
|
1
|
+
# Throughline: ロールバックによるコンテキスト整理の気づき
|
|
2
|
+
|
|
3
|
+
## この文書の位置づけ
|
|
4
|
+
|
|
5
|
+
この文書は **rollback-based context trim の設計メモ** です。
|
|
6
|
+
|
|
7
|
+
関連文書:
|
|
8
|
+
|
|
9
|
+
| 文書 | 役割 |
|
|
10
|
+
|---|---|
|
|
11
|
+
| [THROUGHLINE_CODEX_FIRST_ROADMAP.md](THROUGHLINE_CODEX_FIRST_ROADMAP.md) | 2026-05-06 以降の次フェーズ計画。Codex primary と Codex Rewind 互換を先行する |
|
|
12
|
+
| [THROUGHLINE_CODEX_TRIM_ROLLBACK_FIX_PLAN.md](THROUGHLINE_CODEX_TRIM_ROLLBACK_FIX_PLAN.md) | 2026-05-06 incident 後の修正計画。2026-05-08 の controlled smoke 後、automatic mutation は再有効化し、restore-safety / host primitive audit は diagnostics として残す |
|
|
13
|
+
| [THROUGHLINE_CODEX_TRIM_IMPLEMENTATION_PLAN.md](THROUGHLINE_CODEX_TRIM_IMPLEMENTATION_PLAN.md) | この気づきと Claude / Codex 両対応計画を統合した旧計画と実装履歴。完了済み根拠として参照する |
|
|
14
|
+
| [THROUGHLINE_CODEX_DUAL_SUPPORT.md](THROUGHLINE_CODEX_DUAL_SUPPORT.md) | Throughline を Claude primary のまま Codex adapter / sidecar に対応させる architecture brief |
|
|
15
|
+
|
|
16
|
+
この文書は「rollback は欠けていた delete primitive かもしれない」という洞察を残すもの。実装時は、未検証の host primitive を本線仕様にせず、次フェーズ計画 [THROUGHLINE_CODEX_FIRST_ROADMAP.md](THROUGHLINE_CODEX_FIRST_ROADMAP.md) の Codex Rewind 互換 Phase で実測してから本線 UX に進む。
|
|
17
|
+
|
|
18
|
+
2026-05-06 update: Codex app-server の `thread/rollback` / `thread/inject_items` は live host primitive として実測済み。Throughline CLI には明示 `--codex-thread-id` または `THROUGHLINE_CODEX_THREAD_ID` / `CODEX_THREAD_ID` による current-thread identity、rollout/app-server turn count guard、guarded execute が入った。
|
|
19
|
+
|
|
20
|
+
2026-05-07 correction: VS Code restart / reconnect 後に rollback 済み user prompt が復活したように見える incident が起きたため、Codex rollback / inject は restart-safe な context trim primitive としていったん未証明へ戻した。特に `compacted.replacement_history` など、live app-server read/resume 以外の restore source を検証するまで、`$throughline` と Codex Stop hook auto-refresh は mutation を自動実行しない方針にした。Claude `/rewind` 自動化はまだ有効化しない。
|
|
21
|
+
|
|
22
|
+
2026-05-08 unblock: その後の切り分けで、incident thread の retained text は app-server response 上では `aggregatedOutput` など quoted/tool-output field に分類され、controlled rollback model-visible smoke は app-server restart 境界と VS Code reload/reconnect 境界の両方で `not-reproduced` だった。これを受け、Codex `trim --execute --host codex` と Stop hook auto-refresh の過剰 blocker は解除する。`compacted.replacement_history` retention、restore-safety risk、host primitive audit は引き続き diagnostics だが、単独では mutation 前 refusal にしない。DB memory 不在と rollout/app-server turn-count 不一致は引き続き mutation 前 blocker。
|
|
23
|
+
|
|
24
|
+
2026-05-07 host primitive audit: `throughline codex-host-primitive-audit` で installed Codex app-server schema を機械監査した。`thread/rollback` / `thread/inject_items` / `thread/compact/start` / `thread/start` / `thread/fork` / `thread/resume` は存在するが、rollback 済み user text を current-thread の model-visible input へ復活させない deletion / isolation / projection primitive は見つからなかった。`thread/resume(history)` は schema 上 `[UNSTABLE] FOR CODEX CLOUD - DO NOT USE` で、`thread_id` も ignored になるため、Throughline の current-thread repair primitive には採用しない。
|
|
25
|
+
|
|
26
|
+
## 概要
|
|
27
|
+
|
|
28
|
+
Throughline はもともと、ホスト側の制約に対する回避策として作られた。
|
|
29
|
+
|
|
30
|
+
- Claude Code は、蓄積されたモデル可視コンテキストをユーザーが直接編集できなかった。
|
|
31
|
+
- 長いセッションでは、重い tool I/O がコンテキストの大部分を占める。
|
|
32
|
+
- `/clear` や新規セッションでは、有用な作業記憶まで失われる。
|
|
33
|
+
- Throughline は、有用な記憶を外部DBへ保存し、明示的な baton によって次セッションへ引き継ぐことでこれを解決した。
|
|
34
|
+
|
|
35
|
+
今日の重要な気づき:
|
|
36
|
+
|
|
37
|
+
> conversation-only rollback が使えるなら、rollback は「欠けていた delete primitive」になる。
|
|
38
|
+
|
|
39
|
+
これにより、Throughline の将来像が変わる可能性がある。
|
|
40
|
+
|
|
41
|
+
従来:
|
|
42
|
+
|
|
43
|
+
```text
|
|
44
|
+
旧セッション
|
|
45
|
+
-> /tl baton
|
|
46
|
+
-> /clear または新規セッション
|
|
47
|
+
-> 次セッションが curated memory を受け取る
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
新しい可能性:
|
|
51
|
+
|
|
52
|
+
```text
|
|
53
|
+
同一セッション / 同一スレッド
|
|
54
|
+
-> 全turn履歴を Throughline DB に保存
|
|
55
|
+
-> conversation context を開始直後まで rollback
|
|
56
|
+
-> curated L1/L2 memory を再注入
|
|
57
|
+
-> 同じセッション / スレッドで続行
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
これが成立すると、Throughline は「セッション間ハンドオフツール」から、「モデル可視コンテキストの外部マネージャ」へ進化できる。
|
|
61
|
+
|
|
62
|
+
## なぜ重要か
|
|
63
|
+
|
|
64
|
+
元の Throughline 設計は間違っていたわけではない。ホスト側に制約があった。
|
|
65
|
+
|
|
66
|
+
本質的な問題は「セッションをまたぐ必要があること」ではなく、次の一点だった。
|
|
67
|
+
|
|
68
|
+
```text
|
|
69
|
+
モデルが見る蓄積コンテキストを、ユーザーが直接編集できない。
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
Throughline はこの問題を、記憶をホストの外へ逃がすことで解いた。
|
|
73
|
+
|
|
74
|
+
- L1: 古いturnの一行要約
|
|
75
|
+
- L2: 最近の user / assistant テキスト
|
|
76
|
+
- L3: tool I/O、コマンド出力、thinking、system/tool noise などの重い詳細
|
|
77
|
+
|
|
78
|
+
重要なのは、Throughline が価値ある記憶を SQLite に保持していること。
|
|
79
|
+
|
|
80
|
+
もしホストが conversation-only rollback を提供するなら、Throughline は rollback を使ってモデル可視履歴を削除し、自前DBから必要な記憶だけを戻せる。
|
|
81
|
+
|
|
82
|
+
## 標準コンパクションのもったいなさ
|
|
83
|
+
|
|
84
|
+
標準 compaction は便利だが、Throughline の目的とは相性が悪い。
|
|
85
|
+
|
|
86
|
+
Throughline が避けたいのは、重い tool I/O をもう一度モデルに読ませること。
|
|
87
|
+
|
|
88
|
+
標準 compaction は多くの場合、次のような流れになる。
|
|
89
|
+
|
|
90
|
+
```text
|
|
91
|
+
重い蓄積コンテキスト
|
|
92
|
+
-> モデルに送る
|
|
93
|
+
-> モデルが要約する
|
|
94
|
+
-> 軽いコンテキストとして残す
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
つまり、トークンを節約するためにトークンを燃やす。
|
|
98
|
+
|
|
99
|
+
Throughline では、必要な記憶はすでに構造化して保存されている。
|
|
100
|
+
|
|
101
|
+
- user / assistant text はそのまま保存できる
|
|
102
|
+
- tool I/O は SQLite に退避できる
|
|
103
|
+
- summary は lazy に作れる
|
|
104
|
+
- detail は必要なときだけ取り出せる
|
|
105
|
+
|
|
106
|
+
したがって理想は「現在のコンテキストを要約する」ことではない。
|
|
107
|
+
|
|
108
|
+
理想はこれ。
|
|
109
|
+
|
|
110
|
+
```text
|
|
111
|
+
現在の model-visible context を、モデルに再読させずに削除または置換する。
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
## 欲しいプリミティブ
|
|
115
|
+
|
|
116
|
+
理想のホストAPIは、たとえば次のようなもの。
|
|
117
|
+
|
|
118
|
+
```text
|
|
119
|
+
thread/context/replace
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
または:
|
|
123
|
+
|
|
124
|
+
```text
|
|
125
|
+
thread/context/edit
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
ほしい意味論:
|
|
129
|
+
|
|
130
|
+
- UI履歴は残す
|
|
131
|
+
- audit log / transcript / rollout record は残す
|
|
132
|
+
- ローカルファイル変更は維持する
|
|
133
|
+
- 次回モデルへ渡す履歴だけを置換する
|
|
134
|
+
- 外部ツールが生成した memory items を受け取れる
|
|
135
|
+
- rollback / restore 用のメタデータを残せる
|
|
136
|
+
|
|
137
|
+
つまり:
|
|
138
|
+
|
|
139
|
+
```text
|
|
140
|
+
UI履歴は残る。
|
|
141
|
+
詳細ログも残る。
|
|
142
|
+
Throughline DBも残る。
|
|
143
|
+
次の model input だけが変わる。
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
## Codex で見えたこと
|
|
147
|
+
|
|
148
|
+
Codex には、関連する app-server primitive がいくつかある。
|
|
149
|
+
|
|
150
|
+
Codex CLI / app-server documentation から確認できたもの:
|
|
151
|
+
|
|
152
|
+
- `thread/read`
|
|
153
|
+
- `thread/turns/list`
|
|
154
|
+
- `thread/compact/start`
|
|
155
|
+
- `thread/rollback`
|
|
156
|
+
- `thread/inject_items`
|
|
157
|
+
- `turn/start`
|
|
158
|
+
- `turn/steer`
|
|
159
|
+
- `thread/resume`
|
|
160
|
+
- `thread/fork`
|
|
161
|
+
|
|
162
|
+
重要な挙動:
|
|
163
|
+
|
|
164
|
+
- `thread/compact/start` は、同一 thread の手動 history compaction を起動する。
|
|
165
|
+
- `thread/inject_items` は、raw Responses API items を loaded thread の model-visible history へ追加する。
|
|
166
|
+
- `thread/rollback` は、最後の N turns を agent の in-memory context から落とし、future resume でも pruned history が見えるよう rollback marker を永続化する。
|
|
167
|
+
- `ThreadRollbackParams` は `threadId` と `numTurns` だけを持つ。
|
|
168
|
+
- rollback の説明では、thread history のみを変更し、ローカルファイル変更は戻さないとされている。
|
|
169
|
+
|
|
170
|
+
つまり Codex の `thread/rollback` は、かなり conversation-only rollback に近い。
|
|
171
|
+
|
|
172
|
+
2026-05-06 の実測:
|
|
173
|
+
|
|
174
|
+
- Codex CLI `0.128.0-alpha.1` で `stdio://` app-server は newline-delimited JSON request / response で操作できた。
|
|
175
|
+
- `thread/read includeTurns:true` は persisted thread を読める。
|
|
176
|
+
- `thread/rollback` は loaded thread にだけ効く。persisted thread に直接呼ぶと `thread not found`。
|
|
177
|
+
- `thread/resume` 後に `thread/rollback { numTurns: 1 }` を呼ぶと、1 turn の検証 thread は 0 turns になった。
|
|
178
|
+
- rollback 後に `thread/inject_items` へ developer message item を入れ、次の `turn/start` で marker `TL_PHASE6_INJECT_OK` が model-visible になった。
|
|
179
|
+
- rollout JSONL には injected item が developer role の response item として記録された。
|
|
180
|
+
|
|
181
|
+
残る未検証は、複数 turn の partial rollback、rollback marker の resume 後挙動、ローカルファイル変更が戻らないことの実ファイル付き smoke、UI 表示差分。
|
|
182
|
+
|
|
183
|
+
参考:
|
|
184
|
+
|
|
185
|
+
- <https://github.com/openai/codex/blob/main/codex-rs/app-server/README.md>
|
|
186
|
+
- <https://pkg.go.dev/github.com/tta-lab/codex-server-go/protocol#ThreadRollbackParams>
|
|
187
|
+
|
|
188
|
+
## Claude で見えたこと
|
|
189
|
+
|
|
190
|
+
Claude Code にも rollback / rewind mechanism がある。
|
|
191
|
+
|
|
192
|
+
Claude Code の `/rewind` は以下をサポートする。
|
|
193
|
+
|
|
194
|
+
- conversation only restore
|
|
195
|
+
- code only restore
|
|
196
|
+
- both code and conversation restore
|
|
197
|
+
- selected point 以降の summarize
|
|
198
|
+
|
|
199
|
+
つまり Claude には、手動UXとして conversation-only rollback がすでにある。
|
|
200
|
+
|
|
201
|
+
参考:
|
|
202
|
+
|
|
203
|
+
- <https://code.claude.com/docs/en/checkpointing>
|
|
204
|
+
|
|
205
|
+
違いは、自動化できる表面にありそう。
|
|
206
|
+
|
|
207
|
+
- Claude は `/rewind` による手動UXが強い。
|
|
208
|
+
- Codex は `thread/rollback` が app-server/API primitive として見えているため、外部ツールから自動化しやすい可能性がある。
|
|
209
|
+
|
|
210
|
+
概念的な綺麗さは、Claude も Codex も同じ。
|
|
211
|
+
|
|
212
|
+
## 新しい中核アーキテクチャ
|
|
213
|
+
|
|
214
|
+
新しいアーキテクチャ:
|
|
215
|
+
|
|
216
|
+
```text
|
|
217
|
+
host session / thread
|
|
218
|
+
|
|
|
219
|
+
v
|
|
220
|
+
Throughline event / log watcher
|
|
221
|
+
|
|
|
222
|
+
v
|
|
223
|
+
Throughline SQLite DB
|
|
224
|
+
- L1 skeletons
|
|
225
|
+
- L2 bodies
|
|
226
|
+
- L3 details
|
|
227
|
+
|
|
|
228
|
+
v
|
|
229
|
+
conversation-only rollback
|
|
230
|
+
|
|
|
231
|
+
v
|
|
232
|
+
curated memory injection
|
|
233
|
+
|
|
|
234
|
+
v
|
|
235
|
+
same session / thread continues
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
ホストの session は作業の器になる。
|
|
239
|
+
|
|
240
|
+
Throughline DB は永続記憶になる。
|
|
241
|
+
|
|
242
|
+
rollback は delete operation になる。
|
|
243
|
+
|
|
244
|
+
injection は restore operation になる。
|
|
245
|
+
|
|
246
|
+
## Codex 候補フロー
|
|
247
|
+
|
|
248
|
+
Codex で考えられる流れ:
|
|
249
|
+
|
|
250
|
+
```text
|
|
251
|
+
1. Throughline が Codex log または app-server events から各turnを捕捉する。
|
|
252
|
+
2. Throughline が thread_id、turn index、timestamp、text、details を記録する。
|
|
253
|
+
3. ユーザーまたは自動処理が /tl-trim を起動する。
|
|
254
|
+
4. Throughline が現在 thread の保存済みturn数を数える。
|
|
255
|
+
5. Throughline が L1/L2/L3 から curated context を組み立てる。
|
|
256
|
+
6. Codex app-server に以下を送る。
|
|
257
|
+
thread/rollback { threadId, numTurns: saved_turn_count }
|
|
258
|
+
7. Codex の model-visible history が pruned される。
|
|
259
|
+
8. Throughline が以下を送る。
|
|
260
|
+
thread/inject_items { threadId, items: curated_memory_items }
|
|
261
|
+
9. ユーザーは同じ Codex thread で続行する。
|
|
262
|
+
```
|
|
263
|
+
|
|
264
|
+
現行実装では、Codex Stop hook 後の 90% automatic refresh は guarded rollback / inject
|
|
265
|
+
mutation を試行する。明示 CLI の `throughline trim --execute --host codex --codex-thread-id <id>`
|
|
266
|
+
も env gate なしで実行する。明示 Codex thread identity、injectable Throughline DB memory、
|
|
267
|
+
rollout/app-server turn count guard は live mutation の最低条件であり、durable success は
|
|
268
|
+
post-execute rollout evidence で `execute-durable-verified` として別判定する。
|
|
269
|
+
|
|
270
|
+
重要な考え:
|
|
271
|
+
|
|
272
|
+
```text
|
|
273
|
+
Throughline は毎turnを記録しているため、安全に rollback できる最大turn数を自分で知っている。
|
|
274
|
+
```
|
|
275
|
+
|
|
276
|
+
ただし、L1 / L2 を戻すだけでは足りない。
|
|
277
|
+
|
|
278
|
+
初期 Throughline で実際に落とし穴になったのは、「DB から L1 / L2 は確かに引き継がれるが、モデルがそれを **今やっている作業** として認識しない」ことだった。現行 `/tl` はこの問題を、旧セッションの Claude 自身に in-flight memo(次の一手 / 方針 / 未解決 / TODO)を書かせ、その memo を resume context の先頭に置くことで解消している。
|
|
279
|
+
|
|
280
|
+
ただし、memo を先頭に置くこと自体が唯一のスマートな解ではない。Claude / Codex の通常 context では、任意の text に「これは今取り込み中」という隠し属性が付くわけではなく、role / instruction authority、現在 turn への近さ、section boundary、metadata、最新 user request との接続によって、モデルが作業文脈として読む。したがって Throughline は、rollback 後に注入する curated memory を「過去ログ」ではなく「現在タスクに使う作業コンテキスト」として明示し、冒頭と末尾の両方に読み方を置く必要がある。
|
|
281
|
+
|
|
282
|
+
`/tl-trim` でも同じ制約を維持する。rollback 後に注入する curated memory は、L1 / L2 / L3 references だけでなく、current-work memo、active work thread framing、後続行優先 / supersession rule を含める。これを省くと、trim 後のモデルは「過去ログは読めるが、作業の続きとして走れない」状態に戻る。
|
|
283
|
+
|
|
284
|
+
## Claude 候補フロー
|
|
285
|
+
|
|
286
|
+
Claude で考えられる流れ:
|
|
287
|
+
|
|
288
|
+
```text
|
|
289
|
+
1. Throughline が hooks / transcript processing で毎turnを捕捉する。
|
|
290
|
+
2. ユーザーが trim operation を起動する。
|
|
291
|
+
3. Claude conversation を conversation-only rewind で戻す。
|
|
292
|
+
4. Throughline が curated context を再注入する。
|
|
293
|
+
5. ユーザーは同じ Claude session で続行する。
|
|
294
|
+
```
|
|
295
|
+
|
|
296
|
+
未解決なのは、Claude の conversation-only rewind を外部ツールから綺麗に自動化できるか。
|
|
297
|
+
|
|
298
|
+
できない場合、UX は手動寄りになる。
|
|
299
|
+
|
|
300
|
+
```text
|
|
301
|
+
/rewind conversation only
|
|
302
|
+
/tl restore
|
|
303
|
+
```
|
|
304
|
+
|
|
305
|
+
または Throughline がこの手順をユーザーに案内する。
|
|
306
|
+
|
|
307
|
+
## Throughline がすでに重要データを持っている理由
|
|
308
|
+
|
|
309
|
+
Throughline は毎turnを捕捉する。
|
|
310
|
+
|
|
311
|
+
したがって次の情報を知っている。
|
|
312
|
+
|
|
313
|
+
- current session id
|
|
314
|
+
- current thread id
|
|
315
|
+
- captured turn count
|
|
316
|
+
- どのturnが L1 / L2 / L3 か
|
|
317
|
+
- どの details が退避済みか
|
|
318
|
+
- 安全に rollback できる最大深度
|
|
319
|
+
- 再開に必要な curated memory
|
|
320
|
+
|
|
321
|
+
これは rollback-based trimming に必要な情報そのもの。
|
|
322
|
+
|
|
323
|
+
rollback 後にホストがすべてを覚えている必要はない。Throughline が覚えている。
|
|
324
|
+
|
|
325
|
+
## 設計の変化
|
|
326
|
+
|
|
327
|
+
旧アイデンティティ:
|
|
328
|
+
|
|
329
|
+
```text
|
|
330
|
+
Throughline は、セッション間の明示的ハンドオフシステム。
|
|
331
|
+
```
|
|
332
|
+
|
|
333
|
+
新しい可能性:
|
|
334
|
+
|
|
335
|
+
```text
|
|
336
|
+
Throughline は、model-visible context の外部マネージャ。
|
|
337
|
+
```
|
|
338
|
+
|
|
339
|
+
旧中核操作:
|
|
340
|
+
|
|
341
|
+
```text
|
|
342
|
+
memory を保存する
|
|
343
|
+
-> clear / new session
|
|
344
|
+
-> memory を注入する
|
|
345
|
+
```
|
|
346
|
+
|
|
347
|
+
新中核操作:
|
|
348
|
+
|
|
349
|
+
```text
|
|
350
|
+
memory を保存する
|
|
351
|
+
-> conversation を rollback する
|
|
352
|
+
-> memory を注入する
|
|
353
|
+
```
|
|
354
|
+
|
|
355
|
+
これは大きなUX改善になる。
|
|
356
|
+
|
|
357
|
+
- 新規セッションが不要
|
|
358
|
+
- 通常ケースでは session baton が不要
|
|
359
|
+
- 標準 compaction によるトークン消費が不要
|
|
360
|
+
- current session / thread に閉じるため、誤継承が起きにくい
|
|
361
|
+
- 詳細記憶は必要なときだけ取得できる
|
|
362
|
+
|
|
363
|
+
## 重要な注意点
|
|
364
|
+
|
|
365
|
+
まだ検証が必要なこと。
|
|
366
|
+
|
|
367
|
+
Codex:
|
|
368
|
+
|
|
369
|
+
- 複数 turn thread で `thread/rollback` の partial `numTurns` が期待通りに効くか。
|
|
370
|
+
- `thread/rollback` は thread 内の全turn数と同じ `numTurns` を受け入れるか。単一 turn の full rollback は成功済み。
|
|
371
|
+
- `thread/inject_items` は次の model-visible context に確実に入るか。developer message item では成功済み。
|
|
372
|
+
- `thread/inject_items` が受け付ける Responses API item 形式の許容範囲は何か。developer message item は成功済み。
|
|
373
|
+
- rollback 後、UI履歴はどう見えるか。消えるのか、marker付きで残るのか。
|
|
374
|
+
- rollback marker は `thread/resume` 後も期待通りに効くか。
|
|
375
|
+
- full rollback が token accounting を壊したり、不要な auto-compaction を誘発しないか。
|
|
376
|
+
- 実行中turnでも可能か、それとも idle 状態でのみ可能か。
|
|
377
|
+
|
|
378
|
+
Claude:
|
|
379
|
+
|
|
380
|
+
- `/rewind` conversation-only を自動化できるか。
|
|
381
|
+
- VS Code extension でも十分な制御ができるか、それとも CLI が必要か。
|
|
382
|
+
- 新規セッションなしで Throughline memory を再注入できるか。
|
|
383
|
+
- UI automation に頼らず、安全で反復可能な操作にできるか。
|
|
384
|
+
|
|
385
|
+
## コマンド候補
|
|
386
|
+
|
|
387
|
+
将来のコマンド名候補:
|
|
388
|
+
|
|
389
|
+
```text
|
|
390
|
+
/tl-trim
|
|
391
|
+
/tl-prune
|
|
392
|
+
/tl-rollback
|
|
393
|
+
/tl-repack
|
|
394
|
+
/tl-context
|
|
395
|
+
```
|
|
396
|
+
|
|
397
|
+
想定する意味:
|
|
398
|
+
|
|
399
|
+
```text
|
|
400
|
+
/tl-trim
|
|
401
|
+
現在状態を Throughline DB に保存する。
|
|
402
|
+
model-visible conversation を安全な範囲まで rollback する。
|
|
403
|
+
curated memory を注入する。
|
|
404
|
+
同じ session / thread で続行する。
|
|
405
|
+
```
|
|
406
|
+
|
|
407
|
+
バリエーション候補:
|
|
408
|
+
|
|
409
|
+
```text
|
|
410
|
+
/tl-trim --keep-recent 20
|
|
411
|
+
/tl-trim --all
|
|
412
|
+
/tl-trim --dry-run
|
|
413
|
+
/tl-trim --detail-on-demand
|
|
414
|
+
/tl-trim --no-summary
|
|
415
|
+
```
|
|
416
|
+
|
|
417
|
+
## 次の作業
|
|
418
|
+
|
|
419
|
+
推奨する次の検証:
|
|
420
|
+
|
|
421
|
+
1. 小さな Codex app-server integration harness を Throughline 側に作る。
|
|
422
|
+
2. test thread を開始し、数turn実行する。
|
|
423
|
+
3. turn を最小Throughline風DBまたはJSONへ捕捉する。
|
|
424
|
+
4. `thread/rollback` を partial `numTurns` で呼ぶ。
|
|
425
|
+
5. `thread/rollback` を full `numTurns` で呼ぶ。
|
|
426
|
+
6. rollback 後に `thread/read includeTurns:true` を確認する。
|
|
427
|
+
7. rollback 後の rollout JSONL を確認する。
|
|
428
|
+
8. `thread/inject_items` に simple curated memory item を渡す。
|
|
429
|
+
9. 新しいturnを開始し、モデルが injected memory を見ているか確認する。
|
|
430
|
+
10. rollback + injection 後の resume 挙動を確認する。
|
|
431
|
+
|
|
432
|
+
成功条件:
|
|
433
|
+
|
|
434
|
+
```text
|
|
435
|
+
full または near-full rollback が動く。
|
|
436
|
+
ローカルファイル変更は維持される。
|
|
437
|
+
injected curated memory が次の model turn から見える。
|
|
438
|
+
Throughline が対象 Codex thread を明示的に識別でき、誤 thread を rollback しない。
|
|
439
|
+
標準 compaction は不要。
|
|
440
|
+
同じ thread / session で続行できる。
|
|
441
|
+
```
|
|
442
|
+
|
|
443
|
+
これが通るなら、Throughline は session handoff を超えて進化できる。
|
|
444
|
+
|
|
445
|
+
## 一行の気づき
|
|
446
|
+
|
|
447
|
+
```text
|
|
448
|
+
Rollback is the missing delete primitive.
|
|
449
|
+
```
|
|
450
|
+
|
|
451
|
+
日本語で言うなら:
|
|
452
|
+
|
|
453
|
+
```text
|
|
454
|
+
ロールバックは、欠けていた「削除」プリミティブだった。
|
|
455
|
+
```
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "throughline",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.25",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Claude Code hooks plugin for structured context compression (/clear-safe persistent memory)",
|
|
6
6
|
"keywords": [
|
|
@@ -21,12 +21,16 @@
|
|
|
21
21
|
"files": [
|
|
22
22
|
"bin/",
|
|
23
23
|
"src/",
|
|
24
|
+
"codex/skills/",
|
|
24
25
|
".claude/commands/",
|
|
26
|
+
".codex-sidecar.yml",
|
|
27
|
+
"docs/",
|
|
28
|
+
"CHANGELOG.md",
|
|
25
29
|
"README.md",
|
|
26
30
|
"LICENSE"
|
|
27
31
|
],
|
|
28
32
|
"scripts": {
|
|
29
|
-
"test": "node --test src/*.test.mjs"
|
|
33
|
+
"test": "node --test src/*.test.mjs src/cli/*.test.mjs"
|
|
30
34
|
},
|
|
31
35
|
"engines": {
|
|
32
36
|
"node": ">=22.5"
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import { getDb } from '../db.mjs';
|
|
2
|
+
import { captureCodexRolloutToDb } from '../codex-capture.mjs';
|
|
3
|
+
import { resolveCodexThreadIdentity } from '../codex-thread-identity.mjs';
|
|
4
|
+
|
|
5
|
+
function parseArgs(argv) {
|
|
6
|
+
const out = {
|
|
7
|
+
codexThreadId: null,
|
|
8
|
+
codexHome: null,
|
|
9
|
+
projectPath: process.cwd(),
|
|
10
|
+
json: false,
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
for (let i = 0; i < argv.length; i++) {
|
|
14
|
+
const arg = argv[i];
|
|
15
|
+
if (arg === '--codex-thread-id') {
|
|
16
|
+
const value = argv[++i];
|
|
17
|
+
if (!value || value.startsWith('-')) throw new Error('--codex-thread-id requires an id');
|
|
18
|
+
out.codexThreadId = value;
|
|
19
|
+
} else if (arg === '--codex-home') {
|
|
20
|
+
const value = argv[++i];
|
|
21
|
+
if (!value || value.startsWith('-')) throw new Error('--codex-home requires a path');
|
|
22
|
+
out.codexHome = value;
|
|
23
|
+
} else if (arg === '--project') {
|
|
24
|
+
const value = argv[++i];
|
|
25
|
+
if (!value || value.startsWith('-')) throw new Error('--project requires a path');
|
|
26
|
+
out.projectPath = value;
|
|
27
|
+
} else if (arg === '--json') {
|
|
28
|
+
out.json = true;
|
|
29
|
+
} else {
|
|
30
|
+
throw new Error(`unknown argument: ${arg}`);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
return out;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export async function run(argv) {
|
|
38
|
+
let parsed;
|
|
39
|
+
try {
|
|
40
|
+
parsed = parseArgs(argv);
|
|
41
|
+
} catch (err) {
|
|
42
|
+
const msg = err instanceof Error ? err.message : 'unknown';
|
|
43
|
+
process.stderr.write(`[codex-capture] ${msg}\n`);
|
|
44
|
+
process.exit(1);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const identity = resolveCodexThreadIdentity({ codexThreadId: parsed.codexThreadId }, process.env);
|
|
48
|
+
if (!identity.codexThreadId) {
|
|
49
|
+
process.stderr.write(
|
|
50
|
+
'[codex-capture] missing Codex thread id. Pass --codex-thread-id <id> or set THROUGHLINE_CODEX_THREAD_ID / CODEX_THREAD_ID.\n',
|
|
51
|
+
);
|
|
52
|
+
process.exit(1);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
let result;
|
|
56
|
+
try {
|
|
57
|
+
result = captureCodexRolloutToDb(getDb(), {
|
|
58
|
+
threadId: identity.codexThreadId,
|
|
59
|
+
codexHome: parsed.codexHome ?? undefined,
|
|
60
|
+
projectPath: parsed.projectPath,
|
|
61
|
+
});
|
|
62
|
+
} catch (err) {
|
|
63
|
+
const msg = err instanceof Error ? err.message : 'unknown';
|
|
64
|
+
process.stderr.write(`[codex-capture] ${msg}\n`);
|
|
65
|
+
process.exit(1);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
const output = {
|
|
69
|
+
...result,
|
|
70
|
+
codexThreadIdSource: identity.codexThreadIdSource,
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
if (parsed.json) {
|
|
74
|
+
process.stdout.write(JSON.stringify(output, null, 2) + '\n');
|
|
75
|
+
} else {
|
|
76
|
+
process.stdout.write(renderReport(output) + '\n');
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
process.exit(result.status === 'captured' ? 0 : 1);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
function renderReport(result) {
|
|
83
|
+
const lines = [];
|
|
84
|
+
lines.push('## Throughline Codex Capture');
|
|
85
|
+
lines.push('');
|
|
86
|
+
lines.push(`Status: ${result.status}`);
|
|
87
|
+
if (result.reason) lines.push(`Reason: ${result.reason}`);
|
|
88
|
+
lines.push(`Codex thread: ${result.threadId}`);
|
|
89
|
+
lines.push(`Throughline session: ${result.sessionId}`);
|
|
90
|
+
lines.push(`Project: ${result.projectPath}`);
|
|
91
|
+
if (result.rolloutPath) lines.push(`Rollout: ${result.rolloutPath}`);
|
|
92
|
+
lines.push(`Captured turns: ${result.capturedTurns}`);
|
|
93
|
+
lines.push(`Captured rows: ${result.capturedRows}`);
|
|
94
|
+
return lines.join('\n');
|
|
95
|
+
}
|