@elizaos/client 1.5.5-alpha.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (209) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +350 -0
  3. package/dist/assets/empty-module-CLMscLYw.js +1 -0
  4. package/dist/assets/main-BBZ_3lkn.css +5999 -0
  5. package/dist/assets/main-C5zNUkXH.js +7 -0
  6. package/dist/assets/main-Dz64ENQg.js +614 -0
  7. package/dist/assets/react-vendor-DM5m98rr.js +545 -0
  8. package/dist/assets/ui-vendor-BQCqNqg0.js +1 -0
  9. package/dist/elizaos-avatar.png +0 -0
  10. package/dist/elizaos-icon.png +0 -0
  11. package/dist/elizaos-logo-light.png +0 -0
  12. package/dist/elizaos.webp +0 -0
  13. package/dist/favicon.ico +0 -0
  14. package/dist/images/agents/agent1.png +0 -0
  15. package/dist/images/agents/agent2.png +0 -0
  16. package/dist/images/agents/agent3.png +0 -0
  17. package/dist/images/agents/agent4.png +0 -0
  18. package/dist/images/agents/agent5.png +0 -0
  19. package/dist/index.html +14 -0
  20. package/index.html +24 -0
  21. package/package.json +159 -0
  22. package/postcss.config.js +3 -0
  23. package/public/elizaos-avatar.png +0 -0
  24. package/public/elizaos-icon.png +0 -0
  25. package/public/elizaos-logo-light.png +0 -0
  26. package/public/elizaos.webp +0 -0
  27. package/public/favicon.ico +0 -0
  28. package/public/images/agents/agent1.png +0 -0
  29. package/public/images/agents/agent2.png +0 -0
  30. package/public/images/agents/agent3.png +0 -0
  31. package/public/images/agents/agent4.png +0 -0
  32. package/public/images/agents/agent5.png +0 -0
  33. package/src/App.tsx +222 -0
  34. package/src/components/AgentDetailsPanel.tsx +147 -0
  35. package/src/components/ChatInputArea.tsx +196 -0
  36. package/src/components/ChatMessageListComponent.tsx +139 -0
  37. package/src/components/actionTool.tsx +186 -0
  38. package/src/components/add-agent-card.tsx +77 -0
  39. package/src/components/agent-action-viewer.tsx +816 -0
  40. package/src/components/agent-avatar-stack.tsx +121 -0
  41. package/src/components/agent-card.cy.tsx +259 -0
  42. package/src/components/agent-card.tsx +177 -0
  43. package/src/components/agent-creator.tsx +142 -0
  44. package/src/components/agent-log-viewer.tsx +645 -0
  45. package/src/components/agent-memory-edit-overlay.tsx +461 -0
  46. package/src/components/agent-memory-viewer.tsx +504 -0
  47. package/src/components/agent-settings.tsx +270 -0
  48. package/src/components/agent-sidebar.tsx +178 -0
  49. package/src/components/api-key-dialog.tsx +113 -0
  50. package/src/components/app-sidebar.tsx +685 -0
  51. package/src/components/array-input.tsx +116 -0
  52. package/src/components/audio-recorder.tsx +292 -0
  53. package/src/components/avatar-panel.tsx +141 -0
  54. package/src/components/character-form.tsx +1138 -0
  55. package/src/components/chat.tsx +1813 -0
  56. package/src/components/combobox.tsx +187 -0
  57. package/src/components/confirmation-dialog.tsx +59 -0
  58. package/src/components/connection-error-banner.tsx +101 -0
  59. package/src/components/connection-status.cy.tsx +73 -0
  60. package/src/components/connection-status.tsx +155 -0
  61. package/src/components/copy-button.tsx +35 -0
  62. package/src/components/delete-button.tsx +24 -0
  63. package/src/components/env-settings.tsx +261 -0
  64. package/src/components/group-card.tsx +160 -0
  65. package/src/components/group-panel.tsx +543 -0
  66. package/src/components/input-copy.tsx +21 -0
  67. package/src/components/logs-page.tsx +41 -0
  68. package/src/components/media-content.tsx +385 -0
  69. package/src/components/memory-graph.tsx +170 -0
  70. package/src/components/missing-secrets-dialog.tsx +72 -0
  71. package/src/components/onboarding-tour.tsx +247 -0
  72. package/src/components/page-title.tsx +8 -0
  73. package/src/components/plugins-panel.tsx +383 -0
  74. package/src/components/profile-card.tsx +66 -0
  75. package/src/components/profile-overlay.tsx +283 -0
  76. package/src/components/retry-button.tsx +28 -0
  77. package/src/components/secret-panel.tsx +1505 -0
  78. package/src/components/server-management.tsx +264 -0
  79. package/src/components/split-button.tsx +148 -0
  80. package/src/components/stop-agent-button.tsx +99 -0
  81. package/src/components/ui/alert-dialog.cy.tsx +333 -0
  82. package/src/components/ui/alert-dialog.tsx +115 -0
  83. package/src/components/ui/alert.tsx +49 -0
  84. package/src/components/ui/avatar.cy.tsx +180 -0
  85. package/src/components/ui/avatar.tsx +57 -0
  86. package/src/components/ui/badge.cy.tsx +146 -0
  87. package/src/components/ui/badge.tsx +43 -0
  88. package/src/components/ui/button.cy.tsx +177 -0
  89. package/src/components/ui/button.tsx +56 -0
  90. package/src/components/ui/card.cy.tsx +160 -0
  91. package/src/components/ui/card.tsx +73 -0
  92. package/src/components/ui/chat/animated-markdown.tsx +59 -0
  93. package/src/components/ui/chat/chat-bubble.tsx +178 -0
  94. package/src/components/ui/chat/chat-container.tsx +51 -0
  95. package/src/components/ui/chat/chat-input.cy.tsx +169 -0
  96. package/src/components/ui/chat/chat-input.tsx +47 -0
  97. package/src/components/ui/chat/chat-message-list.tsx +61 -0
  98. package/src/components/ui/chat/chat-tts-button.tsx +199 -0
  99. package/src/components/ui/chat/code-block.tsx +79 -0
  100. package/src/components/ui/chat/expandable-chat.tsx +131 -0
  101. package/src/components/ui/chat/hooks/useAutoScroll.ts +86 -0
  102. package/src/components/ui/chat/markdown.tsx +209 -0
  103. package/src/components/ui/chat/message-loading.tsx +48 -0
  104. package/src/components/ui/checkbox.cy.tsx +170 -0
  105. package/src/components/ui/checkbox.tsx +30 -0
  106. package/src/components/ui/collapsible.cy.tsx +283 -0
  107. package/src/components/ui/collapsible.tsx +9 -0
  108. package/src/components/ui/command.cy.tsx +313 -0
  109. package/src/components/ui/command.tsx +143 -0
  110. package/src/components/ui/dialog.cy.tsx +279 -0
  111. package/src/components/ui/dialog.tsx +104 -0
  112. package/src/components/ui/dropdown-menu.cy.tsx +273 -0
  113. package/src/components/ui/dropdown-menu.tsx +281 -0
  114. package/src/components/ui/input.cy.tsx +82 -0
  115. package/src/components/ui/input.tsx +27 -0
  116. package/src/components/ui/label.cy.tsx +157 -0
  117. package/src/components/ui/label.tsx +19 -0
  118. package/src/components/ui/resizable.tsx +42 -0
  119. package/src/components/ui/scroll-area.cy.tsx +242 -0
  120. package/src/components/ui/scroll-area.tsx +46 -0
  121. package/src/components/ui/select.cy.tsx +277 -0
  122. package/src/components/ui/select.tsx +155 -0
  123. package/src/components/ui/separator.cy.tsx +145 -0
  124. package/src/components/ui/separator.tsx +29 -0
  125. package/src/components/ui/sheet.cy.tsx +324 -0
  126. package/src/components/ui/sheet.tsx +119 -0
  127. package/src/components/ui/sidebar.tsx +734 -0
  128. package/src/components/ui/skeleton.cy.tsx +149 -0
  129. package/src/components/ui/skeleton.tsx +17 -0
  130. package/src/components/ui/split-button.cy.tsx +274 -0
  131. package/src/components/ui/split-button.tsx +112 -0
  132. package/src/components/ui/switch.tsx +28 -0
  133. package/src/components/ui/tabs.cy.tsx +271 -0
  134. package/src/components/ui/tabs.tsx +53 -0
  135. package/src/components/ui/textarea.cy.tsx +136 -0
  136. package/src/components/ui/textarea.tsx +26 -0
  137. package/src/components/ui/toast.cy.tsx +209 -0
  138. package/src/components/ui/toast.tsx +126 -0
  139. package/src/components/ui/toaster.tsx +29 -0
  140. package/src/components/ui/tooltip.cy.tsx +244 -0
  141. package/src/components/ui/tooltip.tsx +30 -0
  142. package/src/config/agent-templates.ts +349 -0
  143. package/src/config/voice-models.ts +181 -0
  144. package/src/constants.ts +23 -0
  145. package/src/context/AuthContext.tsx +44 -0
  146. package/src/context/ConnectionContext.tsx +194 -0
  147. package/src/entry.tsx +9 -0
  148. package/src/hooks/__tests__/use-agent-tab-state.test.ts +137 -0
  149. package/src/hooks/__tests__/use-agent-update.test.tsx +250 -0
  150. package/src/hooks/__tests__/use-character-convert.test.ts +102 -0
  151. package/src/hooks/__tests__/use-panel-width-state.test.ts +243 -0
  152. package/src/hooks/__tests__/use-sidebar-state.test.ts +117 -0
  153. package/src/hooks/use-agent-management.ts +130 -0
  154. package/src/hooks/use-agent-tab-state.ts +74 -0
  155. package/src/hooks/use-agent-update.ts +469 -0
  156. package/src/hooks/use-character-convert.ts +138 -0
  157. package/src/hooks/use-confirmation.ts +55 -0
  158. package/src/hooks/use-delete-agent.ts +123 -0
  159. package/src/hooks/use-dm-channels.ts +198 -0
  160. package/src/hooks/use-elevenlabs-voices.ts +83 -0
  161. package/src/hooks/use-file-upload.ts +224 -0
  162. package/src/hooks/use-mobile.tsx +19 -0
  163. package/src/hooks/use-onboarding.tsx +49 -0
  164. package/src/hooks/use-panel-width-state.ts +147 -0
  165. package/src/hooks/use-partial-update.ts +288 -0
  166. package/src/hooks/use-plugin-details.ts +462 -0
  167. package/src/hooks/use-plugins.ts +119 -0
  168. package/src/hooks/use-query-hooks.ts +1263 -0
  169. package/src/hooks/use-server-agents.ts +62 -0
  170. package/src/hooks/use-server-version.tsx +47 -0
  171. package/src/hooks/use-sidebar-state.ts +50 -0
  172. package/src/hooks/use-socket-chat.ts +264 -0
  173. package/src/hooks/use-toast.ts +260 -0
  174. package/src/hooks/use-version.tsx +64 -0
  175. package/src/index.css +146 -0
  176. package/src/lib/api-client-config.ts +53 -0
  177. package/src/lib/api-type-mappers.ts +196 -0
  178. package/src/lib/export-utils.ts +123 -0
  179. package/src/lib/logger.ts +19 -0
  180. package/src/lib/media-utils.ts +170 -0
  181. package/src/lib/pca.test.ts +17 -0
  182. package/src/lib/pca.ts +52 -0
  183. package/src/lib/socketio-manager.ts +664 -0
  184. package/src/lib/utils.ts +168 -0
  185. package/src/main.tsx +16 -0
  186. package/src/mocks/empty-module.ts +12 -0
  187. package/src/mocks/node-module.ts +57 -0
  188. package/src/polyfills.ts +37 -0
  189. package/src/routes/agent-detail.tsx +30 -0
  190. package/src/routes/agent-list.tsx +27 -0
  191. package/src/routes/agent-settings.tsx +48 -0
  192. package/src/routes/character-detail.tsx +52 -0
  193. package/src/routes/character-form.tsx +79 -0
  194. package/src/routes/character-list.tsx +38 -0
  195. package/src/routes/chat.tsx +128 -0
  196. package/src/routes/createAgent.tsx +13 -0
  197. package/src/routes/group-new.tsx +50 -0
  198. package/src/routes/group.tsx +29 -0
  199. package/src/routes/home.tsx +218 -0
  200. package/src/routes/not-found.tsx +71 -0
  201. package/src/test/setup.ts +154 -0
  202. package/src/types/crypto-browserify.d.ts +4 -0
  203. package/src/types/index.ts +13 -0
  204. package/src/types/rooms.ts +8 -0
  205. package/src/types.ts +84 -0
  206. package/src/vite-env.d.ts +40 -0
  207. package/tailwind.config.ts +90 -0
  208. package/tsconfig.json +10 -0
  209. package/vite.config.ts +102 -0
