@cocorograph/hub-agent 0.7.86 → 0.7.87
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 +1 -1
- package/src/claude-stream-bridge.mjs +65 -31
- package/src/main.mjs +20 -7
package/package.json
CHANGED
|
@@ -2401,31 +2401,76 @@ export class ClaudeStreamBridge extends EventEmitter {
|
|
|
2401
2401
|
* @param {string} cwd
|
|
2402
2402
|
* @returns {string|null}
|
|
2403
2403
|
*/
|
|
2404
|
-
|
|
2404
|
+
/**
|
|
2405
|
+
* cwd 内で「最終アクティビティが最も新しいライブセッション」を返す共通スキャン。
|
|
2406
|
+
*
|
|
2407
|
+
* ⚠️ this.sessions **と** _liveBySession の union を走査する (P5 / T06570 症状3): detach で
|
|
2408
|
+
* セッションは this.sessions から外れるが _liveBySession に warm 保持される (_softDetachSession)。
|
|
2409
|
+
* this.sessions だけを見ると「browser を離れた稼働中セッション」を取りこぼし、bound id /
|
|
2410
|
+
* turn_active が落ちて state loop が jsonl を mtime 最新で誤バインドし、非選択行のドットが
|
|
2411
|
+
* 稼働中でも赤へ誤落ちする。reconcileWarmBounds / shutdown と同じ union 走査に揃える。
|
|
2412
|
+
*
|
|
2413
|
+
* @param {string} cwd
|
|
2414
|
+
* @param {boolean} [requireSessionId=true] sessionId 未確定の fresh を除外するか (jsonl バインド用途は true)。
|
|
2415
|
+
* @returns {ClaudeStreamSession|null}
|
|
2416
|
+
*/
|
|
2417
|
+
_liveSessionForCwd(cwd, requireSessionId = true) {
|
|
2405
2418
|
if (!cwd) return null
|
|
2406
2419
|
let best = null
|
|
2407
2420
|
let bestAt = -1
|
|
2408
2421
|
const seen = new Set()
|
|
2409
|
-
|
|
2410
|
-
|
|
2411
|
-
|
|
2412
|
-
|
|
2413
|
-
|
|
2414
|
-
if (
|
|
2415
|
-
|
|
2416
|
-
|
|
2417
|
-
|
|
2418
|
-
|
|
2419
|
-
|
|
2420
|
-
|
|
2421
|
-
|
|
2422
|
-
|
|
2423
|
-
|
|
2422
|
+
// this.sessions (attached) を先に走査してから _liveBySession (soft-detached の warm) を走査し
|
|
2423
|
+
// union にする。同一セッションは両 Map に載り得るため seen で識別重複を排除する。
|
|
2424
|
+
const scan = (iter) => {
|
|
2425
|
+
for (const session of iter) {
|
|
2426
|
+
if (seen.has(session) || session.cwd !== cwd) continue
|
|
2427
|
+
if (requireSessionId && !session.sessionId) continue
|
|
2428
|
+
seen.add(session)
|
|
2429
|
+
let at = 0
|
|
2430
|
+
for (const t of session.lastActivityByStream.values()) {
|
|
2431
|
+
if (t > at) at = t
|
|
2432
|
+
}
|
|
2433
|
+
// `>=` (2026-07-03): Date.now() はミリ秒精度のため、短時間に複数セッションへ
|
|
2434
|
+
// attach+input すると同一ミリ秒でタイが起き得る。挿入順/走査順で後に来るものを採用する
|
|
2435
|
+
// のが「最終アクティビティが最も新しい」の意図に合う (attached を先に走査するため、
|
|
2436
|
+
// attached と warm がタイなら warm を採る=どちらも同一 cwd の生存 session で無害)。
|
|
2437
|
+
if (at >= bestAt) {
|
|
2438
|
+
bestAt = at
|
|
2439
|
+
best = session
|
|
2440
|
+
}
|
|
2424
2441
|
}
|
|
2425
2442
|
}
|
|
2443
|
+
scan(this.sessions.values())
|
|
2444
|
+
scan(this._liveBySession.values())
|
|
2426
2445
|
return best
|
|
2427
2446
|
}
|
|
2428
2447
|
|
|
2448
|
+
/**
|
|
2449
|
+
* main.mjs (startStateLoop / tmux.list_sessions ハンドラ) が使うチャット版 bound session 解決。
|
|
2450
|
+
* cwd 内で最終活性が最も新しいライブセッションの sessionId を返す。背景は _liveSessionForCwd 参照。
|
|
2451
|
+
* @param {string} cwd
|
|
2452
|
+
* @returns {string|null}
|
|
2453
|
+
*/
|
|
2454
|
+
liveSessionIdForCwd(cwd) {
|
|
2455
|
+
return this._liveSessionForCwd(cwd, true)?.sessionId ?? null
|
|
2456
|
+
}
|
|
2457
|
+
|
|
2458
|
+
/**
|
|
2459
|
+
* P5 (T06570 症状3): cwd のライブチャットセッションの **agent 側ターン進行フラグ** (_busy) を返す。
|
|
2460
|
+
* _busy は SDK query ライフサイクル (ターン開始で true / result・idle・exit で同期 false) 由来で、
|
|
2461
|
+
* browser の attach/detach やストリーム tick に依存しない権威信号。state loop の turn_active に
|
|
2462
|
+
* これを採ることで、browser を離れた稼働中セッションでもドットが青のまま追従し、ターン終了時は
|
|
2463
|
+
* 次 tick で即座に赤へ落ちる (誤青を生まない)。Codex の codexTurnActiveForThread と対称。
|
|
2464
|
+
* fresh (sessionId 未確定) の初回ターンも稼働中として拾うため requireSessionId=false。
|
|
2465
|
+
* @param {string} cwd
|
|
2466
|
+
* @returns {boolean|null} true=ターン進行中 / false=idle / null=このcwdにライブチャットセッション無し (純TUI等)
|
|
2467
|
+
*/
|
|
2468
|
+
chatTurnActiveForCwd(cwd) {
|
|
2469
|
+
const s = this._liveSessionForCwd(cwd, false)
|
|
2470
|
+
if (!s) return null
|
|
2471
|
+
return s._busy === true
|
|
2472
|
+
}
|
|
2473
|
+
|
|
2429
2474
|
/**
|
|
2430
2475
|
* 指定 cwd でライブなセッションへメッセージを直接注入する (sibling-inject 用、
|
|
2431
2476
|
* 2026-07-04)。同一マシン内の別プロセス (ask-sibling スキル経由の cockpit_orch.py)
|
|
@@ -2445,21 +2490,10 @@ export class ClaudeStreamBridge extends EventEmitter {
|
|
|
2445
2490
|
*/
|
|
2446
2491
|
sendToCwd(cwd, text) {
|
|
2447
2492
|
if (!cwd || !text) return false
|
|
2448
|
-
|
|
2449
|
-
|
|
2450
|
-
|
|
2451
|
-
|
|
2452
|
-
if (seen.has(session) || session.cwd !== cwd) continue
|
|
2453
|
-
seen.add(session)
|
|
2454
|
-
let at = 0
|
|
2455
|
-
for (const t of session.lastActivityByStream.values()) {
|
|
2456
|
-
if (t > at) at = t
|
|
2457
|
-
}
|
|
2458
|
-
if (at >= bestAt) {
|
|
2459
|
-
bestAt = at
|
|
2460
|
-
best = session
|
|
2461
|
-
}
|
|
2462
|
-
}
|
|
2493
|
+
// P5 (T06570): warm (soft-detached) セッションも対象にするため union 走査へ委譲する
|
|
2494
|
+
// (従来は this.sessions のみで、detach 済みだが生きているセッションへ sibling-inject できず
|
|
2495
|
+
// no_live_session_for_cwd になっていた)。sessionId 未確定の fresh も対象にするため false。
|
|
2496
|
+
const best = this._liveSessionForCwd(cwd, false)
|
|
2463
2497
|
if (!best) return false
|
|
2464
2498
|
best.sendMessage({ role: "user", content: text }).catch((err) => {
|
|
2465
2499
|
this.logger?.error(
|
package/src/main.mjs
CHANGED
|
@@ -2579,14 +2579,27 @@ export function startStateLoop({ client, plugins, logger, intervalMs, claudeBrid
|
|
|
2579
2579
|
case "codex":
|
|
2580
2580
|
turnActive = await codexTurnActiveForThread(codexThreadId)
|
|
2581
2581
|
break
|
|
2582
|
-
case "claude":
|
|
2583
|
-
|
|
2584
|
-
|
|
2585
|
-
|
|
2586
|
-
|
|
2587
|
-
|
|
2588
|
-
)
|
|
2582
|
+
case "claude": {
|
|
2583
|
+
// P5 (T06570 症状3): チャットセッションは、そのセッションの SDK query ライフサイクル由来の
|
|
2584
|
+
// 権威フラグ _busy を最優先する (Codex の rollout flag 優先と対称)。これにより browser を
|
|
2585
|
+
// 離れて detach され this.sessions から外れた warm セッションでも、稼働中ターンの turn_active
|
|
2586
|
+
// が維持され、非選択行のドットが赤へ誤落ちしない。加えて bare-shell pane を dead 判定して
|
|
2587
|
+
// 赤へ倒す paneAliveProbe 経路 (チャットは実体が SDK 子プロセスで pane は素のシェル) も回避する。
|
|
2588
|
+
// chatTurnActiveForCwd が null (= この cwd にライブチャットセッション無し = 純 TUI / bare pane)
|
|
2589
|
+
// のときだけ、従来の jsonl 末尾 + pane probe 経路にフォールバックする (TUI 挙動は不変)。
|
|
2590
|
+
const chatActive = claudeBridge?.chatTurnActiveForCwd?.(s.cwd)
|
|
2591
|
+
if (chatActive === true || chatActive === false) {
|
|
2592
|
+
turnActive = chatActive
|
|
2593
|
+
} else {
|
|
2594
|
+
turnActive = await turnActiveForCwd(
|
|
2595
|
+
s.cwd,
|
|
2596
|
+
resolveStateLoopBoundSessionId(boundSessions, s.session_name, s.cwd, claudeBridge),
|
|
2597
|
+
s.session_name,
|
|
2598
|
+
{ paneAliveProbe: paneClaudeAliveOrUnknown },
|
|
2599
|
+
)
|
|
2600
|
+
}
|
|
2589
2601
|
break
|
|
2602
|
+
}
|
|
2590
2603
|
default:
|
|
2591
2604
|
turnActive = null
|
|
2592
2605
|
}
|