393e62ba9f
Follow-up to #23573: chip-heavy answers are long by design, and each stream flush re-ran `protectChatReferencesForMarkdown` and `marked.lexer` over the whole message, so render cost grew quadratically with message length. This makes the per-flush cost proportional to the appended text instead, and offsets the new code by removing dead AI chat code. ## Streaming render - **Incremental block splitting.** `getMarkdownBlocksIncrementally` reuses blocks that can no longer change and re-tokenizes only the trailing ones. Two trailing blocks stay unstable, not one: a loose list followed by a blank line still merges with a later item (`- a\n\n` + `- b` is one list token). Uses `Lexer.blockTokens` instead of `marked.lexer` since only block raws are needed and the full lexer also runs the inline tokenizer. Simulated stream over a 22 KB chip-heavy message (120 chars/flush, matching the 100 ms flush throttle): 191 ms → 3.7 ms cumulative. The test suite pins char-by-char equivalence against full `marked.lexer` output across loose lists, unclosed fences, setext headings, tables, CRLF and chip markers. - **Per-block reference protection.** `protectChatReferencesForMarkdown` moved behind the existing block memo, so settled blocks never re-run reference parsing during a stream. - **Anchored open pattern.** `(?<!\[)\[\[+` anchors marker matching to the start of a bracket run. The greedy `+` from #23798 backtracked at every position inside a run, once per alternative: 429 ms → ~1 ms on a 10 KB bracket-run input. A run start always yields the same match, so no valid marker is lost. Also an `includes('[[')` bail-out in `findChatReferences`, which runs on every text node of the streaming block. ## Chip lookups `fieldMetadataItemByIdSelector` did `objectMetadataItems.find(obj => obj.fields.some(...))` per chip — O(workspace fields) each time the agent's tool calls trigger a metadata refetch mid-chat. The by-id and by-name map selectors mostly already existed with almost no consumers; this wires `fieldMetadataItemByIdSelector`, `objectMetadataItemFamilySelector` and `viewFromViewIdFamilySelector` to them (adding the missing `objectMetadataItemsByIdMapSelector` and `viewsByIdMapSelector`) and adds `areEqual` so unchanged lookups keep referential stability. ## Offscreen messages Settled messages (everything except the streaming last one) get `content-visibility: auto`, so long threads skip layout and paint for messages scrolled out of view. `contain-intrinsic-size: auto` keeps remembered heights, so scroll positions stay accurate once a message has been painted. ## Removed `ReasoningSummaryDisplay`, `agentChatMessagesComponentState`, `CHAT_THREADS_PAGE_SIZE`, `AgentResponseFormat` and `getFieldIcon` had no consumers. `TextWithChatReferences` and `protectChatReferencesForMarkdown` shared a duplicated segment-slicing loop, now in `getChatReferenceSegments`, and the nine identical per-tag markdown component entries collapse into `createChatReferenceElement`. The branch lands at +354/−329 including the new test suite; production code is net negative. Incidental: `marked` added to jest's `transformIgnorePatterns` allowlist (ESM-only, previously imported by no test). --- _Generated by [Claude Code](https://claude.ai/code/session_01MN8FVc63J4SJQHXWwzUwkh)_ <!-- This is an auto-generated description by cubic. --> <a href="https://cubic.dev/pr/twentyhq/twenty/pull/23831?utm_source=github" target="_blank" rel="noopener noreferrer" data-no-image-dialog="true"><picture><source media="(prefers-color-scheme: dark)" srcset="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"><source media="(prefers-color-scheme: light)" srcset="https://www.cubic.dev/buttons/review-in-cubic-light.svg"><img alt="Review in cubic" src="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"></picture></a> <!-- End of auto-generated description by cubic. -->