throughline 0.9.1 → 0.10.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,34 @@ shipped to npm but were not individually tagged on GitHub.
10
10
 
11
11
  ## [Unreleased]
12
12
 
13
+ ## [0.10.0] — 2026-08-17
14
+
15
+ ### Added
16
+
17
+ - Grok is a first-class hook host. CamelCase envelopes are normalized to
18
+ `grok:<sessionId>` and L2 is recovered from Grok `chat_history.jsonl`.
19
+ `throughline install` writes `~/.grok/hooks/throughline.json` with absolute
20
+ `node` + `bin/throughline.mjs` commands so Desktop GUI PATH can fire them.
21
+ The v0.9.1 Claude-facing no-op for non-Claude envelopes is withdrawn.
22
+ - `throughline grok-continue --session <id>` starts a person-facing Grok seat
23
+ whose first user text is the handoff-context body. cwd is the source
24
+ session's `project_path`, not the caller's cwd. The first user text is
25
+ preamble + context + continue + wait. Missing context or project_path does
26
+ not spawn. `--rules`, aiterm, and `--from` are not used. macOS Terminal only.
27
+ - Grok `/tl` writes the baton and then launches `grok-continue` as a side
28
+ effect. Claude `/tl`, Codex `/tl`, and Grok `/clear` do not launch it.
29
+ Empty-L2 sources (including a `merged_into` chain member with no bodies)
30
+ do not spawn. The list of record is the session directory under
31
+ `~/.grok/sessions/<encodeURIComponent(cwd)>/`. Desktop Inactive folding is
32
+ not a success condition.
33
+
34
+ ### Documentation
35
+
36
+ - README, README.ja, contributor entrypoints, docs overview, ADR 0021
37
+ current state, and the successor-launch plan now state the live Grok
38
+ `/tl` → `grok-continue` contract. Historical ADRs and archived plans
39
+ remain point-in-time records.
40
+
13
41
  ## [0.9.1] — 2026-08-14
14
42
 
15
43
  ### Fixed
package/README.ja.md CHANGED
@@ -38,6 +38,41 @@ throughline install # hook / Codex skill / VS Code monitor task を登録
38
38
  `/clear` を打てば新セッションはゼロからではなく、**思考の途中から再開** される。
39
39
  `/clear` を経由しない新規 chat / VS Code 再起動では `/tl` で前任を指名できる。
40
40
 
41
+ Grok Desktop も first-class host である。`throughline install` は
42
+ `~/.grok/hooks/throughline.json` を書く。Grok の `/tl` は今の窓へ注入せず、
43
+ Terminal に新席を立てる(詳細は下)。
44
+
45
+ <details>
46
+ <summary><b>Grok も併用する場合</b> Grok hooks も登録される — クリックで詳細</summary>
47
+
48
+ global install は `~/.grok/hooks/throughline.json` に絶対 `node` +
49
+ installed `bin/throughline.mjs` の SessionStart / UserPromptSubmit / Stop を書く。
50
+ bare `throughline` は書かない(Grok Desktop の GUI PATH では見えない)。
51
+
52
+ ターンは `grok:<sessionId>` として保存し、L2 は
53
+ `~/.grok/sessions/<encodeURIComponent(cwd)>/<id>/chat_history.jsonl` から回収する。
54
+ Grok は UserPromptSubmit stdout や書き換えた `chat_history.jsonl` をライブ
55
+ モデル文脈へ入れない。
56
+
57
+ L2 がある Grok 席で `/tl` を打つと、バトンを書いたあと次を副作用起動する:
58
+
59
+ ```bash
60
+ throughline grok-continue --session grok:<id>
61
+ ```
62
+
63
+ cwd は源セッションの `project_path` であり、呼び出し元 cwd は使わない。
64
+ 初手 user 文は前文 + handoff-context 本文 + 続き + 待機。context /
65
+ `project_path` が無ければ spawn しない。Claude / Codex の `/tl` と Grok の
66
+ `/clear` では起動しない。aiterm / `--rules` / `--from` は使わない。
67
+ macOS Terminal のみ。
68
+
69
+ 新席は `~/.grok/sessions/<encodeURIComponent(cwd)>/` のトップレベル
70
+ ディレクトリ。Desktop の Inactive 畳みは成功条件にしない。L2 が無い源
71
+ (`merged_into` チェーンの空席など)では起動しない。新しい chat で 1〜2
72
+ 往復してから `/tl` する。
73
+
74
+ </details>
75
+
41
76
  <details>
42
77
  <summary><b>Codex も併用する場合</b> Codex hooks も登録される — クリックで詳細</summary>
43
78
 
@@ -324,7 +359,7 @@ Throughline state をまだ書いていない現在セッションも表示で
324
359
 
325
360
  | コマンド | 役割 |
326
361
  | --- | --- |
327
- | `throughline install` | hook / Codex UserPromptSubmit・PostToolUse・Stop hook / Codex skill を登録し、VS Code 配下なら現プロジェクトの monitor task も配置 |
362
+ | `throughline install` | hook / Codex UserPromptSubmit・PostToolUse・Stop hook / Codex skill / `~/.grok/hooks/throughline.json` を登録し、VS Code 配下なら現プロジェクトの monitor task も配置 |
328
363
  | `throughline install --project` | 現リポジトリの `.claude/settings.json` だけに hook を登録 |
