@cocorograph/hub-agent 0.7.3 → 0.7.4
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 +12 -1
- package/src/tmux.mjs +42 -1
package/package.json
CHANGED
package/src/main.mjs
CHANGED
|
@@ -68,6 +68,7 @@ import {
|
|
|
68
68
|
listSessions as listTmuxSessions,
|
|
69
69
|
listWorktreeNameHistory,
|
|
70
70
|
listWorktreeStubs,
|
|
71
|
+
isPaneRunningClaude,
|
|
71
72
|
rebindClaudeSession,
|
|
72
73
|
shouldSkipRebindRespawn,
|
|
73
74
|
recoverTuiInput,
|
|
@@ -2080,13 +2081,23 @@ async function dispatch(msg, ctx) {
|
|
|
2080
2081
|
// resume は targetId をキー、fresh は session_id 不在のため bind の request_id を
|
|
2081
2082
|
// キーにする (同一マウントの再送は同 request_id で skip、新規要求は再マウントで
|
|
2082
2083
|
// 新 request_id になり 1 回だけ起動する)。
|
|
2084
|
+
// 実プロセス由来の「claude 実行中か」。respawn-pane -k による生成中 claude の誤 kill
|
|
2085
|
+
// (症状D 再発 D2: armed 140s cap / capture gap で generating=false に倒れ、--resume bind
|
|
2086
|
+
// でも実行中 claude を kill していた)を、capture/armed に依存しない pane_current_command で防ぐ。
|
|
2087
|
+
const paneRunningClaude = await isPaneRunningClaude(sessionName, { logger })
|
|
2083
2088
|
let rebind = { ok: false, skipped: false }
|
|
2084
2089
|
const bindKey = fresh ? `fresh:${request_id || ""}` : targetId
|
|
2085
2090
|
if (bindKey && sessionName && (fresh || targetId)) {
|
|
2086
2091
|
if (ctx.tuiReboundSessions.get(sessionName) === bindKey) {
|
|
2087
2092
|
rebind = { ok: true, skipped: true }
|
|
2088
2093
|
} else if (
|
|
2089
|
-
shouldSkipRebindRespawn({
|
|
2094
|
+
shouldSkipRebindRespawn({
|
|
2095
|
+
generating,
|
|
2096
|
+
fresh,
|
|
2097
|
+
targetId,
|
|
2098
|
+
newestId,
|
|
2099
|
+
paneRunningClaude,
|
|
2100
|
+
})
|
|
2090
2101
|
) {
|
|
2091
2102
|
// 生成中 claude を再接続/remount の bind で kill しない (謎停止対策)。respawn を抑止して
|
|
2092
2103
|
// 既存の生成中 claude を温存する。bindKey は記録して以降の remount を冪等化する
|
package/src/tmux.mjs
CHANGED
|
@@ -1237,8 +1237,49 @@ export async function recoverTuiInput(name, expectText, opts = {}) {
|
|
|
1237
1237
|
* @param {{generating?: boolean, fresh?: boolean, targetId?: string|null, newestId?: string|null}} a
|
|
1238
1238
|
* @returns {boolean}
|
|
1239
1239
|
*/
|
|
1240
|
-
|
|
1240
|
+
/**
|
|
1241
|
+
* ペインの前景プロセスが claude を実行中か(= respawn-pane -k で kill すると in-flight 応答を失う)
|
|
1242
|
+
* を pane_current_command から判定する。capture-pane スクレイプや armed(140s cap) の脆い推論に
|
|
1243
|
+
* 依存しない実プロセス由来の判定で、hub-agent 再起動・armed cap・capture gap を跨いで有効。
|
|
1244
|
+
* シェル(fish/bash/zsh/sh/dash/tmux/login)なら false。判定不能時は安全側 false(= respawn 許可)。
|
|
1245
|
+
*/
|
|
1246
|
+
export async function isPaneRunningClaude(name, opts = {}) {
|
|
1247
|
+
if (!name) return false
|
|
1248
|
+
try {
|
|
1249
|
+
const { stdout } = await execFileP(tmuxBin(opts), [
|
|
1250
|
+
"display-message",
|
|
1251
|
+
"-p",
|
|
1252
|
+
"-t",
|
|
1253
|
+
`${name}:`,
|
|
1254
|
+
"-F",
|
|
1255
|
+
"#{pane_current_command}",
|
|
1256
|
+
])
|
|
1257
|
+
const cmd = (stdout || "").trim().toLowerCase()
|
|
1258
|
+
if (!cmd) return false
|
|
1259
|
+
const SHELLS = new Set(["fish", "bash", "zsh", "sh", "dash", "tmux", "login"])
|
|
1260
|
+
// TUI チャットのペインで走るのは claude(claude / claude.exe / node 等)かシェルのいずれか。
|
|
1261
|
+
// シェルでない前景プロセス = claude 実行中とみなす(命名差 claude.exe/node に強い)。
|
|
1262
|
+
return !SHELLS.has(cmd)
|
|
1263
|
+
} catch {
|
|
1264
|
+
return false
|
|
1265
|
+
}
|
|
1266
|
+
}
|
|
1267
|
+
|
|
1268
|
+
export function shouldSkipRebindRespawn({
|
|
1269
|
+
generating,
|
|
1270
|
+
fresh,
|
|
1271
|
+
targetId,
|
|
1272
|
+
newestId,
|
|
1273
|
+
paneRunningClaude,
|
|
1274
|
+
} = {}) {
|
|
1241
1275
|
if (fresh) return false
|
|
1276
|
+
// 実行中 claude を kill しない最優先ガード(症状D / 再発 D2 の根治): ペインが現に claude を
|
|
1277
|
+
// 実行中で、最新セッション(= 今動いている会話)への再 bind(remount/reconnect)なら、respawn
|
|
1278
|
+
// (respawn-pane -k) は破壊的なだけで不要。generating の脆い検出(isArmed 140s cap / capture gap)
|
|
1279
|
+
// で false に倒れても、実プロセス由来の paneRunningClaude が守る。targetId 未指定(= newest 解決前)
|
|
1280
|
+
// も「最新への再 bind」とみなす。明示的な別会話への切替(targetId !== newestId)と +新規(fresh)は
|
|
1281
|
+
// 従来どおり respawn する。
|
|
1282
|
+
if (paneRunningClaude && (!targetId || targetId === newestId)) return true
|
|
1242
1283
|
if (!generating) return false
|
|
1243
1284
|
return !!targetId && targetId === newestId
|
|
1244
1285
|
}
|