@@ -0,0 +1,504 @@
1
+ import type { Memory, UUID } from '@elizaos/core';
2
+ import { Database, LoaderIcon, Pencil, Search, Brain, User, Bot, Clock, Copy } from 'lucide-react';
3
+ import { useCallback, useEffect, useRef, useState } from 'react';
4
+ import { useAgentMemories, useAgents } from '@/hooks/use-query-hooks';
5
+ import { Button } from '@/components/ui/button';
6
+ import { Input } from '@/components/ui/input';
7
+ import {
8
+ Select,
9
+ SelectContent,
10
+ SelectItem,
11
+ SelectTrigger,
12
+ SelectValue,
13
+ } from '@/components/ui/select';
14
+ import { Separator } from '@/components/ui/separator';
15
+ import MemoryEditOverlay from './agent-memory-edit-overlay';
16
+
17
+ // Number of items to load per batch
18
+ const ITEMS_PER_PAGE = 15;
19
+
20
+ interface MemoryContent {
21
+ thought?: boolean | string;
22
+ channelType?: string;
23
+ source?: string;
24
+ text?: string;
25
+ metadata?: {
26
+ fileType?: string;
27
+ title?: string;
28
+ filename?: string;
29
+ path?: string;
30
+ description?: string;
31
+ };
32
+ }
33
+
34
+ interface ChatMemoryContent extends MemoryContent {
35
+ text?: string;
36
+ actions?: string[];
37
+ thought?: boolean | string;
38
+ inReplyTo?: string;
39
+ providers?: string[];
40
+ channelType?: string;
41
+ }
42
+
43
+ enum MemoryType {
44
+ all = 'all',
45
+ currentChat = 'currentChat',
46
+ messagesReceived = 'messagesReceived',
47
+ messagesSent = 'messagesSent',
48
+ facts = 'facts',
49
+ }
50
+
51
+ interface AgentMemoryViewerProps {
52
+ agentId: UUID;
53
+ agentName: string;
54
+ channelId?: UUID; // Renamed from roomId to channelId for clarity
55
+ }
56
+
57
+ export function AgentMemoryViewer({ agentId, agentName, channelId }: AgentMemoryViewerProps) {
58
+ const [selectedType, setSelectedType] = useState<MemoryType>(MemoryType.all);
59
+ const [searchQuery, setSearchQuery] = useState('');
60
+ const [editingMemory, setEditingMemory] = useState<Memory | null>(null);
61
+ const [visibleItems, setVisibleItems] = useState(ITEMS_PER_PAGE);
62
+ const [loadingMore, setLoadingMore] = useState(false);
63
+ const scrollContainerRef = useRef<HTMLDivElement>(null);
64
+
65
+ // Get all agents to look up names by ID
66
+ const { data: agentsData } = useAgents();
67
+
68
+ // Fetch from appropriate table(s) based on selected type
69
+ const messagesTableName = selectedType === MemoryType.facts ? undefined : 'messages';
70
+ const factsTableName =
71
+ selectedType === MemoryType.facts || selectedType === MemoryType.all ? 'facts' : undefined;
72
+
73
+ // Only pass channelId when "Current Chat" is selected
74
+ const channelIdToUse =
75
+ selectedType === MemoryType.currentChat && channelId ? channelId : undefined;
76
+
77
+ const {
78
+ data: messagesData = [],
79
+ isLoading: isLoadingMessages,
80
+ error: messagesError,
81
+ } = useAgentMemories(agentId, messagesTableName, channelIdToUse);
82
+ const {
83
+ data: factsData = [],
84
+ isLoading: isLoadingFacts,
85
+ error: factsError,
86
+ } = useAgentMemories(agentId, factsTableName, channelIdToUse);
87
+
88
+ // Combine memories from both sources
89
+ const memories = [...messagesData, ...factsData];
90
+ const isLoading = isLoadingMessages || isLoadingFacts;
91
+ const error = messagesError || factsError;
92
+
93
+ // Filter and search memories
94
+ const filteredMemories = memories.filter((memory: Memory) => {
95
+ // Type filter
96
+ if (selectedType !== MemoryType.all && selectedType !== MemoryType.currentChat) {
97
+ // Facts are handled by table selection, so if we're on facts table, show all
98
+ if (selectedType === MemoryType.facts) {
99
+ return true; // Already filtered by table
100
+ }
101
+
102
+ // For messages table, filter by type
103
+ if (selectedType === MemoryType.messagesSent && memory.entityId !== memory.agentId)
104
+ return false;
105
+ if (selectedType === MemoryType.messagesReceived && memory.entityId === memory.agentId)
106
+ return false;
107
+ }
108
+
109
+ // Search filter
110
+ if (searchQuery.trim()) {
111
+ const query = searchQuery.toLowerCase();
112
+ const content = memory.content as ChatMemoryContent;
113
+ const searchableText = [content?.text, content?.thought, memory.id, memory.metadata]
114
+ .filter(Boolean)
115
+ .join(' ')
116
+ .toLowerCase();
117
+
118
+ return searchableText.includes(query);
119
+ }
120
+
121
+ return true;
122
+ });
123
+
124
+ // Handle scroll for infinite loading
125
+ const handleScroll = useCallback(() => {
126
+ if (!scrollContainerRef.current || loadingMore || visibleItems >= filteredMemories.length) {
127
+ return;
128
+ }
129
+
130
+ const { scrollTop, scrollHeight, clientHeight } = scrollContainerRef.current;
131
+ const scrolledToBottom = scrollTop + clientHeight >= scrollHeight - 200;
132
+
133
+ if (scrolledToBottom) {
134
+ setLoadingMore(true);
135
+ setTimeout(() => {
136
+ setVisibleItems((prev) => Math.min(prev + ITEMS_PER_PAGE, filteredMemories.length));
137
+ setLoadingMore(false);
138
+ }, 500);
139
+ }
140
+ }, [loadingMore, visibleItems, filteredMemories.length]);
141
+
142
+ // Reset visible items when filter changes
143
+ useEffect(() => {
144
+ setVisibleItems(ITEMS_PER_PAGE);
145
+ }, [selectedType, searchQuery]);
146
+
147
+ // Set up scroll listener
148
+ useEffect(() => {
149
+ const scrollContainer = scrollContainerRef.current;
150
+ if (scrollContainer) {
151
+ scrollContainer.addEventListener('scroll', handleScroll);
152
+ return () => scrollContainer.removeEventListener('scroll', handleScroll);
153
+ }
154
+ }, [handleScroll]);
155
+
156
+ const formatDate = (timestamp: number) => {
157
+ const date = new Date(timestamp);
158
+ const now = new Date();
159
+ const diffInHours = (now.getTime() - date.getTime()) / (1000 * 60 * 60);
160
+
161
+ if (diffInHours < 24) {
162
+ return date.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' });
163
+ } else if (diffInHours < 168) {
164
+ // 7 days
165
+ return date.toLocaleDateString([], { weekday: 'short', hour: '2-digit', minute: '2-digit' });
166
+ } else {
167
+ return date.toLocaleDateString([], {
168
+ month: 'short',
169
+ day: 'numeric',
170
+ hour: '2-digit',
171
+ minute: '2-digit',
172
+ });
173
+ }
174
+ };
175
+
176
+ const getMemoryIcon = (memory: Memory, content: ChatMemoryContent) => {
177
+ if (content?.thought) return Brain;
178
+ if (memory.entityId === memory.agentId) return Bot;
179
+ return User;
180
+ };
181
+
182
+ const copyToClipboard = async (text: string) => {
183
+ try {
184
+ await navigator.clipboard.writeText(text);
185
+ } catch (error) {
186
+ console.error('Failed to copy:', error);
187
+ }
188
+ };
189
+
190
+ // Group messages by date
191
+ const groupMessagesByDate = (messages: Memory[]) => {
192
+ const groups: Record<string, Memory[]> = {};
193
+
194
+ for (const memory of messages) {
195
+ const date = new Date(memory.createdAt || 0);
196
+ const dateKey = date.toLocaleDateString();
197
+
198
+ if (!groups[dateKey]) {
199
+ groups[dateKey] = [];
200
+ }
201
+ groups[dateKey].push(memory);
202
+ }
203
+
204
+ return groups;
205
+ };
206
+
207
+ const visibleMemories = filteredMemories.slice(0, visibleItems);
208
+ const hasMoreToLoad = visibleItems < filteredMemories.length;
209
+ const messageGroups = groupMessagesByDate(visibleMemories);
210
+
211
+ // Loading state
212
+ if (isLoading && memories.length === 0) {
213
+ return (
214
+ <div className="flex flex-col h-[calc(100vh-100px)] min-h-[400px] w-full">
215
+ <div className="flex items-center justify-center flex-1">
216
+ <div className="flex flex-col items-center gap-4">
217
+ <LoaderIcon className="h-8 w-8 animate-spin text-muted-foreground" />
218
+ <div className="text-center">
219
+ <h3 className="font-medium">Loading Memories</h3>
220
+ <p className="text-sm text-muted-foreground">
221
+ Fetching agent conversation history...
222
+ </p>
223
+ </div>
224
+ </div>
225
+ </div>
226
+ </div>
227
+ );
228
+ }
229
+
230
+ // Error state
231
+ if (error) {
232
+ return (
233
+ <div className="flex flex-col h-[calc(100vh-100px)] min-h-[400px] w-full">
234
+ <div className="flex items-center justify-center flex-1">
235
+ <div className="text-center">
236
+ <Database className="h-12 w-12 text-destructive mx-auto mb-4" />
237
+ <h3 className="font-medium text-destructive">Failed to Load Memories</h3>
238
+ <p className="text-sm text-muted-foreground">
239
+ There was an error loading the agent memories.
240
+ </p>
241
+ </div>
242
+ </div>
243
+ </div>
244
+ );
245
+ }
246
+
247
+ // Empty state
248
+ const EmptyState = () => (
249
+ <div className="flex flex-col items-center justify-center flex-1 text-center p-8">
250
+ <Database className="h-16 w-16 text-muted-foreground/30 mb-4" />
251
+ <h3 className="text-lg font-medium mb-2">No Memories Found</h3>
252
+ <p className="text-muted-foreground max-w-md mb-4">
253
+ {searchQuery
254
+ ? `No memories match "${searchQuery}". Try adjusting your search or filter.`
255
+ : "This agent hasn't created any memories yet. Memories will appear here as the agent interacts."}
256
+ </p>
257
+ {searchQuery && (
258
+ <Button variant="outline" onClick={() => setSearchQuery('')}>
259
+ Clear Search
260
+ </Button>
261
+ )}
262
+ </div>
263
+ );
264
+
265
+ // Memory card component
266
+ const MemoryCard = ({ memory }: { memory: Memory }) => {
267
+ const content = memory.content as ChatMemoryContent;
268
+ const IconComponent = getMemoryIcon(memory, content);
269
+ const isAgent = memory.entityId === memory.agentId;
270
+
271
+ // Look up entity name from agents data or fallback to metadata
272
+ const getEntityName = () => {
273
+ if (isAgent) {
274
+ // For agents, try to find the agent name by ID
275
+ const agent = agentsData?.data?.agents?.find((a) => a.id === memory.entityId);
276
+ return agent?.name || agentName;
277
+ } else {
278
+ // For users, use raw metadata or fallback
279
+ return (memory.metadata as any)?.raw?.senderName || memory.metadata?.source || 'User';
280
+ }
281
+ };
282
+
283
+ const entityName = getEntityName();
284
+
285
+ return (
286
+ <div className="border rounded-lg p-4 bg-card hover:bg-accent/5 transition-colors group">
287
+ {/* Header */}
288
+ <div className="flex items-start justify-between mb-3">
289
+ <div className="flex items-center gap-3">
290
+ <div className="p-2 rounded-md bg-muted">
291
+ <IconComponent className="h-4 w-4" />
292
+ </div>
293
+ <div>
294
+ <div className="flex items-center gap-2">
295
+ <span className="font-medium text-sm">{entityName}</span>
296
+ <span className="text-xs px-2 py-0.5 rounded bg-muted text-muted-foreground">
297
+ {content?.thought ? 'Thought' : isAgent ? 'Agent' : 'User'}
298
+ </span>
299
+ </div>
300
+ <div className="flex items-center gap-2 text-xs text-muted-foreground mt-1">
301
+ <Clock className="h-3 w-3" />
302
+ <span>{formatDate(memory.createdAt || 0)}</span>
303
+ {memory.id && (
304
+ <>
305
+ <span>•</span>
306
+ <code className="text-[10px] bg-muted px-1 rounded">{memory.id.slice(-8)}</code>
307
+ </>
308
+ )}
309
+ </div>
310
+ </div>
311
+ </div>
312
+
313
+ {/* Action buttons */}
314
+ <div className="flex items-center gap-1 opacity-0 group-hover:opacity-100 transition-opacity">
315
+ {content?.text && (
316
+ <Button
317
+ variant="ghost"
318
+ size="sm"
319
+ onClick={() => copyToClipboard(content.text || '')}
320
+ className="h-8 w-8 p-0"
321
+ title="Copy text"
322
+ >
323
+ <Copy className="h-3 w-3" />
324
+ </Button>
325
+ )}
326
+ {memory.id && (
327
+ <Button
328
+ variant="ghost"
329
+ size="sm"
330
+ onClick={() => setEditingMemory(memory)}
331
+ className="h-8 w-8 p-0"
332
+ title="Edit memory"
333
+ >
334
+ <Pencil className="h-3 w-3" />
335
+ </Button>
336
+ )}
337
+ </div>
338
+ </div>
339
+
340
+ {/* Content */}
341
+ <div className="space-y-3">
342
+ {/* Main text */}
343
+ {content?.text && (
344
+ <div className="bg-muted/50 rounded-md p-3">
345
+ <p className="text-sm leading-relaxed whitespace-pre-wrap">{content.text}</p>
346
+ </div>
347
+ )}
348
+
349
+ {/* Thought process */}
350
+ {content?.thought && (
351
+ <div className="border-l-2 border-muted pl-3">
352
+ <div className="flex items-center gap-2 mb-1">
353
+ <Brain className="h-3 w-3 text-muted-foreground" />
354
+ <span className="text-xs font-medium text-muted-foreground">Thought Process</span>
355
+ </div>
356
+ <p className="text-xs text-muted-foreground italic leading-relaxed">
357
+ {String(content.thought)}
358
+ </p>
359
+ </div>
360
+ )}
361
+
362
+ {/* Tags */}
363
+ {(content?.actions || content?.providers || content?.source) && (
364
+ <div className="flex flex-wrap gap-1">
365
+ {content.actions?.map((action) => (
366
+ <span
367
+ key={action}
368
+ className="text-xs px-2 py-1 bg-muted rounded text-muted-foreground"
369
+ title="Action"
370
+ >
371
+ {action}
372
+ </span>
373
+ ))}
374
+ {content.providers?.map((provider) => (
375
+ <span
376
+ key={provider}
377
+ className="text-xs px-2 py-1 bg-muted rounded text-muted-foreground"
378
+ title="Provider"
379
+ >
380
+ {provider}
381
+ </span>
382
+ ))}
383
+ {content.source && (
384
+ <span className="text-xs px-2 py-1 bg-muted rounded text-muted-foreground">
385
+ {content.source}
386
+ </span>
387
+ )}
388
+ </div>
389
+ )}
390
+
391
+ {/* Metadata (simplified) */}
392
+ {memory.metadata && Object.keys(memory.metadata).length > 0 && (
393
+ <details className="text-xs">
394
+ <summary className="cursor-pointer text-muted-foreground hover:text-foreground">
395
+ View metadata
396
+ </summary>
397
+ <div className="mt-2 p-2 bg-muted/30 rounded text-[10px] font-mono overflow-x-auto">
398
+ <pre>{JSON.stringify(memory.metadata, null, 2)}</pre>
399
+ </div>
400
+ </details>
401
+ )}
402
+ </div>
403
+ </div>
404
+ );
405
+ };
406
+
407
+ return (
408
+ <div className="flex flex-col h-[calc(100vh-100px)] min-h-[400px] w-full">
409
+ {/* Header */}
410
+ <div className="flex justify-between items-center mb-4 px-4 pt-4 flex-none border-b pb-3">
411
+ <div className="flex items-center gap-2">
412
+ <h3 className="text-lg font-medium">Memories</h3>
413
+ {!isLoading && (
414
+ <span className="ml-2 text-xs px-2 py-1 rounded bg-muted text-muted-foreground">
415
+ {filteredMemories.length}
416
+ </span>
417
+ )}
418
+ </div>
419
+ <div className="flex items-center gap-2">
420
+ {/* Search */}
421
+ <div className="relative">
422
+ <Search className="absolute left-3 top-1/2 transform -translate-y-1/2 h-4 w-4 text-muted-foreground" />
423
+ <Input
424
+ placeholder="Search memories..."
425
+ value={searchQuery}
426
+ onChange={(e) => setSearchQuery(e.target.value)}
427
+ className="pl-10 w-full min-w-0 max-w-64"
428
+ />
429
+ </div>
430
+ {/* Type Filter */}
431
+ <Select
432
+ value={selectedType}
433
+ onValueChange={(value) => setSelectedType(value as MemoryType)}
434
+ >
435
+ <SelectTrigger className="w-40">
436
+ <SelectValue placeholder="Filter memories" />
437
+ </SelectTrigger>
438
+ <SelectContent>
439
+ <SelectItem value={MemoryType.all}>All Messages</SelectItem>
440
+ {channelId && <SelectItem value={MemoryType.currentChat}>Current Chat</SelectItem>}
441
+ <SelectItem value={MemoryType.messagesSent}>Agent Messages</SelectItem>
442
+ <SelectItem value={MemoryType.messagesReceived}>User Messages</SelectItem>
443
+ <SelectItem value={MemoryType.facts}>Facts</SelectItem>
444
+ </SelectContent>
445
+ </Select>
446
+ </div>
447
+ </div>
448
+
449
+ {/* Content */}
450
+ <div ref={scrollContainerRef} className="flex-1 overflow-y-auto px-4">
451
+ {filteredMemories.length === 0 ? (
452
+ <EmptyState />
453
+ ) : (
454
+ <div className="space-y-4">
455
+ {Object.entries(messageGroups).map(([date, messages]) => (
456
+ <div key={date} className="space-y-3">
457
+ <div className="flex items-center gap-3 py-2">
458
+ <Separator className="flex-1" />
459
+ <span className="text-sm font-medium text-muted-foreground px-2">{date}</span>
460
+ <Separator className="flex-1" />
461
+ </div>
462
+ <div className="space-y-3">
463
+ {messages.map((memory) => (
464
+ <MemoryCard key={memory.id || memory.createdAt} memory={memory} />
465
+ ))}
466
+ </div>
467
+ </div>
468
+ ))}
469
+
470
+ {/* Load more */}
471
+ {hasMoreToLoad && (
472
+ <div className="flex justify-center py-6">
473
+ {loadingMore ? (
474
+ <div className="flex items-center gap-2 text-muted-foreground">
475
+ <LoaderIcon className="h-4 w-4 animate-spin" />
476
+ <span className="text-sm">Loading more memories...</span>
477
+ </div>
478
+ ) : (
479
+ <Button
480
+ variant="outline"
481
+ onClick={() => setVisibleItems((prev) => prev + ITEMS_PER_PAGE)}
482
+ className="px-8"
483
+ >
484
+ Load More
485
+ </Button>
486
+ )}
487
+ </div>
488
+ )}
489
+ </div>
490
+ )}
491
+ </div>
492
+
493
+ {/* Edit overlay */}
494
+ {editingMemory && (
495
+ <MemoryEditOverlay
496
+ isOpen={!!editingMemory}
497
+ onClose={() => setEditingMemory(null)}
498
+ memory={editingMemory}
499
+ agentId={agentId}
500
+ />
501
+ )}
502
+ </div>
503
+ );
504
+ }