@cocorograph/hub-agent 0.6.95 → 0.6.97
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 +56 -1
- package/src/proc-introspect.mjs +6 -0
- package/src/state.mjs +18 -7
package/package.json
CHANGED
package/src/main.mjs
CHANGED
|
@@ -890,6 +890,17 @@ async function startSessionEventWatcher({ client, logger, readinessTracker }) {
|
|
|
890
890
|
readinessTracker.arm(sessionName)
|
|
891
891
|
}
|
|
892
892
|
}
|
|
893
|
+
// 計装 (2026-06-19): ターン境界イベントと arm を成功送信時に記録する。従来は session.event
|
|
894
|
+
// が WS 未接続スキップ時しかログに残らず、arm→ターン進行→ready の追跡が事後にできなかった
|
|
895
|
+
// (生成中/停止判定の障害が「ログから盲目」だった主因)。armed は prompt_submit/stop/idle_hint で立つ。
|
|
896
|
+
logger?.info(
|
|
897
|
+
{
|
|
898
|
+
session_name: sessionName,
|
|
899
|
+
event: data.event,
|
|
900
|
+
armed: readinessTracker ? readinessTracker.isArmed(sessionName) : undefined,
|
|
901
|
+
},
|
|
902
|
+
"session.event push",
|
|
903
|
+
)
|
|
893
904
|
client.send({
|
|
894
905
|
type: "session.event",
|
|
895
906
|
session_name: sessionName,
|
|
@@ -1027,11 +1038,20 @@ function startReadinessLoop({ tracker, logger, intervalMs = 700 }) {
|
|
|
1027
1038
|
// 窓経過後に false へ落ちる (ターン終了後のスピナー固着=症状A を悪化させない短さ)。
|
|
1028
1039
|
const OUTPUT_ACTIVE_MS = Number(process.env.HUB_AGENT_OUTPUT_ACTIVE_MS ?? 8000)
|
|
1029
1040
|
|
|
1041
|
+
// 権威的な「応答停止 (ハング)」判定の無進捗しきい値。ターン進行中 (armed) なのに、実際の
|
|
1042
|
+
// 進捗 (子プロセス実行中 / 出力フロー / ペインのライブ processing) がこの時間継続して観測
|
|
1043
|
+
// されなければ stalled とする。frontend 側の jsonl 無音タイマー (長時間ツールで誤検知し、
|
|
1044
|
+
// 本当のハングでは turnActive クリアにより見逃す) を置き換える正本。frontend の
|
|
1045
|
+
// TURN_STALL_WARN_MS と揃える。
|
|
1046
|
+
const STALL_WARN_MS = Number(process.env.HUB_AGENT_STALL_WARN_MS ?? 90000)
|
|
1047
|
+
|
|
1030
1048
|
function startStateLoop({ client, plugins, logger, intervalMs, claudeBridge, readinessTracker }) {
|
|
1031
1049
|
const lastByName = new Map() // session_name → {status, context_pct}
|
|
1032
1050
|
const lastTurnAtByName = new Map() // session_name → 最後に event ファイル化した turnAt
|
|
1033
1051
|
// (i) session_name → {sig, changedAt}: 出力領域署名の最終変化時刻 (出力フロー検知用)。
|
|
1034
1052
|
const outputFlowByName = new Map()
|
|
1053
|
+
// 権威的 stall 判定: session_name → 最後に「進捗あり」と観測した時刻 (ms)。
|
|
1054
|
+
const lastProgressAtByName = new Map()
|
|
1035
1055
|
let stopped = false
|
|
1036
1056
|
|
|
1037
1057
|
// RC-8: WS 再接続のたびに差分送信の基準 (lastByName) をクリアし、次 tick で全 session.state を
|
|
@@ -1118,6 +1138,20 @@ function startStateLoop({ client, plugins, logger, intervalMs, claudeBridge, rea
|
|
|
1118
1138
|
outputActive = Date.now() - changedAt < OUTPUT_ACTIVE_MS
|
|
1119
1139
|
}
|
|
1120
1140
|
const procBusy = childBusy || outputActive
|
|
1141
|
+
// 権威的 stall 判定。armed (ターン進行中=prompt_submit 受信後 stop/ready 未) なのに
|
|
1142
|
+
// 進捗 (procBusy=子プロセス/出力フロー or ペインの live processing) が STALL_WARN_MS
|
|
1143
|
+
// 継続して無いとき stalled=true。armed でない / 進捗ありのときは進捗時刻を更新して
|
|
1144
|
+
// 無進捗カウントをリセットする。frontend はこれを応答停止バナーの正本に使う。
|
|
1145
|
+
const armed = readinessTracker
|
|
1146
|
+
? readinessTracker.isArmed(s.session_name)
|
|
1147
|
+
: false
|
|
1148
|
+
const nowMs = Date.now()
|
|
1149
|
+
const progressing = procBusy || status === "processing"
|
|
1150
|
+
if (progressing || !armed) {
|
|
1151
|
+
lastProgressAtByName.set(s.session_name, nowMs)
|
|
1152
|
+
}
|
|
1153
|
+
const lastProgressAt = lastProgressAtByName.get(s.session_name) ?? nowMs
|
|
1154
|
+
const stalled = armed && nowMs - lastProgressAt >= STALL_WARN_MS
|
|
1121
1155
|
const prev = lastByName.get(s.session_name)
|
|
1122
1156
|
if (
|
|
1123
1157
|
!prev ||
|
|
@@ -1125,7 +1159,8 @@ function startStateLoop({ client, plugins, logger, intervalMs, claudeBridge, rea
|
|
|
1125
1159
|
prev.context_pct !== contextPct ||
|
|
1126
1160
|
prev.permission_mode !== permissionMode ||
|
|
1127
1161
|
prev.stable !== stable ||
|
|
1128
|
-
prev.proc_busy !== procBusy
|
|
1162
|
+
prev.proc_busy !== procBusy ||
|
|
1163
|
+
prev.stalled !== stalled
|
|
1129
1164
|
) {
|
|
1130
1165
|
lastByName.set(s.session_name, {
|
|
1131
1166
|
status,
|
|
@@ -1133,7 +1168,26 @@ function startStateLoop({ client, plugins, logger, intervalMs, claudeBridge, rea
|
|
|
1133
1168
|
permission_mode: permissionMode,
|
|
1134
1169
|
stable,
|
|
1135
1170
|
proc_busy: procBusy,
|
|
1171
|
+
stalled,
|
|
1136
1172
|
})
|
|
1173
|
+
// 計装 (2026-06-19): 状態の「変化時」(差分送信なので低頻度) に値を記録する。従来は
|
|
1174
|
+
// session.state が WS 未接続スキップ時しかログに残らず、proc_busy/stalled/status の実値が
|
|
1175
|
+
// 事後追跡できなかった (生成中/停止判定の障害がログから検証不能だった主因)。childBusy と
|
|
1176
|
+
// outputActive を分けて出し、proc 内省と出力フローのどちらが busy を立てたか切り分け可能にする。
|
|
1177
|
+
logger?.info(
|
|
1178
|
+
{
|
|
1179
|
+
session_name: s.session_name,
|
|
1180
|
+
status,
|
|
1181
|
+
proc_busy: procBusy,
|
|
1182
|
+
child_busy: childBusy,
|
|
1183
|
+
output_active: outputActive,
|
|
1184
|
+
stalled,
|
|
1185
|
+
armed,
|
|
1186
|
+
stable,
|
|
1187
|
+
context_pct: contextPct,
|
|
1188
|
+
},
|
|
1189
|
+
"session.state push",
|
|
1190
|
+
)
|
|
1137
1191
|
client.send({
|
|
1138
1192
|
type: "session.state",
|
|
1139
1193
|
session_name: s.session_name,
|
|
@@ -1142,6 +1196,7 @@ function startStateLoop({ client, plugins, logger, intervalMs, claudeBridge, rea
|
|
|
1142
1196
|
permission_mode: permissionMode,
|
|
1143
1197
|
stable,
|
|
1144
1198
|
proc_busy: procBusy,
|
|
1199
|
+
stalled,
|
|
1145
1200
|
})
|
|
1146
1201
|
}
|
|
1147
1202
|
}
|
package/src/proc-introspect.mjs
CHANGED
|
@@ -324,6 +324,12 @@ export class ReadinessTracker {
|
|
|
324
324
|
return this.byName.get(name)?.busy ?? false
|
|
325
325
|
}
|
|
326
326
|
|
|
327
|
+
/** ターン進行中か (prompt_submit で arm 後、ready/cap で disarm するまで true)。
|
|
328
|
+
* 生成中・Stop フック中の両方を含む。state loop が権威的な stall 判定に使う。 */
|
|
329
|
+
isArmed(name) {
|
|
330
|
+
return this.byName.get(name)?.armed ?? false
|
|
331
|
+
}
|
|
332
|
+
|
|
327
333
|
/** readiness loop が観測対象に含めるべきか (armed / busy / grace 中)。 */
|
|
328
334
|
isActive(name) {
|
|
329
335
|
const s = this.byName.get(name)
|
package/src/state.mjs
CHANGED
|
@@ -134,18 +134,29 @@ function footerRegion(text, lines = 8) {
|
|
|
134
134
|
}
|
|
135
135
|
|
|
136
136
|
/**
|
|
137
|
-
* 作業スピナー行 (ライブステータス行) の構造パターン。実機採取 (2026-06-15):
|
|
137
|
+
* 作業スピナー行 (ライブステータス行) の構造パターン。実機採取 (2026-06-15〜):
|
|
138
138
|
* "· Blanching… (2m 4s · ↓ 7.5k tokens)" (出力中)
|
|
139
139
|
* "✽ Scurrying… (16m 55s · ↓ 66.8k tokens)" (出力中)
|
|
140
140
|
* "· Puzzling… (11m 12s · thinking)" (思考中・tokens 語が無い)
|
|
141
|
-
*
|
|
142
|
-
*
|
|
143
|
-
*
|
|
144
|
-
*
|
|
145
|
-
*
|
|
141
|
+
* "✻ Thinking (3s)" (省略記号なし・タイマーのみ)
|
|
142
|
+
* "· Puzzling (2m · thinking)" (省略記号なし・分のみ秒省略)
|
|
143
|
+
* = 行頭グリフ (·/✻/✽/✶… 1 文字) + 語 + 〔省略記号 または 括弧ライブタイマー〕の少なくとも一方。
|
|
144
|
+
*
|
|
145
|
+
* 緩和の経緯 (2026-06-19): 旧版は「省略記号 **かつ** 括弧タイマー **かつ** 秒必須」を全要求して
|
|
146
|
+
* いたため、claude TUI の版/フェーズで書式が少しでも変わると processing を取りこぼし、生成中なのに
|
|
147
|
+
* 三点リーダーが消える主因になっていた (実機相当の "✻ Thinking…"(タイマー無) / "✻ Thinking (3s)"
|
|
148
|
+
* (省略記号無) / "(2m · thinking)"(分のみ秒無) / 深いインデント が全て NOMATCH と実証)。そこで:
|
|
149
|
+
* - 省略記号 (…/...) **または** 括弧タイマーのどちらか一方で可 (両方必須をやめる)
|
|
150
|
+
* - タイマーは「分のみ」「秒のみ」「分秒」いずれも可 (秒必須をやめる)
|
|
151
|
+
* - 行頭インデント制限 (\s{0,4}) を撤廃 (背の高いペイン/折返しで字下げが深い版に備える)
|
|
152
|
+
* 緩めても安全な理由: 検出は必ず footerRegion + _belowIsOnlyChrome (下が入力欄チロムのみ) を
|
|
153
|
+
* 通すため、本文プローズ・表セル・箇条書きへの誤点灯は下チロム判定が弾く。行頭は英数字・空白・
|
|
154
|
+
* 本文記号 (-*>#|) を除く 1 文字に限定し、語は ASCII 英字に限る (日本語プローズは非マッチ)。
|
|
155
|
+
* 完了サマリー "✻ Brewed for 2m 52s" は省略記号も**括弧**タイマーも持たない (タイマーが括弧外) ため
|
|
156
|
+
* 引き続き一致しない。
|
|
146
157
|
*/
|
|
147
158
|
const SPINNER_LINE_RE =
|
|
148
|
-
/^\s
|
|
159
|
+
/^\s*[^\sA-Za-z0-9\-*>#|]\s*[A-Za-z]+(?:\s+[A-Za-z]+)*\s*(?:(?:…|\.\.\.)|\(\s*(?:\d+\s*m(?:\s*\d+\s*s)?|\d+\s*s)\b)/
|
|
149
160
|
|
|
150
161
|
/**
|
|
151
162
|
* スピナー行 i の「下」が入力欄チロム (空行/罫線/❯ プロンプト/tips/字下げ継続/権限バナー)
|