@cocorograph/hub-agent 0.7.76 → 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 +1 -1
- package/src/codex-stream-bridge.mjs +36 -3
package/package.json
CHANGED
|
@@ -126,7 +126,27 @@ function flattenThreadItems(thread, { maxItems = HISTORY_MAX_ITEMS, maxBytes = H
|
|
|
126
126
|
const turns = Array.isArray(thread?.turns) ? thread.turns : []
|
|
127
127
|
const all = []
|
|
128
128
|
for (const turn of turns) {
|
|
129
|
-
if (Array.isArray(turn?.items))
|
|
129
|
+
if (!Array.isArray(turn?.items)) continue
|
|
130
|
+
// 表示用バブル時刻 (症状⑧: 履歴 hydrate で全バブルが reload 時刻 = Date.now() になる問題)。
|
|
131
|
+
// app-server の ThreadItem は per-item timestamp を持たない (生成スキーマで確認済み)。
|
|
132
|
+
// ターン単位の時刻 (単位は秒) を各 item に at_ms (ms epoch) として付与する。
|
|
133
|
+
//
|
|
134
|
+
// ⚠️ ライブ経路との整合 (round2 minor): item の種別で使う時刻を変える。
|
|
135
|
+
// - userMessage: ライブでは item/completed.completedAtMs = **ターン冒頭**の時刻で届く
|
|
136
|
+
// (ユーザー入力の echo は即完了するため)。履歴で completedAt(ターン終了) を当てると、
|
|
137
|
+
// 長いターンでリロード後にユーザーバブルが終了時刻へ飛ぶ。→ Turn.startedAt を優先。
|
|
138
|
+
// - それ以外 (agentMessage/tool 等): ライブでは各 item の完了時刻で届くので、
|
|
139
|
+
// ターン終了に近い Turn.completedAt を優先 (無ければ startedAt)。
|
|
140
|
+
const num = (v) => (typeof v === "number" ? v : null)
|
|
141
|
+
const startSec = num(turn.startedAt)
|
|
142
|
+
const endSec = num(turn.completedAt)
|
|
143
|
+
const secFor = (item) =>
|
|
144
|
+
item?.type === "userMessage" ? (startSec ?? endSec) : (endSec ?? startSec)
|
|
145
|
+
for (const item of turn.items) {
|
|
146
|
+
const sec = secFor(item)
|
|
147
|
+
const atMs = sec != null ? Math.round(sec * 1000) : null
|
|
148
|
+
all.push(atMs != null && item && typeof item === "object" ? { ...item, at_ms: atMs } : item)
|
|
149
|
+
}
|
|
130
150
|
}
|
|
131
151
|
const total = all.length
|
|
132
152
|
const kept = []
|
|
@@ -341,6 +361,12 @@ export class CodexStreamBridge extends EventEmitter {
|
|
|
341
361
|
// 通知 (ターン進捗) の両方で更新する。LRU は「最終活性が最も古い非 protected」を退避する。
|
|
342
362
|
// Date.now() は hub-agent 実行環境で利用可。
|
|
343
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(),
|
|
344
370
|
ready: null,
|
|
345
371
|
}
|
|
346
372
|
session.ready = new Promise((resolve) => {
|
|
@@ -590,9 +616,12 @@ export class CodexStreamBridge extends EventEmitter {
|
|
|
590
616
|
switch (msg.method) {
|
|
591
617
|
case "turn/started":
|
|
592
618
|
session.activeTurnId = msg.params?.turn?.id || null
|
|
619
|
+
// 会話ターン境界で「新しさ」を更新(閲覧 keepalive では更新しない)。
|
|
620
|
+
session.lastTurnAtMs = Date.now()
|
|
593
621
|
break
|
|
594
622
|
case "turn/completed":
|
|
595
623
|
session.activeTurnId = null
|
|
624
|
+
session.lastTurnAtMs = Date.now()
|
|
596
625
|
this._autoResolveStalePendingApprovals(session, msg.params?.turn?.id)
|
|
597
626
|
// Stage 5: turn 完了後の reap 予約は不要。以降 idle になれば周期 reconcile が
|
|
598
627
|
// 長 idle TTL / LRU で扱う(detach 済みでも 7 日 warm 保持)。
|
|
@@ -813,7 +842,9 @@ export class CodexStreamBridge extends EventEmitter {
|
|
|
813
842
|
thread_id: session.threadId || null,
|
|
814
843
|
busy: !!session.activeTurnId,
|
|
815
844
|
detached: !!session.detachRequested,
|
|
816
|
-
idle_ms
|
|
845
|
+
// idle_ms は「新しさ」の指標(一覧の並び・宛先選定に使う)なので、閲覧 keepalive で
|
|
846
|
+
// 動く lastActivityMs ではなく **会話ターン境界時刻 lastTurnAtMs** を基準にする。
|
|
847
|
+
idle_ms: now - (session.lastTurnAtMs ?? session.lastActivityMs ?? now),
|
|
817
848
|
})
|
|
818
849
|
}
|
|
819
850
|
return out
|
|
@@ -859,7 +890,9 @@ export class CodexStreamBridge extends EventEmitter {
|
|
|
859
890
|
let bestAt = -1
|
|
860
891
|
for (const session of this.sessions.values()) {
|
|
861
892
|
if (session.dead || session.cwd !== cwd) continue
|
|
862
|
-
|
|
893
|
+
// 宛先選定の「最新」は会話ターン境界時刻で判定する(閲覧 keepalive で動く
|
|
894
|
+
// lastActivityMs を使うと「見ているだけの古い会話」が本命を追い越す)。
|
|
895
|
+
const at = session.lastTurnAtMs ?? session.lastActivityMs ?? 0
|
|
863
896
|
if (at >= bestAt) {
|
|
864
897
|
bestAt = at
|
|
865
898
|
best = session
|