329
364
  | `throughline uninstall` | hook を削除 |
330
365
  | `throughline monitor` | マルチセッション監視を起動 |
@@ -335,6 +370,7 @@ Throughline state をまだ書いていない現在セッションも表示で
335
370
  | `throughline doctor --trim --host claude` | trim boundary と手動手順を診断 |
336
371
  | `throughline handoff-preview --session <id>` | Codex 向け `throughline_handoff` JSON projection を表示 |
337
372
  | `throughline handoff-context --session <id> --json` | SessionStart と同じ引き継ぎ文脈を versioned JSON で取得。記憶行の `session_id` と `sessions.merged_into` は変更せず、同一端末内の別ベンダーランチャーから使える |
373
+ | `throughline grok-continue --session <id>` | handoff-context を初手 user 文にした対話 Grok 席を立てる。cwd は源の `project_path`。ready でなければ spawn しない。`--rules` なし。macOS Terminal のみ |
338
374
  | `throughline codex-sidecar-diagnostics` | この project の `codex-sidecar` diagnostics status を確認 |
339
375
  | `throughline codex-sidecar-dry-run` | App Server を呼ばずに read-only sidecar request を正規化表示 |
340
376
  | `throughline trim --dry-run --host codex` | Codex same-thread trim の dry-run preview |
@@ -360,7 +396,7 @@ latest session推測・`sessions.merged_into`変更・L1/L2/L3 rowの所属変
360
396
 
361
397
  | コマンド | 役割 |
362
398
  | --- | --- |
363
- | `/tl` | 引き継ぎバトンを書き込む (auto path を OFF にしているユーザー / `/clear` 経由しない引継ぎの逃げ道) |
399
+ | `/tl` | 引き継ぎバトンを書き込む (auto path を OFF にしているユーザー / `/clear` 経由しない引継ぎの逃げ道)。Grok ではバトン成功後に `grok-continue` も起動する |
364
400
  | `/sc-detail <時刻>` | 過去ターンの L2 本文と L3 ツール I/O を取得 |
365
401
 
366
402
  > v0.4.0 から auto-handoff がデフォルト ON です。`/clear` だけで新セッションが
@@ -387,6 +423,8 @@ latest session推測・`sessions.merged_into`変更・L1/L2/L3 rowの所属変
387
423
  - [`docs/03_inheritance_on_clear_only.md`](docs/03_inheritance_on_clear_only.md) — `/tl` バトン引き継ぎ方式の設計判断記録 (schema v6–v7)
388
424
  - [`docs/08_codex_dual_support.md`](docs/08_codex_dual_support.md) — Claude 主軸を維持したまま Codex 対応を足すための architecture brief
389
425
  - [`docs/09_rollback_context_trim_insight.md`](docs/09_rollback_context_trim_insight.md) — rollback / trim 設計 insight。復元 memory を current work として読ませる制約も記録
426
+ - [`docs/adr/0021-grok-host-capture.md`](docs/adr/0021-grok-host-capture.md) — Grok first-class host と `/tl` → `grok-continue` の現行契約
427
+ - [`docs/plan_grok-successor-launch.md`](docs/plan_grok-successor-launch.md) — Grok 後継席の CLI・初手・非目標・実機受入
390
428
  - [`docs/07_codex_trim_implementation_plan.md`](docs/07_codex_trim_implementation_plan.md) — Claude/Codex 両対応と rollback trim の統合 TODO 計画
391
429
  - [`docs/04_public_release_plan.md`](docs/04_public_release_plan.md) — 公開配布化プラン、§ 0 フォールバック禁止ルール、バージョン別実装ステータス
392
430
  - [`docs/15_windows_ci_release_latency_plan.md`](docs/15_windows_ci_release_latency_plan.md) — Windows CI性能gateとACL契約を維持するrelease工程
package/README.md CHANGED
@@ -39,6 +39,10 @@ type `/clear` — the new session resumes mid-thought instead of starting from
39
39
  zero. (For non-`/clear` boundaries such as a brand-new chat or a VSCode
40
40
  restart, type `/tl` first to mark the predecessor.)
41
41
 
42
+ Grok Desktop is also a first-class host. `throughline install` writes
43
+ `~/.grok/hooks/throughline.json`. On Grok, `/tl` does not inject into the
44
+ current window — it starts a new Terminal seat. See below.
45
+
42
46
  <details>
43
47
  <summary><b>Also using Codex?</b> Global install registers Codex hooks too — click for details.</summary>
44
48
 
@@ -60,6 +64,40 @@ guarded `trim --execute --host codex` surface.
60
64
 
61
65
  </details>
62
66
 
