@cocorograph/hub-agent 0.6.83 → 0.6.84

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.
Files changed (2) hide show
  1. package/package.json +1 -1
  2. package/src/state.mjs +33 -10
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cocorograph/hub-agent",
3
- "version": "0.6.83",
3
+ "version": "0.6.84",
4
4
  "description": "Hub Hosted Cockpit のローカル常駐 agent。Hub と outbound WSS で接続し、ローカルの tmux/pty を中継する。",
5
5
  "type": "module",
6
6
  "license": "UNLICENSED",
package/src/state.mjs CHANGED
@@ -132,16 +132,33 @@ function footerRegion(text, lines = 8) {
132
132
  }
133
133
 
134
134
  /**
135
- * 作業スピナーのフッター行を検出する。claude TUI の生成中フッターは
136
- * "✻ Cogitating… (12s · ↑ 1.2k tokens · esc to interrupt)" のように
137
- * 「経過秒」と「トークンカウンタ」が同一行に同居する。idle/waiting の権限バナーや
138
- * 通常本文には出ないシグネチャなので、中断フッター文言 ("esc to interrupt")
139
- * 未描画 (ターン序盤) / locale / ツール実行中などで取りこぼされる区間でも
140
- * 生成中を拾える。誤検出を抑えるためフッター領域 + 同一行同居を必須にする。
135
+ * 作業スピナーのフッター行を検出する (生成中=processing の補助シグナル)。
136
+ *
137
+ * 現行 claude TUI の生成中フッターは語が毎ターン変わる (実機採取 2026-06-15):
138
+ * Blanching… (2m 4s · ↓ 7.5k tokens)" (出力中)
139
+ * "✽ Scurrying… (16m 55s · 66.8k tokens)" (出力中)
140
+ * Puzzling… (11m 12s · thinking)" (思考中・tokens 語が無い)
141
+ * 語 (Blanching/Puzzling/Processing/Orbiting/Scurrying…) は無数にあり版で増減するため
142
+ * 語リストでは判定しない。また現行版は "esc to interrupt" をフッターに出さない (主シグナル
143
+ * が実機で死んでいる)。版に依存せず安定する構造は「<語>… (<ライブ経過タイマー> …)」=
144
+ * 任意語 + 省略記号 + 括弧で囲まれた経過時間。完了サマリー "✻ Brewed for 2m 52s" は省略
145
+ * 記号も括弧タイマーも持たないので拾わない。誤検出を抑えるため判定はフッター領域に限定する。
146
+ *
147
+ * 2 系統で判定する:
148
+ * (a) 構造シグナル: "<語>… (<m? s>" のライブタイマー。tokens の有無に依存しないので
149
+ * thinking フッターも拾う (旧実装の偽陰性の根治)。tips 行 (⎿ Tip:…) でスピナー行が
150
+ * 押し上げられる分を見込み少し広い領域 (12 行) を走査する。構造が限定的なので
151
+ * スクロールバックの古フッター "(99s · …) old footer" 等の誤検出に強い。
152
+ * (b) 旧シグナル (回帰防止): "tokens" 語 + 経過秒の同一行同居。タイマーが括弧直後でない
153
+ * 語順 "(↑ 3.4k tokens · 7s)" も拾う。誤検出しやすいのでフッター 8 行に限定する。
141
154
  */
142
155
  function detectWorkingSpinner(text) {
143
- const footer = footerRegion(text)
144
- for (const line of footer.split("\n")) {
156
+ // (a) 構造シグナル: 任意語 + 省略記号 + 括弧ライブタイマー (tokens 非依存・thinking も拾う)
157
+ for (const line of footerRegion(text, 12).split("\n")) {
158
+ if (/[A-Za-z]+(?:…|\.\.\.)\s*\(\s*(?:\d+\s*m\s*)?\d+\s*s\b/.test(line)) return true
159
+ }
160
+ // (b) 旧シグナル: tokens カウンタ + 経過秒の同一行同居 (フッター 8 行限定)。
161
+ for (const line of footerRegion(text, 8).split("\n")) {
145
162
  if (/\btokens\b/i.test(line) && /(?:^|[\s(])\d+\s*s\b/.test(line)) return true
146
163
  }
147
164
  return false
@@ -441,10 +458,16 @@ function _outputSignature(text) {
441
458
  }
442
459
 
443
460
  /** 内容安定による「確実に停止」判定。processing (ローダー在中) は常に false で、
444
- * 停止後に出力領域が STABLE_CONFIRM_MS 不変であれば true。 */
461
+ * 停止後に出力領域が STABLE_CONFIRM_MS 不変であれば true。
462
+ *
463
+ * 多重防御 (2026-06-15): status の読み違いがあっても、ライブスピナー (経過タイマー) が
464
+ * ペインに在る間は「確実に停止」を絶対に返さない。frontend は stable=true を受けると
465
+ * 2 サンプル消灯確認 (paneOffConfirmed) をバイパスして即消灯するため、将来 claude が
466
+ * 未知のフッター形式を出して status を取りこぼしても、タイマーが画面に出ている限り
467
+ * 即消灯の暴発を防ぐ保険にする (生成中ローダー消失の再発防止)。 */
445
468
  function _computeStable(sessionName, status, text) {
446
469
  const now = Date.now()
447
- if (status === "processing") {
470
+ if (status === "processing" || detectWorkingSpinner(text)) {
448
471
  _stabilityByName.set(sessionName, { sig: _outputSignature(text), since: now })
449
472
  return false
450
473
  }