Files
openchamber/packages/ui/src/components/chat/message/hiddenUserMessage.ts
T
Bohdan Triapitsyn 8e718ee58c feat: merge hidden-user turns and move message metadata to turn footer
- Remove assistant message headers; show provider icon, model, agent,
  thinking variant, duration and time in the turn footer (metadata left,
  hover-revealed actions right)
- Merge turns started by hidden user messages (subagent nudges) into the
  previous turn so Activity, footer and spacing stay continuous
- Treat compaction summary text (info.summary) as justification activity
  in sorted mode and skip it when picking the turn summary
- Interleave activity segments with standalone tool rows so Agent Task
  sits chronologically between activity sections
2026-07-19 22:58:22 +03:00

35 lines
1.4 KiB
TypeScript

import type { Message, Part } from '@opencode-ai/sdk/v2';
import { deriveMessageRole } from './messageRole';
import { filterVisibleParts, normalizeParts } from './partUtils';
import { normalizeUserDisplayParts } from './normalizeUserDisplayParts';
/**
* A user message is hidden when none of its parts survive display
* normalization (e.g. synthetic subagent-completion nudges). Turns separated
* only by such messages should render as one continuous flow.
*/
// Streaming recomputes turn projections often; cache by parts reference so
// unchanged messages resolve without re-running display normalization.
const hiddenByPartsPlanMode = new WeakMap<Part[], boolean>();
const hiddenByPartsNoPlanMode = new WeakMap<Part[], boolean>();
export const isHiddenUserMessage = (
entry: { info: Message; parts: Part[] } | null | undefined,
options: { planModeEnabled: boolean }
): boolean => {
if (!entry) return false;
if (!deriveMessageRole(entry.info).isUser) return false;
const cache = options.planModeEnabled ? hiddenByPartsPlanMode : hiddenByPartsNoPlanMode;
const cached = cache.get(entry.parts);
if (cached !== undefined) {
return cached;
}
const parts = normalizeUserDisplayParts(normalizeParts(entry.parts), { planModeEnabled: options.planModeEnabled });
const hidden = filterVisibleParts(parts, { includeReasoning: true }).length === 0;
cache.set(entry.parts, hidden);
return hidden;
};