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
18 changes: 13 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,15 +54,23 @@ npm run tauri dev

起動すると Tauri の窓が開き、部屋ソケットが待ち受けを始めます。フロントエンドの dev サーバは `vite.config.ts` で 1420 番に固定しています(`src-tauri/tauri.conf.json` の `devUrl` と一致させる必要があるため)。

上の行が**アカウント**です。「+」で作り、「−」で削除します。アカウントは起動していなくても存在するもので、名前・色・作業ディレクトリ・起動オプションを持ちます。編集はその場で保存されます
上の行は**参加するアカウントを選んで参加するだけ**の行です。アカウントを作る・直す・消すのは、右の「参加者」の見出しの隣にある「+」と、各行の「編集」から開くウィンドウで行います

「名前」と「色」は、そのアカウントが部屋で名乗る名前と、発言に付く色です。**いつでも変えられます。** 同一性は名前ではなく内部の id にあるため、改名しても稼働中のセッションの登録は追随します。色を選ばずに参加することもでき、その場合は名前から色が決まります。タイトルバーの「名前」「色」は同じものの人間側です(人間はアカウントではなく、この画面の話者として参加します)
そのウィンドウには、名前・種別・色と、AI のアカウントなら作業ディレクトリ・起動オプションがあります。**決定を押すまで何も保存されません。** 「+」は空のフォームを開くだけで、取消せばアカウントは作られません。編集も同じで、取消せば元のままです。削除はそのウィンドウの中にあり、二度押しです

起動オプション」には `--dangerously-skip-permissions` のように、CLI へ渡したいオプションをそのまま書けます。アプリは部屋の channel エントリ(`server:pullcept-room-<slug>-<hash>`)をここへ統合するので、別の channel サーバを指定しても部屋の入力路は残ります。実際に起動する行は入力欄の右に表示されます
名前」と「色」は、そのアカウントが部屋で名乗る名前と、発言に付く色です。**いつでも変えられます。** 同一性は名前ではなく内部の id にあるため、改名しても稼働中のセッションの登録は追随します。色を選ばずに参加することもでき、その場合は名前から色が決まります

作業ディレクトリの入力欄があります。初回はホームディレクトリが入っているので、セッションを動かしたいディレクトリへ変更してください
「種別」は `user`(人間)か `AI`(このアプリが起動する CLI)です。**作るときに選びます。** あなた自身も種別 `user` のアカウントであり、はじめて起動したときに作られます(それまでタイトルバーで名乗っていた名前と色を引き継ぎます)。種別 `user` のアカウントは起動しません——人間の下に動かす CLI は無いためです

右の参加者パネルに、部屋にいる参加者と、まだ起動していないアカウント(「未起動」)が並びます。その下に「端末」の列があり、この画面が端末を持っているアカウントが一行ずつ出ます。行を押すとその端末が診断面に出て、行の「終了」でそのアカウントのセッションを終了できます。終了は二度押しです——一度目でボタンが「本当に終了」に変わり、数秒で戻ります。さらに下にセッションの裏の値(部屋ソケット、セッションの生死、接続方法、起動コマンド、作業ディレクトリ、開始時刻、ウィンドウ)が出ます。部屋ソケット以外は、選んでいる端末のアカウントのものです。発言の色は参加者ごとに違い、名簿の点と同じ色です。色は選んだものが優先され、選んでいなければ自分は accent(青)、他の参加者は名前から決まります。名簿では自分の行に「(あなた)」が付きます。宛先に選べるのは部屋に居る参加者だけで、未起動のアカウントは出ません。
「起動オプション」には `--dangerously-skip-permissions` のように、CLI へ渡したいオプションをそのまま書けます。アプリは部屋の channel エントリ(`server:pullcept-room-<slug>-<hash>`)をここへ統合するので、別の channel サーバを指定しても部屋の入力路は残ります。実際に起動する行はその下に表示されます。

作業ディレクトリは初回だけホームディレクトリが入っているので、セッションを動かしたいディレクトリへ変更してください。

