Skip to content

Commit 163fada

Browse files
committed
fix(copilot): keep prose a tag wrapped instead of a JSON payload
Third real case from a trace (1206fd8a): `<workspace_resource>the gmail-agent workflow</workspace_resource>` — a matched pair whose body is plain prose. The sentence rendered as "...once I wired up to handle the welcome sequence" with its subject silently removed. The marker test could not fire (prose contains no tag markers), so it fell to the deliberate drop-malformed-payload path. But that path exists for an agent emitting BROKEN JSON, not for a tag wrapping prose. The distinction is whether the body was ever an attempted payload, which isViableJsonPrefix already answers: `{"type":"single_select"}` is a well-formed JSON value failing its shape guard and keeps being dropped; `the gmail-agent workflow` was never a payload and is emitted.
1 parent ba545fa commit 163fada

2 files changed

Lines changed: 27 additions & 10 deletions

File tree

apps/sim/app/workspace/[workspaceId]/home/components/message-content/components/special-tags/special-tags.test.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -176,6 +176,19 @@ describe('parseSpecialTags with <question>', () => {
176176
)
177177
})
178178

179+
it('keeps prose a tag wrapped instead of a payload', () => {
180+
// Verbatim from a real message (trace 1206fd8a): a matched pair whose body
181+
// is plain prose, never an attempted JSON payload. The sentence read
182+
// "...once I wired up to handle the welcome sequence" with the subject gone.
183+
const raw =
184+
'once I wired up <workspace_resource>the gmail-agent workflow</workspace_resource> to handle the welcome sequence.'
185+
const rendered = parseSpecialTags(raw, false)
186+
.segments.map((segment) => ('content' in segment ? segment.content : ''))
187+
.join('')
188+
expect(rendered).toContain('the gmail-agent workflow')
189+
expect(rendered).toContain('to handle the welcome sequence')
190+
})
191+
179192
it('still drops a marker-free malformed payload rather than showing raw JSON', () => {
180193
// The complement of the case above: no tag markers in the body, so this is
181194
// a genuinely broken emission from the agent, not swallowed prose.

apps/sim/app/workspace/[workspaceId]/home/components/message-content/components/special-tags/special-tags.tsx

Lines changed: 14 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -685,17 +685,21 @@ export function parseSpecialTags(content: string, isStreaming: boolean): ParsedS
685685
const parsedTag = parseSpecialTagData(nearestTagName, body)
686686
if (parsedTag) {
687687
segments.push(parsedTag)
688-
} else if (TAG_SHAPED_MARKER.test(body)) {
689-
// The body failed to parse AND contains tag markers, which means the
690-
// close we matched was not this opener's — the model was explaining tag
691-
// syntax and a later example like `<workspace_resource>...</workspace_resource>`
692-
// closed an earlier opener, making paragraphs of prose the "body".
693-
// Dropping that silently loses the text and resumes mid-sentence, so emit
694-
// it verbatim. Streamdown escapes the markers, so it reads as literal text.
688+
} else if (
689+
// The close we matched was not this opener's: the model was explaining tag
690+
// syntax and a later example closed an earlier opener, making paragraphs
691+
// of prose the "body".
692+
TAG_SHAPED_MARKER.test(body) ||
693+
// Or the tag wrapped prose that was never an attempted payload at all.
694+
(JSON_BODY_TAG_NAMES.has(nearestTagName) && !isViableJsonPrefix(body))
695+
) {
696+
// Either way the markers were literal text, so dropping the span loses
697+
// real content and resumes mid-sentence. Emit it verbatim — Streamdown
698+
// escapes the markers, so it reads as literal text.
695699
//
696-
// A marker-free body that merely fails validation is a genuinely
697-
// malformed payload from the agent; that keeps being dropped rather than
698-
// showing the user raw JSON.
700+
// A body that IS a well-formed JSON value and merely fails its shape
701+
// guard keeps being dropped: that is a genuinely malformed payload from
702+
// the agent, and showing the user raw JSON there would be a regression.
699703
const literal = content.slice(nearestStart, closeIdx + closeTag.length)
700704
if (literal.trim()) {
701705
segments.push({ type: 'text', content: literal })

0 commit comments

Comments
 (0)