Compare commits

...

2 Commits

Author SHA1 Message Date
AndyMik90 419bd3f2da fix: improve toSafeString with array handling and apply to all list renders
- Simplify toSafeString signature from redundant union to plain `unknown`
- Add explicit Array.isArray guard before the object branch so arrays are
  joined with ', ' instead of producing '0: val, 1: val' via Object.entries
- Verified all existing .map() render sites: what_worked, what_failed,
  recommendations, and discoveries.recommendations already use toSafeString;
  subtasks_completed and changed_files are typed as string[] so no change needed

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-22 12:20:59 +01:00
AndyMik90 d27b28abc6 fix: resolve React Error #31 when expanding memories in Context (fixes #1879)
Memory items in what_worked, what_failed, and recommendations arrays could
be objects (e.g. {category, recommendation}) instead of strings when stored
by Graphiti or AI agents that deviate from the expected string format.

Add toSafeString() helper that safely converts any value to a renderable
string, with specific handling for the {category, recommendation} object
shape that caused the error. Update ParsedSessionInsight types to reflect
that these arrays can contain mixed string/object items.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-21 21:45:33 +01:00
@@ -26,9 +26,9 @@ interface ParsedSessionInsight {
spec_id?: string;
session_number?: number;
subtasks_completed?: string[];
what_worked?: string[];
what_failed?: string[];
recommendations_for_next_session?: string[];
what_worked?: Array<string | Record<string, unknown>>;
what_failed?: Array<string | Record<string, unknown>>;
recommendations_for_next_session?: Array<string | Record<string, unknown>>;
discoveries?: {
file_insights?: Array<{ path?: string; purpose?: string; changes_made?: string }>;
patterns_discovered?: Array<{ pattern?: string; applies_to?: string } | string>;
@@ -39,11 +39,35 @@ interface ParsedSessionInsight {
why_it_worked?: string;
why_it_failed?: string;
};
recommendations?: string[];
recommendations?: Array<string | Record<string, unknown>>;
changed_files?: string[];
};
}
function toSafeString(item: unknown): string {
if (typeof item === 'string') return item;
if (item === null || item === undefined) return '';
if (Array.isArray(item)) return item.map(i => toSafeString(i)).join(', ');
if (typeof item === 'object') {
const obj = item as Record<string, unknown>;
// Handle {category, recommendation} object - the known cause of React Error #31
if (typeof obj.category === 'string' && typeof obj.recommendation === 'string') {
return `[${obj.category}] ${obj.recommendation}`;
}
// Try other common single-field patterns
if (typeof obj.recommendation === 'string') return obj.recommendation;
if (typeof obj.text === 'string') return obj.text;
if (typeof obj.description === 'string') return obj.description;
if (typeof obj.message === 'string') return obj.message;
// Fallback: combine all string values from the object
const parts = Object.entries(obj)
.filter(([, v]) => typeof v === 'string')
.map(([k, v]) => `${k}: ${v}`);
return parts.length > 0 ? parts.join(', ') : JSON.stringify(item);
}
return String(item);
}
function parseMemoryContent(content: string): ParsedSessionInsight | null {
try {
return JSON.parse(content);
@@ -207,7 +231,7 @@ export function MemoryCard({ memory }: MemoryCardProps) {
<SectionHeader icon={CheckCircle2} title="What Worked" count={parsed.what_worked.length} />
<ul className="space-y-0.5">
{parsed.what_worked.map((item, idx) => (
<ListItem key={idx} variant="success">{item}</ListItem>
<ListItem key={idx} variant="success">{toSafeString(item)}</ListItem>
))}
</ul>
</div>
@@ -219,7 +243,7 @@ export function MemoryCard({ memory }: MemoryCardProps) {
<SectionHeader icon={XCircle} title="What Failed" count={parsed.what_failed.length} />
<ul className="space-y-0.5">
{parsed.what_failed.map((item, idx) => (
<ListItem key={idx} variant="error">{item}</ListItem>
<ListItem key={idx} variant="error">{toSafeString(item)}</ListItem>
))}
</ul>
</div>
@@ -261,10 +285,10 @@ export function MemoryCard({ memory }: MemoryCardProps) {
/>
<ul className="space-y-0.5">
{parsed.recommendations_for_next_session?.map((item, idx) => (
<ListItem key={`rec-${idx}`}>{item}</ListItem>
<ListItem key={`rec-${idx}`}>{toSafeString(item)}</ListItem>
))}
{parsed.discoveries?.recommendations?.map((item, idx) => (
<ListItem key={`disc-rec-${idx}`}>{item}</ListItem>
<ListItem key={`disc-rec-${idx}`}>{toSafeString(item)}</ListItem>
))}
</ul>
</div>