Skip to content

social: map Bot Social conversation pauses to an actionable error (#72) - #73

Merged
Birfy merged 1 commit into
mainfrom
social-paused-chat-72
Oct 15, 2026
Merged

Birfy merged 1 commit into
mainfrom
social-paused-chat-72

Conversation

@hsliuustc0106

@hsliuustc0106 hsliuustc0106 commented Oct 15, 2026 •

Copy link
Copy Markdown
Collaborator

Fixes #72. Rebased on current main (includes #69/#70: Friends' Bots pending approvals — complementary, no overlap).

What

A Bot Social server pauses conversations by policy and rejects sends with 409 conversation_paused (turn budget reached, loop suspected, emergency stop). Until now FlyBot surfaced that only as a bare card — Not sent: Bot Social send_as_member: turn budget exhausted; owners can extend the conversation — with no state, no way forward, and the remote-chat sender even retried the friendship's other member Bots against the same pause.

Both send paths (the bot-social transport, so message_friend_bot too, and the user's direct remote-chat sender) now map the pause to an error that:

  • keeps the server's reason ("turn budget exhausted; owners can extend the conversation"),
  • says resending cannot help, and points at the Bot Social console (<server>/ui) — or suggests removing and re-adding the friend to start a fresh conversation, and
  • is not retried across member Bots: the pause belongs to the pair, not to the member that hit it.

Detection is by the MCP error's code === 'conversation_paused' with a message fallback (the owner-MCP wraps the server's ApiError into an isError result; seen live in the friend-chat incident).

Testing

Fail-without-change: the new test (a paused conversation explains the pause and names the console, without retrying other members) fails on the parent tip with the raw send error and passes with the mapped one.

Docs

  • docs/subsystems/social.md: new invariant 3 (policy pauses never surface as bare send errors; no cross-member retry).
  • CHANGELOG [Unreleased] entry under Fixed.

Verifier (tip 47882421f4b5e5c4be25d001c6b8fe0d0112b8a8, rebased on main 2ed9c32)

Ran at the tip:

  • plugins/social suite: 34/34 pass (includes the new paused-conversation test, red at the parent tip, and Pending Bot Social approvals are hard to discover in the UI #69's tests)
  • plugins/tree suite: 49/49 pass
  • core npm test: 610/610 pass
  • node scripts/ds-bot-patches.mjs check: ok (9 patches, core = ds-bot + patches)
  • node scripts/build-client.mjs --check: ok
  • npm run lint: exit 0 (pre-existing warnings only, untouched files); arch-docs: OK (4 changed file(s))

Not run: smoke bench (no core/index.js / core/src/host/ changes — plugin-only diff), Desktop, real-server round trip (covered by the unit-level mock of the owner-MCP error shape observed live).

A Bot Social server pauses conversations by policy and rejects sends with
409 conversation_paused (turn budget reached, loop suspected, emergency
stop). FlyBot surfaced that only as a bare "Not sent: Bot Social
send_as_member: ..." card with no way forward, and the user-facing pause
reason never explained that resending cannot help (#72).

Both send paths (the bot-social transport and the remote-chat sender) now
map the pause to an error that names the server's reason and the Bot
Social console, and the remote-chat sender no longer tries the
friendship's other member Bots for a paused conversation: the pause
belongs to the pair, not to the member that hit it. The test fails on the
previous tip with the raw send error and passes with the mapped one.
@Birfy
Birfy merged commit 6184caf into main Oct 15, 2026
5 checks passed

@hsliuustc0106 hsliuustc0106 left a comment

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Verifier review

Verdict: PASS at tip 47882421f4b5e5c4be25d001c6b8fe0d0112b8a8 (base 2ed9c32 = current main, CI green at tip).

Tip SHA: 47882421f4b5e5c4be25d001c6b8fe0d0112b8a8
Ran:
  - plugins/social suite at this tip → 34/34 pass (includes the new paused-conversation test)
  - fail-without-change: the new test applied to base 2ed9c32 (only the test file) → FAILS
    with the raw "Not sent: Bot Social send_as_member: turn budget exhausted" error,
    passes at this tip
  - diff inspection vs claims (transport + remote-chat sender mapping, single send_as_member
    call, dropStaged still on the error path, docs invariant 3, CHANGELOG) → consistent
Not run:
  - real Bot Social server round trip (covered by the unit mock of the owner-MCP error
    shape quoted from the live incident)
  - core suite / patches check / smoke — no core files in the diff; CI green at this tip
OpenCode↔Codex: n/a
Verdict: PASS

No actionable findings for the scoped risk. Both send paths map the pause; a paused conversation is not retried across the friendship's other member Bots (the test asserts exactly one send_as_member call); dropStaged still runs on the error path.

Security / remote strings (in scope — remote text becomes model-visible)

  • P3, not blocking, pre-existing exposure (this PR does not make it worse): mapSendError (plugins/social/src/social.js:360-364) embeds the remote server's error message verbatim into the new error text. The friend's Bot Social server is peer-operated, so per code-standards ("Security: fences and remote strings") the reason should be sanitized before embedding — strip control characters/newlines, cap length. Suggest a follow-up issue with a negative test.

This is a Verifier report, not merge authority.

@hsliuustc0106
hsliuustc0106 deleted the social-paused-chat-72 branch October 15, 2026 08:16
cursor Bot pushed a commit to Birfy/FlyBot that referenced this pull request Oct 15, 2026
ThinkFlowLab#81)

A friend's Bot mentioned in a group never answers in that group: the
mention goes out through sendUserMentions, but the reply is delivered
only to the friend's Bot's own chat, and a denied or failed send was
visible nowhere but the host log. In a live conversation both sides
ended up believing the other was silent (ThinkFlowLab#80, ThinkFlowLab#81).

The host now posts one receipt line into the room per mention: where
the reply lands (the friend's chat under Friends' Bots), or why the
send did not leave (deny reason, hold, transport error - the mapped
pause message from ThinkFlowLab#73 included). The receipt is a relay source with a
new 'mention' role outside the room's round trigger (groups.js opens)
and RELAY_ROLES, so it opens no round and folds no turns; the client
renders it as a muted event line. Peer names pass through cleanName.
Mentions from a Bot's own chat keep their behavior.

Tests: the room receipt for sent, hold and deny; none for plain
messages or outside groups. Fails without the fix.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Friend chat: Bot Social conversation pause (turn budget) surfaces only as a bare "Not sent" card, with no state or recovery path

2 participants