From 365e2a0896bfda6d15f4169a8315a35f05750342 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Peer=20St=C3=B6cklmair?= Date: Wed, 29 Jul 2026 11:15:27 +0300 Subject: [PATCH] fix(v10/cloudflare): Also skip cf: prefixed DOs Backport of: #22795 --- packages/cloudflare/src/utils/internalStorageKey.ts | 5 ++++- .../test/instrumentDurableObjectStorage.test.ts | 11 +++++++++++ .../cloudflare/test/utils/internalStorageKey.test.ts | 6 ++++++ 3 files changed, 21 insertions(+), 1 deletion(-) diff --git a/packages/cloudflare/src/utils/internalStorageKey.ts b/packages/cloudflare/src/utils/internalStorageKey.ts index 9706bc424b5e..12c40bada805 100644 --- a/packages/cloudflare/src/utils/internalStorageKey.ts +++ b/packages/cloudflare/src/utils/internalStorageKey.ts @@ -20,11 +20,14 @@ export function targetsCloudflareInternalKey(key: string | undefined, allowlist? // Framework-managed KV namespaces: // - `cf_` — agents / ai-chat internal state (mirrors the internal SQL table convention) + // - `cf:` — agents chat-recovery entries (`cf:chat-recovery:*`, `cf:chat:*`); the same reserved + // `cf` namespace, but colon-separated instead of underscore-separated // - `__ps_` — partyserver internals (e.g. `__ps_name`) // - `/` — MCP OAuth client state (`///{token,client_info,state,...}`), // read on every MCP tool call. User keys on an Agent rarely use a leading slash; if one does, // the allowlist opts it back in. - const isFrameworkKey = key.startsWith('cf_') || key.startsWith('__ps_') || key.startsWith('/'); + const isFrameworkKey = + key.startsWith('cf_') || key.startsWith('cf:') || key.startsWith('__ps_') || key.startsWith('/'); if (!isFrameworkKey) { return false; } diff --git a/packages/cloudflare/test/instrumentDurableObjectStorage.test.ts b/packages/cloudflare/test/instrumentDurableObjectStorage.test.ts index ec311fe66188..1c8fc20f2605 100644 --- a/packages/cloudflare/test/instrumentDurableObjectStorage.test.ts +++ b/packages/cloudflare/test/instrumentDurableObjectStorage.test.ts @@ -343,6 +343,17 @@ describe('instrumentDurableObjectStorage', () => { expect(startSpanSpy).not.toHaveBeenCalled(); }); + it('does not create a span for cf:-prefixed chat-recovery keys', async () => { + const startSpanSpy = vi.spyOn(sentryCore, 'startSpan'); + const instrumented = instrumentDurableObjectStorage(createMockStorage()); + + await instrumented.put('cf:chat-recovery:progress', 1); + await instrumented.get('cf:chat-recovery:incident:abc'); + await instrumented.list({ prefix: 'cf:chat-recovery:incident:' }); + + expect(startSpanSpy).not.toHaveBeenCalled(); + }); + it('does not create a span for a cf_-prefixed put with object entries', async () => { const startSpanSpy = vi.spyOn(sentryCore, 'startSpan'); const instrumented = instrumentDurableObjectStorage(createMockStorage()); diff --git a/packages/cloudflare/test/utils/internalStorageKey.test.ts b/packages/cloudflare/test/utils/internalStorageKey.test.ts index b26379b8ea69..3f00d1438ab9 100644 --- a/packages/cloudflare/test/utils/internalStorageKey.test.ts +++ b/packages/cloudflare/test/utils/internalStorageKey.test.ts @@ -7,6 +7,12 @@ describe('targetsCloudflareInternalKey', () => { expect(targetsCloudflareInternalKey('cf_mcp_servers')).toBe(true); }); + it('matches cf:-prefixed keys (agents chat-recovery namespace)', () => { + expect(targetsCloudflareInternalKey('cf:chat-recovery:incident:abc')).toBe(true); + expect(targetsCloudflareInternalKey('cf:chat-recovery:progress')).toBe(true); + expect(targetsCloudflareInternalKey('cf:chat:recovering')).toBe(true); + }); + it('matches __ps_-prefixed keys', () => { expect(targetsCloudflareInternalKey('__ps_name')).toBe(true); });