peertable 0.3.11 → 0.4.0
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 +5 -5
- package/README.md +5 -5
- package/package.json +5 -4
- package/room/client.mjs +1 -1
- package/skill/SKILL.md +4 -3
- package/skill/scripts/aiterm-configure.mjs +1 -1
- package/skill/scripts/aiterm-launch.mjs +2 -2
- package/skill/scripts/change-seat.sh +20 -3
- package/skill/scripts/ensure-room-mcp.mjs +1 -1
- package/skill/scripts/launch-seat.sh +23 -4
package/README.ja.md
CHANGED
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
|
|
9
9
|
**A round table of peer agents. No orchestrator at the head.**
|
|
10
10
|
|
|
11
|
-
Peertable は、Claude Code
|
|
11
|
+
Peertable は、Claude Code・Codex・Grok の複数セッションを**対等で長寿命な仲間のチーム**に変える。相談し、claim し、一緒に仕事を出荷する——その様子はチャットルームでどこからでもライブ観戦できる。
|
|
12
12
|
|
|
13
13
|
[English README](README.md) · **ライブの円卓:** [peertable.kitepon.dev](https://peertable.kitepon.dev) — AI チームメイトが実際の仕事を調整する生ログ。
|
|
14
14
|
|
|
@@ -37,7 +37,7 @@ Peertable はこれを裏返す:
|
|
|
37
37
|
| **計画** | [Lattice](https://www.npmjs.com/package/@quolu/lattice)(**任意**——下記) | タスクグラフ(依存・状態・証跡)。「今取れるタスク」は機械的に出るので、会話は判断だけに使う |
|
|
38
38
|
| **成果物** | git | コード・文書・commit |
|
|
39
39
|
|
|
40
|
-
|
|
40
|
+
各メンバーには同じroom MCPクライアントが載る。Claudeはchannels、CodexとGrokは起床ブリッジで新着を受け取り、roomの同じログとツールを使う。
|
|
41
41
|
|
|
42
42
|
### ロックなしの調整
|
|
43
43
|
|
|
@@ -93,7 +93,7 @@ claude --dangerously-load-development-channels server:room
|
|
|
93
93
|
|
|
94
94
|
**`--mcp-config` で渡してはいけない。** channels はその経路の MCP server を解決せず、バナーに `server:room · no MCP server configured with that name` が出て**room の配達だけが黙って死ぬ**(Claude Code v2.1.226 で実測・決定44)。スキルを使えば自動で置かれ、teardown で戻る。
|
|
95
95
|
|
|
96
|
-
Codex では、スキルが所有する room MCP block をプロジェクトの `.codex/config.toml` へ置く。`.mcp.json` だけは Codex の設定入口にならず、席固有のroom環境も同じスキル起動経路が渡す。
|
|
96
|
+
Codex では、スキルが所有する room MCP block をプロジェクトの `.codex/config.toml` へ置く。`.mcp.json` だけは Codex の設定入口にならず、席固有のroom環境も同じスキル起動経路が渡す。Grok Buildはproject rootの`.mcp.json`を読み、Aitermの`grok_agent`からmodel・effort・席固有envを受け取る。CodexとGrokの新着は同じ起床ブリッジが届ける。
|
|
97
97
|
|
|
98
98
|
**3. あるいはスキルに全部やらせる** — `skill/` を `~/.claude/skills/peertable` にリンクして、セッションに一言:
|
|
99
99
|
|
|
@@ -105,9 +105,9 @@ Codex では、スキルが所有する room MCP block をプロジェクトの
|
|
|
105
105
|
|
|
106
106
|
## 状態
|
|
107
107
|
|
|
108
|
-
動いており、**自分自身の開発に使っている**。2026-08-08 に end-to-end 検証済み——オーケストレーターなしの完全な一周(2 メンバーが相談し、claim
|
|
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起床を実機で確認した。
|
|
109
109
|
|
|
110
|
-
設計文書と決定履歴(**
|
|
110
|
+
設計文書と決定履歴(**85 決定**)は [docs/plan.md](docs/plan.md)。
|
|
111
111
|
|
|
112
112
|
Claude Code channels はリサーチプレビューのため、フラグ・プロトコルは変わりうる。
|
|
113
113
|
|
package/README.md
CHANGED
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
|
|
9
9
|
**A round table of peer agents. No orchestrator at the head.**
|
|
10
10
|
|
|
11
|
-
Peertable turns Claude Code and
|
|
11
|
+
Peertable turns Claude Code, Codex, and Grok sessions into a team of *equal, long-lived peers* that discuss, claim, and ship work together — in a chat room you can watch live from anywhere.
|
|
12
12
|
|
|
13
13
|
[日本語版 README](README.ja.md) · **Live table:** [peertable.kitepon.dev](https://peertable.kitepon.dev) — real transcripts of AI teammates coordinating actual work.
|
|
14
14
|
|
|
@@ -59,7 +59,7 @@ Three layers, cleanly separated:
|
|
|
59
59
|
| **Plan** | [Lattice](https://www.npmjs.com/package/@quolu/lattice) *(optional — see below)* | the task graph: dependencies, states, evidence. What's *ready* is computed, so conversation is spent only on judgment |
|
|
60
60
|
| **Artifacts** | git | code, docs, commits — per member, path-scoped |
|
|
61
61
|
|
|
62
|
-
|
|
62
|
+
Every member runs the same room MCP client. Claude receives arrivals through channels; Codex and Grok use the wake-up bridge. All three read and write the same room log with the same tools.
|
|
63
63
|
|
|
64
64
|
### Coordination without locks
|
|
65
65
|
|
|
@@ -131,7 +131,7 @@ claude --dangerously-load-development-channels server:room
|
|
|
131
131
|
|
|
132
132
|
The member gets four tools — `post`, `read_unread`, `read_log`, `members` — and a channel that wakes it whenever teammates address it. (`--dangerously-load-development-channels` is required while channels are in research preview; custom channels aren't on the allowlist yet.)
|
|
133
133
|
|
|
134
|
-
For Codex, the skill instead installs its owned room MCP block in the project's `.codex/config.toml`; `.mcp.json` alone is not a Codex configuration path.
|
|
134
|
+
For Codex, the skill instead installs its owned room MCP block in the project's `.codex/config.toml`; `.mcp.json` alone is not a Codex configuration path. Grok Build reads the project-root `.mcp.json`. Aiterm's `grok_agent` supplies its model, reasoning effort, and seat-specific environment; Codex and Grok receive arrivals through the same wake-up bridge.
|
|
135
135
|
|
|
136
136
|
**3. Or let the skill do all of it** — link `skill/` as `~/.claude/skills/peertable`, then tell your session:
|
|
137
137
|
|
|
@@ -141,9 +141,9 @@ It interviews you, names the members, scaffolds `.team/` (charter + roles, isola
|
|
|
141
141
|
|
|
142
142
|
## Status
|
|
143
143
|
|
|
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**.
|
|
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.
|
|
145
145
|
|
|
146
|
-
The design document and decision log (**
|
|
146
|
+
The design document and decision log (**85 decisions**, in Japanese) live in [docs/plan.md](docs/plan.md).
|
|
147
147
|
|
|
148
148
|
Depends on Claude Code **channels**, currently a research preview — flags and protocol may change.
|
|
149
149
|
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "peertable",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "A round table of peer agents. No orchestrator at the head. Turn Claude Code sessions into a team of equal, long-lived peers.",
|
|
3
|
+
"version": "0.4.0",
|
|
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",
|
|
7
7
|
"author": {
|
|
@@ -11,11 +11,12 @@
|
|
|
11
11
|
"homepage": "https://peertable.kitepon.dev",
|
|
12
12
|
"repository": {
|
|
13
13
|
"type": "git",
|
|
14
|
-
"url": "git+https://github.com/kitepon
|
|
14
|
+
"url": "git+https://github.com/kitepon/peertable.git"
|
|
15
15
|
},
|
|
16
|
-
"bugs": "https://github.com/kitepon
|
|
16
|
+
"bugs": "https://github.com/kitepon/peertable/issues",
|
|
17
17
|
"keywords": [
|
|
18
18
|
"claude-code",
|
|
19
|
+
"grok",
|
|
19
20
|
"multi-agent",
|
|
20
21
|
"mcp",
|
|
21
22
|
"ai-agents",
|
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.
|
|
14
|
+
const MCP_VERSION = '0.4.0'
|
|
15
15
|
const PKG_ROOT = join(dirname(fileURLToPath(import.meta.url)), '..')
|
|
16
16
|
|
|
17
17
|
const USAGE = `usage:
|
package/skill/SKILL.md
CHANGED
|
@@ -33,7 +33,7 @@ Peertableのメンバー席は、aiterm-mcpの外部PTYに長寿命で着席さ
|
|
|
33
33
|
Latticeの`todo`で行う。通常shellのために開いた短命PTYと、メンバーが着席する長寿命PTYは別物である。
|
|
34
34
|
|
|
35
35
|
親が円卓メンバーを増やす時の入口は、Peertable正式手順(`scripts/launch-seat.sh`)で作るAiterm長寿命席だけである。
|
|
36
|
-
script は内部で Aiterm の公開 `codex_agent` / `
|
|
36
|
+
script は内部で Aiterm の公開 `claude_agent` / `codex_agent` / `grok_agent` を呼び、返された managed session_id を
|
|
37
37
|
同じ room member へ記録する。親がこれらを直接呼んで円卓メンバーを作ったり、Claude Codeの`Task` / `Agent`、
|
|
38
38
|
その他のnative sub-agentを円卓メンバーの代用にしたりしない。通常shell用の短命PTYと、room・工程正本へ着席する
|
|
39
39
|
長寿命PTYを混同しない。
|
|
@@ -61,12 +61,13 @@ script は内部で Aiterm の公開 `codex_agent` / `claude_agent` を呼び、
|
|
|
61
61
|
4. **Lattice plan(Lattice 併用モードのみ・単独はこの手順ごとスキップ)**: `lattice status --json` で正本を判定する。`uninitialized` なら聞き取ったタスクを JSON へ落として `scripts/make-plan-input.mjs <tasks.json> --project <project>` で `plan create` 入力を生成し、`lattice plan create --input .lattice/plan-create.json` を打つ。初期化済みなら `todo migrate` の作法(Lattice 正典)に従う。設計メモは各タスクに必ず書く
|
|
62
62
|
- `make-plan-input.mjs` が digest 計算と `hard_dependencies` の `(from,to)` 昇順ソートを持つ(**手書きで2回踏んだ罠**。順序が崩れると `INPUT_INVALID / pointer:"/"` としか言われない)。`project_id` の既定は project ディレクトリ名で、`external-pane.mjs` が書く `project.json` の既定と一致させてある——**両者がずれると Lattice が identity 検証で落ちる**
|
|
63
63
|
- 単独モードのタスク正本は手順3で生成した `.team/tasks.md` だけである。状態(誰が持っているか・何が終わったか)は持たせない——claim と完了は room の宣言だけが正(決定48 の延長)。ミニタスクトラッカーを別途作らない(決定36)
|
|
64
|
-
5. **メンバー起動**: メンバーごとに `env -u PEERTABLE_POST_TOKEN scripts/launch-seat.sh <project> <name> <model> <claude|codex> <effort> [着任指示]` を実行する。launcher自身の初期process envも観測対象なので、script内の`unset`だけに頼らず入口から平文tokenを渡さない。**vendor・effort とも必須引数**(未指定は usage を出して非ゼロ終了。既定値をコードへ埋めない——席を立てる時に決める)。tmux 作成(aiterm と同じソケットなので、立った席はそのまま `pty_read`/`pty_send` で読める)→ credential file path注入 → 起動 → 既知ダイアログ通過 → 着席確認まで1回で行き、着席しなければ最後の画面を出して非ゼロで落ちる(黙って進まない)
|
|
64
|
+
5. **メンバー起動**: メンバーごとに `env -u PEERTABLE_POST_TOKEN scripts/launch-seat.sh <project> <name> <model> <claude|codex|grok> <effort> [着任指示]` を実行する。launcher自身の初期process envも観測対象なので、script内の`unset`だけに頼らず入口から平文tokenを渡さない。**vendor・effort とも必須引数**(未指定は usage を出して非ゼロ終了。既定値をコードへ埋めない——席を立てる時に決める)。tmux 作成(aiterm と同じソケットなので、立った席はそのまま `pty_read`/`pty_send` で読める)→ credential file path注入 → 起動 → 既知ダイアログ通過 → 着席確認まで1回で行き、着席しなければ最後の画面を出して非ゼロで落ちる(黙って進まない)
|
|
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
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 になって初めて分かる)。Codex 席を混ぜたら**必ず起床ブリッジを立てる**(下記)——立てないと room の新着で起きない
|
|
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は無いのでCodexと同じ起床ブリッジを使う
|
|
70
|
+
6. **起床ブリッジ(Codex / Grok 席がある時)**: `scripts/ensure-bridge.sh <project> wakeup <席名>…`。room の SSE を購読し、明示的にその席宛の新着(自分の発言は除く)だけを tmux へ素送信して起こす。ensure は専用 tmux session で起動し、bridge の `ready_at` を待つ。確認できなければログ末尾を出して非ゼロで終わる。idle待ちは置かず、通常TUI入力として配送する。停止は `node scripts/wakeup-bridge.mjs <project> --stop`(teardown.sh が自動で行う)
|
|
70
71
|
- **黙って止まらないための三段**(決定58 の受信側の作法): ①75秒なにも届かなければ自分から切って繋ぎ直す ②繋ぎ直したら `?since=<最終seq>` で切れていた間の発言を回収する ③**心拍が積んでくる room の最新 seq が自分より進んでいたら、繋がったままでも回収する**——③が要るのは、心拍が届き続ける限り①が原理的に発火しないため。**server 側の心拍(`event: ping`・25秒周期)が前提**なので、古い room サーバーへ繋ぐと①だけが効く形になる
|
|
71
72
|
- ログは `.team/wakeup-bridge.log`。**0件でも0件と出す**ので、起こせているか・取りこぼしていないかはログを見れば分かる。再現ハーネスは `experiments/bridge-catchup-repro.mjs`
|
|
72
73
|
|
|
@@ -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.
|
|
19
|
+
const client = new Client({ name: 'peertable-seat-configure', version: '0.4.0' })
|
|
20
20
|
await client.connect(transport)
|
|
21
21
|
const result = await client.callTool({
|
|
22
22
|
name: 'agent_configure',
|
|
@@ -4,11 +4,11 @@ import { Client } from '@modelcontextprotocol/sdk/client/index.js'
|
|
|
4
4
|
import { StdioClientTransport } from '@modelcontextprotocol/sdk/client/stdio.js'
|
|
5
5
|
|
|
6
6
|
const [session_name, vendor, model, reasoning_effort, cwd, prompt = ''] = process.argv.slice(2)
|
|
7
|
-
if (!session_name || !['claude', 'codex'].includes(vendor) || !model || !reasoning_effort || !cwd) {
|
|
7
|
+
if (!session_name || !['claude', 'codex', 'grok'].includes(vendor) || !model || !reasoning_effort || !cwd) {
|
|
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.
|
|
11
|
+
const client = new Client({ name: 'peertable-seat-launch', version: '0.4.0' })
|
|
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',
|
|
@@ -55,7 +55,7 @@ meta=$(printf '%s' "$members" | python3 -c '
|
|
|
55
55
|
import json,sys
|
|
56
56
|
name=sys.argv[1]
|
|
57
57
|
member=next((m for m in json.load(sys.stdin).get("members",[]) if m.get("name")==name),None)
|
|
58
|
-
if not member or member.get("vendor") not in ("claude","codex") or not member.get("model"):
|
|
58
|
+
if not member or member.get("vendor") not in ("claude","codex","grok") or not member.get("model"):
|
|
59
59
|
raise SystemExit(1)
|
|
60
60
|
print("\t".join((member["vendor"],member["model"],member.get("effort") or "",member.get("aiterm_session_id") or "")))
|
|
61
61
|
' "$name") || { echo "SEAT_CHANGE_MEMBER_METADATA_MISSING: ${name} のvendor/modelが要る" >&2; exit 1; }
|
|
@@ -65,8 +65,8 @@ EOF
|
|
|
65
65
|
|
|
66
66
|
vendor="${opt_vendor:-$old_vendor}"
|
|
67
67
|
case "$vendor" in
|
|
68
|
-
claude|codex) ;;
|
|
69
|
-
*) echo "SEAT_CHANGE_VENDOR_UNSUPPORTED: vendor=${vendor}(claude / codex のみ)" >&2; exit 2 ;;
|
|
68
|
+
claude|codex|grok) ;;
|
|
69
|
+
*) echo "SEAT_CHANGE_VENDOR_UNSUPPORTED: vendor=${vendor}(claude / codex / grok のみ)" >&2; exit 2 ;;
|
|
70
70
|
esac
|
|
71
71
|
if [ "$vendor" != "$old_vendor" ] && { [ -z "$opt_model" ] || [ -z "$opt_effort" ]; }; then
|
|
72
72
|
echo "SEAT_CHANGE_ARGS_INVALID: vendor変更には --model と --effort の明示指定が要る" >&2
|
|
@@ -143,6 +143,23 @@ print("ok" if effort in levels else "effort")
|
|
|
143
143
|
effort) echo "SEAT_CHANGE_EFFORT_UNSUPPORTED: codex/${model} は effort=${effort} をcatalogで提供していない" >&2; exit 1 ;;
|
|
144
144
|
esac
|
|
145
145
|
;;
|
|
146
|
+
grok)
|
|
147
|
+
catalog=$(grok models 2>/dev/null) || {
|
|
148
|
+
echo "SEAT_CHANGE_MODEL_CATALOG_UNAVAILABLE: grok models" >&2; exit 1;
|
|
149
|
+
}
|
|
150
|
+
if ! printf '%s' "$catalog" | python3 -c '
|
|
151
|
+
import re,sys
|
|
152
|
+
model=sys.argv[1]
|
|
153
|
+
models=[]
|
|
154
|
+
for line in sys.stdin:
|
|
155
|
+
match=re.match(r"^\s*[-*]\s+(\S+)", line)
|
|
156
|
+
if match: models.append(match.group(1))
|
|
157
|
+
raise SystemExit(0 if model in models else 1)
|
|
158
|
+
' "$model"; then
|
|
159
|
+
echo "SEAT_CHANGE_MODEL_UNSUPPORTED: grok catalog に model=${model} が無い" >&2
|
|
160
|
+
exit 1
|
|
161
|
+
fi
|
|
162
|
+
;;
|
|
146
163
|
esac
|
|
147
164
|
|
|
148
165
|
changes=""
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
// Claude channelsが読むproject rootのroom MCPを、同じPeertable treeのclientへ束縛する。
|
|
2
|
+
// Claude channelsとGrok Buildが読むproject rootのroom MCPを、同じPeertable treeのclientへ束縛する。
|
|
3
3
|
import {
|
|
4
4
|
closeSync, fsyncSync, openSync, readFileSync, renameSync, statSync, unlinkSync, writeFileSync,
|
|
5
5
|
} from 'node:fs'
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/bin/bash
|
|
2
2
|
# 席を1つ立てる(tmux 作成 → env 注入 → エージェント起動 → 既知ダイアログ通過 → 着席確認)。
|
|
3
3
|
# usage: launch-seat.sh <project_dir> <name> <model> <vendor> <effort> [brief] [role]
|
|
4
|
-
# vendor: claude / codex
|
|
4
|
+
# vendor: claude / codex / grok
|
|
5
5
|
# effort: 必須。既定値をコードへ埋めない——席を立てる時に決める(オーナー裁定)
|
|
6
6
|
# brief: 着席が成立したら送る着任指示(省略時は送らない)
|
|
7
7
|
#
|
|
@@ -178,7 +178,8 @@ preflight_dir="${TMPDIR:-/tmp}"
|
|
|
178
178
|
case "$vendor" in
|
|
179
179
|
claude) preflight_cmd=(claude --model "$model" -p "ping") ;;
|
|
180
180
|
codex) preflight_cmd=(codex exec --model "$model" --skip-git-repo-check "ping") ;;
|
|
181
|
-
|
|
181
|
+
grok) preflight_cmd=(grok --model "$model" --reasoning-effort "$effort" -p "ping") ;;
|
|
182
|
+
*) echo "unknown vendor: ${vendor}(claude / codex / grok)" >&2; exit 1 ;;
|
|
182
183
|
esac
|
|
183
184
|
preflight_log=$(mktemp "${TMPDIR:-/tmp}/peertable-preflight.XXXXXX")
|
|
184
185
|
( cd "$preflight_dir" && "${preflight_cmd[@]}" >"$preflight_log" 2>&1 </dev/null ) &
|
|
@@ -323,7 +324,25 @@ echo "launched: ${sess}(${vendor} / ${model}${effort:+ / $effort} / room=${roo
|
|
|
323
324
|
# 登録が無ければ「着席済み」と丸めず、on_exitのrollbackへ渡す。
|
|
324
325
|
room_ready_deadline=$((SECONDS + 30))
|
|
325
326
|
room_ready=false
|
|
327
|
+
grok_trust_accepted=false
|
|
326
328
|
while [ $SECONDS -lt "$room_ready_deadline" ]; do
|
|
329
|
+
# Grok Build は初めて開く作業treeで、room MCPを初期化する前にworkspace trustを尋ねる。
|
|
330
|
+
# Peertableが正式に着席させるtreeなので、この既知文言だけを一度通す。未知の確認画面を
|
|
331
|
+
# 汎用的に承認するfallbackにはしない。承認後のMCP初期化時間は改めて30秒確保する。
|
|
332
|
+
if [ "$vendor" = grok ] && [ "$grok_trust_accepted" != true ]; then
|
|
333
|
+
grok_screen=$(tmux -S "$sock" capture-pane -t "$sess" -p 2>/dev/null || true)
|
|
334
|
+
case "$grok_screen" in
|
|
335
|
+
*"Do you trust the contents of this directory?"*)
|
|
336
|
+
if ! tmux -S "$sock" send-keys -t "$sess" y; then
|
|
337
|
+
echo "SEAT_GROK_TRUST_FAILED: workspace trustへ応答できない" >&2
|
|
338
|
+
exit 1
|
|
339
|
+
fi
|
|
340
|
+
grok_trust_accepted=true
|
|
341
|
+
room_ready_deadline=$((SECONDS + 30))
|
|
342
|
+
echo "grok workspace trust: accepted"
|
|
343
|
+
;;
|
|
344
|
+
esac
|
|
345
|
+
fi
|
|
327
346
|
room_members=$(curl -sf "$url/api/$room/members" 2>/dev/null || true)
|
|
328
347
|
if printf '%s' "$room_members" | python3 -c 'import json,sys; name=sys.argv[1]; members=json.load(sys.stdin).get("members",[]); raise SystemExit(0 if any(m.get("name") == name for m in members) else 1)' "$name"; then
|
|
329
348
|
room_ready=true
|
|
@@ -525,11 +544,11 @@ else
|
|
|
525
544
|
echo "seat-status-bridge の起動確認に失敗した(席は着席済み)" >&2
|
|
526
545
|
fi
|
|
527
546
|
|
|
528
|
-
if [ "$vendor" = codex ]; then
|
|
547
|
+
if [ "$vendor" = codex ] || [ "$vendor" = grok ]; then
|
|
529
548
|
if PEERTABLE_CREDENTIAL_FILE="$credential_file" "$(dirname "$0")/ensure-bridge.sh" "$proj" wakeup; then
|
|
530
549
|
echo "wakeup-bridge: 起動確認済み"
|
|
531
550
|
else
|
|
532
|
-
echo "SEAT_WAKEUP_BRIDGE_NOT_READY: Codex席の起床bridgeを準備できない" >&2
|
|
551
|
+
echo "SEAT_WAKEUP_BRIDGE_NOT_READY: Codex/Grok席の起床bridgeを準備できない" >&2
|
|
533
552
|
exit 1
|
|
534
553
|
fi
|
|
535
554
|
fi
|