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: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -119,9 +119,9 @@ Point FlyBot at that server in `~/.dsh/profiles/web/cordis.patch.yml`, then rest
displayName: Your Name
```

On first start FlyBot registers your home. No Bot is shared until you grant it. If onboarding fails because that home handle is already taken (HTTP 409), **Friends' Bots** shows the handle and says to change `displayName` in this config, then restart. To grant a Bot: right-click that Bot → **Invite to this Bot**, send the link; your friend pastes it under the sidebar **+** → **Add friend**. Accepting the invite grants that Bot only.
On first start FlyBot registers your home. No Bot is shared until you grant it. If that home handle is already taken, FlyBot retries a suffixed name (`Your Name2`, …) instead of asking you to edit this config. **Friends' Bots** shows your connected handle with Copy. To grant a Bot: right-click that Bot → **Invite to this Bot**, send the link; your friend pastes it under the sidebar **+** → **Add friend**. Accepting the invite grants that Bot only.

A friend's inbound messages arrive immediately. What your Bots send back may wait in **Friends' Bots** until you Approve (or Reject), unless Auto-accept is on for that friendship. If a friend sees no reply, look for the **to approve** badge there.
A friend's inbound messages arrive immediately. What your Bots send back waits in the open chat until you Approve (or Reject). Auto-send for a new friend is off until you turn it on. If a friend sees no reply, look for the **to approve** badge in **Friends' Bots**.

Without a `server`, the social plugin stays idle by design. Tree works without Bot Social.

Expand Down
4 changes: 2 additions & 2 deletions README.zh.md
Original file line number Diff line number Diff line change
Expand Up @@ -114,9 +114,9 @@ export PORT=8787
displayName: 你的名字
```

首次启动只登记你家。在你授权之前,没有任何 Bot 被共享。若入驻因家句柄已被占用而失败(HTTP 409),**「好友的 Bot」** 会写出被占用的句柄,并提示在上述配置里改 `displayName` 后重启。授权某个 Bot:右键该 Bot → **邀请使用此 Bot**,把链接发给对方;对方在侧栏 **+** → **添加好友** 里粘贴。接受邀请只授权这一个 Bot。
首次启动只登记你家。在你授权之前,没有任何 Bot 被共享。若家句柄已被占用,FlyBot 会自动改用带后缀的名字(`你的名字2` …),而不用你去改配置。**「好友的 Bot」** 会显示你的句柄并提供复制。授权某个 Bot:右键该 Bot → **邀请使用此 Bot**,把链接发给对方;对方在侧栏 **+** → **添加好友** 里粘贴。接受邀请只授权这一个 Bot。

好友发来的消息会立刻送达。你的 Bot 回过去的内容可能停在 **「好友的 Bot」** 里等你批准(或拒绝),除非对该好友开了自动接受。如果对方只看见自己的话,先看侧栏的 **待批准** 角标。
好友发来的消息会立刻送达。你的 Bot 回过去的内容会停在当前聊天里等你批准(或拒绝)。新好友默认关闭自动发送。如果对方只看见自己的话,先看 **「好友的 Bot」** 里的 **待批准** 角标。

未配置 `server` 时,社交插件会故意保持空闲。树插件不依赖 Bot Social。

