peertable 0.4.32 → 0.4.34
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/SKILL.md +6 -4
- package/skill/scripts/aiterm-launch.mjs +4 -1
- package/skill/scripts/launch-seat.sh +3 -0
- package/skill/scripts/seat-status-bridge.mjs +4 -3
- package/skill/scripts/seat-usage.mjs +15 -0
- package/skill/templates/parent.md +3 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "peertable",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.34",
|
|
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
|
@@ -13,7 +13,7 @@ import { findModelsDoc, resolveSeatIdentity } from '../skill/scripts/resolve-sea
|
|
|
13
13
|
|
|
14
14
|
// client.mjs 側のハードコード版数。package.json の version と一致していることを
|
|
15
15
|
// diagnostics の version_consistency が見る(2 つの版数源の drift 検出。決定45)
|
|
16
|
-
const MCP_VERSION = '0.4.
|
|
16
|
+
const MCP_VERSION = '0.4.34'
|
|
17
17
|
const PKG_ROOT = join(dirname(fileURLToPath(import.meta.url)), '..')
|
|
18
18
|
|
|
19
19
|
const USAGE = `usage:
|
package/skill/SKILL.md
CHANGED
|
@@ -67,7 +67,7 @@ script は内部で Aiterm の公開 `claude_agent` / `codex_agent` / `grok_agen
|
|
|
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
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 になって初めて分かる)。**DM を席 TUI へ入れる配達は Codex / Grok だけ**(下記)——Claude 席は room client が `notifications/claude/channel` を送る(8/12 に送信ループを削った回帰を 0.4.5 で戻した)。Codex は MCP 通知だけではターンを始めないので、room に残すだけでは席に届いたことにならない
|
|
69
69
|
- **Grok 席**(`vendor=grok`): project rootの`.mcp.json`をGrok Buildが読み、同じroom clientと席固有envをAitermが渡す。**user `~/.grok/config.toml` の booth/lattice 等 MCP は載せない**——席専用 `GROK_HOME`(`.team/seats/<name>.grok-home`、auth.json と ui だけ)を preflight と live 席の両方へ渡す。通らなければ席を立てず、別 model へ落とさない。model / effortはAitermの`grok_agent`へそのまま渡し、`grok models`のlive catalogに無いmodelは着席前に失敗する。初めて開くtreeの既知workspace trustは着席処理が通す。channelsは無いので同じ TUI 配達を使う。Grok TUIの既定はターン中の素送信を今の仕事へ混ぜず入力キューへ積むので、配達はGrok席がidleになるまで送らない
|
|
70
|
-
6. **DM の TUI 配達(Codex / Grok 席がある時)**: `scripts/ensure-bridge.sh <project> wakeup <席名>…`。room の SSE を購読し、明示的にその席宛の新着(自分の発言は除く)だけを席の TUI へ素送信する。専用の起床デーモンではない——Codex / Grok が MCP 通知からターンを始めないための配達口である。ensure は専用 tmux session で起動し、bridge の `ready_at`
|
|
70
|
+
6. **DM の TUI 配達(Codex / Grok 席がある時)**: `scripts/ensure-bridge.sh <project> wakeup <席名>…`。room の SSE を購読し、明示的にその席宛の新着(自分の発言は除く)だけを席の TUI へ素送信する。専用の起床デーモンではない——Codex / Grok が MCP 通知からターンを始めないための配達口である。ensure は専用 tmux session で起動し、bridge の `ready_at` を待つ。確認できなければログ末尾を出して非ゼロで終わる。**pid が生きているだけでは本人ではない**(Windows は pid を再利用する。2026-08-21 実測: 死んだ bridge の pid が oracle-mcp に再利用され、ensure が起動済みと誤認、全員宛キックオフが席へ届かなかった)。live 判定は record の `last_progress_at` が新しいこと。偽の pid は kill しない。Codexは即送信、Grokはidle待ち。親(`parent_watch`)は対象にしない。`observe` 欠落は対象外ではなく配達失敗としてログに出し、再試行する。停止は `node scripts/wakeup-bridge.mjs <project> --stop`(teardown.sh が自動で行う)
|
|
71
71
|
- **黙って止まらないための三段**(決定58 の受信側の作法): ①75秒なにも届かなければ自分から切って繋ぎ直す ②繋ぎ直したら `?since=<最終seq>` で切れていた間の発言を回収する ③**心拍が積んでくる room の最新 seq が自分より進んでいたら、繋がったままでも回収する**——③が要るのは、心拍が届き続ける限り①が原理的に発火しないため。**server 側の心拍(`event: ping`・25秒周期)が前提**なので、古い room サーバーへ繋ぐと①だけが効く形になる
|
|
72
72
|
- ログは `.team/wakeup-bridge.log`。**0件でも0件と出す**ので、TUI へ入れているか・取りこぼしていないかはログを見れば分かる。再現ハーネスは `experiments/bridge-catchup-repro.mjs`
|
|
73
73
|
|
|
@@ -231,7 +231,8 @@ witness をどう生成するかは**対象 project 側の作法に従う**(La
|
|
|
231
231
|
- Lattice 書込には actor 環境変数 3 点が必須
|
|
232
232
|
- `--parallel-frontier` が要るのは、**ready が複数あって誰も着手していない frontier の最初の start だけ**(無いと `PARALLEL_DISPATCH_REQUIRED / parallel_frontier_requires_declaration` で弾かれる)。ready が1件だけ、または既に誰かが着手している frontier へ後から乗る場合は素の `start` でよい。フラグが効くのは**取る task が `next_ready` に居る時だけ**で、他人が着手済みの task へ付けると `PARALLEL_DISPATCH_INVALID / parallel_frontier_not_applicable` になる——「フラグが使えない」ではなく「**その task はもう空いていない**」の意味である。記録があるのに対象工程が未宣言・失効なら `todo start` は `INDEPENDENCE_UNVERIFIED` で拒否する(Lattice ADR 0182)。記録が無い plan の start は従来どおり助言だけ
|
|
233
233
|
- **`done.sh` は feat SHA が `origin/main` の祖先でなければ canonical main へ merge して push する。** 親は着地しない。続けて remaining の independence を compile してから戻る。lattice run receipt の未着地は別軸で、警告のまま
|
|
234
|
-
- **independence compile は remaining A を含める。** 現在の ready だけを compile すると、次の frontier の start が拒否される。`next_ready` が witness に無い compile 自体も `INDEPENDENCE_READY_UNDECLARED` で拒否する。stale なら席が `.team/scripts/independence-refresh.sh`
|
|
234
|
+
- **independence compile は remaining A を含める。** 現在の ready だけを compile すると、次の frontier の start が拒否される。`next_ready` が witness に無い compile 自体も `INDEPENDENCE_READY_UNDECLARED` で拒否する。stale なら席が `.team/scripts/independence-refresh.sh` を打つ。campaign を起こす最初の compile は kickoff より前。H を最初の next_ready に並べると `MAX_TODOS=8` で compile できず、席は intake 待ちで止まる(2026-08-21 8B)。H は最初の frontier の外に置く。途中の stale 再 compile は席が打つ。
|
|
235
|
+
- **部屋へ書いたことを配達成功としない。** `.team/wakeup-bridge-delivery.json` の `last_seq` がキックオフ seq 以上になるまで、席は起きていない。
|
|
235
236
|
- **mission が古くても親は書き換えない。** 工程が変わったら席が `set-mission.sh` を自分で打つ。チップと `[mission]` の1行が正本で、席の再起動はしない
|
|
236
237
|
- 同時書込は `STORE_WRITE_CONFLICT` 等で明示的に負ける。1〜2 秒待って再実行すれば通る(正常系)
|
|
237
238
|
- evidence は記述子 JSON。記述子ファイル自体も repo 内相対パスに置く(repo 外絶対パスは INVALID_ARGUMENTS)。`.team/scripts/done.sh` が正規経路。証跡の置き場は **`evidence/<plan_key>/<task_id>.md`**——task_id は campaign を跨いで再利用されるので、平置きにすると前の campaign の監査証跡を上書きで消す(2026-08-08 実測)
|
|
@@ -239,8 +240,9 @@ witness をどう生成するかは**対象 project 側の作法に従う**(La
|
|
|
239
240
|
- **メンバー起動の既知ダイアログは2種だけ**(実測 2026-08-08): 未信頼ディレクトリの workspace trust(`1. Yes, I trust this folder`)と開発 channel 警告(`1. I am using this for local development`)。`--dangerously-skip-permissions` を付けているので MCP 同意ダイアログは出ない。信頼済みディレクトリでは trust も出ない
|
|
240
241
|
- **Codex 席のダイアログは3種で、更新案内と hooks の既定は誤り**(Codex CLI v0.146.0 および 2026-08-20 実測): ディレクトリ trust(`1. Yes, continue`=既定で正しい)。**更新案内(`1. Update now`)は既定のまま Enter を押すと立卓の途中で `npm install -g @openai/codex` が走る**ので「2. Skip」。**`Hooks need review` は room MCP 初期化より前に出る**(`SEAT_ROOM_MCP_NOT_READY` のまま rollback)。`launch-seat.sh` は「2. Trust all and continue」を通す。失敗時は rollback 前に `.team/<name>-pane-on-fail.txt` を残す。待ち時間のポーリングで代用しない
|
|
241
242
|
- **Codex はターン実行中でも素送信を受け付ける**(実測 2026-08-08)。busy 中に送った文言はそのターンの中で読まれ、指示どおりに動く(steering)。Codex 席への TUI 配達は idle 待ちを持たない
|
|
242
|
-
- **Grok はターン実行中の素送信を今の仕事へ混ぜない**(実測 2026-08-17・Grok Build TUI 既定 `follow_up_behavior=queue`)。届いた文は入力キューへ積まれ、次の user ターンになる。TUI 配達は Grok 席だけ pane が idle になるまで送らない。busy の判定は `esc to interrupt
|
|
243
|
-
-
|
|
243
|
+
- **Grok はターン実行中の素送信を今の仕事へ混ぜない**(実測 2026-08-17・Grok Build TUI 既定 `follow_up_behavior=queue`)。届いた文は入力キューへ積まれ、次の user ターンになる。TUI 配達は Grok 席だけ pane が idle になるまで送らない。busy の判定は `esc to interrupt` に加え、Grok 固有の `Waiting for response` / `Responding…` / `[stop]` / 未完了 `[hooks: 1/3]`(実測 2026-08-21。Grok は `esc to interrupt` を出さないので、これを見ないと作業中が待機に見える)、待ち中の `send a message to interrupt`、番号付きキュー+`Enter:send now`
|
|
244
|
+
- **Grok の `Help improve Grok [Opt out] [Opt in]` バナーは席を死んだように見せる**(実測 2026-08-21。auth の `coding_data_retention_opt_out` だけでは消えない)。`launch-seat.sh` は `GROK_PRIVACY_NOTICE_ROLLOUT=0` を着席 env へ渡す。キー送信では消えない。既存席は再着席するまで残る
|
|
245
|
+
- **席の沈黙は「詰まり」と同義ではない。** 発言間隔やファイルの更新時刻から止まったと判定しない——実装が終わって検証に時間を使っているだけのことがある。判定は `tmux_at capture-pane -t peer-<名前> -p`(POSIX は `tmux -S <sock>`、Windows psmux は `tmux -L aiterm-<hash>`。決定88)で**実状態を読む**: 画面に **`esc to interrupt` が在れば長いターンの最中**(通知はターン後にまとめて届くので、呼びかけを足しても速くならない)/Grok は **`Waiting for response` / `Responding…` / `[stop]`** が同じ意味/選択ダイアログで止まっているなら既知の停止要因/`Help improve Grok` バナーは承認待ち/`pane_dead=1` なら落ちている。**スピナーの動名詞(`Cogitating…` 等)で判定しない**——毎回ランダムなので語そのものは使わない(2026-08-08 実測)。Fable のツール実行中は `… (7m 48s` の経過時間行と `/btw` の `without interrupting Claude's current work` が固定句として出る。思考中は `thinking with` / `almost done thinking`。**`esc to interrupt` は Claude 席のステータス行にも Codex 席の `Working (…)` にも入る共通マーカー**。Grok には無い。**読み取りだけなら相手の作業を壊さない**ので、憶測を room へ流す前にこれを見る(2026-08-08 に2人が独立に踏み、先に憶測を流した側が訂正を出した)
|
|
244
246
|
- **`claude-in-chrome` の呼び出しは返らないことがある**。原因は2種で、解き方が違う(2026-08-08 に席1つが9分半沈黙して実測):
|
|
245
247
|
- **接続ブラウザが複数あって、拡張がどれを使うか選ばせている**——選択待ちのまま返らない。**AI 側から解ける**(オーナーに「どちらを使うか」を一言聞けば済む)。今回の実例はこちら。デバッグ接続が宙吊りのまま「Claude がこのブラウザのデバッグを開始しました[キャンセル]」バナーが残る形もあり、キャンセルを押せば呼び出しは即エラーで返る
|
|
246
248
|
- **ブラウザに alert/confirm 等のモーダルが出ている**——拡張が以後のコマンドを受け取れない。**AI 側から解けない**ので、人がダイアログを閉じるしかない
|
|
@@ -20,7 +20,10 @@ if (process.env.PEERTABLE_PLAN) env_vars.push(
|
|
|
20
20
|
'PEERTABLE_PLAN', 'LATTICE_CLI', 'LATTICE_TODO_ACTOR_HOST',
|
|
21
21
|
'LATTICE_TODO_ACTOR_SESSION', 'LATTICE_TODO_ACTOR_AGENT',
|
|
22
22
|
)
|
|
23
|
-
if (vendor === 'grok'
|
|
23
|
+
if (vendor === 'grok') {
|
|
24
|
+
if (process.env.GROK_HOME) env_vars.push('GROK_HOME')
|
|
25
|
+
env_vars.push('GROK_PRIVACY_NOTICE_ROLLOUT')
|
|
26
|
+
}
|
|
24
27
|
if (vendor === 'codex' && process.env.CODEX_HOME) env_vars.push('CODEX_HOME')
|
|
25
28
|
const result = await client.callTool({
|
|
26
29
|
name: `${vendor}_agent`,
|
|
@@ -372,7 +372,10 @@ if [ "$mode" = "lattice" ]; then
|
|
|
372
372
|
[ -n "$lattice_cli" ] && launch_env+=("LATTICE_CLI=$lattice_cli")
|
|
373
373
|
fi
|
|
374
374
|
if [ "$vendor" = grok ]; then
|
|
375
|
+
# 実測 2026-08-21: auth の coding_data_retention_opt_out だけでは
|
|
376
|
+
# `Help improve Grok [Opt out] [Opt in]` バナーが残る。席が死んだように見える。
|
|
375
377
|
launch_env+=("GROK_HOME=$grok_home")
|
|
378
|
+
launch_env+=("GROK_PRIVACY_NOTICE_ROLLOUT=0")
|
|
376
379
|
fi
|
|
377
380
|
if [ "$vendor" = codex ]; then
|
|
378
381
|
codex_home="${proj}/.team/seats/${name}.codex"
|
|
@@ -7,9 +7,10 @@
|
|
|
7
7
|
//
|
|
8
8
|
// 判定(2026-08-08 実測。**推測でパターンを書かない**):
|
|
9
9
|
// busy = pane の末尾に `esc to interrupt` が在る。Claude 席のステータス行にも Codex 席の `Working (…)` にも
|
|
10
|
-
//
|
|
11
|
-
//
|
|
12
|
-
//
|
|
10
|
+
// 同じ文字列が入る。Grok はこれを出さず `Waiting for response` / `Responding…` / `[stop]` を出す
|
|
11
|
+
// (2026-08-21 実測。これを見ないと作業中の Grok 席が idle に見える)。実行中の動名詞
|
|
12
|
+
// (Cogitating/Coalescing/Effecting/Gallivanting/Fermenting/Symbioting…)は**毎回変わる**ので
|
|
13
|
+
// 判定に使わない——語で照合すると全席を idle と誤判定して、画面が嘘をつく
|
|
13
14
|
// blocked = 既知の承認ダイアログ文言が末尾に在る(`esc to interrupt` は消えている)。2026-08-08 実測:
|
|
14
15
|
// 確認ダイアログで停止した席が busy と `esc to interrupt` 不在から idle 側に誤判定され、
|
|
15
16
|
// 動けない席へ卓が代走を申し出るところまで進んだ。判定順は busy → blocked → idle
|
|
@@ -191,6 +191,14 @@ export const BLOCKED_MARKERS = [
|
|
|
191
191
|
'Do you want to proceed?',
|
|
192
192
|
]
|
|
193
193
|
|
|
194
|
+
/** Grok TUI の SpaceXAI coding-data バナー(2026-08-21 実測。入力は通るが席が死んだように見える)。 */
|
|
195
|
+
export function isGrokPrivacyBanner(tail) {
|
|
196
|
+
return typeof tail === 'string'
|
|
197
|
+
&& tail.includes('Help improve Grok')
|
|
198
|
+
&& tail.includes('[Opt out]')
|
|
199
|
+
&& tail.includes('[Opt in]')
|
|
200
|
+
}
|
|
201
|
+
|
|
194
202
|
/**
|
|
195
203
|
* pane 末尾の生文字列から画面状態を判定する。判定順は busy → blocked → idle
|
|
196
204
|
* (承認プロンプト表示中は `esc to interrupt` が消えるので busy を先に見る)。
|
|
@@ -205,7 +213,14 @@ export function classifyPaneTail(tail) {
|
|
|
205
213
|
if (tail.includes("without interrupting Claude's current work")) return 'busy'
|
|
206
214
|
if (tail.includes('Calling tools')) return 'busy'
|
|
207
215
|
if (/…\s*\(\d+(?:m \d+)?s\b/u.test(tail)) return 'busy'
|
|
216
|
+
// Grok Build TUI は esc to interrupt を出さない(2026-08-21 実測)。
|
|
217
|
+
// 生成中は `Waiting for response…` / `Responding…` と `[stop]`。
|
|
218
|
+
// 完了後の `Worked for 38s` は idle。未完了 hook だけ `[hooks: 1/3]`。
|
|
219
|
+
if (tail.includes('Waiting for response') || tail.includes('Responding…') || tail.includes('Responding...')) return 'busy'
|
|
220
|
+
if (tail.includes('[stop]')) return 'busy'
|
|
221
|
+
if (/\[hooks:\s*\d+\/\d+\]/u.test(tail)) return 'busy'
|
|
208
222
|
if (BLOCKED_MARKERS.some(marker => tail.includes(marker))) return 'blocked'
|
|
223
|
+
if (isGrokPrivacyBanner(tail)) return 'blocked'
|
|
209
224
|
return 'idle'
|
|
210
225
|
}
|
|
211
226
|
|
|
@@ -20,7 +20,9 @@
|
|
|
20
20
|
|
|
21
21
|
- 着卓(member 登録)と席数制御(決定68の運用側: ready+active実装ToDo数に合わせて起こす/畳む)
|
|
22
22
|
- 作業者から監査担当への最終試験結果提出と、監査担当による工程クローズが正本へ記録されたことの観測
|
|
23
|
-
-
|
|
23
|
+
- 着地と、途中の independence 再 compile は席の仕事である。親は代行しない
|
|
24
|
+
- campaign を起こす最初の remaining A compile は kickoff より前。H を最初の next_ready に並べない(`MAX_TODOS=8`。並べると compile できず席の intake が止まる)
|
|
25
|
+
- 部屋へ書いたことを配達成功としない。`.team/wakeup-bridge-delivery.json` の `last_seq` が対象 seq 以上になるまで、席は起きていない。ensure-bridge の live 判定は `last_progress_at`。pid 生存だけでは本人ではない
|
|
24
26
|
- mission の更新は席の仕事である。親は代行しない
|
|
25
27
|
- 承認 gate・オーナーとの接点、裁定依頼の運搬(自分で判断せずオーナー宛の議題として運ぶ)
|
|
26
28
|
- model / effort 変更依頼への対応(本人の自然文を親が判断し、確定したtargetだけを
|