@cocorograph/hub-agent 0.7.42 → 0.7.44

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.42",
3
+ "version": "0.7.44",
4
4
  "description": "Hub Hosted Cockpit のローカル常駐 agent。Hub と outbound WSS で接続し、ローカルの tmux/pty を中継する。",
5
5
  "type": "module",
6
6
  "license": "UNLICENSED",
@@ -1197,12 +1197,44 @@ class ClaudeStreamSession {
1197
1197
  abortTurn() {
1198
1198
  // 常駐 query は interrupt() で現ターンのみ中断する (query は継続、次 input 受付継続)。
1199
1199
  if (this._residentQuery && typeof this._residentQuery.interrupt === "function") {
1200
+ const wasBusy = this._busy
1200
1201
  try {
1201
1202
  this._residentQuery.interrupt()
1202
1203
  } catch {
1203
1204
  /* ignore */
1204
1205
  }
1205
1206
  this._busy = false
1207
+ // SDK-P0-2 (2026-07-07): resident 経路のターン終端イベント非対称の解消。
1208
+ // per-message の abort (_runPerMessage finally) は合成 result (aborted_by_user) を
1209
+ // frontend へ emit + onTurnSettled を呼んで UI (turnActive/interrupting) とサイドバー
1210
+ // 信号 (processing→waiting) を即時解除するが、resident の abortTurn は interrupt() が
1211
+ // result を発火しない (下記 T05699 コメント参照) ため、どちらも出さず UI は 120s 無音
1212
+ // ウォッチドッグ / サイドバーは 90s TTL 失効まで「停止中…」で固着していた。ここで
1213
+ // per-message と対称な終端イベントを出し、即時にターン終了を確定させる。
1214
+ // - 実行中ターンがあった (wasBusy) ときだけ emit する (未走行での空 abort に
1215
+ // 「中断しました」フッターを出さない)。
1216
+ // - close()/reap 経由の abort (_closed=true) では browser は既に離脱しているため抑止する。
1217
+ // - drain タイミングは変更しない (既存の同期 drain を維持し、result+idle 二重 drain や
1218
+ // 中断ターンの遅延 idle が新ターンを誤終了させる新種レースを持ち込まない)。
1219
+ // - 合成 result の uuid は重複排除 (isDuplicateEvent の result 署名判定) 回避のため必須。
1220
+ if (wasBusy && !this._closed) {
1221
+ try {
1222
+ this.onTurnSettled?.()
1223
+ } catch {
1224
+ /* ignore */
1225
+ }
1226
+ try {
1227
+ this._emit({
1228
+ type: "result",
1229
+ subtype: "aborted_by_user",
1230
+ uuid: randomUUID(),
1231
+ session_id: this.sessionId ?? undefined,
1232
+ timestamp: new Date().toISOString(),
1233
+ })
1234
+ } catch {
1235
+ /* ignore */
1236
+ }
1237
+ }
1206
1238
  // T05699 残タスク #2-1 (2026-07-04): interrupt() は現ターンの result を発火しない
1207
1239
  // (query は継続、次 input 受付継続のみ)。そのため abort 時点で _pendingMessages に
1208
1240
  // 溜まっている送信待ちメッセージは、result 経由の drain (_runResidentQuery 内の
package/src/ws-client.mjs CHANGED
@@ -86,6 +86,17 @@ const BUFFERED_CTRL_TYPES = new Set([
86
86
  const CTRL_BUFFER_MAX_FRAMES = 200
87
87
  const CTRL_BUFFER_MAX_AGE_MS = 30_000
88
88
 
89
+ /** SDK-P2 (2026-07-08): claude.event が「ephemeral な生成中 delta」かどうか。
90
+ * stream_event の content_block_delta (text_delta / thinking_delta) は高頻度で流れる
91
+ * ephemeral (liveText 用) であり、切断中に ctrl バッファへ積むと確定イベントを evict する。
92
+ * 確定イベント (assistant/result/user/system) と非 delta stream_event (message_start 等,
93
+ * 低頻度) はバッファ対象として残す。 */
94
+ function isEphemeralDeltaEvent(obj) {
95
+ const ev = obj?.event
96
+ if (!ev || ev.type !== "stream_event") return false
97
+ return ev.event?.type === "content_block_delta"
98
+ }
99
+
89
100
  export class WsClient extends EventEmitter {
90
101
  /**
91
102
  * @param {{ hub_url: string, agent_id: string, agent_token: string }} config
@@ -260,6 +271,15 @@ export class WsClient extends EventEmitter {
260
271
  return false
261
272
  }
262
273
  if (BUFFERED_CTRL_TYPES.has(type)) {
274
+ // SDK-P2 (2026-07-08): ephemeral な stream_event の content_block_delta (text/thinking
275
+ // delta) は ctrl バッファに積まない。切断中は生成が続くと大量の delta が流れ、200 frame
276
+ // の ctrl リングを埋め尽くして低頻度の確定イベント (assistant/result/user/system) を
277
+ // evict していた (再接続後に確定バブルが出ず「異常に遅い」S5 の真因)。delta は liveText
278
+ // 用の ephemeral で、確定 assistant イベント (これはバッファ対象) が全文を持つため、
279
+ // 切断中の delta を捨てても再接続後の表示は確定イベントで復元される。
280
+ if (type === "claude.event" && isEphemeralDeltaEvent(obj)) {
281
+ return false
282
+ }
263
283
  this._bufferCtrl(obj)
264
284
  return false
265
285
  }