From 6384697b03d0b2c4f4f199f6d6995147a75e54cf Mon Sep 17 00:00:00 2001 From: Haoqing Wang <78337154+hqhq1025@users.noreply.github.com> Date: Wed, 25 Mar 2026 05:36:41 +0800 Subject: [PATCH] fix(cli): filter isMeta and isCompactSummary messages in local and remote mode (#359) --- cli/src/claude/claudeLocalLauncher.test.ts | 28 ++++++++++ cli/src/claude/claudeLocalLauncher.ts | 5 ++ .../claude/utils/OutgoingMessageQueue.test.ts | 51 +++++++++++++++++++ cli/src/claude/utils/OutgoingMessageQueue.ts | 2 +- 4 files changed, 85 insertions(+), 1 deletion(-) create mode 100644 cli/src/claude/utils/OutgoingMessageQueue.test.ts diff --git a/cli/src/claude/claudeLocalLauncher.test.ts b/cli/src/claude/claudeLocalLauncher.test.ts index fedf6a0a..a4e855aa 100644 --- a/cli/src/claude/claudeLocalLauncher.test.ts +++ b/cli/src/claude/claudeLocalLauncher.test.ts @@ -105,4 +105,32 @@ describe('claudeLocalLauncher message filtering', () => { expect(sentMessages).toHaveLength(2) }) + + it('filters out isMeta messages (e.g. skill injections)', async () => { + const { session, sentMessages } = createSessionStub() + await claudeLocalLauncher(session as never) + + harness.scannerOnMessage!({ + type: 'user', + isMeta: true, + uuid: '1', + message: { content: [{ type: 'text', text: '# Skill content...' }] } + }) + + expect(sentMessages).toHaveLength(0) + }) + + it('filters out isCompactSummary messages', async () => { + const { session, sentMessages } = createSessionStub() + await claudeLocalLauncher(session as never) + + harness.scannerOnMessage!({ + type: 'assistant', + isCompactSummary: true, + uuid: '1', + message: { content: 'compacted context' } + }) + + expect(sentMessages).toHaveLength(0) + }) }) diff --git a/cli/src/claude/claudeLocalLauncher.ts b/cli/src/claude/claudeLocalLauncher.ts index 5bada013..291569cd 100644 --- a/cli/src/claude/claudeLocalLauncher.ts +++ b/cli/src/claude/claudeLocalLauncher.ts @@ -15,6 +15,11 @@ export async function claudeLocalLauncher(session: Session): Promise<'switch' | if (message.type === 'summary') { return } + // Filter out internal meta messages (e.g. skill injections) and + // compact summaries to avoid them appearing in the web UI + if (message.isMeta || message.isCompactSummary) { + return + } // Filter out invisible system messages (e.g. init, stop_hook_summary) // to avoid them showing as raw JSON in the web UI if (!isClaudeChatVisibleMessage(message)) { diff --git a/cli/src/claude/utils/OutgoingMessageQueue.test.ts b/cli/src/claude/utils/OutgoingMessageQueue.test.ts new file mode 100644 index 00000000..9530bf33 --- /dev/null +++ b/cli/src/claude/utils/OutgoingMessageQueue.test.ts @@ -0,0 +1,51 @@ +import { afterEach, beforeEach, describe, expect, it } from 'vitest' +import { OutgoingMessageQueue } from './OutgoingMessageQueue' + +describe('OutgoingMessageQueue message filtering', () => { + let sent: Array> + let queue: OutgoingMessageQueue + + beforeEach(() => { + sent = [] + queue = new OutgoingMessageQueue((msg) => { sent.push(msg) }) + }) + + afterEach(() => { + queue.destroy() + }) + + it('sends normal messages', async () => { + queue.enqueue({ type: 'assistant', uuid: '1' }) + queue.enqueue({ type: 'user', uuid: '2' }) + await queue.flush() + + expect(sent).toHaveLength(2) + }) + + it('filters out system messages', async () => { + queue.enqueue({ type: 'system', subtype: 'init', uuid: '1' }) + queue.enqueue({ type: 'assistant', uuid: '2' }) + await queue.flush() + + expect(sent).toHaveLength(1) + expect(sent[0]).toMatchObject({ type: 'assistant' }) + }) + + it('filters out isMeta messages', async () => { + queue.enqueue({ type: 'user', isMeta: true, uuid: '1' }) + queue.enqueue({ type: 'assistant', uuid: '2' }) + await queue.flush() + + expect(sent).toHaveLength(1) + expect(sent[0]).toMatchObject({ type: 'assistant' }) + }) + + it('filters out isCompactSummary messages', async () => { + queue.enqueue({ type: 'assistant', isCompactSummary: true, uuid: '1' }) + queue.enqueue({ type: 'user', uuid: '2' }) + await queue.flush() + + expect(sent).toHaveLength(1) + expect(sent[0]).toMatchObject({ type: 'user' }) + }) +}) diff --git a/cli/src/claude/utils/OutgoingMessageQueue.ts b/cli/src/claude/utils/OutgoingMessageQueue.ts index 6d214c65..a3b1736e 100644 --- a/cli/src/claude/utils/OutgoingMessageQueue.ts +++ b/cli/src/claude/utils/OutgoingMessageQueue.ts @@ -121,7 +121,7 @@ export class OutgoingMessageQueue { // Send if not already sent if (!item.sent) { - if (item.logMessage.type !== 'system') { + if (item.logMessage.type !== 'system' && !item.logMessage.isMeta && !item.logMessage.isCompactSummary) { this.sendFunction(item.logMessage); } item.sent = true;