@cocorograph/hub-agent 0.7.77 → 0.7.78

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.77",
3
+ "version": "0.7.78",
4
4
  "description": "Hub Hosted Cockpit のローカル常駐 agent。Hub と outbound WSS で接続し、ローカルの tmux/pty を中継する。",
5
5
  "type": "module",
6
6
  "license": "UNLICENSED",
@@ -361,6 +361,12 @@ export class CodexStreamBridge extends EventEmitter {
361
361
  // 通知 (ターン進捗) の両方で更新する。LRU は「最終活性が最も古い非 protected」を退避する。
362
362
  // Date.now() は hub-agent 実行環境で利用可。
363
363
  lastActivityMs: Date.now(),
364
+ // 相方選定・一覧の「新しさ」用の会話ターン境界時刻。lastActivityMs(keepalive 閲覧で
365
+ // 更新される warm/reap 保護用)と役割を分離し、**turn/started・turn/completed のときだけ**
366
+ // 更新する。これにより「見ているだけ(keepalive)」で並び順・ask-sibling 宛先選定が動く
367
+ // 問題を根治する(症状⑨と同じ「read/閲覧時刻を鮮度信号に流用しない」原則)。初期値は
368
+ // attach 時刻=新規セッションは「たった今作った」で上位に来る(閲覧中の古い会話に負けない)。
369
+ lastTurnAtMs: Date.now(),
364
370
  ready: null,
365
371
  }
366
372
  session.ready = new Promise((resolve) => {
@@ -610,9 +616,12 @@ export class CodexStreamBridge extends EventEmitter {
610
616
  switch (msg.method) {
611
617
  case "turn/started":
612
618
  session.activeTurnId = msg.params?.turn?.id || null
619
+ // 会話ターン境界で「新しさ」を更新(閲覧 keepalive では更新しない)。
620
+ session.lastTurnAtMs = Date.now()
613
621
  break
614
622
  case "turn/completed":
615
623
  session.activeTurnId = null
624
+ session.lastTurnAtMs = Date.now()
616
625
  this._autoResolveStalePendingApprovals(session, msg.params?.turn?.id)
617
626
  // Stage 5: turn 完了後の reap 予約は不要。以降 idle になれば周期 reconcile が
618
627
  // 長 idle TTL / LRU で扱う(detach 済みでも 7 日 warm 保持)。
@@ -833,7 +842,9 @@ export class CodexStreamBridge extends EventEmitter {
833
842
  thread_id: session.threadId || null,
834
843
  busy: !!session.activeTurnId,
835
844
  detached: !!session.detachRequested,
836
- idle_ms: now - (session.lastActivityMs ?? now),
845
+ // idle_ms は「新しさ」の指標(一覧の並び・宛先選定に使う)なので、閲覧 keepalive で
846
+ // 動く lastActivityMs ではなく **会話ターン境界時刻 lastTurnAtMs** を基準にする。
847
+ idle_ms: now - (session.lastTurnAtMs ?? session.lastActivityMs ?? now),
837
848
  })
838
849
  }
839
850
  return out
@@ -879,7 +890,9 @@ export class CodexStreamBridge extends EventEmitter {
879
890
  let bestAt = -1
880
891
  for (const session of this.sessions.values()) {
881
892
  if (session.dead || session.cwd !== cwd) continue
882
- const at = session.lastActivityMs ?? 0
893
+ // 宛先選定の「最新」は会話ターン境界時刻で判定する(閲覧 keepalive で動く
894
+ // lastActivityMs を使うと「見ているだけの古い会話」が本命を追い越す)。
895
+ const at = session.lastTurnAtMs ?? session.lastActivityMs ?? 0
883
896
  if (at >= bestAt) {
884
897
  bestAt = at
885
898
  best = session