Skip to content

fix(client): the Bot exchange sheet renders and copies a stored message's fence and its trace #1423

Closed

Description

@HugoXK

Written-by: Dev Lead

Reproduction (main 7a4fa61e77b0791485b6add4866b0f334be8ef2e)

A Bot↔Bot exchange sheet renders each stored entry by handing the stored string straight to the bubbles:

// core/src/client/exchange.js:55
h(Bubbles, { texts: [entry.text], roster, selfId: bot.id, actions, lead: … })

Bubbles passes that same string to MessageTools (core/src/client/cells.js:49), whose Copy and Download use it verbatim (:127). So the sheet is a read path with no read step at all, and a stored exchange line keeps its wire format:

  • a fenced line is shown and copied whole: <untrusted-message from="User">\nfirst\n\nsecond\n</untrusted-message> reaches the bubble and the clipboard as that same string, fence markers, from="User" and all;
  • a leading delegation trace line is shown and copied: Chief → 助手\n\nB kick test 4 > hello is rendered as Chief → 助手 on the first line.

Every other read surface of the same stored line hides both (the transcript uses transcriptOf, the ⌘K row uses previewOf). The exchange sheet shows the arrow and the fence to a person reading it, and Copy hands the wire format to whatever they paste into.

Expected

The sheet reads the line once, before it renders, and both the bubble and Copy/Download/Quote get that one value:

  • a complete <untrusted-message …>…</untrusted-message> wrap (nested wraps included) is unwrapped: markers and from="…" gone, the interior text kept, and text before/after the wrap not dropped;
  • a leading delegation trace line is dropped;
  • paragraph breaks inside the message survive byte for byte — this is a multi-paragraph body, not a one-line row, so the read must not flatten \n\n;
  • a line that was never fenced or traced renders and copies exactly as today, byte for byte;
  • the fence/arrow shown here must not come back through Copy: the clipboard string equals the displayed string.

Accepted trade-off, to be stated in the PR body: a body that really contains <untrusted-message …> / from="…" as prose is treated as a marker (same trade-off as #1347 and #1406), and a residual marker fragment left by an earlier unwrap is dropped too.

Scope

  • core/src/client/exchange.js — one reader for the entry text; the map hands Bubbles that reader's output and nothing else
  • core/test/exchange-read.test.js — new: fenced wrap, nested wrap, residual fragment, multi-paragraph body preserved, plain body untouched, and a rendered bubble whose MarkdownText text and navigator.clipboard.writeText string are equal and free of <untrusted, from=", Chief →
  • generated: core/client.js, patches/ds-bot/0003-flybot-api-client.patch (+ the patches/ds-bot/topics.json entry this upstream client file needs), patches/ds-bot/0008-client-bundle.patch, patches/ds-bot/files/test/exchange-read.test.js
  • docs/subsystems/core-client.md if the module doc lists the client files

Out of scope, must not change: the write side and entry.text, the other tabs, Bubbles and MessageTools themselves, previewOf / readableLine (#1394, #1408), transcriptOf and the details-drawer copy (#1406), palette.js, voice.js. visibleText must not be used here: it flattens paragraphs, which the exchange sheet has to keep.

Acceptance

  • red first on base (node --import ./test/harness-resolve.mjs --test test/exchange-read.test.js in core/), quoting the leaked fence and the trace line; green on the tip with the same command
  • knife A: put back only texts: [entry.text] → the red is the defect itself (<untrusted-message from="User">\nChief → 助手\n\nfirst para\n\nsecond para\n</untrusted-message> vs first para\n\nsecond para), then restore
  • knife B: swap the reader for visibleText(stripTrace(body)) → red on flattened paragraphs and on the trace, proving the flattening is excluded, then restore; git diff clean afterwards, core/src/client/exchange.js sha256 back to the tip's
  • TZ=UTC npm run verify exit 0 with per-package counts, node scripts/ds-bot-patches.mjs check three ok, build-client --check 0, arch-docs OK
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    area:uiWeb/desktop UI and copybugSomething isn't working

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions