diff --git a/extensions/copilot/src/platform/telemetry/common/telemetry.ts b/extensions/copilot/src/platform/telemetry/common/telemetry.ts index 07e43a833ff..a19a044f29c 100644 --- a/extensions/copilot/src/platform/telemetry/common/telemetry.ts +++ b/extensions/copilot/src/platform/telemetry/common/telemetry.ts @@ -219,6 +219,11 @@ const COMPRESSED_CHUNK_SUFFIX = 'Chunk'; // value from instead of having to branch on whether the value happened to be chunked. const ALWAYS_COMPRESSED_CHUNK_KEYS = new Set(['messagesJson', 'diffsJSON']); +// Overrides for the base name of the compressed chunk family. By default the chunk family is named +// `Chunk`, but some backend mappings expect a different casing. For `messagesJson` the backend +// expects `messagesJSONChunk` (uppercased JSON), while the original `` column keeps its casing. +const COMPRESSED_CHUNK_KEY_OVERRIDES: { readonly [key: string]: string } = { messagesJson: 'messagesJSON' }; + // Compressor used by multiplexProperties to gzip + base64 encode oversized property values. It is // registered once by the Node layer (via setTelemetryPropertyCompressor) because Node's `zlib` is // unavailable in the common layer; until then multiplexProperties falls back to plain chunking. It @@ -273,8 +278,9 @@ export async function multiplexProperties( // padding). No redundant plain continuation family is produced. newProperties[key] = value!.slice(0, MAX_PROPERTY_LENGTH); const compressed = await compress(value!); + const chunkKey = COMPRESSED_CHUNK_KEY_OVERRIDES[key] ?? key; for (let offset = 0, index = 1; offset < compressed.length && index <= MAX_CONCATENATED_PROPERTIES; offset += MAX_PROPERTY_LENGTH, index++) { - const columnName = index === 1 ? `${key}${COMPRESSED_CHUNK_SUFFIX}` : `${key}${COMPRESSED_CHUNK_SUFFIX}_${index}`; + const columnName = index === 1 ? `${chunkKey}${COMPRESSED_CHUNK_SUFFIX}` : `${chunkKey}${COMPRESSED_CHUNK_SUFFIX}_${index}`; newProperties[columnName] = compressed.slice(offset, offset + MAX_PROPERTY_LENGTH); } continue; diff --git a/extensions/copilot/src/platform/telemetry/test/node/telemetry.spec.ts b/extensions/copilot/src/platform/telemetry/test/node/telemetry.spec.ts index 1c3edbda270..430bace261d 100644 --- a/extensions/copilot/src/platform/telemetry/test/node/telemetry.spec.ts +++ b/extensions/copilot/src/platform/telemetry/test/node/telemetry.spec.ts @@ -384,9 +384,11 @@ suite('multiplexProperties compression', function () { // Known-large fields are always chunked in compressed form for backend uniformity. expect(result.diffsJSONChunk).toBeDefined(); - expect(result.messagesJsonChunk).toBeDefined(); + // messagesJson uses the uppercase-JSON chunk family name expected by the backend. + expect(result.messagesJSONChunk).toBeDefined(); + expect(result.messagesJsonChunk).toBeUndefined(); expect(gunzipFromBase64(joinCompressedChunks(result, 'diffsJSONChunk'))).toBe('small'); - expect(gunzipFromBase64(joinCompressedChunks(result, 'messagesJsonChunk'))).toBe('tiny'); + expect(gunzipFromBase64(joinCompressedChunks(result, 'messagesJSONChunk'))).toBe('tiny'); // The original columns still carry the (short) uncompressed value. expect(result.diffsJSON).toBe('small'); expect(result.messagesJson).toBe('tiny'); @@ -395,6 +397,21 @@ suite('multiplexProperties compression', function () { expect(result.otherChunk).toBeUndefined(); }); + test('emits the messagesJSONChunk family (with numbered suffixes) for large messagesJson', async () => { + const original = pseudoRandomString(60000); // Poorly compressible -> compressed base64 > 8192. + const result = await multiplexProperties({ messagesJson: original }, gzipBase64) as { [key: string]: string }; + + // The original column carries just the first uncompressed chunk; no plain continuation family. + expect(result.messagesJson).toBe(original.slice(0, 8192)); + expect(result.messagesJson_02).toBeUndefined(); + // Compressed family uses the uppercase-JSON name, including numbered suffixes. + expect(result.messagesJSONChunk).toBeDefined(); + expect(result.messagesJSONChunk_2).toBeDefined(); + expect(result.messagesJsonChunk).toBeUndefined(); + expect(result.messagesJsonChunk_2).toBeUndefined(); + expect(gunzipFromBase64(joinCompressedChunks(result, 'messagesJSONChunk'))).toBe(original); + }); + test('falls back to the plain continuation family when no compressor is provided', async () => { const original = 'x'.repeat(20000); const result = await multiplexProperties({ diffsJSON: original });