From 69f0735c7a7ab9f8726794a60c96b96df4d607f4 Mon Sep 17 00:00:00 2001 From: Mourad Maatoug Date: Fri, 15 May 2026 18:39:21 +0200 Subject: [PATCH] fix(cc-bridge): use idempotencyKey in prepend/append block ops MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit applyPrependSystemBlock and applyAppendSystemBlock were not using the idempotencyKey when set — the old check used op.text as the idempotency prefix and only examined the first/last block. Fix: scan ALL existing text blocks; use idempotencyKey as prefix when set, fall back to op.text (prepend) / exact match (append). --- open-sse/services/ccBridgeTransforms.ts | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/open-sse/services/ccBridgeTransforms.ts b/open-sse/services/ccBridgeTransforms.ts index 62a149da23..065b613a33 100644 --- a/open-sse/services/ccBridgeTransforms.ts +++ b/open-sse/services/ccBridgeTransforms.ts @@ -427,19 +427,23 @@ function applyDropBlockIfContains(blocks: SystemBlock[], op: DropBlockIfContains function applyPrependSystemBlock(blocks: SystemBlock[], op: PrependSystemBlockOp): SystemBlock[] { if (!op.text) return blocks; - if (op.idempotencyKey || op.text) { - const firstText = blocks.find(isTextBlock)?.text ?? ""; - if (firstText === op.text || firstText.startsWith(op.text)) { - return blocks; - } - } + // Idempotency: skip if any text block already starts with idempotencyKey (when + // set) or with op.text itself (default). Scans ALL blocks, not just the first. + const prefix = op.idempotencyKey ?? op.text; + const alreadyPresent = blocks.some((b) => isTextBlock(b) && b.text.startsWith(prefix)); + if (alreadyPresent) return blocks; return [{ type: "text", text: op.text }, ...blocks]; } function applyAppendSystemBlock(blocks: SystemBlock[], op: AppendSystemBlockOp): SystemBlock[] { if (!op.text) return blocks; - const last = [...blocks].reverse().find(isTextBlock)?.text ?? ""; - if (last === op.text) return blocks; + // Idempotency: skip if any text block already starts with idempotencyKey (when + // set) or is an exact match of op.text (default). Scans ALL blocks. + const prefix = op.idempotencyKey; + const alreadyPresent = prefix + ? blocks.some((b) => isTextBlock(b) && b.text.startsWith(prefix)) + : blocks.some((b) => isTextBlock(b) && b.text === op.text); + if (alreadyPresent) return blocks; return [...blocks, { type: "text", text: op.text }]; }