cmux NEXT agent pane: a refused answer shows one message, not two glued together
When an answer on a question card was refused, the card appended the refusal's raw message to its own sentence. With acpmux's person-required refusal that read "Couldn’t send your answer. Try again. approve this on the Mac app: allowing a permission is accepted only from the cmux app, never from the CLI, a script or an agent": two messages run together, the second in acpmux's CLI English.
The card now shows one plain message, chosen by where the error came from:
- A refusal the pane has words for (a host refusal,
isHostRefusalin transportErrors.ts) shows those words alone. - A message already in the person's language follows the card's sentence, as before. That covers the Swift host's
userMessage(aNativeError, e.g. "The agent is busy." or "acpmux is not running. Open a new agent chat to start it.") and the pane's ownnative.*words ("The agent connection is not open.", timed out, interrupted). - Anything else is acpmux's English for its CLI. It shows only "Couldn’t send your answer. Try again." and goes to the console.
The review of the first head found that it also hid the localized messages, which broke agent-question-card.test.ts ("…Try again. The agent is busy."). This head keeps those messages; only acpmux's English is dropped.
acpmux's permission.person_required gets its own sentence in #23890; until that lands, this card shows "Couldn’t send your answer. Try again." for it.
Follow-up (not in this PR): a question-card reply that carries answers never gets a gesture ticket (direct.ts permission(), ~4026, asks for one only when answers === undefined). That is the likely root cause of the person-required refusal in the live report, so "Try again" there may hit the same refusal.
Risk: normal. One component's error line; no wire or behavior change.
Changelog
- Fixed: A refused answer on an agent question card shows one plain message instead of two run together.
Test
webviews/test/question-card-reply-failed.test.tsx (jsdom): renders the card with a host that refuses the reply, answers it, and reads the alert line. Cases: acpmux's person-required refusal, acpmux's English for an unmapped reason (exact text), an unknown failure, a transport refusal, a NativeError host message, and the pane's error.notOpen.
- First round. Red on ad02f70beec4c8c9b140b6ee3465d05ed1a30b5d: fleet step 77597e151ba9d061112325f6, all 3 tests failed. Green on 31eb7620256279f4eccec41b44dcee48f8771588: fleet step c7f3ddd04386d424d39fff6f.
- Review fix. Red on c837bb54c9f6155e55d520d28be192cfc73c5b25 (test only): fleet step 40844f300841079b9f7ae691 (aws-m4pro-7). The two new localized-message cases failed (received "Couldn’t send your answer. Try again." alone) and the other 4 passed. Green on fe39f0b2cdd27aef88a75c8f299e966b3c16c55b: fleet step 3d395f30e9671f4ff1a25f47 (aws-m4pro-4). All 6 passed;
question-card-submit.test.tsxandagent-question-card.test.tswere in the same run.
Observed / Not run
Observed:
- The fleet steps above.
scripts/cmux-next/prepush-gate.py: no new violations.vp fmtleft the changed files unchanged.
Not run:
agent-question-card.test.ts's browser-lane cases, observed. That file reported PASS on both the red and the green fleet step, so its Playwright cases most likely skipped there. CI's react-apps-check is what runs them. That its "The agent is busy." expectation now holds comes from reading the code: the stubbed{ ok: false, error: { userMessage } }reply becomes aNativeErrorin native.ts.- The full webviews suite,
bun run check, and a live refused answer in the app.
No capture: the error line keeps its look; only its text changes.
Agent session: cosmic-gray
🤖 Generated with Claude Code