peertable 0.4.2 → 0.4.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/README.ja.md +1 -1
- package/README.md +1 -1
- package/package.json +1 -1
- package/room/client.mjs +1 -1
- package/skill/SKILL.md +1 -1
- package/skill/scripts/aiterm-configure.mjs +1 -1
- package/skill/scripts/aiterm-launch.mjs +1 -1
- package/skill/scripts/launch-seat.sh +7 -7
- package/skill/scripts/parent-watch.mjs +1 -2
- package/skill/scripts/seat-usage.mjs +12 -8
- package/skill/scripts/wakeup-delivery.mjs +4 -0
package/README.ja.md
CHANGED
|
@@ -107,7 +107,7 @@ Codex では、スキルが所有する room MCP block をプロジェクトの
|
|
|
107
107
|
|
|
108
108
|
動いており、**自分自身の開発に使っている**。2026-08-08 に end-to-end 検証済み——オーケストレーターなしの完全な一周(2 メンバーが相談し、claim し、インターフェースを交渉し、見つけた罠を共有して小さなプロジェクトを出荷)を**外部介入ゼロ**で完走。2026-08-13の実席ライフサイクルでは、作業席が親を通じてsession contextを保ったままmodel / effortを変更し、再起動後はroomと工程正本から再着任した。2026-08-14にはGrok 4.6席の着席、room参加、同一sessionの4.6↔4.5変更、DM起床を実機で確認した。2026-08-17にGrok席はidle待ち、broadcastは本文を残し、tmuxの無い親でbridge cursorが止まらないよう直した。
|
|
109
109
|
|
|
110
|
-
現在のnpm releaseは **peertable 0.4.
|
|
110
|
+
現在のnpm releaseは **peertable 0.4.4**。
|
|
111
111
|
|
|
112
112
|
設計文書と決定履歴(**87 決定**)は [docs/plan.md](docs/plan.md)。
|
|
113
113
|
|
package/README.md
CHANGED
|
@@ -143,7 +143,7 @@ It interviews you, names the members, scaffolds `.team/` (charter + roles, isola
|
|
|
143
143
|
|
|
144
144
|
Working, and used to build itself. First verified end-to-end on 2026-08-08 with a full no-orchestrator loop: two members consulted, claimed, negotiated an interface, shared a discovered pitfall, and shipped a small project with **zero external intervention**. A 2026-08-13 real-seat lifecycle verified in-place model/effort changes and restart recovery. On 2026-08-14, a Grok 4.6 seat joined the room, changed 4.6↔4.5 in the same session, and woke on a direct message in a live acceptance run. On 2026-08-17 the wake-up path was corrected so Grok seats wait for idle, broadcasts keep their body, and a parent without a tmux seat cannot stall the bridge cursor.
|
|
145
145
|
|
|
146
|
-
The current npm release is **peertable 0.4.
|
|
146
|
+
The current npm release is **peertable 0.4.4**.
|
|
147
147
|
|
|
148
148
|
The design document and decision log (**87 decisions**, in Japanese) live in [docs/plan.md](docs/plan.md).
|
|
149
149
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "peertable",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.4",
|
|
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
|
@@ -11,7 +11,7 @@ import { fileURLToPath } from 'node:url'
|
|
|
11
11
|
|
|
12
12
|
// client.mjs 側のハードコード版数。package.json の version と一致していることを
|
|
13
13
|
// diagnostics の version_consistency が見る(2 つの版数源の drift 検出。決定45)
|
|
14
|
-
const MCP_VERSION = '0.4.
|
|
14
|
+
const MCP_VERSION = '0.4.4'
|
|
15
15
|
const PKG_ROOT = join(dirname(fileURLToPath(import.meta.url)), '..')
|
|
16
16
|
|
|
17
17
|
const USAGE = `usage:
|
package/skill/SKILL.md
CHANGED
|
@@ -65,7 +65,7 @@ script は内部で Aiterm の公開 `claude_agent` / `codex_agent` / `grok_agen
|
|
|
65
65
|
- 起動前に `pty_list` で既存の `peer-*` 席を確認する(前の卓の残骸を99席実測したことがある)。同名の席は launch-seat.sh が落としてから立て直す
|
|
66
66
|
- 着任指示を第6引数に渡すと着席後に送る。文面: 「あなたは「<日本語名>」。.team/roles/member.md を読んで着任し、作業ループを開始せよ。全タスク完了の宣言まで自律的に続けること。」
|
|
67
67
|
- 席が読む env は script が組み立てる(`PEERTABLE_URL` / `PEERTABLE_ROOM` / `PEERTABLE_MEMBER` / `PEERTABLE_CREDENTIAL_FILE`、Lattice 併用なら `PEERTABLE_PLAN` と actor 3点)。token値は席別`0600` fileにだけ置き、pathだけを席へ渡す。**channels は `--mcp-config` の MCP server を解決しない**(実測 2026-08-08・Claude Code v2.1.226・決定44)ため、room の MCP 定義は setup.sh が project root へ置く `.mcp.json` が正。Peertable管理下fileはlaunch時にもcurrent-tree clientへ同期する。project に既存 `.mcp.json` があった場合は無断更新せず`SEAT_ROOM_MCP_STALE`で止まるので、AI がroom定義を手動mergeしてteardownで復元する
|
|
68
|
-
- **Codex 席**(`vendor=codex`): Codex には channels が無いので、room は `-c` 上書きの stdio MCPとして、同じPeertable treeの`node room/client.mjs`を差す。**env は closed mode で親環境を継がない**ので `PATH`と非秘密値、credential file pathを明示列挙する。effortはmember metadataだけでなく`model_reasoning_effort`へも同じ値を渡す。モデル名は ChatGPT アカウントで使える slug を渡す(`~/.codex/config.toml` の `model` が既定値の参考。使えない slug は起動後の最初のターンで 400
|
|
68
|
+
- **Codex 席**(`vendor=codex`): Codex には channels が無いので、room は `-c` 上書きの stdio MCPとして、同じPeertable treeの`node room/client.mjs`を差す。**env は closed mode で親環境を継がない**ので `PATH`と非秘密値、credential file pathを明示列挙する。effortはmember metadataだけでなく`model_reasoning_effort`へも同じ値を渡す。モデル名は ChatGPT アカウントで使える slug を渡す(`~/.codex/config.toml` の `model` が既定値の参考。使えない slug は起動後の最初のターンで 400 になって初めて分かる)。**起床ブリッジは Claude / Codex / Grok とも必須**(下記)——Claude の room client は channel 通知を送らないので、ブリッジ無しでは idle 席は部屋に書かれても起きない(2026-08-19 実測)
|
|
69
69
|
- **Grok 席**(`vendor=grok`): project rootの`.mcp.json`をGrok Buildが読み、同じroom clientと席固有envをAitermが渡す。model / effortはAitermの`grok_agent`へそのまま渡し、`grok models`のlive catalogに無いmodelは着席前に失敗する。初めて開くtreeの既知workspace trustは着席処理が通す。channelsは無いので起床ブリッジを使う。Grok TUIの既定はターン中の素送信を今の仕事へ混ぜず入力キューへ積むので、ブリッジはGrok席がidleになるまで送らない
|
|
70
70
|
6. **起床ブリッジ(Codex / Grok 席がある時)**: `scripts/ensure-bridge.sh <project> wakeup <席名>…`。room の SSE を購読し、明示的にその席宛の新着(自分の発言は除く)だけを tmux へ素送信して起こす。ensure は専用 tmux session で起動し、bridge の `ready_at` を待つ。確認できなければログ末尾を出して非ゼロで終わる。Codexは即送信、Grokはidle待ち。親(`parent_watch` / `observe: null`)は対象にしない。停止は `node scripts/wakeup-bridge.mjs <project> --stop`(teardown.sh が自動で行う)
|
|
71
71
|
- **黙って止まらないための三段**(決定58 の受信側の作法): ①75秒なにも届かなければ自分から切って繋ぎ直す ②繋ぎ直したら `?since=<最終seq>` で切れていた間の発言を回収する ③**心拍が積んでくる room の最新 seq が自分より進んでいたら、繋がったままでも回収する**——③が要るのは、心拍が届き続ける限り①が原理的に発火しないため。**server 側の心拍(`event: ping`・25秒周期)が前提**なので、古い room サーバーへ繋ぐと①だけが効く形になる
|
|
@@ -16,7 +16,7 @@ if (!session_id || (!model && !reasoning_effort)) {
|
|
|
16
16
|
// Aiterm のmanaged session metadataは実行時state root(macOSではTMPDIR)にある。
|
|
17
17
|
// stdio transportの既定環境は親のTMPDIRを落とすため、同じ公開serverへ明示継承する。
|
|
18
18
|
const transport = new StdioClientTransport({ command: 'aiterm-mcp', env: process.env })
|
|
19
|
-
const client = new Client({ name: 'peertable-seat-configure', version: '0.4.
|
|
19
|
+
const client = new Client({ name: 'peertable-seat-configure', version: '0.4.4' })
|
|
20
20
|
await client.connect(transport)
|
|
21
21
|
const result = await client.callTool({
|
|
22
22
|
name: 'agent_configure',
|
|
@@ -8,7 +8,7 @@ if (!session_name || !['claude', 'codex', 'grok'].includes(vendor) || !model ||
|
|
|
8
8
|
throw new Error('SEAT_AITERM_LAUNCH_ARGS_INVALID')
|
|
9
9
|
}
|
|
10
10
|
|
|
11
|
-
const client = new Client({ name: 'peertable-seat-launch', version: '0.4.
|
|
11
|
+
const client = new Client({ name: 'peertable-seat-launch', version: '0.4.4' })
|
|
12
12
|
await client.connect(new StdioClientTransport({ command: 'aiterm-mcp', env: process.env }))
|
|
13
13
|
const env_vars = [
|
|
14
14
|
'PEERTABLE_URL', 'PEERTABLE_ROOM', 'PEERTABLE_MEMBER', 'PEERTABLE_CREDENTIAL_FILE',
|
|
@@ -566,13 +566,13 @@ else
|
|
|
566
566
|
echo "seat-status-bridge の起動確認に失敗した(席は着席済み)" >&2
|
|
567
567
|
fi
|
|
568
568
|
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
569
|
+
# Claude の room client は channel 通知を送らない(実配送は wakeup-bridge 一系統)。
|
|
570
|
+
# Codex / Grok に加えて Claude 席でもブリッジを立てる。立てないと idle の Claude は部屋に書かれても起きない。
|
|
571
|
+
if PEERTABLE_CREDENTIAL_FILE="$credential_file" "$(dirname "$0")/ensure-bridge.sh" "$proj" wakeup; then
|
|
572
|
+
echo "wakeup-bridge: 起動確認済み"
|
|
573
|
+
else
|
|
574
|
+
echo "SEAT_WAKEUP_BRIDGE_NOT_READY: 起床bridgeを準備できない" >&2
|
|
575
|
+
exit 1
|
|
576
576
|
fi
|
|
577
577
|
|
|
578
578
|
if [ -z "$brief" ]; then brief_completed=true; fi
|
|
@@ -45,11 +45,10 @@ function readLatticeState(previous = null) {
|
|
|
45
45
|
if (previous?.source === source && previous.error === undefined) return previous
|
|
46
46
|
try {
|
|
47
47
|
const lattice = resolveLatticeExecutable(latticeCli)
|
|
48
|
-
const status = JSON.parse(execFileSync(lattice.command,
|
|
48
|
+
const status = JSON.parse(execFileSync(lattice.command, lattice.argv, {
|
|
49
49
|
cwd: project,
|
|
50
50
|
encoding: 'utf8',
|
|
51
51
|
stdio: ['ignore', 'pipe', 'pipe'],
|
|
52
|
-
shell: lattice.shell,
|
|
53
52
|
}))
|
|
54
53
|
if (!Array.isArray(status.next_ready) || !Array.isArray(status.active_set)) {
|
|
55
54
|
throw new Error('invalid schema')
|
|
@@ -100,17 +100,18 @@ export function tmuxArgv(extraArgs = [], { socket, env = process.env, platform =
|
|
|
100
100
|
|
|
101
101
|
/**
|
|
102
102
|
* npm の extensionless shim は Windows の execFile で ENOENT。
|
|
103
|
-
* 隣の .cmd
|
|
103
|
+
* 隣の .cmd を cmd.exe /c で起動する(shell:true は DEP0190 かつ引数連結)。
|
|
104
104
|
*/
|
|
105
105
|
export function resolveLatticeExecutable(cli, { platform = process.platform, exists = existsSync } = {}) {
|
|
106
|
-
if (typeof cli !== 'string' || !cli) return { command: cli,
|
|
107
|
-
if (platform !== 'win32') return { command: cli,
|
|
106
|
+
if (typeof cli !== 'string' || !cli) return { command: cli, argv: ['todo', 'status', '--json'] }
|
|
107
|
+
if (platform !== 'win32') return { command: cli, argv: ['todo', 'status', '--json'] }
|
|
108
108
|
const lower = cli.toLowerCase()
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
109
|
+
const cmd = lower.endsWith('.cmd') || lower.endsWith('.bat') || lower.endsWith('.exe')
|
|
110
|
+
? cli
|
|
111
|
+
: exists(`${cli}.cmd`)
|
|
112
|
+
? `${cli}.cmd`
|
|
113
|
+
: cli
|
|
114
|
+
return { command: process.env.ComSpec || 'cmd.exe', argv: ['/d', '/c', cmd, 'todo', 'status', '--json'] }
|
|
114
115
|
}
|
|
115
116
|
|
|
116
117
|
/** member の自己申告を優先し、無い既存 member だけ旧 session 名へ互換フォールバックする。 */
|
|
@@ -197,6 +198,9 @@ export const BLOCKED_MARKERS = [
|
|
|
197
198
|
export function classifyPaneTail(tail) {
|
|
198
199
|
if (typeof tail !== 'string') return 'idle'
|
|
199
200
|
if (tail.includes('esc to interrupt')) return 'busy'
|
|
201
|
+
// Claude Code 2.1 / Fable TUI は思考中に esc to interrupt を出さない(2026-08-19 実測)。
|
|
202
|
+
// 動名詞(Wibbling 等)は毎回変わるので使わない。思考中の固定句だけを busy にする。
|
|
203
|
+
if (tail.includes('thinking with') || tail.includes('almost done thinking')) return 'busy'
|
|
200
204
|
if (BLOCKED_MARKERS.some(marker => tail.includes(marker))) return 'blocked'
|
|
201
205
|
return 'idle'
|
|
202
206
|
}
|
|
@@ -24,6 +24,10 @@ export function isWakeupBridgeTarget(member) {
|
|
|
24
24
|
if (!member || typeof member.name !== 'string' || member.name.length === 0) return false
|
|
25
25
|
if (member.delivery?.kind === 'parent_watch') return false
|
|
26
26
|
if (member.observe === null) return false
|
|
27
|
+
const observe = member.observe
|
|
28
|
+
if (!observe || typeof observe !== 'object' || typeof observe.tmux_target !== 'string' || !observe.tmux_target) {
|
|
29
|
+
return false
|
|
30
|
+
}
|
|
27
31
|
return true
|
|
28
32
|
}
|
|
29
33
|
|