Conversation
…nal (#78115) Slack, Discord, Google Chat and the relay all put the option text on the button label. Every client clips labels well below the API cap (Slack ~48 columns, narrower on mobile, no tooltip), so a 50-character option from a grilling/wayfinder skill was unreadable and the prompt unanswerable. When any option is wider than the label budget, the numbered full text goes in the message body and the buttons are labelled 1..N (Telegram and WhatsApp Cloud already worked this way). A press still resolves to the canonical choice text via the clarify entry; positional values/ids are unchanged. CJK wide characters count as two columns. One shared helper (gateway.platforms.helpers.numbered_clarify_choices) so the four adapters cannot drift again.
૮ >ﻌ< ა ci reviewran on 32d76fb — fix(gateway): long clarify choices render in full, buttons g
|
|
Exact-head pick-one on #111093 head 32d76fb. Competing open PRs for long clarify-option readability across messaging surfaces:
KEEP #111093: multi-platform + relay consolidation that already names the single-path leaves. CHECK #77547 / #93154: close as salvaged/superseded by #111093. Abort if any listed head drifts. |
|
Exact-head pick-one on #111093 head 32d76fb. Competing open PRs for long clarify-option readability across messaging surfaces:
KEEP #111093: multi-platform + relay consolidation that already names the single-path leaves. CHECK #77547 / #93154: close as salvaged/superseded by #111093. Abort if any listed head drifts. |
kvnloo
left a comment
There was a problem hiding this comment.
The shared trigger is the right call. One width rule in numbered_clarify_choices (gateway/platforms/helpers.py) replaces four adapters guessing label budgets on their own, and counting CJK double matches how the clients actually clip. But the trigger is only half the budget. The other half is where the full text lands. Discord's send_clarify caps the numbered list at 1000 characters before appending the hint, and Discord rejects any embed field value over 1024. With enough long options the field reaches about 1060 characters and the API refuses the whole message. In user terms: you ask Hermes a question with a dozen long options on Discord and the question never arrives at all. No clipped label, no numbered list, just a failed send while the agent waits for an answer the user never saw.
Verdict: comment
-
Discord "Choices" embed field can exceed the 1024-character field-value cap.
send_clarifyin plugins/platforms/discord/adapter.py builds the value as the numbered list sliced to 1000 plus the 58-character hint, reaching about 1060 characters. The embed description goes through_embed_bodytrimming but field values get no length check anywhere in_send_prompt, so Discord returns 400 and the clarify prompt never sends. Reachable: choices are capped at 24, and roughly ten 90-character options already push the list past 961. Suggest capping the slice at 1024 minus the hint length instead of a flat 1000, or trimming the combined string. -
(non-blocking) The PR body says WhatsApp Cloud already renders this way. It does not.
send_clarifyin plugins/platforms/whatsapp/adapter.py sends a native poll, not a numbered list. Telegram (plugins/platforms/telegram/adapter.py) is the accurate model. Docs-only correction. -
(non-blocking) The Discord numbered list gets no markdown escaping. Slack passes
escape=_escintonumbered_clarify_choices, but the Discord call passes none, so a choice containing*or_renders with unintended formatting in the embed field while its button label shows it literally. Cosmetic. -
(non-blocking) No tests for the Discord or Google Chat numbered paths. The new invariant tests cover Slack (including the 25-codepoint CJK case) and the relay; the Discord embed truncation and the Google Chat positional-button switch are untested. A Discord test with a dozen long choices would have caught finding 1.
-
(nit, non-blocking)
display_widthin gateway/platforms/helpers.py counts combining marks (for example the variation selector in "✏️") as one column each, slightly inflating emoji-heavy labels. Conservative direction, triggers the numbered list a hair early. No user-visible harm.
Verified against base: short-option paths are byte-identical to before on all four adapters (Slack's escaping and the 75-char label path unchanged when the helper returns None; relay text and options unchanged; Google Chat body and buttons unchanged since a None result implies the old 80-char cap never fired; Discord hint unchanged). Values, action ids, and the c0..cN/other ids are untouched, so _handle_clarify_action, the double-click guard, and the pending-prompt registry behave exactly as before.
|
Thanks for this — filed #113555 earlier today and the triager pointed me here. The One edge case worth a look, in the Slack Separately, happy to share data from a variant we've been running in production since |
|
Independent verification on the PR head (32d76fb): the two touched test files pass locally, 18/18 (canonical runner). Diff check and Windows-footgun scan clean. Traced all four adapters: Slack, Google Chat, and the relay switch to positional labels only when the helper returns text, and button values, callback ids, and option ids are unchanged, so a press still resolves to the canonical choice. Discord buttons were already positional (N. choice within the 80-char cap); the PR adds the full list to the embed body there. Helper edge cases hold: empty choices return None via all([]), non-string choices go through str, escape is per-adapter. No findings. |
Multiple-choice clarify prompts on Slack, Discord, Google Chat and the relay now show every option in full, so a long option is readable and answerable instead of a clipped button label (#78115).
Changes
gateway/platforms/helpers.py::numbered_clarify_choices/display_width: one shared rule. When any option is wider than the platform's label budget (CJK counts double), return the numbered full-text list; otherwiseNoneand the adapter keeps its current per-choice labels.1..N. Values (clarify_id|idx) and action ids unchanged, so_handle_clarify_actionand the double-click guard are untouched.c0..cN/other; the pending-prompt registry still maps back to the choice text.choiceparameter still carries the full string.Validation
send_clarify(["Build a REST API with auth, rate limits & <pagination>", "MVP only"])on Slack → button labelBuild a REST API with auth, rate limits & <pag…(clipped by the client under the 75-char API cap), body❓ Scope?only.❓ Scope?\n\n1. Build a REST API with auth, rate limits & <pagination>\n2. MVP only, buttons1,2,✏️ Other….origin/mainand green here;tests/gateway/test_{slack,discord,google_chat}*.py+tests/gateway/relay889 tests green.Root cause: four adapters independently put the option text on the button and trusted the API length cap, but every client clips by rendered width with no tooltip.
Supersedes #77547 (@hrsong99, Slack-only, added per-option section blocks) and #93154 (@tw0316, relay-only); both diagnosed the same class and are credited here. Surfaced again today on X by the original reporter.
Infographic