@cocorograph/hub-agent 0.8.83 → 0.8.84

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.
@@ -107,26 +107,6 @@ export function descendantsOf(rootPid, procMap, children = buildChildren(procMap
107
107
  return out
108
108
  }
109
109
 
110
- /**
111
- * tmux session の pane_pid を取得する (失敗時 null)。
112
- */
113
- export async function getPanePid(sessionName, { tmuxBin = "tmux", execFileImpl = execFileP } = {}) {
114
- if (!sessionName || /[/\\]/.test(sessionName)) return null
115
- try {
116
- const { stdout } = await execFileImpl(tmuxBin, [
117
- "display-message",
118
- "-p",
119
- "-t",
120
- `${sessionName}:`,
121
- "-F",
122
- "#{pane_pid}",
123
- ])
124
- const n = Number(stdout.trim())
125
- return Number.isFinite(n) && n > 0 ? n : null
126
- } catch {
127
- return null
128
- }
129
- }
130
110
 
131
111
  /**
132
112
  * pane_pid から対話 claude 本体の pid を解決する。
@@ -88,6 +88,13 @@ export const SIBLING_REQUESTS_DIR =
88
88
  const RESTART_REQUEST_TIMEOUT_MS = 5_000
89
89
  const RESTART_RESULT_POLL_MS = 20
90
90
  const SIBLING_SWEEP_DEBOUNCE_MS = 25
91
+ /**
92
+ * fs.watch の取りこぼし保険。macOS の FSEvents は通知を落とす・遅らせることがあり、通知でしか
93
+ * 再走査しないと、置かれた要求が次の別通知まで処理されない (2026-09-19 実測: テストで数回に 1 回、
94
+ * 2 秒以内に通知が届かなかった)。二重処理は _ingest の _seen で防ぐので、定期走査と通知走査は
95
+ * 重なってよい。
96
+ */
97
+ export const SIBLING_BACKSTOP_SWEEP_MS = 1000
91
98
 
92
99
  export const ACTIVE_DELEGATIONS_PER_SESSION_LIMIT = 100
93
100
  export const DELEGATION_LEDGER_RETENTION_MS = 24 * 60 * 60 * 1000
@@ -443,6 +450,10 @@ export class SiblingInjectBridge {
443
450
  this._seen = new Set()
444
451
  this._watcher = null
445
452
  this._sweepTimer = null
453
+ this._backstopTimer = null
454
+ this._backstopRunning = false
455
+ /** start/stop の世代。await 中に stop された start が後から watcher を張らないようにする。 */
456
+ this._lifecycleGen = 0
446
457
  this._now = now
447
458
  this.reviewEvents = new ReviewEventStore(path.join(dir, "review-events"), now)
448
459
  this._processKill = processKill
@@ -718,6 +729,7 @@ export class SiblingInjectBridge {
718
729
  /** 監視ディレクトリを作成し、既存要求を sweep してから fs.watch を張る。 */
719
730
  async start() {
720
731
  this.stop()
732
+ const gen = this._lifecycleGen
721
733
  try {
722
734
  await this._prepareDir()
723
735
  } catch (err) {
@@ -728,6 +740,9 @@ export class SiblingInjectBridge {
728
740
  return
729
741
  }
730
742
  await this._sweep({ startup: true })
743
+ // await の間に stop() / 再 start() が走っていたら、この start は watcher を張らない
744
+ // (張ると閉じる持ち主のいない fs.watch が残り、プロセスが終了しなくなる)。
745
+ if (gen !== this._lifecycleGen) return
731
746
  try {
732
747
  this._watcher = this._watchFn(this.dir, () => {
733
748
  // fs.watch のfilenameはOS都合で欠落・統合されうる。tmp作成とrenameを
@@ -747,6 +762,19 @@ export class SiblingInjectBridge {
747
762
  "sibling-inject watcher: fs.watch failed, watcher disabled",
748
763
  )
749
764
  }
765
+ // watcher が張れなかった場合も、定期走査だけで要求を処理し続ける。
766
+ this._backstopTimer = setInterval(() => {
767
+ if (this._backstopRunning) return
768
+ this._backstopRunning = true
769
+ this._sweep()
770
+ .catch((err) => {
771
+ this.logger?.warn({ err: err?.message }, "sibling-inject backstop sweep failed")
772
+ })
773
+ .finally(() => {
774
+ this._backstopRunning = false
775
+ })
776
+ }, SIBLING_BACKSTOP_SWEEP_MS)
777
+ this._backstopTimer.unref?.()
750
778
  }
751
779
 
752
780
  /** 起動時 / 取りこぼし保険: dir 内の全要求ファイルを読む。 */
@@ -1288,6 +1316,7 @@ export class SiblingInjectBridge {
1288
1316
 
1289
1317
  /** watcher を閉じる。 */
1290
1318
  stop() {
1319
+ this._lifecycleGen += 1
1291
1320
  try {
1292
1321
  this._watcher?.close()
1293
1322
  } catch {
@@ -1296,6 +1325,8 @@ export class SiblingInjectBridge {
1296
1325
  this._watcher = null
1297
1326
  if (this._sweepTimer) clearTimeout(this._sweepTimer)
1298
1327
  this._sweepTimer = null
1328
+ if (this._backstopTimer) clearInterval(this._backstopTimer)
1329
+ this._backstopTimer = null
1299
1330
  }
1300
1331
  }
1301
1332