@cocorograph/hub-agent 0.8.78 → 0.8.79

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.8.78",
3
+ "version": "0.8.79",
4
4
  "description": "Hub Hosted Cockpit のローカル常駐 agent。Hub と outbound WSS で接続し、ローカルの Claude / Codex チャットと tmux セッションを中継する。",
5
5
  "type": "module",
6
6
  "license": "UNLICENSED",
@@ -48,6 +48,29 @@ function idleRank(item) {
48
48
  return typeof item?.idle_ms === "number" && item.idle_ms >= 0 ? item.idle_ms : Infinity
49
49
  }
50
50
 
51
+ /**
52
+ * 記述子の会話 ID。claude は `session_id`、codex は `thread_id` に入る。
53
+ * まだ確定していない会話 (初回入力前の attach 等) は両方とも欠けるので null を返す。
54
+ */
55
+ function conversationIdOf(item) {
56
+ if (typeof item?.session_id === "string" && item.session_id) return item.session_id
57
+ if (typeof item?.thread_id === "string" && item.thread_id) return item.thread_id
58
+ return null
59
+ }
60
+
61
+ /**
62
+ * 記述子が示す接続状態。⚠️ **2 つの CLI で持ち方が逆**で、claude は `attached`
63
+ * (claude-stream-bridge.mjs `_describeCandidate`)、codex は `detached`
64
+ * (codex-stream-bridge.mjs `_describe` = `!!session.detachRequested`) を返す。
65
+ * 片方だけを見ると、もう片方は接続中でも常に「非接続」として記録され、旧インスタンスの
66
+ * 残留調査を誤らせる。どちらも無ければ **false ではなく null** (不明) を返す。
67
+ */
68
+ function attachedOf(item) {
69
+ if (typeof item?.attached === "boolean") return item.attached
70
+ if (typeof item?.detached === "boolean") return !item.detached
71
+ return null
72
+ }
73
+
51
74
  /** ローカル検出した宛先と、毎回取得するライブ会話を返す。 */
52
75
  export async function buildChatSessionsSnapshot({
53
76
  bridges,
@@ -96,11 +119,24 @@ export async function buildChatSessionsSnapshot({
96
119
  }
97
120
  const kept = live[prior]
98
121
  const replace = idleRank(item) < idleRank(kept)
122
+ const winner = replace ? item : kept
123
+ const loser = replace ? kept : item
124
+ // ⚠️ cwd だけでは衝突の正体が分からない。想定していたのは「別 workspace 配下の同名
125
+ // worktree」(cwd が違う) だが、実際に観測されたのは **cwd が同じ** 衝突で、これは
126
+ // 同名 worktree では説明できない。会話 ID を出さないと「同じ会話が二重に生きている」
127
+ // (bridge に旧インスタンスが残る) のか「同じフォルダで別会話が 2 本走っている」のかを
128
+ // 区別できず、2026-09-17 に実際にログだけでは判別できずに詰まった。attached は
129
+ // 「bridge から見て今ブラウザが繋がっているか」で、旧インスタンスの残骸は false に
130
+ // なりやすい (ブラウザ側の実際の生存を保証する値ではない)。
99
131
  logger?.warn?.(
100
132
  {
101
133
  session_name: sessionName,
102
134
  kept_cwd: replace ? cwd : kept.cwd,
103
135
  dropped_cwd: replace ? kept.cwd : cwd,
136
+ kept_conversation_id: conversationIdOf(winner),
137
+ dropped_conversation_id: conversationIdOf(loser),
138
+ kept_attached: attachedOf(winner),
139
+ dropped_attached: attachedOf(loser),
104
140
  },
105
141
  "chat.sessions: session name collision (folded)",
106
142
  )