@cocorograph/hub-agent 0.8.24 → 0.8.26

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/README.md CHANGED
@@ -62,9 +62,7 @@ hub-agent stop # 停止方法のヒント表示
62
62
  hub-agent install-service [--bin PATH]
63
63
  hub-agent uninstall-service
64
64
  hub-agent plugins list # ~/.hub/plugins/ から読み込み済プラグイン
65
- hub-agent plugins examples # repo 同梱の install 可能 example
66
- hub-agent plugins install <name> [--force]
67
- hub-agent plugins uninstall <name>
65
+ hub-agent plugins uninstall <name> # ~/.hub/plugins/<prio>-<name>/ を削除
68
66
  ```
69
67
 
70
68
  ## 設定ファイル
@@ -86,7 +84,8 @@ hub-agent/
86
84
  │ ├── config.mjs # ~/.hub/agent.json 管理
87
85
  │ ├── enroll.mjs # enrollment フロー
88
86
  │ ├── ws-client.mjs # outbound WSS + reconnect with jitter
89
- │ ├── pty-bridge.mjs # @lydell/node-pty 多重化 (fd leak 回避フォーク)
87
+ │ ├── claude-stream-bridge.mjs # Claude Agent SDK チャット中継
88
+ │ ├── codex-stream-bridge.mjs # Codex app-server チャット中継
90
89
  │ ├── tmux.mjs # tmux exec/list/create/kill
91
90
  │ ├── state.mjs # session status / context_pct 検知
92
91
  │ ├── skills.mjs # ~/.claude/skills 集計
@@ -98,7 +97,8 @@ hub-agent/
98
97
  │ ├── co.cocorograph.hub-agent.plist
99
98
  │ └── hub-agent.service
100
99
  ├── scripts/
101
- │ └── fix-node-pty-perms.mjs # @lydell/node-pty spawn-helper +x 修復 (postinstall)
100
+ │ ├── install.sh # ワンライナーセットアップ (macOS / Linux / WSL)
101
+ │ └── install.ps1 # Windows (WSL2 導入 + Task Scheduler 登録)
102
102
  └── test/
103
103
  ```
104
104
 
@@ -107,21 +107,22 @@ hub-agent/
107
107
  - [x] Sprint A: 仕様書 (Hub knowledge id=6080)
108
108
  - [x] Sprint B: Hub 側 enrollment API + WS endpoint 雛形
109
109
  - [x] Sprint C: 本リポジトリの雛形 + outbound WSS + hook system
110
- - [x] Sprint D: pty bridge
111
110
  - [x] Sprint E: tmux exec/list/create/kill 移植 + auto list UI
112
111
  - [x] Sprint F: Hub Browser↔Agent multiplex 中継
113
112
  - [x] Sprint G: Hub frontend /user/cockpit/{agents,terminal} ページ
114
113
  - [x] Sprint H: skills / state dot / usage badge リレー
115
114
  - [x] Sprint I: 接続復旧 (jitter + force reconnect + Celery stale 検知)
116
115
  - [x] Sprint J: 本リポジトリ publish 準備 + launchd/systemd 連携
117
- - [x] Sprint K: remote-ops プラグイン例 (`plugins/10-tailscale-remote/`)
118
116
 
119
- ## 同梱プラグイン
117
+ ## プラグイン
120
118
 
121
- - [`plugins/10-tailscale-remote`](./plugins/10-tailscale-remote/README.md) — 自宅
122
- Mac (hub-agent host) を会社 Mac (`remote_host`) の tmux に対する透過プロキシ化。
123
- `hub-agent plugins install 10-tailscale-remote` で `~/.hub/plugins/` にコピー、
124
- `config.json` を編集して restart で有効化
119
+ プラグインは利用者が `~/.hub/plugins/<prio>-<name>/plugin.mjs` を自分で置く運用です。
120
+ 置けば `plugins list` から読み込まれ、`plugins uninstall <prio>-<name>` で削除できます。
121
+
122
+ リポジトリ同梱の example プラグインはありません。唯一同梱していた
123
+ `plugins/10-tailscale-remote/` は pty spawn を横取りするプラグインで、pty 中継の撤去と
124
+ 同時に削除しました。同梱 example が 0 件になったため、それを配る `plugins examples` /
125
+ `plugins install` と `package.json` の `files` の `plugins/` も外しています。
125
126
 