67
+ <details>
68
+ <summary><b>Also using Grok?</b> Global install registers Grok hooks — click for details.</summary>
69
+
70
+ Global install writes `~/.grok/hooks/throughline.json` with absolute `node` +
71
+ installed `bin/throughline.mjs` for SessionStart, UserPromptSubmit, and Stop.
72
+ Do not register a bare `throughline` command: Grok Desktop's GUI PATH will not
73
+ see it.
74
+
75
+ Turns are stored as `grok:<sessionId>`. L2 is recovered from
76
+ `~/.grok/sessions/<encodeURIComponent(cwd)>/<id>/chat_history.jsonl`.
77
+ Grok does not feed UserPromptSubmit stdout or a rewritten `chat_history.jsonl`
78
+ into the live model prompt.
79
+
80
+ After `/tl` on a Grok session whose `handoff-context` is `ready`, Throughline
81
+ writes the baton and then runs:
82
+
83
+ ```bash
84
+ throughline grok-continue --session grok:<id>
85
+ ```
86
+
87
+ cwd is the source session's `project_path`, not the caller's cwd. The first
88
+ user text is preamble + the handoff-context body + continue + wait. Missing
89
+ context or project_path does not spawn. Claude `/tl`, Codex `/tl`, and Grok
90
+ `/clear` do not launch this CLI. Do not use aiterm, `--rules`, or `--from`.
91
+ macOS Terminal only.
92
+
93
+ The new seat is a top-level directory under
94
+ `~/.grok/sessions/<encodeURIComponent(cwd)>/`. Desktop Inactive folding is
95
+ not the success condition. A source with no L2 (including a `merged_into`
96
+ chain member with zero bodies) does not spawn — open a fresh chat, exchange
97
+ one or two turns, then `/tl`.
98
+
99
+ </details>
100
+
63
101
  ## How it compares
64
102
 
65
103
  | | **Throughline** | `/clear` (built-in) | `/compact` (built-in) | MemGPT / SummaryBufferMemory |
@@ -774,7 +812,7 @@ local-only contracts.
774
812
 
775
813
  | Command | What it does |
776
814
  | ---------------------------------------------- | ------------------------------------------------------------ |
777
- | `throughline install` | Register Claude user hooks/slash commands, the global Codex UserPromptSubmit/PostToolUse/Stop hooks, the global `$throughline` Codex skill, and the current VS Code monitor task when applicable |
815
+ | `throughline install` | Register Claude user hooks/slash commands, the global Codex UserPromptSubmit/PostToolUse/Stop hooks, the global `$throughline` Codex skill, `~/.grok/hooks/throughline.json`, and the current VS Code monitor task when applicable |
778
816
  | `throughline install --project` | Register Claude hooks/slash commands in this repo only |
779
817
  | `throughline uninstall` | Remove Throughline-managed Claude hooks/slash commands, only the Throughline-managed Codex hook, and the `$throughline` Codex skill |
780
818
  | `throughline monitor [--all] [--session <id>]` | Run the multi-session token monitor |
@@ -797,6 +835,7 @@ local-only contracts.
797
835
  | `throughline runtime-errors compact --json` | Remove only acknowledged, resolved aggregates after retention; open or unacknowledged records remain |
798
836
  | `throughline handoff-preview --session <id>` | Print a Codex-facing `throughline_handoff` JSON projection |
799
837
  | `throughline handoff-context --session <id> --json` | Print the exact SessionStart inheritance context as versioned JSON without moving memory rows or changing `sessions.merged_into`; intended for local launchers that need portable cross-vendor context |
838
+ | `throughline grok-continue --session <id>` | Spawn a person-facing Grok seat whose first user text is the handoff-context body. cwd is the source session `project_path`. Does not spawn without ready context. No `--rules`. macOS Terminal only |
800
839
  | `throughline codex-capture --codex-thread-id <id>` | Capture active Codex rollout turns into a `codex:<thread_id>` DB session |
801
840
  | `throughline codex-summarize --session codex:<id>` | Summarize captured Codex L2 into L1 with the Codex CLI backend |
802
841
  | `throughline codex-resume --session codex:<id>` | Render Codex active-work context from a captured Codex session |
@@ -840,7 +879,7 @@ Slash commands (invoked by the user in Claude Code):
840
879
 
841
880
  | Command | What it does |
842
881
  | ------------- | ----------------------------------------------------------------- |
843
- | `/tl` | Write a handoff baton (explicit inheritance signal across non-`/clear` boundaries — new chat / VSCode restart) |
882
+ | `/tl` | Write a handoff baton (explicit inheritance signal across non-`/clear` boundaries — new chat / VSCode restart). On Grok, also launches `grok-continue` after a successful baton write |
844
883
  | `/clear` | Built-in Claude Code reset. Throughline's `UserPromptSubmit` hook also writes a baton so the next session inherits the cleared session's memory |
845
884
  | `/sc-detail <time>` | Retrieve L2 body text and L3 tool I/O for a past turn |
846
885
 
@@ -854,7 +893,7 @@ Slash commands (invoked by the user in Claude Code):
854
893
 
855
894
  Hook subcommands (invoked by Claude Code, not by humans):
856
895
  `session-start` (SessionStart), `process-turn` (Stop),
857
- `prompt-submit` (UserPromptSubmit — detects `/tl` and `/clear` and writes a baton).
896
+ `prompt-submit` (UserPromptSubmit — detects `/tl` and `/clear` and writes a baton; Grok `/tl` also launches `grok-continue`).
858
897
 
859
898
  ### Observer completed-turn feed (development)
860
899
 
@@ -12,6 +12,7 @@
12
12
  * throughline recall --l2|--l1 # 注入案内から辿る pull 用 read-only 記憶取得
13
13
  * throughline handoff-preview # Codex-facing throughline_handoff JSON preview
14
14
  * throughline handoff-context --session <id> --json # Read-only inheritance context JSON
15
+ * throughline grok-continue --session <id> # Spawn a Grok seat whose first user text is handoff-context
15
16
  * throughline auditor-context --json # Read-only bounded auditor context JSON
