diff --git a/apps/sim/app/workspace/[workspaceId]/home/components/message-content/components/special-tags/special-tags.test.ts b/apps/sim/app/workspace/[workspaceId]/home/components/message-content/components/special-tags/special-tags.test.ts
index 09444ecccbf..ddc71a9ea95 100644
--- a/apps/sim/app/workspace/[workspaceId]/home/components/message-content/components/special-tags/special-tags.test.ts
+++ b/apps/sim/app/workspace/[workspaceId]/home/components/message-content/components/special-tags/special-tags.test.ts
@@ -149,13 +149,75 @@ describe('parseSpecialTags with ', () => {
expect(segments).toEqual([{ type: 'text', content: 'Thinking about it. ' }])
})
- it('strips a trailing partial opening tag while streaming', () => {
- const { segments, hasPendingTag } = parseSpecialTags('Let me ask. {
+ // Verbatim from a real message (trace b095e080). The model explained the
+ // tag and ended with a backticked example containing a REAL closing tag,
+ // which closed the earlier opener and made everything between it the body.
+ // That body is not valid JSON, so the segment was dropped and the render
+ // resumed mid-sentence at ") is what actually produces the interactive
+ // chip." — three paragraphs silently gone.
+ const raw =
+ 'Here you go — with the ending tag intentionally malformed as ``:\n\n' +
+ '{"type": "file", "path": "files/notes.md", "title": "notes.md"}\n\n' +
+ "Since the closing tag doesn't match the opening ``, the chat won't " +
+ 'recognize it as a valid resource chip. A properly matched pair ' +
+ '(`...`) is what actually produces the interactive chip.'
+
+ const rendered = parseSpecialTags(raw, false)
+ .segments.map((segment) => ('content' in segment ? segment.content : ''))
+ .join('')
+
+ expect(rendered).toContain("Since the closing tag doesn't match")
+ expect(rendered).toContain('A properly matched pair')
+ expect(rendered).toContain('"path": "files/notes.md"')
+ // No segment renders as a resource chip — the body was never valid.
+ expect(parseSpecialTags(raw, false).segments.some((s) => s.type === 'workspace_resource')).toBe(
+ false
+ )
})
- it('drops a question tag with an invalid body but keeps surrounding text', () => {
+ it('still parses a valid tag that follows a rejected one', () => {
+ // Before the rewrite, rejecting an unclosed tag abandoned the rest of the
+ // message, so this tag was never parsed at all.
+ const { segments } = parseSpecialTags(
+ 'I use loosely here. Anyway: [{"title":"A","description":"d"}] done.',
+ false
+ )
+ expect(segments.map((segment) => segment.type)).toContain('options')
+ })
+
+ it('loses nothing when the model writes no closing tag at all', () => {
+ // Verbatim from a real message (trace 220cc02d). No close tag exists, so no
+ // marker rule can fire — but the JSON value completes and prose follows,
+ // which settles it at the first space. Asserted as LOSSLESS: mid-stream and
+ // complete, every character survives.
+ const raw =
+ 'The dataset lives in {"type": "file", "path": "files/notes.md"} and I keep coming back to it whenever I need a quick reference. It never quite has everything.'
+ const join = (result: ReturnType) =>
+ result.segments.map((segment) => ('content' in segment ? segment.content : '')).join('')
+
+ const streaming = parseSpecialTags(raw, true)
+ expect(streaming.hasPendingTag).toBe(false)
+ expect(join(streaming)).toBe(raw)
+ expect(join(parseSpecialTags(raw, false))).toBe(raw)
+ })
+
+ it('keeps prose a tag wrapped instead of a payload', () => {
+ // Verbatim from a real message (trace 1206fd8a): a matched pair whose body
+ // is plain prose, never an attempted JSON payload. The sentence read
+ // "...once I wired up to handle the welcome sequence" with the subject gone.
+ const raw =
+ 'once I wired up the gmail-agent workflow to handle the welcome sequence.'
+ const rendered = parseSpecialTags(raw, false)
+ .segments.map((segment) => ('content' in segment ? segment.content : ''))
+ .join('')
+ expect(rendered).toContain('the gmail-agent workflow')
+ expect(rendered).toContain('to handle the welcome sequence')
+ })
+
+ it('still drops a marker-free malformed payload rather than showing raw JSON', () => {
+ // The complement of the case above: no tag markers in the body, so this is
+ // a genuinely broken emission from the agent, not swallowed prose.
const { segments, hasPendingTag } = parseSpecialTags(
'Before. {"type":"single_select"} After.',
false
@@ -166,6 +228,182 @@ describe('parseSpecialTags with ', () => {
{ type: 'text', content: ' After.' },
])
})
+
+ it('drops that same payload even when its JSON quotes tag syntax', () => {
+ // The marker scan must blank JSON strings the way the streaming path does.
+ // Scanning the raw body sees `` inside the prompt, calls the span
+ // literal text, and renders the raw payload — the outcome `discard` exists
+ // to prevent.
+ const { segments } = parseSpecialTags(
+ 'A [{"type":"single_select","prompt":"use here?"}] B',
+ false
+ )
+ expect(segments).toEqual([
+ { type: 'text', content: 'A ' },
+ { type: 'text', content: ' B' },
+ ])
+ })
+
+ it('does not flash the payload while the closing tag is still arriving', () => {
+ // Each frame below is a real mid-stream state: the JSON value has closed, so
+ // without tolerating an arriving close the trailing ``.
+ for (const fragment of ['<', '', '[{"title":"a","description":"b"}]${fragment}`,
+ true
+ )
+ expect(hasPendingTag).toBe(true)
+ expect(segments.map((s) => ('content' in s ? s.content : '')).join('')).toBe('see ')
+ }
+ })
+
+ it('still rejects a close whose name is wrong rather than merely unfinished', () => {
+ // The counterpart to the case above: `` can never grow
+ // into ``, so it settles immediately instead of hiding
+ // the rest of the message for the remainder of the stream.
+ const { hasPendingTag } = parseSpecialTags(
+ 'see {"type":"file","path":"a.md"} and then prose.',
+ true
+ )
+ expect(hasPendingTag).toBe(false)
+ })
+
+ it('keeps a valid tag whose close an earlier broken tag would borrow', () => {
+ // The first opener misspells its close, so it reaches forward and matches
+ // the SECOND tag's close, swallowing a perfectly good resource into one
+ // literal span. Resuming past the opener re-scans the interior instead.
+ const raw =
+ 'See {"type":"file","path":"a.md"}\n' +
+ 'and a real one: {"type":"file","path":"b.md","title":"b"}'
+ const { segments } = parseSpecialTags(raw, false)
+ expect(segments.some((s) => s.type === 'workspace_resource')).toBe(true)
+ expect(segments.map((s) => ('content' in s ? s.content : '')).join('')).toContain(
+ ''
+ )
+ })
+
+ it('still renders a matched pair whose body IS valid', () => {
+ const raw =
+ 'see {"type":"file","path":"files/a.md","title":"a.md"} ok'
+ const { segments } = parseSpecialTags(raw, false)
+ expect(segments.some((s) => s.type === 'workspace_resource')).toBe(true)
+ })
+
+ it('shows prose immediately mid-stream instead of blanking the rest', () => {
+ // The failure this replaces: everything after the marker stayed invisible
+ // for the remainder of the stream, then reappeared when it ended.
+ const content = 'The `` chip only renders for a real file.'
+ const { segments, hasPendingTag } = parseSpecialTags(content, true)
+ expect(hasPendingTag).toBe(false)
+ expect(segments.map((s) => ('content' in s ? s.content : s.type)).join('')).toContain(
+ 'chip only renders for a real file.'
+ )
+ })
+
+ it('shows text once the JSON value has closed and stray content follows', () => {
+ // Verbatim shape from a real message (trace afbeefd0): the close tag was
+ // TRUNCATED to `{"type":"file","path":"files/notes.md"} ('content' in s ? s.content : '')).join('')).toContain(
+ 'I brew a cup of coffee'
+ )
+ })
+
+ it('tolerates braces inside JSON strings when tracking depth', () => {
+ const raw = 'x {"title":"a } b","path":"files/a.md"'
+ expect(parseSpecialTags(raw, true).hasPendingTag).toBe(true)
+ })
+
+ it('still suppresses a JSON-bodied tag that is genuinely mid-stream', () => {
+ const { segments, hasPendingTag } = parseSpecialTags(
+ 'Here you go {"type":"file","id":"abc"',
+ true
+ )
+ expect(hasPendingTag).toBe(true)
+ expect(segments).toEqual([{ type: 'text', content: 'Here you go ' }])
+ })
+
+ it('bails when a foreign closing tag appears inside the body', () => {
+ // Tags never nest, so a close for a different tag proves the opener was text.
+ // The array is left UNCLOSED on purpose: with a closed value the JSON rule
+ // would independently reject the trailing content, and this test would still
+ // pass with the nesting rule deleted. Unclosed, only nesting can explain it.
+ const { hasPendingTag } = parseSpecialTags('see [{"title":"a" more', true)
+ expect(hasPendingTag).toBe(false)
+ })
+
+ it('does not bail on tag syntax quoted inside a JSON string', () => {
+ // The false positive this guards: a question whose text legitimately quotes
+ // another tag. Bailing would show raw JSON that later snaps into a card.
+ const streaming = 'ok [{"type":"single_select","prompt":"Use the tag?"'
+ expect(parseSpecialTags(streaming, true).hasPendingTag).toBe(true)
+ })
+
+ it('resolves that same question correctly once it closes', () => {
+ // The other half of the guarantee: the body the streaming case refused to
+ // bail on does render as a question card, so nothing flickered for nothing.
+ const complete =
+ 'ok [{"type":"single_select","prompt":"Use the tag?","options":[{"id":"y","label":"Yes"},{"id":"n","label":"No"}]}]'
+ const { segments } = parseSpecialTags(complete, false)
+ expect(segments.some((s) => s.type === 'question')).toBe(true)
+ })
+
+ it('still bails on a marker outside the JSON strings', () => {
+ // Escapes must not end the string early, and a marker in real body position
+ // is still evidence.
+ const streaming = 'ok [{"prompt":"a \\" quote"} '
+ expect(parseSpecialTags(streaming, true).hasPendingTag).toBe(false)
+ })
+
+ it('rejects an opener a nested one disproves, then judges the inner on its own', () => {
+ // Each opener is evaluated independently. The first is disproved by the
+ // nested opener and its text is released immediately; the second is a fresh
+ // candidate that nothing has ruled out yet, so it holds mid-stream.
+ const streaming = parseSpecialTags('a b c', true)
+ expect(streaming.hasPendingTag).toBe(true)
+ expect(
+ streaming.segments.map((segment) => ('content' in segment ? segment.content : '')).join('')
+ ).toBe('a b ')
+
+ // Once the stream ends nothing can close it, so the whole line is shown.
+ const done = parseSpecialTags('a b c', false)
+ expect(done.hasPendingTag).toBe(false)
+ expect(
+ done.segments.map((segment) => ('content' in segment ? segment.content : '')).join('')
+ ).toBe('a b c')
+ })
+
+ it('keeps suppressing an unclosed thinking tag with prose — its body is not JSON', () => {
+ // Documents the deliberate gap: `thinking` bodies are prose, so the JSON
+ // heuristic cannot apply and only the nesting rule can rescue it.
+ const { hasPendingTag } = parseSpecialTags('a still reasoning about', true)
+ expect(hasPendingTag).toBe(true)
+ })
+
+ it('renders an unclosed tag as text once the message is complete', () => {
+ const content =
+ 'The `` file chip only renders when its path points to a real file.'
+ const { segments, hasPendingTag } = parseSpecialTags(content, false)
+ expect(hasPendingTag).toBe(false)
+ // Asserted on the joined text, not segment boundaries: the renderer
+ // concatenates adjacent text segments, so how the span is split is not
+ // observable to a reader.
+ expect(segments.every((segment) => segment.type === 'text')).toBe(true)
+ expect(segments.map((segment) => ('content' in segment ? segment.content : '')).join('')).toBe(
+ content
+ )
+ })
+
+ it('strips a trailing partial opening tag while streaming', () => {
+ const { segments, hasPendingTag } = parseSpecialTags('Let me ask. {
diff --git a/apps/sim/app/workspace/[workspaceId]/home/components/message-content/components/special-tags/special-tags.tsx b/apps/sim/app/workspace/[workspaceId]/home/components/message-content/components/special-tags/special-tags.tsx
index 60227897d94..7dbae91aae6 100644
--- a/apps/sim/app/workspace/[workspaceId]/home/components/message-content/components/special-tags/special-tags.tsx
+++ b/apps/sim/app/workspace/[workspaceId]/home/components/message-content/components/special-tags/special-tags.tsx
@@ -465,35 +465,341 @@ function parseSpecialTagData(
}
/**
- * Parses inline special tags (``, ``, ``) from streamed
- * text content. Complete tags are extracted into typed segments; incomplete
- * tags (still streaming) are suppressed from display and flagged via
- * `hasPendingTag` so the caller can show a loading indicator.
+ * Any tag-shaped marker, including names that are not special tags at all — the
+ * model inventing `` is exactly the case that matters.
+ */
+const TAG_SHAPED_MARKER = /<\/?[a-zA-Z][\w-]*>/
+
+/**
+ * The one tag whose body is prose rather than JSON (see {@link parseTextTagBody}),
+ * so a non-JSON body there says nothing about whether a close is still coming.
+ */
+const PROSE_BODY_TAG_NAME: (typeof SPECIAL_TAG_NAMES)[number] = 'thinking'
+
+/**
+ * Tags whose body must be JSON.
+ *
+ * Derived from {@link SPECIAL_TAG_NAMES} rather than hand-listed: a new tag is
+ * JSON-bodied by default, so forgetting to update this set cannot silently
+ * downgrade it to the weaker prose heuristics. Opting a tag out is an explicit
+ * edit to {@link PROSE_BODY_TAG_NAME}.
+ */
+const JSON_BODY_TAG_NAMES: ReadonlySet<(typeof SPECIAL_TAG_NAMES)[number]> = new Set(
+ SPECIAL_TAG_NAMES.filter((name) => name !== PROSE_BODY_TAG_NAME)
+)
+
+/**
+ * How much of an unclosed body to inspect per parse.
+ *
+ * Both rules in {@link unclosedTagCannotResolve} decide on their FIRST piece of
+ * evidence — the first foreign marker, or the first character that breaks JSON
+ * viability — so a bounded window reaches the same verdict as the full remainder
+ * for any real payload. Unbounded, the check is O(remaining length) and runs
+ * once per opener inside a parse that re-runs for every streamed chunk, so a
+ * long reply repeatedly mentioning a tag name in prose costs seconds of
+ * main-thread time. Evidence past the window only defers the decision to a later
+ * chunk, which is the same conservative direction the rules already take.
+ */
+const MAX_UNCLOSED_BODY_SCAN = 4096
+
+/**
+ * Strip the contents of JSON string literals from `body`, replacing them with
+ * spaces so every other index is preserved.
+ *
+ * A JSON tag body can legitimately quote tag syntax — a `` asking
+ * which tag to use, or a `` whose title mentions one. Those
+ * markers live inside a string and say nothing about whether the tag will
+ * close, so the nesting rule must not see them. Tracks escapes so a `\"` inside
+ * a string does not end it early. Handles an unterminated trailing string, which
+ * is the normal state mid-stream.
+ */
+function blankJsonStringLiterals(body: string): string {
+ let out = ''
+ let inString = false
+ let escaped = false
+
+ for (const char of body) {
+ if (escaped) {
+ escaped = false
+ out += ' '
+ continue
+ }
+ if (char === '\\' && inString) {
+ escaped = true
+ out += ' '
+ continue
+ }
+ if (char === '"') {
+ inString = !inString
+ out += '"'
+ continue
+ }
+ out += inString ? ' ' : char
+ }
+
+ return out
+}
+
+/**
+ * True while `body` could still grow into a single valid JSON value.
+ *
+ * Checking only the first character is not enough: a body like
+ * `{"type":"file"}`, which no marker rule sees.
+ *
+ * Both are conservative: they only fire on content that could not have parsed.
+ * A false positive would merely show text early that a later chunk resolves
+ * into a tag — the end-of-stream parse still produces the correct final render.
+ */
+function unclosedTagCannotResolve(
+ tagName: (typeof SPECIAL_TAG_NAMES)[number],
+ body: string
+): boolean {
+ const pending = dropArrivingClose(body, `${tagName}>`)
+ // For a JSON-bodied tag, ignore markers inside string literals: quoting tag
+ // syntax is legitimate content, and treating it as evidence would bail on a
+ // tag that goes on to close correctly — showing raw JSON that then snaps into
+ // a rendered card.
+ const isJsonBodied = JSON_BODY_TAG_NAMES.has(tagName)
+ const scannable = isJsonBodied ? blankJsonStringLiterals(pending) : pending
+ for (const name of SPECIAL_TAG_NAMES) {
+ // A close for this tag is absent by definition here, so this catches a
+ // FOREIGN close; the open check catches nesting, including self-nesting.
+ if (scannable.includes(`${name}>`) || scannable.includes(`<${name}>`)) return true
+ }
+
+ if (isJsonBodied && !isViableJsonPrefixOf(scannable)) return true
+
+ return false
+}
+
+/**
+ * Drop a trailing fragment that could still grow into `closeTag`.
+ *
+ * Mid-stream the closing marker arrives a character at a time, so a body sits at
+ * `]` completes. That fragment is an
+ * arriving close, not stray content — counting it as fatal is what made a
+ * perfectly valid tag show its raw payload as text until the final `>` landed.
+ *
+ * Only a fragment at the very END is dropped, so evidence that the close is
+ * genuinely wrong still lands immediately: a misspelled ``
+ * is not a prefix of ``, and a truncated ` 0; n--) {
+ if (body.endsWith(closeTag.slice(0, n))) return body.slice(0, -n)
+ }
+ return body
+}
+
+/**
+ * How one opening tag resolved. Naming the four outcomes is the point: the
+ * parser previously decided each case inline, which is how "drop it" quietly
+ * became the fallback for situations that were never malformed payloads.
+ */
+type TagResolution =
+ /** Body parsed; emit the typed segment and resume after the closing tag. */
+ | { outcome: 'segment'; segment: ContentSegment; resumeAt: number }
+ /** Provably not a tag; render the span verbatim and resume after it. */
+ | { outcome: 'literal'; resumeAt: number }
+ /** A well-formed payload that failed its shape guard — dropped deliberately. */
+ | { outcome: 'discard'; resumeAt: number }
+ /** Still streaming and a close remains plausible; suppress the remainder. */
+ | { outcome: 'pending' }
+
+/**
+ * Why a failed body was never an attempted payload — so the markers were literal
+ * text and the span must be shown rather than swallowed. `null` means the body
+ * really was a payload that failed its shape guard.
+ *
+ * The two reasons resume differently, which is why they are distinguished
+ * rather than collapsed into a boolean (see {@link resolveTagAt}).
+ */
+type LiteralTextReason =
+ /** The body carries tag markers, so the close we matched belongs elsewhere. */
+ | 'foreign-markers'
+ /** The tag wrapped prose that was never JSON to begin with. */
+ | 'never-a-payload'
+
+function literalTextReason(
+ tagName: (typeof SPECIAL_TAG_NAMES)[number],
+ body: string
+): LiteralTextReason | null {
+ const isJsonBodied = JSON_BODY_TAG_NAMES.has(tagName)
+ // Markers inside a JSON string are content, not evidence — a `` may
+ // legitimately quote tag syntax in its prompt. Scanning the raw body here
+ // would classify a broken payload as literal text and render it as raw JSON,
+ // which is exactly what `discard` exists to prevent. Mirrors the same blanking
+ // in unclosedTagCannotResolve, which judges the same body mid-stream.
+ const scannable = isJsonBodied ? blankJsonStringLiterals(body) : body
+ if (TAG_SHAPED_MARKER.test(scannable)) return 'foreign-markers'
+ if (isJsonBodied && !isViableJsonPrefixOf(scannable)) return 'never-a-payload'
+ return null
+}
+
+/**
+ * `content.indexOf(needle, from)` memoized per needle.
*
- * Trailing partial opening tags (e.g. `,
+ content: string,
+ needle: string,
+ from: number
+): number {
+ const cached = cache.get(needle)
+ if (cached !== undefined && (cached === -1 || cached >= from)) return cached
+ const idx = content.indexOf(needle, from)
+ cache.set(needle, idx)
+ return idx
+}
+
+function resolveTagAt(
+ content: string,
+ openIndex: number,
+ tagName: (typeof SPECIAL_TAG_NAMES)[number],
+ isStreaming: boolean,
+ closeCache: Map
+): TagResolution {
+ const openTag = `<${tagName}>`
+ const closeTag = `${tagName}>`
+ const bodyStart = openIndex + openTag.length
+ const closeIdx = memoizedIndexOf(closeCache, content, closeTag, bodyStart)
+
+ if (closeIdx === -1) {
+ const inspectable = content.slice(bodyStart, bodyStart + MAX_UNCLOSED_BODY_SCAN)
+ if (isStreaming && !unclosedTagCannotResolve(tagName, inspectable)) {
+ return { outcome: 'pending' }
+ }
+ // Nothing can close it, so only the opener itself is literal. Resuming just
+ // past it (rather than abandoning the message) keeps a genuinely valid tag
+ // later in the same reply parseable.
+ return { outcome: 'literal', resumeAt: bodyStart }
+ }
+
+ const resumeAt = closeIdx + closeTag.length
+ const body = content.slice(bodyStart, closeIdx)
+
+ const parsed = parseSpecialTagData(tagName, body)
+ if (parsed) return { outcome: 'segment', segment: parsed, resumeAt }
+
+ const reason = literalTextReason(tagName, body)
+ if (reason === 'foreign-markers') {
+ // A marker in the body proves the close we matched opened somewhere else —
+ // this opener reached past its own missing close and borrowed a later tag's.
+ // Resuming past the OPENER instead of past that borrowed close re-scans the
+ // interior, so the genuine tag inside still renders instead of being
+ // swallowed into one literal span.
+ return { outcome: 'literal', resumeAt: bodyStart }
+ }
+ if (reason === 'never-a-payload') return { outcome: 'literal', resumeAt }
+
+ // A well-formed value that failed its shape guard is a broken emission from
+ // the agent; showing the user raw JSON there would be worse than nothing.
+ return { outcome: 'discard', resumeAt }
+}
+
+/**
+ * Splits streamed text into renderable segments, extracting complete special
+ * tags and deciding what to do with the ones that never resolve. Incomplete
+ * tags are suppressed and flagged via `hasPendingTag` so the caller can show a
+ * loading indicator, and a trailing partial opening marker (` {
+ if (text.trim()) segments.push({ type: 'text', content: text })
+ }
+
+ const openerCache = new Map()
+ const closeCache = new Map()
+
while (cursor < content.length) {
let nearestStart = -1
let nearestTagName: (typeof SPECIAL_TAG_NAMES)[number] | '' = ''
for (const name of SPECIAL_TAG_NAMES) {
- const idx = content.indexOf(`<${name}>`, cursor)
+ const idx = memoizedIndexOf(openerCache, content, `<${name}>`, cursor)
if (idx !== -1 && (nearestStart === -1 || idx < nearestStart)) {
nearestStart = idx
nearestTagName = name
}
}
- if (nearestStart === -1) {
+ if (nearestStart === -1 || nearestTagName === '') {
let remaining = content.slice(cursor)
if (isStreaming) {
+ // Hide a half-arrived opening marker so it does not flash as text.
const partial = remaining.match(/<[a-z_-]*$/i)
if (partial) {
const fragment = partial[0].slice(1)
@@ -507,41 +813,26 @@ export function parseSpecialTags(content: string, isStreaming: boolean): ParsedS
}
}
- if (remaining.trim()) {
- segments.push({ type: 'text', content: remaining })
- }
+ pushText(remaining)
break
}
- if (nearestStart > cursor) {
- const text = content.slice(cursor, nearestStart)
- if (text.trim()) {
- segments.push({ type: 'text', content: text })
- }
- }
+ pushText(content.slice(cursor, nearestStart))
- const openTag = `<${nearestTagName}>`
- const closeTag = `${nearestTagName}>`
- const bodyStart = nearestStart + openTag.length
- const closeIdx = content.indexOf(closeTag, bodyStart)
+ const resolution = resolveTagAt(content, nearestStart, nearestTagName, isStreaming, closeCache)
- if (closeIdx === -1) {
+ if (resolution.outcome === 'pending') {
hasPendingTag = true
- cursor = content.length
break
}
- const body = content.slice(bodyStart, closeIdx)
- if (!nearestTagName) {
- cursor = closeIdx + closeTag.length
- continue
- }
- const parsedTag = parseSpecialTagData(nearestTagName, body)
- if (parsedTag) {
- segments.push(parsedTag)
+ if (resolution.outcome === 'segment') {
+ segments.push(resolution.segment)
+ } else if (resolution.outcome === 'literal') {
+ pushText(content.slice(nearestStart, resolution.resumeAt))
}
- cursor = closeIdx + closeTag.length
+ cursor = resolution.resumeAt
}
if (segments.length === 0 && !hasPendingTag) {