Skip to content
59 changes: 59 additions & 0 deletions packages/web/src/hooks/__tests__/useSocket-background.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -496,6 +496,65 @@ describe('background thread socket handling', () => {
});
});

describe('F148: context briefing system_info', () => {
it('stores the briefing card message instead of a raw JSON system bubble', () => {
const now = Date.now();

simulateBackgroundMessage({
type: 'system_info',
catId: 'opus',
threadId: 'thread-bg',
content: JSON.stringify({
type: 'context_briefing',
messageId: 'briefing-msg-1',
storedMessage: {
id: 'briefing-msg-1',
content: '看到 13 条 · 省略 8 条 · 锚点 3 条 · 记忆 5 sessions · 证据 3 条',
origin: 'briefing',
timestamp: now,
extra: {
rich: {
v: 1,
blocks: [
{
id: 'briefing-1',
kind: 'card',
v: 1,
title: '看到 13 条 · 省略 8 条 · 锚点 3 条 · 记忆 5 sessions · 证据 3 条',
tone: 'info',
},
],
},
},
},
}),
timestamp: now,
});

const ts = useChatStore.getState().getThreadState('thread-bg');
expect(ts.messages).toEqual([
expect.objectContaining({
id: 'briefing-msg-1',
type: 'system',
content: '看到 13 条 · 省略 8 条 · 锚点 3 条 · 记忆 5 sessions · 证据 3 条',
origin: 'briefing',
extra: {
rich: {
v: 1,
blocks: [
expect.objectContaining({
id: 'briefing-1',
kind: 'card',
title: '看到 13 条 · 省略 8 条 · 锚点 3 条 · 记忆 5 sessions · 证据 3 条',
}),
],
},
},
}),
]);
});
});

describe('regression: background stream chunk merging', () => {
it('merges text chunks from same cat/thread into one assistant message', () => {
const now = Date.now();
Expand Down
15 changes: 15 additions & 0 deletions packages/web/src/hooks/useSocket-background-system-info.ts
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,21 @@ export function consumeBackgroundSystemInfo(
options.store.setThreadMessageUsage(msg.threadId, existingRef.id, parsed.usage);
}
consumed = true;
} else if (parsed?.type === 'context_briefing') {
const storedMessage = parsed.storedMessage as
| { id: string; content: string; origin: string; timestamp: number; extra?: Record<string, unknown> }
| undefined;
if (storedMessage?.id) {
options.store.addMessageToThread(msg.threadId, {
id: storedMessage.id,
type: 'system',
content: storedMessage.content ?? '',
origin: (storedMessage.origin as 'briefing') ?? 'briefing',
timestamp: storedMessage.timestamp ?? Date.now(),
...(storedMessage.extra ? { extra: storedMessage.extra } : {}),
});
}
consumed = true;
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Preserve fallback when briefing payload is incomplete

This branch marks context_briefing as consumed even when no message was actually inserted (for example, if storedMessage exists but id is missing/invalid). In that case the background thread drops the event entirely instead of falling back to the existing raw system bubble path, so users get no visible signal and debugging becomes harder. Only setting consumed = true after a successful addMessageToThread would keep the previous fail-open behavior.

Useful? React with 👍 / 👎.

} else if (parsed?.type === 'context_health') {
const targetCatId = parsed.catId ?? msg.catId;
options.store.setThreadCatInvocation(msg.threadId, targetCatId, {
Expand Down
Loading