Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions docs/0-requirements.md
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,9 @@ push の形(参照実装 `Liplus-Project/github-webhook-mcp` `local-mcp/src/in
- 宛先は任意である。必須にすると人間の入力費用が上がる。
- 宛先の相手は人間でもセッションでもよく、指定の仕方は変わらない。画面の宛先の選択肢は「自分以外の参加者」であり、人間も同じ列に並ぶ。
- 届いた発言はすべて他の参加者のものである。自分の発言は戻らないため、エージェントは自分を認識する必要がない。
- 先に誰かが答えていたら、その発言を読んでから自分の発言を決める。送る直前にも届いている発言をもう一度見て、言おうとしていたことが既に言われていたら送らず、足りないことがあるときだけ足す。全体宛の問いに全員が答える必要はない。宛先なしの問いに二人の参加者が同内容を二重に返した実測(2026-08-22)に対し、口頭で「順番を待ってその会話を見てから話し始める」と伝えたところ挙動は成立した。足りていなかったのは参加者の能力ではなく、これが部屋の規律に書かれていないことである。
- 順番は沈黙とは別軸である。宛先の節が扱うのは誰に向いた発言かであり、こちらは先に答えが出たときにどうするかである。混ぜて「答えなくてよい」だけを渡すと、全体宛の発言に誰も答えない状態を招く。書き方は「黙れ」ではなく「見てから決める」に寄せる。
- 作法が「見よ」と言えるのは届いた発言までである。参加者は床の状態——誰が今組み立てているか——を覗けず、部屋のツールは書き込み専用の `say_to_room` 一本である。届いた発言なら、組み立て中に届いた分も channel が随時運んでいるため送信直前に読み直せる。同時に書き始めた二人は取りこぼす。その残りは状態を出す側と床を読む側の二点セットであり、この作法を入れてなお同時発火が痛むと観測されてから判断する(#47)。規律の無い状態での観測を根拠に protocol を変えない。

### MCP サーバの実装方式

Expand Down Expand Up @@ -200,6 +203,7 @@ liplus-desktop の `stream_parser.rs` および `spawn_stream_pty` / `spawn_stre
- サーバの `.mcp.json` 登録(既存内容はマージして保持、登録鍵は参加者ごと、前回起動のエントリは掃除)と、成立条件を満たす CLI 起動フラグの適用
- 部屋の作法(`instructions`)の初版
- 発言の宛先(部屋 → `post.to` → channel の `meta.to`、送信は `say_to_room` の `to`)と、それを判定材料として名指しする `instructions`
- 順番の作法(先に届いた答えを読んでから決める、送信直前の読み直し。`instructions` のみで、protocol もツールも増やしていない)
- 参加者モデル(統一 `post` フレーム、発言者以外の全参加者への配送、接続同一性による自分の発言の抑止と名簿の同一性、人間を含む名簿)
- 参加の時点の名乗り(起動ごとに選ぶ名前と色。人間側の名前欄と同じ形、`localStorage` に保持)
- 宣言色(`hello` の `hue` / `room_join` の `hue`。宣言 > 自分の accent > 名前からの導出)
Expand Down
5 changes: 5 additions & 0 deletions sidecar/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,11 @@ const INSTRUCTIONS = [
"- 一度の発言は簡潔に。長い説明が必要なときは、まず要点だけ返してください。",
"- 他の参加者の発言を、自分の文脈として取り込まないでください。それぞれが",
" 自分の文脈から同じ会話に参加しています。",
"- 先に誰かが答えていたら、その発言を読んでから自分の発言を決めてください。",
" 全体宛の問いに、全員が答える必要はありません。",
"- 送る直前に、届いている発言をもう一度見てください。組み立てている間にも",
" 発言は届きます。言おうとしていたことが既に言われていたら送らず、",
" 足りないことがあるときだけ足してください。",
].join("\n");

const mcp = new Server(
Expand Down
20 changes: 20 additions & 0 deletions sidecar/test/round-trip.test.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,26 @@ test("a room post reaches the channel, and say_to_room reaches the room", async
/人間と AI を区別しません/,
"instructions must state that participants are not split into human and AI",
);
// Turn-taking. The addressee clauses filter who a message is for; these say
// what to do when someone already answered. Both halves are required: read
// the earlier answer before deciding, and look again at what arrived while
// the message was being composed, since the composing agent cannot see the
// floor and the arrivals are all it has to look at (#49).
assert.match(
init.result.instructions ?? "",
/先に誰かが答えていたら、その発言を読んでから自分の発言を決めて/,
"instructions must tell the agent to read an earlier answer before deciding its own",
);
assert.match(
init.result.instructions ?? "",
/送る直前に、届いている発言をもう一度見て/,
"instructions must tell the agent to re-read what arrived just before sending",
);
assert.match(
init.result.instructions ?? "",
/全員が答える必要はありません/,
"instructions must state that a room-wide question needs no answer from everyone",
);

notify("notifications/initialized", {});

Expand Down