@cocorograph/hub-agent 0.7.13 → 0.7.14

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cocorograph/hub-agent",
3
- "version": "0.7.13",
3
+ "version": "0.7.14",
4
4
  "description": "Hub Hosted Cockpit のローカル常駐 agent。Hub と outbound WSS で接続し、ローカルの tmux/pty を中継する。",
5
5
  "type": "module",
6
6
  "license": "UNLICENSED",
@@ -35,6 +35,17 @@ const _byCwd = new Map()
35
35
  // 一次対策で、本 TTL はその取りこぼし時のバックストップ。
36
36
  const CHAT_SIGNAL_STALE_MS = 90 * 1000
37
37
 
38
+ // RC4 (症状④): stream_event (拡張思考の thinking_delta / 長時間ツール中の partial delta)
39
+ // 受信を status=processing の tick として記録する際の cwd 単位スロットル間隔。stream_event は
40
+ // トークン delta 毎に高頻度で流れるため、無制限に recordChatActivity を呼ぶと _byCwd への書き込み
41
+ // 洪水になる。この間隔で間引いても、>90s の拡張思考バースト / 単発ツールの間 statusAt・updatedAtMs
42
+ // を確実に前進させ、CHAT_SIGNAL_STALE_MS による idle 誤降格 (純 SDK チャットで三点リーダーが消える
43
+ // 事故) を防げる。
44
+ const STREAM_TICK_THROTTLE_MS = 3 * 1000
45
+
46
+ // cwd → 最後に stream tick を記録した時刻 (スロットル判定用)。
47
+ const _lastStreamTickAt = new Map()
48
+
38
49
  const VALID_STATUS = new Set(["processing", "waiting", "idle"])
39
50
 
40
51
  function _now() {
@@ -86,6 +97,33 @@ export function recordChatActivity(cwd, patch = {}, now = _now()) {
86
97
  _byCwd.set(cwd, next)
87
98
  }
88
99
 
100
+ /**
101
+ * RC4 (症状④): stream_event (拡張思考 thinking_delta / 長時間ツール中の partial delta) 受信を
102
+ * status=processing の「生きている」tick として記録する。assistant / result が流れない長い無音
103
+ * 区間 (>90s) でも statusAt・updatedAtMs を前進させ、getChatSignal が CHAT_SIGNAL_STALE_MS で
104
+ * null を返して三点リーダーが消える事故を防ぐ。
105
+ *
106
+ * 設計上の注意:
107
+ * - cwd 単位で STREAM_TICK_THROTTLE_MS に間引く (delta 毎の Map 書き込み洪水を回避)。
108
+ * - delta 本文は一切読まない (タイムスタンプ前進のみ)。拡張思考の本文は ephemeral で、ここで
109
+ * 保持・転送する必要は無く、プライバシー上も読むべきでない。
110
+ * - SDK のイベント順序上 stream_event はターン中の assistant ブロックに属し result より前に
111
+ * 流れる。abort 時は SDK query が即停止して onEvent が止まるため、result/turnsettled で
112
+ * waiting に落ちた後に trailing stream_event が processing を蘇生させる経路は無い。
113
+ *
114
+ * @param {string} cwd
115
+ * @param {number} [now]
116
+ */
117
+ export function recordChatStreamTick(cwd, now = _now()) {
118
+ if (!cwd || typeof cwd !== "string") return
119
+ // 初回 (未 tick) は必ず記録する。`|| 0` で last を 0 既定にすると「時刻 0 で tick 済み」と
120
+ // 区別できず、初回 tick が now < throttle のとき誤って間引かれる。has で未 tick を判定する。
121
+ const last = _lastStreamTickAt.get(cwd)
122
+ if (last !== undefined && now - last < STREAM_TICK_THROTTLE_MS) return
123
+ _lastStreamTickAt.set(cwd, now)
124
+ recordChatActivity(cwd, { status: "processing" }, now)
125
+ }
126
+
89
127
  /**
90
128
  * cwd に対応する生きているチャット信号を返す。stale / 不在なら null。
91
129
  *
@@ -116,11 +154,13 @@ export function getChatSignal(cwd, now = _now()) {
116
154
  export function clearChatSignal(cwd) {
117
155
  if (!cwd || typeof cwd !== "string") return
118
156
  _byCwd.delete(cwd)
157
+ _lastStreamTickAt.delete(cwd)
119
158
  }
120
159
 
121
160
  /** テスト用: ストアを空にする。 */
122
161
  export function _resetChatSignals() {
123
162
  _byCwd.clear()
163
+ _lastStreamTickAt.clear()
124
164
  }
125
165
 
126
- export { CHAT_SIGNAL_STALE_MS }
166
+ export { CHAT_SIGNAL_STALE_MS, STREAM_TICK_THROTTLE_MS }
package/src/main.mjs CHANGED
@@ -92,7 +92,12 @@ import {
92
92
  getUsage,
93
93
  recordChatRateLimit,
94
94
  } from "./usage.mjs"
95
- import { clearChatSignal, getChatSignal, recordChatActivity } from "./chat-signals.mjs"
95
+ import {
96
+ clearChatSignal,
97
+ getChatSignal,
98
+ recordChatActivity,
99
+ recordChatStreamTick,
100
+ } from "./chat-signals.mjs"
96
101
  import {
97
102
  ReadinessTracker,
98
103
  busyChildCount,
@@ -491,6 +496,17 @@ export async function startDaemon({ version, ptyModule, claudeSdk } = {}) {
491
496
  } catch {
492
497
  /* ignore */
493
498
  }
499
+ } else if (event?.type === "stream_event") {
500
+ // RC4 (症状④): 拡張思考 (thinking_delta) や長時間ツール中の partial delta では
501
+ // assistant / result が流れず statusAt / updatedAtMs が前進しないため、>90s の無音で
502
+ // getChatSignal が CHAT_SIGNAL_STALE_MS により null を返し、capture-pane が idle な
503
+ // 純 SDK チャットで三点リーダーが消える。stream_event 受信を throttle 付き processing
504
+ // tick として記録し statusAt を前進させる (本文は読まずタイムスタンプ前進のみ)。
505
+ try {
506
+ recordChatStreamTick(cwd)
507
+ } catch {
508
+ /* ignore */
509
+ }
494
510
  }
495
511
  client.send({ type: "claude.event", stream_id, session_id, event })
496
512
  })