右の参加者パネルは**一つのリスト**です。部屋にいる参加者と、まだ起動していないアカウント(「未起動」)が、種別ごとのグループに分かれて並びます(`user — 1` / `AI — 2` のように、件数も出ます)。名前はその参加者の色で出ます。居ない相手は色を保ったまま薄くなり、点が塗りから輪郭に変わります。自分の行には「(あなた)」が付きます。

端末を持っているアカウントの行を押すと、その端末が診断面に出ます。行の「終了」でそのアカウントのセッションを終了できます。終了は二度押しです——一度目でボタンが「本当に終了」に変わり、数秒で戻ります。未起動のアカウントの行に端末の操作は出ません。

リストの下にセッションの裏の値(部屋ソケット、セッションの生死、接続方法、起動コマンド、作業ディレクトリ、開始時刻、ウィンドウ)が出ます。部屋ソケット以外は、選んでいる端末のアカウントのものです。宛先に選べるのは部屋に居る参加者だけで、未起動のアカウントは出ません。

「診断」を開くと**起動した CLI の端末**が出ます。端末はそのまま操作できます。CLI はフォルダごとに初回の信頼確認を出すため、最初の一回はここから答えてください。

Expand Down
18 changes: 18 additions & 0 deletions crates/mcp-config/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,13 @@ pub struct RoomRegistration<'a> {
pub token: &'a str,
/// Id of the account being launched. The registration key derives from
/// this, so the entry stays put across a rename of the account.
///
/// It is also handed to the sidecar in the env, so the session can name it
/// in `hello` and the room can carry it on the seat. Carried, not consulted:
/// the room decides identity, self-suppression and the `speaker` stamp on
/// the connection, and an account id changes none of that (#39 / #40 /
/// #47). What it buys is the screen being able to say which of its accounts
/// a participant is, without matching on a name (#59).
pub account_id: &'a str,
/// Display name this session speaks under. Written into the env for the
/// sidecar to declare in `hello`, and it is what says whose entry this is
Expand Down Expand Up @@ -276,6 +283,11 @@ pub fn register_sidecar(dir: &Path, room: &RoomRegistration<'_>) -> Result<PathB
env.insert("PULLCEPT_ROOM_URL".into(), json!(room.room_url));
env.insert("PULLCEPT_ROOM_TOKEN".into(), json!(room.token));
env.insert("PULLCEPT_AGENT_NAME".into(), json!(room.agent_name));
// Unconditional, unlike the hue: a launch always knows which account it is
// launching, so an absent key here would mean the launch lost it rather
// than that nobody declared one. Absent on the wire stays a real state —
// it is what a connection with no account behind it sends (#59).
env.insert("PULLCEPT_ACCOUNT_ID".into(), json!(room.account_id));
env.insert("PULLCEPT_ROOM_ID".into(), json!("pullcept"));
// Only when declared. An undeclared participant is a participant the room
// derives a hue for, which is not the same state as one who chose that hue.
Expand Down Expand Up @@ -359,6 +371,12 @@ mod tests {
assert_eq!(server["env"]["PULLCEPT_ROOM_URL"], "ws://127.0.0.1:1234");
assert_eq!(server["env"]["PULLCEPT_ROOM_TOKEN"], "tok");
assert_eq!(server["env"]["PULLCEPT_AGENT_NAME"], "Lin");
// The id the sidecar declares in `hello`, so the room can carry it on
// the seat and the screen can match a participant to one of its
// accounts without matching on a name (#59). The id, not the key
// derived from it: the key is a `.mcp.json` concern, and the room has
// no way back from it to the account.
assert_eq!(server["env"]["PULLCEPT_ACCOUNT_ID"], LIN);
assert_eq!(server["env"]["PULLCEPT_ROOM_ID"], "pullcept");
// Undeclared is the key absent, not a default value: a hue written here
// would be a declaration this participant never made.
Expand Down
Loading