126
127
  ## ライセンス
127
128
 
package/bin/hub-agent.mjs CHANGED
@@ -34,12 +34,8 @@ if (!process.env.LC_ALL) process.env.LC_ALL = "C.UTF-8"
34
34
  import { hasConfig, readConfig } from "../src/config.mjs"
35
35
  import { enroll } from "../src/enroll.mjs"
36
36
  import { resolveSyncTargets, syncBundleTargets } from "../src/hub-bundle.mjs"
37
- import {
38
- installPlugin,
39
- listInstallableExamples,
40
- uninstallPlugin,
41
- } from "../src/plugin-install.mjs"
42
37
  import { loadPlugins } from "../src/plugin-loader.mjs"
38
+ import { uninstallPlugin } from "../src/plugin-uninstall.mjs"
43
39
  import {
44
40
  installService,
45
41
  restartService,
@@ -264,6 +260,11 @@ program
264
260
  }
265
261
  })
266
262
 
263
+ // プラグインは利用者が `~/.hub/plugins/<prio>-<name>/` へ自分で配置する運用なので、
264
+ // CLI が提供するのは利用者配置分の管理 (list / uninstall) だけ。リポ同梱 example を配る
265
+ // `examples` / `install` は、唯一の example (`plugins/10-tailscale-remote/`) が pty spawn を
266
+ // 横取りするプラグインで pty 撤去と同時に削除され、同梱 example が 0 件になったため撤去した
267
+ // (あわせて package.json の files から `plugins/` を外している)。
267
268
  const plugins = program.command("plugins").description("プラグイン管理")
268
269
  plugins
269
270
  .command("list")
@@ -279,40 +280,9 @@ plugins
279
280
  }
280
281
  })
281
282
 
282
- plugins
283
- .command("examples")
284
- .description("リポ同梱の install 可能 example プラグイン一覧")
285
- .action(async () => {
286
- const list = await listInstallableExamples()
287
- if (list.length === 0) {
288
- console.log("(no examples bundled)")
289
- return
290
- }
291
- for (const name of list) {
292
- console.log(`- ${name}`)
293
- }
294
- })
295
-
296
- plugins
297
- .command("install <name>")
298
- .description("repo plugins/<name>/ を ~/.hub/plugins/<name>/ にコピー (config.json は温存)")
299
- .option("--force", "既存ディレクトリを上書きする")
300
- .action(async (name, opts) => {
301
- try {
302
- const r = await installPlugin(name, { force: !!opts.force })
303
- console.log(`installed: ${r.dest}`)
304
- console.log("次のステップ:")
305
- console.log(` $EDITOR ${r.dest}/config.json # 既存なら温存、新規なら example をコピー`)
306
- console.log(` hub-agent restart (launchctl kickstart / systemctl restart)`)
307
- } catch (err) {
308
- console.error(`plugins install failed: ${err.message}`)
309
- process.exit(1)
310
- }
311
- })
312
-
313
283
  plugins
314
284
  .command("uninstall <name>")
