peertable 0.8.34 → 0.8.36
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/room/client.mjs +1 -1
- package/skill/scripts/wakeup-bridge.mjs +56 -5
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "peertable",
|
|
3
|
-
"version": "0.8.
|
|
3
|
+
"version": "0.8.36",
|
|
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.
|
|
17
|
+
const MCP_VERSION = '0.8.36'
|
|
18
18
|
const PKG_ROOT = join(dirname(fileURLToPath(import.meta.url)), '..')
|
|
19
19
|
|
|
20
20
|
const USAGE = `usage:
|
|
@@ -13,7 +13,10 @@
|
|
|
13
13
|
// 実測(2026-08-17・Grok Build TUI): Grok 既定は follow_up_behavior=queue。素送信は
|
|
14
14
|
// 今のターンへ混ざらず入力キューへ積まれ、次の user ターンになる。Grok 席だけ idle を待つ。
|
|
15
15
|
import { execFile } from 'node:child_process'
|
|
16
|
-
import {
|
|
16
|
+
import {
|
|
17
|
+
closeSync, existsSync, openSync, readFileSync, readSync, readdirSync, renameSync, statSync,
|
|
18
|
+
unlinkSync, writeFileSync,
|
|
19
|
+
} from 'node:fs'
|
|
17
20
|
import { join } from 'node:path'
|
|
18
21
|
import { promisify } from 'node:util'
|
|
19
22
|
import { resolvePostToken, resolveSeatObservation, tmuxArgv } from './seat-usage.mjs'
|
|
@@ -354,8 +357,16 @@ function composerSquashed(screen) {
|
|
|
354
357
|
for (let i = lines.length - 1; i >= 0; i--) {
|
|
355
358
|
if (/[›❯]/u.test(lines[i])) { start = i; break }
|
|
356
359
|
}
|
|
357
|
-
|
|
358
|
-
|
|
360
|
+
if (start < 0) return lines.slice(-14).join('').replace(/\s+/gu, '')
|
|
361
|
+
// マーカー行から**直後の空行まで**だけが composer。末尾まで含めると、Codex がターン間に
|
|
362
|
+
// composer の下へ描く受理済み・キュー済みの本文(空行を挟んで表示される)を「残存」と
|
|
363
|
+
// 誤検知する(実被弾 2026-08-29: 受理済み配達を failed 固定し、空 Enter を打ち続けた)。
|
|
364
|
+
// 折返しの composer 続行行は空行を挟まず続くので、この区切りで取りこぼさない。
|
|
365
|
+
let end = lines.length
|
|
366
|
+
for (let i = start + 1; i < lines.length; i++) {
|
|
367
|
+
if (lines[i].trim() === '') { end = i; break }
|
|
368
|
+
}
|
|
369
|
+
return lines.slice(start, end).join('').replace(/\s+/gu, '')
|
|
359
370
|
}
|
|
360
371
|
|
|
361
372
|
const deferredBusy = new Set()
|
|
@@ -492,6 +503,44 @@ async function wake(seat, msgs) {
|
|
|
492
503
|
// Claude 席は「受理された兆候」を成功とみなし、回復キーに Escape を使わない。
|
|
493
504
|
const CLAUDE_ACCEPTED = /paste again to expand|\[Pasted text|esc to interrupt|✻|✽|✳|·\s*\w+ing…/u
|
|
494
505
|
const isClaude = memberHarness(member) === 'claude'
|
|
506
|
+
// Codex席の成立判定は画面でなくrollout transcript(席CODEX_HOMEの正本記録)で行う。
|
|
507
|
+
// 画面照合はTUIのレイアウト・描画タイミングごとに偽陽性/偽陰性を繰り返した(実被弾
|
|
508
|
+
// 2026-08-29に4系統: 会話履歴の残存誤検知・マーカー不在時のフォールバック誤検知・
|
|
509
|
+
// 受理済みキュー表示の誤検知・偽delivered)。submitされた本文はidle時ただちに
|
|
510
|
+
// rolloutへuser messageとして書かれるので、それだけを受理の証拠とする。
|
|
511
|
+
// 実行中ターン(esc to interrupt / to queue message)はCodexが入力をキューする実測
|
|
512
|
+
// (2026-08-08)に基づき受理扱いを維持する。
|
|
513
|
+
const codexSessionsDir = join(proj, '.team', 'seats', `${seat}.codex`, 'sessions')
|
|
514
|
+
const rolloutHasMarker = () => {
|
|
515
|
+
if (marker.length < 8) return false
|
|
516
|
+
let newest = null; let newestM = -Infinity
|
|
517
|
+
const walk = (dir) => {
|
|
518
|
+
let entries
|
|
519
|
+
try { entries = readdirSync(dir, { withFileTypes: true }) } catch { return }
|
|
520
|
+
for (const entry of entries) {
|
|
521
|
+
const file = join(dir, entry.name)
|
|
522
|
+
if (entry.isDirectory()) { walk(file); continue }
|
|
523
|
+
if (!entry.name.startsWith('rollout-') || !entry.name.endsWith('.jsonl')) continue
|
|
524
|
+
let m
|
|
525
|
+
try { m = statSync(file).mtimeMs } catch { continue }
|
|
526
|
+
if (m > newestM) { newestM = m; newest = file }
|
|
527
|
+
}
|
|
528
|
+
}
|
|
529
|
+
walk(codexSessionsDir)
|
|
530
|
+
if (newest === null) return false
|
|
531
|
+
let bytes
|
|
532
|
+
try {
|
|
533
|
+
const size = statSync(newest).size
|
|
534
|
+
const fd = openSync(newest, 'r')
|
|
535
|
+
const len = Math.min(size, 262144)
|
|
536
|
+
const buf = Buffer.alloc(len)
|
|
537
|
+
readSync(fd, buf, 0, len, size - len)
|
|
538
|
+
closeSync(fd)
|
|
539
|
+
bytes = buf.toString('utf8')
|
|
540
|
+
} catch { return false }
|
|
541
|
+
return bytes.replace(/\s+/gu, '').includes(marker)
|
|
542
|
+
}
|
|
543
|
+
const isCodex = memberHarness(member) === 'codex'
|
|
495
544
|
for (let attempt = 0; attempt < 3; attempt++) {
|
|
496
545
|
await sleep(1200)
|
|
497
546
|
const after = await run('tmux', tmuxArgv(['capture-pane', '-t', observation.target, '-p'], { socket: observation.socket }))
|
|
@@ -502,8 +551,10 @@ async function wake(seat, msgs) {
|
|
|
502
551
|
const tailSquashed = composerSquashed(String(after.stdout ?? ''))
|
|
503
552
|
const queuedOrRunning = tailJoined.includes('esc to interrupt') || tailJoined.includes('to queue message')
|
|
504
553
|
|| (isClaude && CLAUDE_ACCEPTED.test(tailJoined))
|
|
505
|
-
const stuck =
|
|
506
|
-
|
|
554
|
+
const stuck = isCodex
|
|
555
|
+
? (!queuedOrRunning && !rolloutHasMarker())
|
|
556
|
+
: ((!isClaude && tailJoined.includes('esc dismiss'))
|
|
557
|
+
|| (marker.length >= 8 && tailSquashed.includes(marker) && !queuedOrRunning))
|
|
507
558
|
if (!stuck) break
|
|
508
559
|
if (attempt === 2) {
|
|
509
560
|
log(`DELIVERY_STUCK: ${seat} の入力欄に本文が残ったまま送信できない(popup/入力詰まり)。受領を確定せず次周期に再試行する`)
|