@cocorograph/hub-agent 0.6.89 → 0.6.90
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/main.mjs +30 -5
- package/src/pty-bridge.mjs +11 -0
package/package.json
CHANGED
package/src/main.mjs
CHANGED
|
@@ -1051,6 +1051,11 @@ export async function maybeSyncClaudeSettings(msg, ctx) {
|
|
|
1051
1051
|
}
|
|
1052
1052
|
}
|
|
1053
1053
|
|
|
1054
|
+
// Plan ε grace guard の猶予 (ms)。backend の集計タイムアウト (_STREAMS_SYNC_TIMEOUT_S=0.5s)
|
|
1055
|
+
// + browser の WS 再接続 + 再 attach を十分に超える値。これ以内に touch された stream は
|
|
1056
|
+
// 「再 attach 中で sync に間に合わなかっただけ」とみなして誤殺しない。10min GC より十分短い。
|
|
1057
|
+
const STREAMS_SYNC_GRACE_MS = 45_000
|
|
1058
|
+
|
|
1054
1059
|
/**
|
|
1055
1060
|
* Plan ε: `agent.streams.sync.response` を受けて backend ↔ agent の現存
|
|
1056
1061
|
* stream_id を能動同期する。
|
|
@@ -1066,14 +1071,34 @@ export function handleStreamsSyncResponse(msg, ctx) {
|
|
|
1066
1071
|
Array.isArray(msg?.active_stream_ids) ? msg.active_stream_ids : [],
|
|
1067
1072
|
)
|
|
1068
1073
|
const localIds = new Set(ctx.ptyBridge.list())
|
|
1074
|
+
// churn 退化スナップショット保護 (ack_lost 根治 / 2026-06-17 根因 rank3)。
|
|
1075
|
+
// backend は browser 群へ _STREAMS_SYNC_TIMEOUT_S(0.5s) だけ問い合わせて active set を
|
|
1076
|
+
// 集める。再接続バースト中 (本番 ~20s 間隔・code 1006) は browser がまだ再 attach できて
|
|
1077
|
+
// おらず空/部分的な set が返り、その一瞬を authoritative とみなして「ローカル全 stream が
|
|
1078
|
+
// 孤児」と誤判定し、生きている PTY を皆殺しにしていた (再 attach 直後の stream を 1 秒後に
|
|
1079
|
+
// 殺し、以後の入力が stream_missing → 数分単位の「送信待ち」固着。0.6.89 ログ最多 churn 源)。
|
|
1080
|
+
// 空 set は信用せず kill を一切行わない (真の孤児は 10min GC + maxStreams 上限が回収する)。
|
|
1081
|
+
const haveAuthoritativeActiveSet = activeIds.size > 0
|
|
1069
1082
|
for (const sid of localIds) {
|
|
1070
|
-
if (
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1083
|
+
if (activeIds.has(sid)) continue
|
|
1084
|
+
if (!haveAuthoritativeActiveSet) continue
|
|
1085
|
+
// grace guard: 直近 STREAMS_SYNC_GRACE_MS 以内に touch (attach/write/resize/pty 出力) された
|
|
1086
|
+
// stream は「browser が再接続中で 0.5s 集計に間に合わなかっただけ」の可能性が高い。猶予内は
|
|
1087
|
+
// 殺さず延命する。真に放置された孤児は猶予超過で従来どおり detach され、10min GC も backstop。
|
|
1088
|
+
const lastSeen = ctx.ptyBridge.getLastSeen?.(sid) ?? 0
|
|
1089
|
+
const idleMs = Date.now() - lastSeen
|
|
1090
|
+
if (lastSeen > 0 && idleMs < STREAMS_SYNC_GRACE_MS) {
|
|
1091
|
+
ctx.logger?.info?.(
|
|
1092
|
+
{ stream_id: sid, idle_ms: idleMs, request_id: msg?.request_id },
|
|
1093
|
+
"Plan ε: skip detach (within grace, browser likely re-attaching)",
|
|
1074
1094
|
)
|
|
1075
|
-
|
|
1095
|
+
continue
|
|
1076
1096
|
}
|
|
1097
|
+
ctx.logger?.warn?.(
|
|
1098
|
+
{ stream_id: sid, request_id: msg?.request_id },
|
|
1099
|
+
"Plan ε: orphan stream detected, detaching",
|
|
1100
|
+
)
|
|
1101
|
+
ctx.ptyBridge.detach({ stream_id: sid })
|
|
1077
1102
|
}
|
|
1078
1103
|
for (const sid of activeIds) {
|
|
1079
1104
|
if (!localIds.has(sid)) {
|
package/src/pty-bridge.mjs
CHANGED
|
@@ -169,6 +169,17 @@ export class PtyBridge extends EventEmitter {
|
|
|
169
169
|
this.lastSeenAt.set(stream_id, this._now())
|
|
170
170
|
}
|
|
171
171
|
|
|
172
|
+
/**
|
|
173
|
+
* stream の最終 touch 時刻 (ms epoch) を返す。未登録 / 未 touch は 0。
|
|
174
|
+
* Plan ε (handleStreamsSyncResponse) の grace 判定で「直近 attach/write/resize/出力が
|
|
175
|
+
* あった stream は browser が再 attach 中なだけ」と見なして誤殺を防ぐために使う。
|
|
176
|
+
* @param {string} stream_id
|
|
177
|
+
* @returns {number}
|
|
178
|
+
*/
|
|
179
|
+
getLastSeen(stream_id) {
|
|
180
|
+
return this.lastSeenAt.get(stream_id) ?? 0
|
|
181
|
+
}
|
|
182
|
+
|
|
172
183
|
/**
|
|
173
184
|
* `gcStaleMs` を超えて idle な stream を一括 reap する。
|
|
174
185
|
*
|