peertable 0.8.33 → 0.8.35

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "peertable",
3
- "version": "0.8.33",
3
+ "version": "0.8.35",
4
4
  "description": "A round table of peer agents. No orchestrator at the head. Turn Claude Code, Codex, and Grok sessions into a team of equal, long-lived peers.",
5
5
  "type": "module",
6
6
  "license": "MIT",
package/room/client.mjs CHANGED
@@ -14,7 +14,7 @@ import { boundedRecent, boundedUnread } from './message-bounds.mjs'
14
14
 
15
15
  // client.mjs 側のハードコード版数。package.json の version と一致していることを
16
16
  // diagnostics の version_consistency が見る(2 つの版数源の drift 検出。決定45)
17
- const MCP_VERSION = '0.8.33'
17
+ const MCP_VERSION = '0.8.35'
18
18
  const PKG_ROOT = join(dirname(fileURLToPath(import.meta.url)), '..')
19
19
 
20
20
  const USAGE = `usage:
@@ -342,6 +342,30 @@ function advanceLastSeq() {
342
342
  if (advanced) saveDeliveryState()
343
343
  }
344
344
 
345
+ // 「入力欄に本文が残っているか」の照合範囲。画面末尾全体で照合すると、配達済みの本文が
346
+ // TUI の会話履歴として画面下部に表示され続ける形(Grok が常時、Codex も直後)を「残存」と
347
+ // 誤検知し、成功した配達を failed 扱いで再試行し続ける(実被弾 2026-08-29: idle の Grok 席へ
348
+ // 空 Enter を打ち続け、receipt が failed で固定された)。入力欄は画面の最下部に居るので、
349
+ // **最後の composer マーカー行(Codex ›・Grok ❯)から末尾まで**だけを照合する。
350
+ // マーカーが見えない画面では従来どおり末尾14行で照合する(狭めすぎて偽成功を作らない)。
351
+ function composerSquashed(screen) {
352
+ const lines = screen.split('\n')
353
+ let start = -1
354
+ for (let i = lines.length - 1; i >= 0; i--) {
355
+ if (/[›❯]/u.test(lines[i])) { start = i; break }
356
+ }
357
+ if (start < 0) return lines.slice(-14).join('').replace(/\s+/gu, '')
358
+ // マーカー行から**直後の空行まで**だけが composer。末尾まで含めると、Codex がターン間に
359
+ // composer の下へ描く受理済み・キュー済みの本文(空行を挟んで表示される)を「残存」と
360
+ // 誤検知する(実被弾 2026-08-29: 受理済み配達を failed 固定し、空 Enter を打ち続けた)。
361
+ // 折返しの composer 続行行は空行を挟まず続くので、この区切りで取りこぼさない。
362
+ let end = lines.length
363
+ for (let i = start + 1; i < lines.length; i++) {
364
+ if (lines[i].trim() === '') { end = i; break }
365
+ }
366
+ return lines.slice(start, end).join('').replace(/\s+/gu, '')
367
+ }
368
+
345
369
  const deferredBusy = new Set()
346
370
  async function passKnownCodexDialog(member) {
347
371
  if (memberHarness(member) !== 'codex') return false
@@ -451,7 +475,7 @@ async function wake(seat, msgs) {
451
475
  const preMarker = text.replace(/\s+/gu, '').slice(-24)
452
476
  const prePane = await run('tmux', tmuxArgv(['capture-pane', '-t', observation.target, '-p'], { socket: observation.socket }))
453
477
  const preLoaded = preMarker.length >= 8
454
- && String(prePane.stdout ?? '').split('\n').slice(-14).join('').replace(/\s+/gu, '').includes(preMarker)
478
+ && composerSquashed(String(prePane.stdout ?? '')).includes(preMarker)
455
479
  if (preLoaded) {
456
480
  log(`入力欄に前周期の本文が残存: ${seat}。再打鍵せず送信だけ再試行する`)
457
481
  await run('tmux', tmuxArgv(['send-keys', '-t', observation.target, 'Enter'], { socket: observation.socket }))
@@ -481,8 +505,9 @@ async function wake(seat, msgs) {
481
505
  const after = await run('tmux', tmuxArgv(['capture-pane', '-t', observation.target, '-p'], { socket: observation.socket }))
482
506
  const tailLines = String(after.stdout ?? '').split('\n').slice(-14)
483
507
  const tailJoined = tailLines.join(' ')
484
- // 折返しはcapture上で行分割されるだけで空白は挟まらないため、空白除去で復元して照合する
485
- const tailSquashed = tailLines.join('').replace(/\s+/gu, '')
508
+ // 折返しはcapture上で行分割されるだけで空白は挟まらないため、空白除去で復元して照合する。
509
+ // 照合は composer 領域だけ(composerSquashed の注記を参照)
510
+ const tailSquashed = composerSquashed(String(after.stdout ?? ''))
486
511
  const queuedOrRunning = tailJoined.includes('esc to interrupt') || tailJoined.includes('to queue message')
487
512
  || (isClaude && CLAUDE_ACCEPTED.test(tailJoined))
488
513
  const stuck = (!isClaude && tailJoined.includes('esc dismiss'))