2026-01-03 13:39:59 +02:00
|
|
|
import type { Part } from '@opencode-ai/sdk/v2';
|
2025-12-07 19:32:53 +02:00
|
|
|
|
|
|
|
|
type PartWithText = Part & { text?: string; content?: string; value?: string };
|
|
|
|
|
|
2026-06-26 19:27:53 +03:00
|
|
|
const isValidPart = (part: unknown): part is Part => {
|
2026-05-01 15:08:44 +03:00
|
|
|
return Boolean(part && typeof part === 'object' && typeof (part as { type?: unknown }).type === 'string');
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
export const normalizeParts = (parts: Part[]): Part[] => {
|
|
|
|
|
return parts.filter(isValidPart);
|
|
|
|
|
};
|
|
|
|
|
|
2025-12-07 19:32:53 +02:00
|
|
|
export const extractTextContent = (part: Part): string => {
|
|
|
|
|
const partWithText = part as PartWithText;
|
|
|
|
|
const rawText = partWithText.text;
|
|
|
|
|
if (typeof rawText === 'string') {
|
|
|
|
|
return rawText;
|
|
|
|
|
}
|
|
|
|
|
return partWithText.content || partWithText.value || '';
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
export const isEmptyTextPart = (part: Part): boolean => {
|
|
|
|
|
if (part.type !== 'text') {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
const text = extractTextContent(part);
|
|
|
|
|
return !text || text.trim().length === 0;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
type PartWithSynthetic = Part & { synthetic?: boolean };
|
|
|
|
|
|
|
|
|
|
interface VisibleFilterOptions {
|
|
|
|
|
includeReasoning?: boolean;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export const filterVisibleParts = (parts: Part[], options: VisibleFilterOptions = {}): Part[] => {
|
|
|
|
|
const { includeReasoning = true } = options;
|
2026-05-01 15:08:44 +03:00
|
|
|
const validParts = normalizeParts(parts);
|
2025-12-07 19:32:53 +02:00
|
|
|
|
2025-12-28 02:38:43 +02:00
|
|
|
// Check if there are any non-synthetic parts
|
2026-05-01 15:08:44 +03:00
|
|
|
const hasNonSynthetic = validParts.some((part) => {
|
2025-12-28 02:38:43 +02:00
|
|
|
const partWithSynthetic = part as PartWithSynthetic;
|
|
|
|
|
return !partWithSynthetic.synthetic;
|
|
|
|
|
});
|
|
|
|
|
|
2026-05-01 15:08:44 +03:00
|
|
|
return validParts.filter((part) => {
|
2025-12-07 19:32:53 +02:00
|
|
|
const partWithSynthetic = part as PartWithSynthetic;
|
|
|
|
|
const isSynthetic = Boolean(partWithSynthetic.synthetic);
|
2026-01-25 15:52:02 +02:00
|
|
|
|
|
|
|
|
if (isSynthetic && part.type === 'text') {
|
|
|
|
|
const text = extractTextContent(part);
|
|
|
|
|
if (text.includes('<system-reminder>')) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-12-28 02:38:43 +02:00
|
|
|
// Only filter out synthetic parts if there are non-synthetic parts present
|
|
|
|
|
// Otherwise, show synthetic parts so the message is displayed
|
|
|
|
|
if (isSynthetic && hasNonSynthetic) {
|
2025-12-07 19:32:53 +02:00
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
if (!includeReasoning && part.type === 'reasoning') {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
const isPatchPart = part.type === 'patch';
|
|
|
|
|
|
|
|
|
|
return !isPatchPart;
|
|
|
|
|
});
|
|
|
|
|
};
|