Expand Down
1 change: 1 addition & 0 deletions changelog.d/136-onboard-suffix.fixed.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
A taken Bot Social home name at first start retries `FlyBot2`, `FlyBot3`, … instead of failing until the owner edits YAML, and Friends' Bots shows the connected handle with Copy. ([#136](https://github.com/ThinkFlowLab/FlyBot/issues/136))
1 change: 1 addition & 0 deletions changelog.d/138-invite-grant-scope.security.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Accepting a per-Bot invite grants only that Bot: the joiner's talk-through Bot is not shared back, Manage access lists people by name and home, and leaving a face-to-face room drops room-only remotes from Friends' Bots. ([#138](https://github.com/ThinkFlowLab/FlyBot/issues/138))
1 change: 1 addition & 0 deletions changelog.d/139-remove-readd.fixed.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Removing a friend's Bot asks once, and joining again after a hide unhides that Bot with English errors instead of raw `relationship_exists` / `invite is used`. ([#139](https://github.com/ThinkFlowLab/FlyBot/issues/139))
1 change: 1 addition & 0 deletions changelog.d/140-approval-default.fixed.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
New friends start with Auto-send off, the owner sees Approve / Reject in the chat (not a waiting bubble), and a message the owner typed is labelled You. ([#140](https://github.com/ThinkFlowLab/FlyBot/issues/140))
14 changes: 11 additions & 3 deletions docs/subsystems/social.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,13 +41,13 @@ Everything in `plugins/social/` (entry `plugins/social/index.js` and any helpers
1. Social code stays inside `plugins/social/`.
2. Uses documented plugin APIs only.
3. A server policy pause (`conversation_paused`: turn budget reached, loop suspected, emergency stop) never surfaces as a bare send error: both send paths (the `bot-social` transport and the remote-chat sender) map it to an error that says resending cannot help and names the Bot Social console, and no other member Bot is tried for it.
4. Access is a per-Bot **grant** (#90): default deny, Chief included. `register` / `registerAll` register a Bot when it is invited, when it joins an invite, when it already has a person-grant or a saved identity, or at face-to-face join (`forFace`, `roomOnly`). `pollOnce` reads inboxes of non-room-only members only. Friends' Bots is `list_remote_bots` (Bots granted to me). Accepting **Invite to this Bot** is the grant; `social/access` + `social/revoke-grant` (via `mcp-client.js` `listGrants` / `revokeGrant`, MCP then REST) let the owner see where the Bot is in use and revoke one person without touching other grants. Core `reach()` / `flybot.grants` are the unbypassable gate. **Face-to-face join (#83 / #89):** bringing my own Bot by number is an implicit room-scoped grant. If it has no identity, join registers it room-only; friends' `list_remote_bots`, invites, polling and `reach()` outside the room stay denied. When that Bot leaves its last face room or the room closes, the plugin revokes the room-only identity (`remove_home_member`). A failed join that created a new room-only identity is revoked too. A later person-grant reuses the handle and drops `roomOnly`.
5. When start fails (including Bot Social onboarding HTTP 409 `handle_taken`), Friends' Bots never shows the host's `Unknown FlyBot endpoint social/…` message. `apply` registers the social UI endpoints with a friendly `Social is not running: <reason>` refusal, `flybot.warn` records the failure in diagnostics, and a 409 names the taken handle (`@<displayName>.home`) and says to change `displayName` in the flybot-social config. The client also maps `bot/not-found` on `social/*` to that idle state.
4. Access is a per-Bot **grant** (#90, #138): default deny, Chief included. `register` / `registerAll` register a Bot when it is invited, when it joins an invite, when it already has a person-grant or a saved identity, or at face-to-face join (`forFace`, `roomOnly`). `pollOnce` reads inboxes of non-room-only members only. Friends' Bots is `list_remote_bots` (Bots granted **to me**), minus reverse-friends of invites I created and minus face-room-only remotes. Accepting **Invite to this Bot** is the grant the other way; joining does not grant my talk-through Bot back. Join always offers **Talk through** (default Chief). `social/access` + `social/revoke-grant` (via `mcp-client.js` `listGrants` / `revokeGrant`, preferring `list_bot_grants` / `revoke_bot_grant`) list people as `Name (@home)` and revoke one person. Core `reach()` / `flybot.grants` are the unbypassable gate. **Face-to-face join (#83 / #89):** bringing my own Bot by number is an implicit room-scoped grant. If it has no identity, join registers it room-only; friends' `list_remote_bots`, invites, polling and `reach()` outside the room stay denied. When that Bot leaves its last face room or the room closes, the plugin revokes the room-only identity (`remove_home_member`) and drops room-scoped remotes from Friends' Bots. A failed join that created a new room-only identity is revoked too. A later person-grant reuses the handle and drops `roomOnly`.
5. First start retries a taken home handle (`FlyBot`, `FlyBot2`, …) instead of failing for a YAML edit (#136). If every retry is still taken, Friends' Bots never shows the host's `Unknown FlyBot endpoint social/…` message: `apply` registers the social UI endpoints with a friendly `Social is not running: <reason>` refusal and `flybot.warn` records the failure. The client also maps `bot/not-found` on `social/*` to that idle state. Friends' Bots shows the connected home handle with Copy.
6. Face-to-face join (#83, #92) is direct (no preview). The owner picks one or more local Bots in the Face-to-face group screen (sidebar **+**); each picker tile and each waiting-room member uses the host Bot avatar (`__flybot__.BotAvatar`), not a shared first-letter badge. The client calls `social/face-join` once per Bot with the same code and shows partial failures. If a chosen Bot has no identity, the plugin registers it first (see invariant 4); registration failures (`handle_taken`, rate limit, timeout) map to English copy and leave no leftover member. Failed join responses never leak `members` / `events` / `room_id`. Every FACE_ROOMS.md error code maps to English copy (`rate_limited` / `locked_out` include `retry_after`). The waiting room polls `social/face-rooms` (which refreshes from Bot Social) and marks the owner's Bots with a tiny accent dot on the tile; **Leave** and **Enter chat** share the footer. **Enter chat** is `social/face-start` then `openSession(roomId)`, and the screen follows if someone else starts. Peer `room.message` text is untrusted: strip fence markers and wrap in `<untrusted-message>` before anything model-visible. Bot replies POST `/v1/face-rooms/{id}/messages`; `pending_approval` shows in the existing queue and in the chat (owner Approve / Reject). **Auto-send my Bot's room messages** is one click (`whoami` / MCP `set_auto_approve { auto_approve_face_rooms }`); if the server answers `confirmation_required`, the same click retries with `confirm_code` and the UI never asks for a 6-character code. REST `/v1/me` is not used with the connector token. A friend's Bot in the room member list and waiting grid uses the host Bot avatar (handle-seeded when Bot Social sends no image). New members only receive events from their join point (`after` cursor; the server also floors it). Join does not grant the Bot to friends. The UI never says published.

### Inline approvals and compact Friends' Bots (#114, #115)

- Held Bot Social drafts (`social/status`, `social/decide-approval`) and matching local `flybot/approvals` holds show as an inline card in the open chat (`flybot.session.dock`): draft text, **Approve** / **Reject** for the owner, **Approve all** when several, and **Waiting for the owner's approval** with no buttons for anyone else. The owner's click is consent (`confirm_code` in the same `decide_approval` call). After a decision the card reads Approved or Rejected. The Friends' Bots "N to approve" block is a one-line jump list (chat name + truncated text, no explainer, no wrapping one word per line). Auto-send of face-room posts is a single compact checkbox (tooltip only) on the Face-to-face rooms header and the open room's overview — not on each pending row or room card.
- Held Bot Social drafts (`social/status`, `social/decide-approval`) and matching local `flybot/approvals` holds show as an inline card in the open chat (`flybot.session.dock`): draft text, **Approve** / **Reject** for the owner, **Approve all** when several, and **Waiting for the owner's approval** with no buttons for anyone else. The owner does not get a waiting bubble in their 1:1; composer holds are labelled **You**. New friends start with **Auto-send** off. The owner's click is consent (`confirm_code` in the same `decide_approval` call). After a decision the card reads Approved or Rejected. The Friends' Bots "N to approve" block is a one-line jump list (chat name + truncated text, no explainer, no wrapping one word per line). Auto-send of face-room posts is a single compact checkbox (tooltip only) on the Face-to-face rooms header and the open room's overview — not on each pending row or room card.
- Each Face-to-face room in Friends' Bots is one compact row: name, a small member-avatar stack (`+N`), a muted open/closed/left/`10m` chip, click-to-open and **Leave** (no **Start** on the card). Closed, left, and ended matching rooms sit under **Past rooms** with **Open** only. With no live room the Face-to-face header and Auto-send are omitted. The list does not say "The number is no longer valid." 1:1 friend chats get the same wait badge as rooms (`flybot.chatApprovals.setProvider` → `bot.pendingCount`). The Friends' Bots header is title plus either the Bot count or the **N to approve** chip (not both) — no **+ Add** and no stacked approval list. **Add friend**, **Accept invite**, **Copy invite link**, and **Face-to-face group** are `flybot.new-chat.action` rows on the sidebar **+**. Add friend / Accept invite expand Friends' Bots and show mint-and-copy plus join / Accept / Decline.

### Face-to-face waiting grid, leave, and social + (#124, #125, #126)
Expand All @@ -64,6 +64,14 @@ Everything in `plugins/social/` (entry `plugins/social/index.js` and any helpers

- Syncing a held outbound draft into chat must not call `remote.chats.deliver` with a home-level handle (`@box.home`) or with no session. Resolve a listed friend Bot under that home, `chats.open` that 1:1 if needed, then deliver so the waiting line lands. If no Bot/session can be opened, skip with a clear log; never throw into the poll loop.

### Onboarding, grants, hide, and approval (#136–#140)

- **#136:** A taken `displayName` at onboard retries `Name2` … rather than refusing to start. Friends' Bots shows `@your.home` with Copy.
- **#137 (deferred):** The owner's chosen Bot avatar/color does not cross homes. Live Bot Social `update_bot_profile` only has `display_name` / `bot_id`; face-room join POST is `{ code, bots: [handle] }`. Peers keep the handle-seeded look until the server grows profile look fields.
- **#138:** Accepting a Helper invite grants Helper only. The joiner picks a talk-through Bot (not shared). Success copy is `{person} shared {Bot} with you.` Manage access lists `SelfB1935 (@selfb1935.home)`. Face-room remotes leave Friends' Bots when the room is left.
- **#139:** **Remove this Bot** asks once (`Remove Helper? Bob can share it again.`). Hide stays local (A's Manage access still shows the grant until A revokes — an incoming un-grant needs Bot Social). Re-joining an existing friendship unhides instead of a raw `relationship_exists`; used invites say “This invite was already used.”
- **#140:** Auto-send defaults off. The owner sees Approve / Reject in the chat, not “Waiting for the owner's approval.” Composer drafts are labelled You.

### Past rooms, unique titles, poll, and + (#129–#133)

- Expired matching (`candidate` whose `expiresAt` has passed) and a peer leaving the last remote seat move the hosted room to **Past rooms** on the next poll (`rememberPastFace`, status `closed` / `left`). The local room stays so **Open** still works; **Leave** is not shown on those cards. Face-room names include the other owners, else the 6-digit code, else a clock time — not a pile of identical "Face-to-face room".
Expand Down
10 changes: 5 additions & 5 deletions plugins/social/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,10 @@ browser: `client.js`. Design: see "Social design: homes and members" in the root
- **One-click onboarding**: on first start, `POST <server>/v1/onboard/flybot` creates the
owner, the home identity (`@<name>.home`), and a connector token in one call;
credentials go to `credentialsPath` (default `$DSH_HOME/bot/flybot-social.json`, mode 600).
If that call fails (for example HTTP 409 because `@<displayName>.home` already exists),
Friends' Bots shows **Social is not running:** with the reason — including the taken
handle and to change `displayName` in the flybot-social config — instead of the host's
`Unknown FlyBot endpoint` message. The same failure is written to Settings → Feedback.
If `@<displayName>.home` is taken, onboarding retries `Name2`, `Name3`, … so a second
install can start without editing YAML. Friends' Bots shows the connected handle with
Copy. If start still fails, it shows **Social is not running:** with the reason instead
of the host's `Unknown FlyBot endpoint` message. The same failure is written to Settings → Feedback.
- **Members**: a local Bot becomes `@<bot>.<home>` (`add_home_member`) when it is invited, when it
joins an invite, or at face-to-face join, stored as field `social.member`. There is no published
flag: access is a per-Bot **grant** (Invite to this Bot → accept). Default deny, Chief included.
Expand All @@ -29,7 +29,7 @@ browser: `client.js`. Design: see "Social design: homes and members" in the root
`flybot/social/auto-approve`). Held messages use the FlyBot approval card; once approved
the `bot-social` transport sends it signed as the member (`send_as_member`).
- **Inbound**: each member's inbox is polled; messages reach that Bot's chat as untrusted text (no owner approval).
- **Outbound hold (Bot Social)**: a draft the server keeps as `pending_approval` is listed on `social/status` (`pendingApprovals`, `approvals`). The owner **Approve** / **Reject**s it on the in-chat card (`flybot.session.dock`); others see “Waiting for the owner's approval”. Friends' Bots shows a badge and a one-line jump list (chat name + truncated text). The owner's click calls `decide_approval` (confirm code used in the same step). This is separate from FlyBot's local approval card. Auto-accept on a friendship skips the hold for later messages. Room messages (`face_room_message`) use the same queue; **Auto-send** (`social/face-auto` → MCP `whoami` / `set_auto_approve { auto_approve_face_rooms }`) is one compact checkbox (tooltip) on the Face-to-face rooms header. Connector tokens cannot `GET`/`PATCH /v1/me`.
- **Outbound hold (Bot Social)**: a draft the server keeps as `pending_approval` is listed on `social/status` (`pendingApprovals`, `approvals`). The owner **Approve** / **Reject**s it on the in-chat card (`flybot.session.dock`); others see “Waiting for the owner's approval” (the owner does not get a waiting bubble in their 1:1). Friends' Bots shows a badge and a one-line jump list (chat name + truncated text). The owner's click calls `decide_approval` (confirm code used in the same step). This is separate from FlyBot's local approval card. **Auto-send** on a friendship is off until the owner turns it on. Room messages (`face_room_message`) use the same queue; face-room **Auto-send** (`social/face-auto` → MCP `whoami` / `set_auto_approve { auto_approve_face_rooms }`) is one compact checkbox (tooltip) on the Face-to-face rooms header. Connector tokens cannot `GET`/`PATCH /v1/me`.
- **Face-to-face rooms**: people next to each other join one group by typing the same 6-digit number. Friends' Bots lists each room as one row (name, avatar stack, open/closed chip, Open / Leave); closed and left rooms fold under **Past rooms**. See Quick Start below.
- **Tools** (every local Bot; an ungranted Bot's send is refused by core `reach()`): `list_friend_bots`, `message_friend_bot`.
- **Legacy**: with `token` + `botId` and no home, the per-bot identity path
Expand Down
Loading
Loading