16
17
  * throughline factory-diagnostics --json # Native factory read-only readiness JSON
17
18
  * throughline migrate --json # Migrate the existing Throughline database only
@@ -82,6 +83,11 @@ switch (cmd) {
82
83
  if (exitCode !== 0) process.exitCode = exitCode;
83
84
  break;
84
85
  }
86
+ case 'grok-continue': {
87
+ const exitCode = (await import('../src/cli/grok-continue.mjs')).run(rest);
88
+ if (exitCode !== 0) process.exitCode = exitCode;
89
+ break;
90
+ }
85
91
  case 'auditor-context': {
86
92
  const exitCode = (await import('../src/cli/auditor-context.mjs')).run(rest);
87
93
  if (exitCode !== 0) process.exitCode = exitCode;
@@ -203,7 +209,7 @@ async function showHelp() {
203
209
  console.log(`throughline v${version}
204
210
 
205
211
  Usage:
206
- throughline install Register hooks in ~/.claude/settings.json
212
+ throughline install Register Claude, Codex, and Grok hooks
207
213
  throughline uninstall Remove hooks
208
214
  throughline monitor Multi-session token monitor (use --all, --session <id>)
209
215
  throughline detail <time> Retrieve L2+L3 detail for a turn (e.g. 14:23:05 or 14:23-14:30)
@@ -211,6 +217,11 @@ Usage:
211
217
  throughline handoff-context --session <id> --json
212
218
  Print the exact inheritance context without
213
219
  changing database ownership
220
+ throughline grok-continue --session <id>
221
+ Spawn a person-facing Grok seat whose first
222
+ user text is the handoff-context body. Does not
223
+ spawn when context is unavailable. Does not pass
224
+ --rules. macOS Terminal only
214
225
  throughline auditor-context --session <id> --project <root>
215
226
  Read only bounded completed user/assistant context
216
227
  for an auditor; requires either --host plus --transcript,
@@ -21,6 +21,7 @@
21
21
  | [14_observer_completed_turn_feed_plan.md](14_observer_completed_turn_feed_plan.md) | Observer向けcompleted-only read / wait CLIの完了済み設計・受入記録。v0.7.0で公開済み |
22
22
  | [15_windows_ci_release_latency_plan.md](15_windows_ci_release_latency_plan.md) | Windows CI 18分の原因、ACL安全網を維持した短縮、release gateの受入条件 |
23
23
  | [16_readonly_handoff_context_plan.md](16_readonly_handoff_context_plan.md) | DB所有権を変更せずSessionStartと同じ記憶を返す、ローカルランチャー向けread-only I/F。v0.9.0で公開済み |
24
+ | [plan_grok-successor-launch.md](plan_grok-successor-launch.md) | Grok `/tl` 後の後継席起動。v0.10.0で公開済み。正本は本ファイルと [ADR 0021](adr/0021-grok-host-capture.md) |
24
25
  | [BUGHUB_RUNTIME_ERROR_STORE_PLAN.md](BUGHUB_RUNTIME_ERROR_STORE_PLAN.md) | local runtime error aggregate store の契約と実装 TODO |
25
26
 
26
27
  ## Supporting Records
@@ -38,6 +39,7 @@
38
39
  - [ADR 0002](adr/0002-observer-claude-completion-receipt.md): Claude completed turnはThroughline所有のStop receiptで固定する。
39
40
  - [ADR 0003](adr/0003-observer-completed-chain-cursor.md): Observer cursorをhost固有のcompleted pair chainとprefix検証へ束縛する。
40
41
  - [ADR 0020](adr/0020-windows-ci-release-latency.md): Windows ACL契約を維持し、境界fixtureと実ACL検証を分離する。
42
+ - [ADR 0021](adr/0021-grok-host-capture.md): Grok を first-class hook host にし、`/tl` 後の記憶再開は `grok-continue` に固定する。
41
43
 
42
44
  Observerの公開境界は`throughline observer-read`/`throughline observer-wait`のJSON-only CLIである。
43
45
  ThroughlineはClaude Stop receiptとCodex rolloutの`task_complete`だけからcompleted cursorを構築し、
@@ -17,6 +17,12 @@ A 案 (= /clear で自動引継ぎ + /tl は逃げ道として残す + /tl-trim
17
17
  > `THROUGHLINE_DISABLE_AUTO_HANDOFF=1` は **fallback path のみに作用** する
18
18
  > ようになった (typed `/clear` / `/tl` には効かない)。
19
19
 
20
+ > **2026-08-17 (ADR 0021 / v0.10.0)**: 本書は Claude `/clear`・`/tl` の現行仕様である。
21
+ > Grok は UserPromptSubmit stdout をモデルへ渡さない。Grok `/tl` の記憶再開は
22
+ > `throughline grok-continue` であり、本書の「次セッション初回プロンプトで注入」を
23
+ > Grok に適用しない。正本は [ADR 0021](adr/0021-grok-host-capture.md) と
24
+ > [plan_grok-successor-launch.md](plan_grok-successor-launch.md)。
25
+
20
26
  > **2026-07-18 (ADR 0016) update**: 注入の中身を push/pull 二段に再設計した。
21
27
  > push (9,500 字) はヘッダ + 現在地アンカー + 案内セクション + **L2 をターン原子で
22
28
  > 入るだけ全文**(L1 は注入しない)。窓 20 ターンの残りは `throughline recall --l2`、
@@ -34,10 +34,10 @@ Throughline を GitHub + npm で公開し、世界中の Claude Code ユーザ
34
34
 
35
35
  ```bash
36
36
  npm install -g throughline # CLI を PATH に配置
37
- throughline install # ~/.claude/settings.json、Codex hook、Codex skill を追記
37
+ throughline install # ~/.claude/settings.json、Codex hook、Codex skill、Grok hook を追記
38
38
  ```
39
39
 
40
- Claude hook コマンドは **`throughline <subcommand>` の PATH 解決型**。node のインストール先や OS が変わっても PATH さえ通っていれば動く。Codex Stop hook Codex App Server / VSCode host の PATH 差分を避けるため、絶対 node + installed `bin/throughline.mjs` で登録する。Codex 手動 UX は `~/.codex/skills/throughline` の `$throughline` skill で自然言語から呼ぶ。
40
+ Claude hook コマンドは **`throughline <subcommand>` の PATH 解決型**。node のインストール先や OS が変わっても PATH さえ通っていれば動く。Codex Stop hook Grok hook host の PATH 差分を避けるため、絶対 node + installed `bin/throughline.mjs` で登録する。Grok は `~/.grok/hooks/throughline.json`。Codex 手動 UX は `~/.codex/skills/throughline` の `$throughline` skill で自然言語から呼ぶ。Grok `/tl` の後継起動は `throughline grok-continue`(macOS Terminal)。
41
41
 
42
42
  ### hook 登録後の `~/.claude/settings.json`(抜粋)
43
43
 
@@ -0,0 +1,60 @@
1
+ # ADR 0021: Treat Grok as a first-class Throughline host
2
+
3
+ Date: 2026-08-17
4
+
5
+ ## Status
6
+
7
+ Accepted
8
+
9
+ ## Context
10
+
11
+ Throughline v0.9.1 no-op'd Grok camelCase hook envelopes (GF04) because Grok was not a factory parent host. Wave 5 made Grok a peer parent. The 2026-08-16 factory ruling requires Throughline Grok capture / restore / handoff.
12
+
13
+ Grok stores turns in `~/.grok/sessions/<encodeURIComponent(cwd)>/<sessionId>/chat_history.jsonl`, not Claude JSONL.
14
+
15
+ ## Decision
16
+
17
+ - Detect the camelCase envelope (`sessionId` + `hookEventName`, no `session_id`) as host=grok.
18
+ - Normalize it to the existing snake_case hook contract and prefix ids with `grok:`.
19
+ - Read L2 from Grok `chat_history.jsonl`. Do not invent a Claude-shaped transcript.
20
+ - Grok host ignores payload `transcriptPath`. Live Stop sends `updates.jsonl`, which has no user/assistant rows.
21
+ - Install product hooks at `~/.grok/hooks/throughline.json`. Do not write factory.json.
22
+ - Grok hook `command` is absolute `node` + `bin/throughline.mjs` + subcommand. Do not write bare `throughline`.
23
+ - Keep Claude and Codex adapters unchanged. Do not mix `grok:` rows into Claude predecessor search.
24
+ - Grok `/tl` / `/clear` / `/new` detection reads the inner `<user_query>`. If the hook `prompt` is empty, fall back to the last user row in `chat_history.jsonl`. Do not treat `source=new` as Claude `source=clear` auto-handoff.
25
+ - Grok UserPromptSubmit stdout is observe-only. Writing into `chat_history.jsonl` does not enter the live model prompt. Live conversation is `updates.jsonl`. Claude still uses stdout additionalContext.
26
+
27
+ ## Consequences
28
+
29
+ - GF04 no-op is withdrawn.
30
+ - Restore/handoff for `grok:` sessions reuse existing DB + `handoff-context` after capture exists.
31
+ - Live Grok hook fire on each seat is a separate install+new-session acceptance.
32
+
33
+ ## 現在地
34
+
35
+ Grok 1.0.4 バイナリの hook 出力型は `GateHookJson`(PreToolUse deny/updatedInput)と `StopHookJson`(block/additionalContext/stopReason)だけ。UserPromptSubmit / SessionStart / PostToolUse の stdout は observe。モデルへ文章を渡せる公式口は (1) PreToolUse deny の reason (2) Stop/SubagentStop の block reason / additionalContext / exit2 stderr。初回トークン前の prompt 注入口は無い。Stop 注入は初回返答のあと 2 周目に載る。`01a00cfe` の chat_history 書き込みは updates に乗らず実証済み。capture と handoff-context は生きている。
36
+
37
+ `/tl` 後の記憶再開は hook stdout や `chat_history.jsonl` 再注入では成立しない。後継経路は Throughline 所有の最小起動に固定する。正本は [plan_grok-successor-launch.md](../plan_grok-successor-launch.md)。
38
+
39
+ - CLI: `throughline grok-continue --session <id>`。`<id>` は `grok:` 接頭辞付き Throughline session id。`--from` は採用しない。
40
+ - 内部で `handoff-context --session <id> --json` を読む。ready でなければ spawn しない。
41
+ - 初手 user 文は次の 4 段だけ。`{context}` は handoff-context の `context` 文字列。JSON envelope は載せない。末尾は待機。
42
+
43
+ ```
44
+ この発言は直前 Throughline 席の履歴を前提とする。
45
+
46
+ {context}
47
+
48
+ 直前の作業の自然な続きとして応答すること。
49
+
50
+ この後ユーザーが指示を出す。何もせず待機すること。
51
+ ```
52
+
53
+ - spawn の cwd は源セッションの `project_path`。呼び出し元 cwd は使わない。共有 `GROK_HOME` の対話 `grok`。位置引数 `[PROMPT]` を使う。`--rules` / `--system-prompt-override` / `--agent` / 単発 `-p` は使わない。aiterm は使わない。
54
+ - Claude / Codex の `/tl` 契約は変えない。Grok `/tl` 成功後の副作用起動だけが配線対象。
55
+ - 一覧の正は `~/.grok/sessions/<encodeURIComponent(cwd)>/<id>/`。Desktop Inactive 畳みは成功条件にしない。
56
+ - L2 が無い源(`merged_into` チェーンの空席など)では spawn しない。新しい chat で 1〜2 往復してから `/tl` する。
57
+
58
+ 2026-08-17 実機受入: `grok:01a00ff1-3f97-70e2-ba76-5acd90561a84` の `/tl` が
59
+ `c01a2689-5b4f-4977-97ce-f73fcf317f94` を Dotagents 棚に立て、初手末尾は待機、
60
+ 後継は待って止まった。v0.10.0 で公開。
@@ -0,0 +1,99 @@
1
+ # Grok successor launch — Throughline 所有の最小起動
2
+
3
+ Date: 2026-08-17
4
+ Status: Accepted(v0.10.0)
5
+ Lattice plan: `grok-successor-launch`
6
+ 対象 repo: Throughline だけ。aiterm は依存にも実装の借り先にもしない。
7
+
8
+ t1-contract(2026-08-17)で起動核の CLI名・初手文面・非目標・受入を固定した。
9
+ 以降の工程は本ファイルと [ADR 0021](adr/0021-grok-host-capture.md) の現在地を正とする。
10
+
11
+ 2026-08-17 実機: Dotagents 棚の `grok:01a00ff1-3f97-70e2-ba76-5acd90561a84` で
12
+ `handoff-context` が `ready` のあと `/tl`。後継
13
+ `~/.grok/sessions/%2FUsers%2Fkite%2FDeveloper%2Fdotagents/c01a2689-5b4f-4977-97ce-f73fcf317f94/`
14
+ が立ち、初手末尾は待機行、モデルは仕事を始めず待った。源の `merged_into` 空席
15
+ (`01a00b38`、L2 0 件)では spawn しない。
16
+
17
+ ## 目的
18
+
19
+ Grok Desktop の UserPromptSubmit はモデルへ本文を渡せない。`/tl` のあと人が新窓を開いても記憶は載らない。代わりに Throughline 自身が、handoff-context を**最初の user 文の前**に置いた普通の Grok 席を一本立てる。
20
+
21
+ ## 非目標
22
+
23
+ 次は成功条件にも実装手段にもしない。
24
+
25
+ - aiterm / tmux / PTY 完了待ち / `role=subagent` を Throughline に持ち込む
26
+ - `grok` 起動に `--rules` を付ける(dashboard の top-level から外れる)
27
+ - `--system-prompt-override` / `--agent` で初手 user 文を代替する
28
+ - Claude / Codex の `/tl` 契約を変える
29
+ - UserPromptSubmit stdout や `chat_history.jsonl` への再注入で Desktop 新窓を直す
30
+ - 実装キャンペーン中の Spotter 作業(npm 公開は製品完遂であり禁止ではない)
31
+
32
+ ## 固定契約
33
+
34
+ ### CLI
35
+
36
+ 名前と引数は次に固定する。
37
+
38
+ ```
39
+ throughline grok-continue --session <id>
40
+ ```
41
+
42
+ - `<id>` は Throughline の session id であり、Grok 由来なら `grok:` 接頭辞を含む。
43
+ - 既存の `handoff-context --session` と同じ flag 名を使う。`--from` は採用しない。
44
+ - この CLI は `handoff-context --session <id> --json` を読む。`status` が `ready` でない、終了が非 0、または context が空なら **spawn しない**。fallback 禁止。
45
+ - JSON envelope(`schema` / `status` / `sessionId`)は初手文へ載せない。載せるのは `context` 文字列だけ。
46
+
47
+ ### spawn
48
+
49
+ - 源セッション(`--session` の Throughline 行)の `project_path` で立てる。呼び出し元の cwd は使わない。`project_path` が無い・読めない・ディレクトリが無いときは spawn しない。
50
+ - 共有 `GROK_HOME`(上書きしない)のまま `grok` を人の席として立てる。
51
+ - 起動は対話セッションである。`-p` / `--prompt` / `--prompt-file` / `--prompt-json` の単発終了経路は使わない。
52
+ - 初手は `grok` の位置引数 `[PROMPT]` に渡す。`--rules` は付けない。
53
+ - hook から呼ぶので対話 TTY は無い。macOS では新しい Terminal 窓で grok を前面に出す。立てた session id が分かれば標準出力にも出す(`grok --resume` できるようにする)。両方できることが受入の強い形である。
54
+
55
+ ### 初手文面
56
+
57
+ 初手 user 文は次の 4 段だけとする。前後の飾り文を足さない。`{context}` は handoff-context が返した `context` 文字列そのもの。末尾の待機が無いと、要約を新しい仕事の着手と誤る。
58
+
59
+ ```
60
+ この発言は直前 Throughline 席の履歴を前提とする。
61
+
62
+ {context}
63
+
64
+ 直前の作業の自然な続きとして応答すること。
65
+
66
+ この後ユーザーが指示を出す。何もせず待機すること。
67
+ ```
68
+
69
+ ### `/tl` 配線
70
+
71
+ - Grok envelope の `/tl` 成功後に、上記 CLI を副作用で起動する。baton は今どおり書く。
72
+ - Claude / Codex ではこの CLI を起動しない。
73
+
74
+ ### 一覧
75
+
76
+ - 新席は `~/.grok/sessions/<encodeURIComponent(cwd)>/<id>/` にトップレベルとして残る。
77
+ - aiterm の Inactive/subagent 隠しを再現しない。
78
+ - Desktop roster が別プロセスを Inactive に畳むのは Grok 側の仕様。受入は session ディレクトリと `grok --resume` 一覧を正とする。
79
+
80
+ ## 受入
81
+
82
+ ### focused(t2 / t3)
83
+
84
+ - `handoff-context` 失敗では `grok` を spawn しない。
85
+ - 初手文面は上の 4 段で、2 段目に `context` 文字列がそのまま含まれ、末尾が待機である。
86
+ - spawn argv に `--rules` が無い。
87
+ - spawn 経路に aiterm / tmux / `role=subagent` が無い。
88
+ - Grok 以外の `/tl` では `grok-continue` を呼ばない。
89
+
90
+ ### 実機(t4 / t5)
91
+
92
+ - この Mac の Grok `/tl` から新席が立ち、session ディレクトリに載る。
93
+ - `grok --resume` 一覧にトップレベルとして見える。Desktop Inactive 畳みは成功条件にしない。
94
+ - その席の最初のモデル応答が前文の記憶を使う。宣言または L2 固有事実が出ること。
95
+ - `chat_history.jsonl` への後書きや hook stdout を成功に数えない。
96
+
97
+ ## 円卓
98
+
99
+ 工程正本は Lattice plan `grok-successor-launch`。実装は Throughline のみ。
package/package.json CHANGED
@@ -1,10 +1,12 @@
1
1
  {
2
2
  "name": "throughline",
3
- "version": "0.9.1",
3
+ "version": "0.10.0",
4
4
  "type": "module",
5
- "description": "Claude Code hooks plugin for structured context compression (/clear-safe persistent memory)",
5
+ "description": "Persistent memory hooks for Claude Code, Codex, and Grok (/clear-safe context compression)",
6
6
  "keywords": [
7
7
  "claude-code",
8
+ "codex",
9
+ "grok",
8
10
  "hooks",
9
11
  "context-compression",
10
12
  "llm"
@@ -0,0 +1,197 @@
1
+ import { spawn } from 'node:child_process';
2
+ import { randomUUID } from 'node:crypto';
3
+ import { existsSync, mkdirSync, writeFileSync } from 'node:fs';
4
+ import { homedir, tmpdir } from 'node:os';
5
+ import { delimiter, join } from 'node:path';
6
+
7
+ import { readHandoffContext, readSessionProjectPath } from './handoff-context.mjs';
8
+
9
+ export const GROK_CONTINUE_PREAMBLE = 'この発言は直前 Throughline 席の履歴を前提とする。';
10
+ export const GROK_CONTINUE_REQUEST = '直前の作業の自然な続きとして応答すること。';
11
+ export const GROK_CONTINUE_WAIT = 'この後ユーザーが指示を出す。何もせず待機すること。';
12
+
13
+ export function parseArgs(argv = []) {
14
+ if (
15
+ argv.length !== 2
16
+ || argv[0] !== '--session'
17
+ || typeof argv[1] !== 'string'
18
+ || argv[1].length === 0
19
+ ) {
20
+ throw new TypeError('usage error');
21
+ }
22
+ return { sessionId: argv[1] };
23
+ }
24
+
25
+ export function buildGrokContinuePrompt(context) {
26
+ if (typeof context !== 'string' || context.length === 0) {
27
+ throw new TypeError('empty context');
28
+ }
29
+ return `${GROK_CONTINUE_PREAMBLE}\n\n${context}\n\n${GROK_CONTINUE_REQUEST}\n\n${GROK_CONTINUE_WAIT}`;
30
+ }
31
+
32
+ export function shQuote(value) {
33
+ return `'${String(value).replace(/'/g, `'\\''`)}'`;
34
+ }
35
+
36
+ export function resolveGrokBin({
37
+ home = homedir(),
38
+ env = process.env,
39
+ exists = existsSync,
40
+ } = {}) {
41
+ const homeBin = join(home, '.grok', 'bin', 'grok');
42
+ if (exists(homeBin)) return homeBin;
43
+ for (const dir of String(env.PATH ?? '').split(delimiter)) {
44
+ if (!dir) continue;
45
+ const candidate = join(dir, 'grok');
46
+ if (exists(candidate)) return candidate;
47
+ }
48
+ return null;
49
+ }
50
+
51
+ export function buildGrokArgv(grokBin, sessionUuid, prompt) {
52
+ return [grokBin, '--session-id', sessionUuid, prompt];
53
+ }
54
+
55
+ export function buildLaunchScript({ cwd, grokBin, sessionUuid, promptFile }) {
56
+ return [
57
+ '#!/bin/sh',
58
+ 'set -e',
59
+ `cd ${shQuote(cwd)}`,
60
+ `exec ${shQuote(grokBin)} --session-id ${shQuote(sessionUuid)} "$(cat ${shQuote(promptFile)})"`,
61
+ '',
62
+ ].join('\n');
63
+ }
64
+
65
+ export function appleScriptForLaunch(launchScriptPath) {
66
+ return [
67
+ 'tell application "Terminal"',
68
+ ' activate',
69
+ ` do script "exec " & quoted form of ${JSON.stringify(launchScriptPath)}`,
70
+ 'end tell',
71
+ '',
72
+ ].join('\n');
73
+ }
74
+
75
+ export function buildContinuePlan({
76
+ context,
77
+ grokBin,
78
+ cwd,
79
+ sessionUuid,
80
+ }) {
81
+ const prompt = buildGrokContinuePrompt(context);
82
+ const grokArgv = buildGrokArgv(grokBin, sessionUuid, prompt);
83
+ if (grokArgv.includes('--rules')) {
84
+ throw new Error('grok-continue must not pass --rules');
85
+ }
86
+ return {
87
+ prompt,
88
+ grokArgv,
89
+ cwd,
90
+ sessionUuid,
91
+ throughlineSessionId: `grok:${sessionUuid}`,
92
+ };
93
+ }
94
+
95
+ export function writeLaunchArtifacts({
96
+ cwd,
97
+ grokBin,
98
+ sessionUuid,
99
+ prompt,
100
+ tmp = tmpdir(),
101
+ }) {
102
+ const dir = join(tmp, `tl-grok-continue-${sessionUuid}`);
103
+ mkdirSync(dir, { recursive: true });
104
+ const promptFile = join(dir, 'prompt.txt');
105
+ const launchFile = join(dir, 'launch.sh');
106
+ writeFileSync(promptFile, prompt);
107
+ writeFileSync(launchFile, buildLaunchScript({
108
+ cwd,
109
+ grokBin,
110
+ sessionUuid,
111
+ promptFile,
112
+ }), { mode: 0o755 });
113
+ return { promptFile, launchFile };
114
+ }
115
+
116
+ export function defaultSpawnLaunch({ launchFile, spawnImpl = spawn }) {
117
+ const child = spawnImpl('osascript', ['-e', appleScriptForLaunch(launchFile)], {
118
+ detached: true,
119
+ stdio: 'ignore',
120
+ });
121
+ child.unref?.();
122
+ return child;
123
+ }
124
+
125
+ export function run(argv = [], {
126
+ stdout = process.stdout,
127
+ stderr = process.stderr,
128
+ readContext = readHandoffContext,
129
+ readProjectPath = readSessionProjectPath,
130
+ resolveBin = resolveGrokBin,
131
+ createSessionId = randomUUID,
132
+ platform = process.platform,
133
+ spawnLaunch = defaultSpawnLaunch,
134
+ } = {}) {
135
+ let sessionId;
136
+ try {
137
+ ({ sessionId } = parseArgs(argv));
138
+ } catch {
139
+ stderr.write('Usage: throughline grok-continue --session <id>\n');
140
+ return 2;
141
+ }
142
+
143
+ let context;
144
+ try {
145
+ context = readContext(sessionId);
146
+ } catch {
147
+ stderr.write('Throughline handoff context could not be read.\n');
148
+ return 1;
149
+ }
150
+ if (!context) {
151
+ stderr.write('Throughline handoff context is not available for that session.\n');
152
+ return 1;
153
+ }
154
+
155
+ let cwd;
156
+ try {
157
+ cwd = readProjectPath(sessionId);
158
+ } catch {
159
+ stderr.write('Throughline session project path could not be read.\n');
160
+ return 1;
161
+ }
162
+ if (!cwd) {
163
+ stderr.write('Throughline session project path is not available for that session.\n');
164
+ return 1;
165
+ }
166
+ if (!existsSync(cwd)) {
167
+ stderr.write('Throughline session project path does not exist.\n');
168
+ return 1;
169
+ }
170
+
171
+ const grokBin = resolveBin();
172
+ if (!grokBin) {
173
+ stderr.write('grok binary was not found.\n');
174
+ return 1;
175
+ }
176
+ if (platform !== 'darwin') {
177
+ stderr.write('throughline grok-continue requires macOS Terminal.\n');
178
+ return 1;
179
+ }
180
+
181
+ const sessionUuid = createSessionId();
182
+ const plan = buildContinuePlan({
183
+ context,
184
+ grokBin,
185
+ cwd,
186
+ sessionUuid,
187
+ });
188
+ const { launchFile } = writeLaunchArtifacts({
189
+ cwd: plan.cwd,
190
+ grokBin,
191
+ sessionUuid: plan.sessionUuid,
192
+ prompt: plan.prompt,
193
+ });
194
+ spawnLaunch({ launchFile, grokArgv: plan.grokArgv, cwd: plan.cwd });
195
+ stdout.write(`${plan.throughlineSessionId}\n`);
196
+ return 0;
197
+ }