315
- .description("~/.hub/plugins/<name>/ を削除")
285
+ .description("~/.hub/plugins/<prio>-<name>/ を削除する")
316
286
  .action(async (name) => {
317
287
  try {
318
288
  const r = await uninstallPlugin(name)
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@cocorograph/hub-agent",
3
- "version": "0.8.24",
4
- "description": "Hub Hosted Cockpit のローカル常駐 agent。Hub と outbound WSS で接続し、ローカルの tmux/pty を中継する。",
3
+ "version": "0.8.26",
4
+ "description": "Hub Hosted Cockpit のローカル常駐 agent。Hub と outbound WSS で接続し、ローカルの Claude / Codex チャットと tmux セッションを中継する。",
5
5
  "type": "module",
6
6
  "license": "UNLICENSED",
7
7
  "private": false,
@@ -19,7 +19,6 @@
19
19
  "scripts": {
20
20
  "start": "node bin/hub-agent.mjs start",
21
21
  "test": "node --test test/*.test.mjs",
22
- "postinstall": "node scripts/fix-node-pty-perms.mjs",
23
22
  "check:publish-on-main": "node scripts/check-publish-on-main.mjs",
24
23
  "prepublishOnly": "node scripts/check-publish-on-main.mjs && npm test"
25
24
  },
@@ -28,13 +27,11 @@
28
27
  "src/",
29
28
  "scripts/",
30
29
  "templates/",
31
- "plugins/",
32
30
  "README.md",
33
31
  "LICENSE"
34
32
  ],
35
33
  "dependencies": {
36
34
  "@anthropic-ai/claude-agent-sdk": "^0.3.158",
37
- "@lydell/node-pty": "1.2.0-beta.12",
38
35
  "commander": "^12.1.0",
39
36
  "pino": "^9.0.0",
40
37
  "ws": "^8.18.0"
@@ -518,10 +518,10 @@ _install_node_lts_linux() {
518
518
  }
519
519
 
520
520
  # ネイティブアドオンのビルドに必要な make / g++ / python3 を導入する。
521
- # PTY backend は @lydell/node-pty に移行済みで全 OS の prebuild を同梱するため通常は
522
- # native ビルド不要だが、prebuild が当たらない環境では node-gyp rebuild に
523
- # フォールバックする。クリーンな Ubuntu には build-essential が無く
524
- # `gyp ERR! not found: make` で失敗する (WSL クリーン Ubuntu で発覚) ため保険で導入する。
521
+ # 現在の依存はすべて純 JS で通常は native ビルド不要だが、optional な native 依存
522
+ # (ws の bufferutil 等) が prebuild なしで拾われると node-gyp rebuild にフォールバック
523
+ # する。クリーンな Ubuntu には build-essential が無く `gyp ERR! not found: make` で
524
+ # 失敗する (WSL クリーン Ubuntu で発覚) ため保険で導入する。
525
525
  # macOS は Xcode CLT 前提なので何もしない。
526
526
  ensure_build_tools() {
527
527
  [[ "$(uname)" == "Darwin" ]] && return 0
@@ -812,7 +812,7 @@ macos_perm_guidance() {
812
812
  cat <<EOF
813
813
 
814
814
  📣 macOS の権限ダイアログについて
815
- hub-agent は tmux/pty を中継するため、初回起動時に macOS から
815
+ hub-agent は tmux セッションとローカルの CLI (Claude / Codex) を中継するため、初回起動時に macOS から
816
816
  「node がローカルネットワーク上の機器を検出することを求めています」
817
817
  「node がアクセシビリティを制御することを求めています」
818
818
  「node がほかのアプリからのデータへのアクセス権を求めています」
@@ -8,15 +8,18 @@
8
8
  * 設計:
9
9
  * - 対象ファイルのバイトオフセットを記録し、増分だけ読んで行単位でパース
10
10
  * - fs.watch(file) の change イベントで増分読み取り (取りこぼし対策に軽いポーリング併用)
11
- * - DISPLAY_TYPES (user/assistant/system/result) の行だけ onEvent に渡す
11
+ * - historyEventFromLine が表示対象と判定した行だけ onEvent に渡す
12
+ * (DISPLAY_TYPES の 4 種 + queue-operation から復元したタスク終端イベント)
12
13
  * - 各行の uuid を含めて渡す (frontend 側で重複排除に使う)
13
14
  * - ファイル不在時は出現を待つ (ディレクトリ監視はせず、ポーリングで存在チェック)
14
15
  */
15
16
  import { watch as fsWatch } from "node:fs"
16
17
  import { open, realpath, stat } from "node:fs/promises"
17
18
 
18
- // 履歴 hydrate と live watch で jsonl→SDK message 整形を共通化 (フィールド取りこぼし防止)。
19
- import { DISPLAY_TYPES, normalizeHistoryEvent } from "./claude-history.mjs"
19
+ // 履歴 hydrate と live watch で「どの行を拾うか + jsonl→SDK message 整形」を共通化
20
+ // (フィールド取りこぼし・片方だけ直すドリフトの防止)。queue-operation からの終端イベント
21
+ // 合成もこの関数の中に閉じているので、live watch も自動的に同じ挙動になる。
22
+ import { historyEventFromLine } from "./claude-history.mjs"
20
23
 
21
24
  // アダプティブ・ポーリング: 最後の追記から FAST_WINDOW_MS 以内は fast、以降は idle 周期。
22
25
  // ~/.claude が iCloud Drive への symlink のため fs.watch (FSEvents) が遅延/不発しやすく、
@@ -98,8 +101,8 @@ export function watchSessionFile({ filePath, onEvent, fromEnd = true, logger })
98
101
  } catch {
99
102
  continue
100
103
  }
101
- if (!obj || !DISPLAY_TYPES.has(obj.type)) continue
102
- const event = normalizeHistoryEvent(obj)
104
+ const event = historyEventFromLine(obj)
105
+ if (!event) continue
103
106
  try {
104
107
  onEvent(event)
105
108
  } catch (err) {
@@ -10,6 +10,8 @@
10
10
  * - cwd エンコード規則: `/`, `.`, `_` などの非英数字を `-` 置換 (Claude CLI と同じ)
11
11
  * - 1 行 = 1 JSON。`type` が 'user' / 'assistant' / 'system' / 'result' のものだけ抽出
12
12
  * (内部 type 'attachment' / 'last-prompt' / 'permission-mode' 等は UI 表示対象外)
13
+ * 例外として `queue-operation` の未配送タスク通知だけは終端イベントへ合成する
14
+ * (synthesizeTaskNotificationEvent 参照。行そのものを表示するわけではない)
13
15
  * - 上限 MAX_HISTORY_LINES (デフォルト 500) で末尾から切る
14
16
  * - ファイル不在 (新規セッション) なら空配列を返す (エラーにしない)
15
17
  */
@@ -210,6 +212,149 @@ export function normalizeHistoryEvent(obj) {
210
212
  return event
211
213
  }
212
214
 
215
+ const TASK_NOTIFICATION_MARKER = "<task-notification>"
216
+ const TASK_NOTIFICATION_CLOSE = "</task-notification>"
217
+ // ヘッダ領域の終わり。ここから先は自由文 (報告全文) なので、鍵と状態の抽出対象にしない。
218
+ const TASK_NOTIFICATION_BODY_TAGS = ["<result>", "<summary>", "<note>"]
219
+ // マスクを素通りする経路なので、長さでも歯止めを掛ける (実データの tool_use_id は 30〜40 字)。
220
+ const MAX_TOOL_USE_ID_LEN = 200
221
+ const MAX_STATUS_LEN = 64
222
+
223
+ /**
224
+ * 「通知の体裁を持つのに合成しなかった」件数。**内容は一切持たない**。
225
+ *
226
+ * ⚠️ これは観測窓であって判定ではない。封筒の検査を厳しくすると、Claude CLI が将来ラッパー・
227
+ * 空白・タグ順を変えたときに**正当な通知まで落ちて幽霊カードが静かに戻る**。その「静かな戻り」に
228
+ * 気づけるよう、件数だけを数える (内容をログへ出すと、まさに伏せ字を回避したい本文が漏れる)。
229
+ */
230
+ let taskNotificationEnvelopeDrops = 0
231
+
232
+ /** 封筒の検査で合成しなかった**累計**件数を返す (テスト用)。 */
233
+ export function getTaskNotificationEnvelopeDrops() {
234
+ return taskNotificationEnvelopeDrops
235
+ }
236
+
237
+ /** 前回 drain 以降に増えた分を返して 0 に戻す (ログへ出す側が使う)。 */
238
+ let taskNotificationEnvelopeDropsReported = 0
239
+ export function drainTaskNotificationEnvelopeDrops() {
240
+ const delta = taskNotificationEnvelopeDrops - taskNotificationEnvelopeDropsReported
241
+ taskNotificationEnvelopeDropsReported = taskNotificationEnvelopeDrops
242
+ return delta
243
+ }
244
+
245
+ /** `<tag>…</tag>` の **最初の** 中身を返す。見つからなければ ""。 */
246
+ function firstTagValue(text, tag) {
247
+ const open = `<${tag}>`
248
+ const start = text.indexOf(open)
249
+ if (start < 0) return ""
250
+ const from = start + open.length
251
+ const end = text.indexOf(`</${tag}>`, from)
252
+ if (end < 0) return ""
253
+ return text.slice(from, end).trim()
254
+ }
255
+
256
+ /**
257
+ * `queue-operation` 行に埋まっている「タスク終端の宣言」を、画面が扱える最小イベントへ
258
+ * 合成する (2026-08-05)。
259
+ *
260
+ * ## なぜ必要か
261
+ * サブエージェント / バックグラウンドタスクの完了通知は、CLI がまず **キューへ積む**
262
+ * (`type:"queue-operation", operation:"enqueue"`) 形で jsonl に書かれ、その後キューから
263
+ * 取り出された時に初めて `type:"user"` の行として配送される。ところが配送前に
264
+ * セッションが閉じる・`remove` されるなどで **user 行が書かれないまま終わる**ケースが多く
265
+ * (2026-08-05 に手元の全 jsonl 2,136 本を走査した実測で、終端宣言 788 件 (tool_use_id
266
+ * ユニーク) のうち 332 件 = 42% が未配送。内訳 completed 285 / failed 29 / killed 18)、
267
+ * 画面側の台帳はそのカードを
268
+ * 「実行中」のまま抱え続ける (= リロードで残る幽霊カード)。
269
+ *
270
+ * enqueue 行の `content` には `<status>` と `<tool-use-id>` が入っており、これは
271
+ * **CLI 自身が書いた終端宣言**なので、時間で推測せずに終端を復元できる。
272
+ *
273
+ * ## ⚠️ 載せてよいのは「鍵」と「状態」と「時刻」だけ
274
+ * `<result>` にはサブエージェントの報告全文が入る (実データで確認済み)。しかしマスク処理
275
+ * `maskMessageObject` (src/tool-result-mask.mjs:165-168) は `const message = obj.message;
276
+ * if (!message …) return obj` で **即 return** するため、`message` を持たないこの合成イベントは
277
+ * **マスクを素通りする**。よって `<result>` / `<summary>` / `<note>` / `<output-file>` の本文は
278
+ * 絶対に載せない (載せると秘匿情報がマスクされないまま browser へ流れる)。`<task-id>` も
279
+ * 画面側が使っていないので載せない。
280
+ *
281
+ * 画面側 (Hub 本体 frontend/lib/cockpit/agentRequest.ts の applyTaskNotificationEvent) は
282
+ * `type:"system" / subtype:"task_notification" / tool_use_id / status` を終端として処理し、
283
+ * `requireRegistered: true` なので未知の tool_use_id で幻のカードが増えることはない。
284
+ * `timestamp` は猶予表示の起点になるので必ず載せる。
285
+ *
286
+ * @param {Record<string, unknown>} obj jsonl 1 行をパースした生 object
287
+ * @returns {Record<string, unknown>|null} 合成イベント。対象外なら null
288
+ */
289
+ export function synthesizeTaskNotificationEvent(obj) {
290
+ if (!obj || obj.type !== "queue-operation" || obj.operation !== "enqueue") return null
291
+ const content = obj.content
292
+ if (typeof content !== "string") return null
293
+ const trimmed = content.trim()
294
+ // ⚠️ **「通知を含む」ではなく「通知そのものである」ことを要求する** (2026-08-05 のクロス
295
+ // レビュー minor)。含むだけで拾うと、**利用者が通知の文面を本文へ貼っただけの入力**を終端
296
+ // 宣言として扱ってしまう。実測でも、このレビューのやり取り自体がキューに積まれ、状態として
297
+ // 日本語の文章を、鍵として 215 字の文字列を拾っていた (実データ 1,692 行中 2 件)。
298
+ // 実データでは正規の通知 1,648 件すべてが `<task-notification>` で始まり、閉じタグを持つ。
299
+ if (!trimmed.startsWith(TASK_NOTIFICATION_MARKER)) {
300
+ if (trimmed.includes(TASK_NOTIFICATION_MARKER)) taskNotificationEnvelopeDrops += 1
301
+ return null
302
+ }
303
+ // ⚠️ 閉じタグは **最後の出現**を採る。最初の出現にすると、`<result>` の本文が
304
+ // `</task-notification>` を引用しているだけで封筒が途中で切れ、正当な通知を落とす。
305
+ const closeAt = trimmed.lastIndexOf(TASK_NOTIFICATION_CLOSE)
306
+ if (closeAt < TASK_NOTIFICATION_MARKER.length) {
307
+ taskNotificationEnvelopeDrops += 1
308
+ return null
309
+ }
310
+ // 閉じタグの後ろに本文が続くものは通知そのものではない (貼り付けの typical な形)。
311
+ if (trimmed.slice(closeAt + TASK_NOTIFICATION_CLOSE.length).trim()) {
312
+ taskNotificationEnvelopeDrops += 1
313
+ return null
314
+ }
315
+ const block = trimmed.slice(TASK_NOTIFICATION_MARKER.length, closeAt)
316
+ // 鍵と状態は **ヘッダ領域** (自由文の本文タグより前) からだけ採る。本文に紛れたタグや、
317
+ // 正規の `<status>` が欠けたときに本文側のタグを拾い上げることを防ぐ。
318
+ let headerEnd = block.length
319
+ for (const tag of TASK_NOTIFICATION_BODY_TAGS) {
320
+ const at = block.indexOf(tag)
321
+ if (at >= 0 && at < headerEnd) headerEnd = at
322
+ }
323
+ const header = block.slice(0, headerEnd)
324
+ const tool_use_id = firstTagValue(header, "tool-use-id")
325
+ const status = firstTagValue(header, "status")
326
+ // 鍵か状態が欠けた通知は画面側でも無視されるので、流すだけ無駄 → 合成しない。
327
+ if (!tool_use_id || !status) {
328
+ taskNotificationEnvelopeDrops += 1
329
+ return null
330
+ }
331
+ // マスクを素通りする経路なので、長さでも歯止めを掛ける (異常に長い値は封筒が壊れたサイン)。
332
+ if (tool_use_id.length > MAX_TOOL_USE_ID_LEN || status.length > MAX_STATUS_LEN) {
333
+ taskNotificationEnvelopeDrops += 1
334
+ return null
335
+ }
336
+ const event = { type: "system", subtype: "task_notification", tool_use_id, status }
337
+ if (obj.timestamp !== undefined) event.timestamp = obj.timestamp
338
+ return event
339
+ }
340
+
341
+ /**
342
+ * jsonl 1 行 (パース済み) を UI 表示用イベントに変換する。表示対象外なら null。
343
+ *
344
+ * ⚠️ **認識と合成はこの 1 関数に閉じる**。履歴 hydrate (readSessionHistory) と live watch
345
+ * (claude-history-watch.mjs) の両方がここを通ること。過去、行のフィルタ / 整形が 2 箇所に
346
+ * 分散して片方だけ更新されるドリフトが起きているため (normalizeHistoryEvent の注記参照)。
347
+ *
348
+ * @param {Record<string, unknown>} obj
349
+ * @returns {Record<string, unknown>|null}
350
+ */
351
+ export function historyEventFromLine(obj) {
352
+ if (!obj || typeof obj !== "object") return null
353
+ // 1 行から出すイベントは最大 1 個 (画面側の終端は先勝ちなので重複は無害だが、増やさない)。
354
+ if (DISPLAY_TYPES.has(obj.type)) return normalizeHistoryEvent(obj)
355
+ return synthesizeTaskNotificationEvent(obj)
356
+ }
357
+
213
358
  /**
214
359
  * cwd 文字列を Claude Code の project dir 名に変換する。
215
360
  * 例: `/Users/kaz/hub/projects/D00585_partition-lab`
@@ -297,10 +442,10 @@ export async function readSessionHistory(filePath, { maxLines = MAX_HISTORY_LINE
297
442
  } catch {
298
443
  continue
299
444
  }
300
- if (!obj || typeof obj !== "object") continue
301
- if (!DISPLAY_TYPES.has(obj.type)) continue
302
- // SDK message と同じ shape にする (余分な meta は落とす)。整形は共通関数に集約。
303
- let event = normalizeHistoryEvent(obj)
445
+ // SDK message と同じ shape にする (余分な meta は落とす)。認識・整形・合成は
446
+ // historyEventFromLine 1 箇所に集約 (live watch も同じ関数を通る)。
447
+ let event = historyEventFromLine(obj)
448
+ if (!event) continue
304
449
  // 秘匿情報 / 非公開スキルプロンプト本文を hydrate 時にも伏せる (古いセッション再表示対策)。
305
450
  if (MASK_ENABLED) event = maskMessageObject(event, toolNames)
306
451
  events.push(event)
@@ -15,8 +15,7 @@
15
15
  * - これは Claude CLI の `claude --resume <id> -p "<msg>"` を毎ターン叩くのと同じ
16
16
  * モデルで、SDK の挙動と素直に噛み合う
17
17
  *
18
- * PtyBridge と並走させる設計。`pty.*` 系メッセージは無傷で、新規 `claude.*` のみ
19
- * 受け持つ。テスト時は SDK を `{ query }` shape で stub 注入する。
18
+ * `claude.*` 系メッセージを受け持つ。テスト時は SDK を `{ query }` shape で stub 注入する。
20
19
  */
21
20
  import { EventEmitter } from "node:events"
22
21
  import { randomUUID } from "node:crypto"
@@ -2427,7 +2426,7 @@ export class ClaudeStreamBridge extends EventEmitter {
2427
2426
  /**
2428
2427
  * 指定 cwd / session_id に紐づく SDK セッションを**即停止**する (T04784 TUI bind 用)。
2429
2428
  *
2430
- * TUI モードに切り替えた session は対話 TUI (tmux/PTY) の claude が write を担うため、
2429
+ * TUI モードに切り替えた session は tmux ペインの対話 claude が write を担うため、
2431
2430
  * SDK セッションを生かしたままだと同一 cwd の jsonl を二重ホストして競合する
2432
2431
  * (DESIGN.md C7)。soft-detach (7 日 resident) では止まらないので、ここで明示 close する。
2433
2432
  *
@@ -11,7 +11,7 @@
11
11
  * claude.input → input() (idle は turn/start、生成中は次ターン FIFO)
12
12
  * claude.interrupt → interrupt() (turn/interrupt)
13
13
  * claude.detach → detach() (TTL 付き graceful close)
14
- * (codex 固有) → keepalive() (閲覧中の生存シグナル。pty.resize 相当)
14
+ * (codex 固有) → keepalive() (閲覧中の生存シグナル)
15
15
  * claude.permission.reply → permissionReply() (client.respond/respondError)
16
16
  * claude.event (生 SDK イベント素通し) → 'event' (生 notification 素通し、同じ思想)
17
17
  * claude.permission.request → 'permission' (承認/入力要求の生 method+params 素通し)
package/src/hooks.mjs CHANGED
@@ -17,7 +17,6 @@
17
17
  export const HOOK_NAMES = Object.freeze([
18
18
  "onAgentStart",
19
19
  "onAgentStop",
20
- "interceptPtySpawn",
21
20
  "interceptTmuxExec",
22
21
  "interceptSessionCreate",
23
22
  "interceptSessionKill",
@@ -26,21 +25,6 @@ export const HOOK_NAMES = Object.freeze([
26
25
  "transformStatusDetection",
27
26
  ])
28
27
 
29
- /**
30
- * @typedef {Object} PtySpawnArgs
31
- * @property {string} sessionName
32
- * @property {number} cols
33
- * @property {number} rows
34
- * @property {Record<string, string>} env
35
- */
36
-
37
- /**
38
- * @typedef {Object} PtySpawnDecision
39
- * @property {string} command
40
- * @property {string[]} args
41
- * @property {Record<string, string>} [env]
42
- */
43
-
44
28
  /**
45
29
  * @typedef {Object} TmuxExecArgs
46
30
  * @property {string[]} args
@@ -60,7 +44,6 @@ export const HOOK_NAMES = Object.freeze([
60
44
  * @property {{
61
45
  * onAgentStart?: (ctx: HookContext) => Promise<void>,
62
46
  * onAgentStop?: (ctx: HookContext) => Promise<void>,
63
- * interceptPtySpawn?: (ctx: HookContext & PtySpawnArgs) => Promise<PtySpawnDecision | null>,
64
47
  * interceptTmuxExec?: (ctx: HookContext & TmuxExecArgs) => Promise<TmuxExecDecision | null>,
65
48
  * }} hooks
66
49
  */