@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.
- package/LICENSE +21 -0
- package/README.md +350 -0
- package/dist/assets/empty-module-CLMscLYw.js +1 -0
- package/dist/assets/main-BBZ_3lkn.css +5999 -0
- package/dist/assets/main-C5zNUkXH.js +7 -0
- package/dist/assets/main-Dz64ENQg.js +614 -0
- package/dist/assets/react-vendor-DM5m98rr.js +545 -0
- package/dist/assets/ui-vendor-BQCqNqg0.js +1 -0
- package/dist/elizaos-avatar.png +0 -0
- package/dist/elizaos-icon.png +0 -0
- package/dist/elizaos-logo-light.png +0 -0
- package/dist/elizaos.webp +0 -0
- package/dist/favicon.ico +0 -0
- package/dist/images/agents/agent1.png +0 -0
- package/dist/images/agents/agent2.png +0 -0
- package/dist/images/agents/agent3.png +0 -0
- package/dist/images/agents/agent4.png +0 -0
- package/dist/images/agents/agent5.png +0 -0
- package/dist/index.html +14 -0
- package/index.html +24 -0
- package/package.json +159 -0
- package/postcss.config.js +3 -0
- package/public/elizaos-avatar.png +0 -0
- package/public/elizaos-icon.png +0 -0
- package/public/elizaos-logo-light.png +0 -0
- package/public/elizaos.webp +0 -0
- package/public/favicon.ico +0 -0
- package/public/images/agents/agent1.png +0 -0
- package/public/images/agents/agent2.png +0 -0
- package/public/images/agents/agent3.png +0 -0
- package/public/images/agents/agent4.png +0 -0
- package/public/images/agents/agent5.png +0 -0
- package/src/App.tsx +222 -0
- package/src/components/AgentDetailsPanel.tsx +147 -0
- package/src/components/ChatInputArea.tsx +196 -0
- package/src/components/ChatMessageListComponent.tsx +139 -0
- package/src/components/actionTool.tsx +186 -0
- package/src/components/add-agent-card.tsx +77 -0
- package/src/components/agent-action-viewer.tsx +816 -0
- package/src/components/agent-avatar-stack.tsx +121 -0
- package/src/components/agent-card.cy.tsx +259 -0
- package/src/components/agent-card.tsx +177 -0
- package/src/components/agent-creator.tsx +142 -0
- package/src/components/agent-log-viewer.tsx +645 -0
- package/src/components/agent-memory-edit-overlay.tsx +461 -0
- package/src/components/agent-memory-viewer.tsx +504 -0
- package/src/components/agent-settings.tsx +270 -0
- package/src/components/agent-sidebar.tsx +178 -0
- package/src/components/api-key-dialog.tsx +113 -0
- package/src/components/app-sidebar.tsx +685 -0
- package/src/components/array-input.tsx +116 -0
- package/src/components/audio-recorder.tsx +292 -0
- package/src/components/avatar-panel.tsx +141 -0
- package/src/components/character-form.tsx +1138 -0
- package/src/components/chat.tsx +1813 -0
- package/src/components/combobox.tsx +187 -0
- package/src/components/confirmation-dialog.tsx +59 -0
- package/src/components/connection-error-banner.tsx +101 -0
- package/src/components/connection-status.cy.tsx +73 -0
- package/src/components/connection-status.tsx +155 -0
- package/src/components/copy-button.tsx +35 -0
- package/src/components/delete-button.tsx +24 -0
- package/src/components/env-settings.tsx +261 -0
- package/src/components/group-card.tsx +160 -0
- package/src/components/group-panel.tsx +543 -0
- package/src/components/input-copy.tsx +21 -0
- package/src/components/logs-page.tsx +41 -0
- package/src/components/media-content.tsx +385 -0
- package/src/components/memory-graph.tsx +170 -0
- package/src/components/missing-secrets-dialog.tsx +72 -0
- package/src/components/onboarding-tour.tsx +247 -0
- package/src/components/page-title.tsx +8 -0
- package/src/components/plugins-panel.tsx +383 -0
- package/src/components/profile-card.tsx +66 -0
- package/src/components/profile-overlay.tsx +283 -0
- package/src/components/retry-button.tsx +28 -0
- package/src/components/secret-panel.tsx +1505 -0
- package/src/components/server-management.tsx +264 -0
- package/src/components/split-button.tsx +148 -0
- package/src/components/stop-agent-button.tsx +99 -0
- package/src/components/ui/alert-dialog.cy.tsx +333 -0
- package/src/components/ui/alert-dialog.tsx +115 -0
- package/src/components/ui/alert.tsx +49 -0
- package/src/components/ui/avatar.cy.tsx +180 -0
- package/src/components/ui/avatar.tsx +57 -0
- package/src/components/ui/badge.cy.tsx +146 -0
- package/src/components/ui/badge.tsx +43 -0
- package/src/components/ui/button.cy.tsx +177 -0
- package/src/components/ui/button.tsx +56 -0
- package/src/components/ui/card.cy.tsx +160 -0
- package/src/components/ui/card.tsx +73 -0
- package/src/components/ui/chat/animated-markdown.tsx +59 -0
- package/src/components/ui/chat/chat-bubble.tsx +178 -0
- package/src/components/ui/chat/chat-container.tsx +51 -0
- package/src/components/ui/chat/chat-input.cy.tsx +169 -0
- package/src/components/ui/chat/chat-input.tsx +47 -0
- package/src/components/ui/chat/chat-message-list.tsx +61 -0
- package/src/components/ui/chat/chat-tts-button.tsx +199 -0
- package/src/components/ui/chat/code-block.tsx +79 -0
- package/src/components/ui/chat/expandable-chat.tsx +131 -0
- package/src/components/ui/chat/hooks/useAutoScroll.ts +86 -0
- package/src/components/ui/chat/markdown.tsx +209 -0
- package/src/components/ui/chat/message-loading.tsx +48 -0
- package/src/components/ui/checkbox.cy.tsx +170 -0
- package/src/components/ui/checkbox.tsx +30 -0
- package/src/components/ui/collapsible.cy.tsx +283 -0
- package/src/components/ui/collapsible.tsx +9 -0
- package/src/components/ui/command.cy.tsx +313 -0
- package/src/components/ui/command.tsx +143 -0
- package/src/components/ui/dialog.cy.tsx +279 -0
- package/src/components/ui/dialog.tsx +104 -0
- package/src/components/ui/dropdown-menu.cy.tsx +273 -0
- package/src/components/ui/dropdown-menu.tsx +281 -0
- package/src/components/ui/input.cy.tsx +82 -0
- package/src/components/ui/input.tsx +27 -0
- package/src/components/ui/label.cy.tsx +157 -0
- package/src/components/ui/label.tsx +19 -0
- package/src/components/ui/resizable.tsx +42 -0
- package/src/components/ui/scroll-area.cy.tsx +242 -0
- package/src/components/ui/scroll-area.tsx +46 -0
- package/src/components/ui/select.cy.tsx +277 -0
- package/src/components/ui/select.tsx +155 -0
- package/src/components/ui/separator.cy.tsx +145 -0
- package/src/components/ui/separator.tsx +29 -0
- package/src/components/ui/sheet.cy.tsx +324 -0
- package/src/components/ui/sheet.tsx +119 -0
- package/src/components/ui/sidebar.tsx +734 -0
- package/src/components/ui/skeleton.cy.tsx +149 -0
- package/src/components/ui/skeleton.tsx +17 -0
- package/src/components/ui/split-button.cy.tsx +274 -0
- package/src/components/ui/split-button.tsx +112 -0
- package/src/components/ui/switch.tsx +28 -0
- package/src/components/ui/tabs.cy.tsx +271 -0
- package/src/components/ui/tabs.tsx +53 -0
- package/src/components/ui/textarea.cy.tsx +136 -0
- package/src/components/ui/textarea.tsx +26 -0
- package/src/components/ui/toast.cy.tsx +209 -0
- package/src/components/ui/toast.tsx +126 -0
- package/src/components/ui/toaster.tsx +29 -0
- package/src/components/ui/tooltip.cy.tsx +244 -0
- package/src/components/ui/tooltip.tsx +30 -0
- package/src/config/agent-templates.ts +349 -0
- package/src/config/voice-models.ts +181 -0
- package/src/constants.ts +23 -0
- package/src/context/AuthContext.tsx +44 -0
- package/src/context/ConnectionContext.tsx +194 -0
- package/src/entry.tsx +9 -0
- package/src/hooks/__tests__/use-agent-tab-state.test.ts +137 -0
- package/src/hooks/__tests__/use-agent-update.test.tsx +250 -0
- package/src/hooks/__tests__/use-character-convert.test.ts +102 -0
- package/src/hooks/__tests__/use-panel-width-state.test.ts +243 -0
- package/src/hooks/__tests__/use-sidebar-state.test.ts +117 -0
- package/src/hooks/use-agent-management.ts +130 -0
- package/src/hooks/use-agent-tab-state.ts +74 -0
- package/src/hooks/use-agent-update.ts +469 -0
- package/src/hooks/use-character-convert.ts +138 -0
- package/src/hooks/use-confirmation.ts +55 -0
- package/src/hooks/use-delete-agent.ts +123 -0
- package/src/hooks/use-dm-channels.ts +198 -0
- package/src/hooks/use-elevenlabs-voices.ts +83 -0
- package/src/hooks/use-file-upload.ts +224 -0
- package/src/hooks/use-mobile.tsx +19 -0
- package/src/hooks/use-onboarding.tsx +49 -0
- package/src/hooks/use-panel-width-state.ts +147 -0
- package/src/hooks/use-partial-update.ts +288 -0
- package/src/hooks/use-plugin-details.ts +462 -0
- package/src/hooks/use-plugins.ts +119 -0
- package/src/hooks/use-query-hooks.ts +1263 -0
- package/src/hooks/use-server-agents.ts +62 -0
- package/src/hooks/use-server-version.tsx +47 -0
- package/src/hooks/use-sidebar-state.ts +50 -0
- package/src/hooks/use-socket-chat.ts +264 -0
- package/src/hooks/use-toast.ts +260 -0
- package/src/hooks/use-version.tsx +64 -0
- package/src/index.css +146 -0
- package/src/lib/api-client-config.ts +53 -0
- package/src/lib/api-type-mappers.ts +196 -0
- package/src/lib/export-utils.ts +123 -0
- package/src/lib/logger.ts +19 -0
- package/src/lib/media-utils.ts +170 -0
- package/src/lib/pca.test.ts +17 -0
- package/src/lib/pca.ts +52 -0
- package/src/lib/socketio-manager.ts +664 -0
- package/src/lib/utils.ts +168 -0
- package/src/main.tsx +16 -0
- package/src/mocks/empty-module.ts +12 -0
- package/src/mocks/node-module.ts +57 -0
- package/src/polyfills.ts +37 -0
- package/src/routes/agent-detail.tsx +30 -0
- package/src/routes/agent-list.tsx +27 -0
- package/src/routes/agent-settings.tsx +48 -0
- package/src/routes/character-detail.tsx +52 -0
- package/src/routes/character-form.tsx +79 -0
- package/src/routes/character-list.tsx +38 -0
- package/src/routes/chat.tsx +128 -0
- package/src/routes/createAgent.tsx +13 -0
- package/src/routes/group-new.tsx +50 -0
- package/src/routes/group.tsx +29 -0
- package/src/routes/home.tsx +218 -0
- package/src/routes/not-found.tsx +71 -0
- package/src/test/setup.ts +154 -0
- package/src/types/crypto-browserify.d.ts +4 -0
- package/src/types/index.ts +13 -0
- package/src/types/rooms.ts +8 -0
- package/src/types.ts +84 -0
- package/src/vite-env.d.ts +40 -0
- package/tailwind.config.ts +90 -0
- package/tsconfig.json +10 -0
- package/vite.config.ts +102 -0
|
@@ -0,0 +1,1263 @@
|
|
|
1
|
+
import { GROUP_CHAT_SOURCE, USER_NAME } from '@/constants';
|
|
2
|
+
// Direct error handling without bridge layer
|
|
3
|
+
import { createElizaClient } from '@/lib/api-client-config';
|
|
4
|
+
import type {
|
|
5
|
+
Agent,
|
|
6
|
+
Content,
|
|
7
|
+
Memory,
|
|
8
|
+
UUID,
|
|
9
|
+
Memory as CoreMemory,
|
|
10
|
+
AgentStatus,
|
|
11
|
+
} from '@elizaos/core';
|
|
12
|
+
import {
|
|
13
|
+
useQuery,
|
|
14
|
+
useMutation,
|
|
15
|
+
useQueryClient,
|
|
16
|
+
useQueries,
|
|
17
|
+
UseQueryResult,
|
|
18
|
+
type DefinedUseQueryResult,
|
|
19
|
+
type UndefinedInitialDataOptions,
|
|
20
|
+
type UseQueryOptions,
|
|
21
|
+
} from '@tanstack/react-query';
|
|
22
|
+
import { useState, useEffect, useCallback, useMemo } from 'react';
|
|
23
|
+
import { useToast } from './use-toast';
|
|
24
|
+
import { getEntityId, randomUUID, moment } from '@/lib/utils';
|
|
25
|
+
import type {
|
|
26
|
+
ServerMessage,
|
|
27
|
+
AgentWithStatus,
|
|
28
|
+
MessageChannel as ClientMessageChannel,
|
|
29
|
+
MessageServer as ClientMessageServer,
|
|
30
|
+
} from '@/types';
|
|
31
|
+
import clientLogger from '@/lib/logger';
|
|
32
|
+
import { useNavigate } from 'react-router-dom';
|
|
33
|
+
import {
|
|
34
|
+
mapApiAgentToClient,
|
|
35
|
+
mapApiChannelToClient,
|
|
36
|
+
mapApiServerToClient,
|
|
37
|
+
mapApiServersToClient,
|
|
38
|
+
mapApiChannelsToClient,
|
|
39
|
+
mapApiMessageToUi,
|
|
40
|
+
mapApiLogToClient,
|
|
41
|
+
mapApiMemoryToClient,
|
|
42
|
+
mapEnumToApiStatus,
|
|
43
|
+
apiDateToTimestamp,
|
|
44
|
+
type AgentLog,
|
|
45
|
+
} from '@/lib/api-type-mappers';
|
|
46
|
+
|
|
47
|
+
// Create ElizaClient instance for direct API calls
|
|
48
|
+
const elizaClient = createElizaClient();
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Represents content with additional user information.
|
|
52
|
+
* @typedef {Object} ContentWithUser
|
|
53
|
+
* @property {string} name - The name of the user.
|
|
54
|
+
* @property {number} createdAt - The timestamp when the content was created.
|
|
55
|
+
* @property {boolean} [isLoading] - Optional flag indicating if the content is currently loading.
|
|
56
|
+
* @property {string} [worldId] - Optional ID of the world associated with the content.
|
|
57
|
+
* @property {string} [id] - Optional ID field.
|
|
58
|
+
*/
|
|
59
|
+
type ContentWithUser = Content & {
|
|
60
|
+
name: string;
|
|
61
|
+
createdAt: number;
|
|
62
|
+
isLoading?: boolean;
|
|
63
|
+
worldId?: UUID;
|
|
64
|
+
id?: UUID; // Add optional ID field
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
// AgentLog type is now imported from api-type-mappers
|
|
68
|
+
|
|
69
|
+
// Constants for stale times
|
|
70
|
+
export const STALE_TIMES = {
|
|
71
|
+
FREQUENT: 30000, // 30 seconds - for data that changes often
|
|
72
|
+
STANDARD: 120000, // 2 minutes - default
|
|
73
|
+
RARE: 600000, // 10 minutes - for rarely changing data
|
|
74
|
+
NEVER: Number.POSITIVE_INFINITY, // Only refetch on explicit invalidation
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
// Network Information API interface
|
|
78
|
+
/**
|
|
79
|
+
* Interface for representing network information.
|
|
80
|
+
*
|
|
81
|
+
* @property {("slow-2g" | "2g" | "3g" | "4g" | "unknown")} effectiveType - The effective network type.
|
|
82
|
+
* @property {boolean} saveData - Indicates if data saver mode is enabled.
|
|
83
|
+
* @property {unknown} [key] - Additional properties with unknown value types.
|
|
84
|
+
*/
|
|
85
|
+
|
|
86
|
+
interface NetworkInformation {
|
|
87
|
+
effectiveType: 'slow-2g' | '2g' | '3g' | '4g' | 'unknown';
|
|
88
|
+
saveData: boolean;
|
|
89
|
+
[key: string]: unknown;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// Network status detection for smart polling
|
|
93
|
+
/**
|
|
94
|
+
* A custom React hook that returns the network status information.
|
|
95
|
+
* Utilizes the Network Information API if available.
|
|
96
|
+
* @returns {{
|
|
97
|
+
* isOffline: boolean,
|
|
98
|
+
* effectiveType: string,
|
|
99
|
+
* saveData: boolean
|
|
100
|
+
* }} The network status information including whether the user is offline, the effective connection type, and if data-saving mode is enabled.
|
|
101
|
+
*/
|
|
102
|
+
const useNetworkStatus = () => {
|
|
103
|
+
// Get navigator.connection if available (Network Information API)
|
|
104
|
+
const connection =
|
|
105
|
+
typeof navigator !== 'undefined' && 'connection' in navigator
|
|
106
|
+
? (navigator as Navigator & { connection: NetworkInformation }).connection
|
|
107
|
+
: null;
|
|
108
|
+
|
|
109
|
+
// Return the effective connection type or a default value
|
|
110
|
+
return {
|
|
111
|
+
isOffline: typeof navigator !== 'undefined' && !navigator.onLine,
|
|
112
|
+
effectiveType: connection?.effectiveType || 'unknown',
|
|
113
|
+
saveData: connection?.saveData || false,
|
|
114
|
+
};
|
|
115
|
+
};
|
|
116
|
+
|
|
117
|
+
// Hook for fetching agents with smart polling
|
|
118
|
+
/**
|
|
119
|
+
* Fetches a list of agents from the server with polling and network-aware intervals.
|
|
120
|
+
*
|
|
121
|
+
* @param options - Optional configuration to override default query behavior.
|
|
122
|
+
* @returns A React Query object containing the agents data and query state.
|
|
123
|
+
*
|
|
124
|
+
* @remark Polling frequency adapts to network conditions, using less frequent polling when offline or on slow connections.
|
|
125
|
+
*/
|
|
126
|
+
export function useAgents(options = {}) {
|
|
127
|
+
const network = useNetworkStatus();
|
|
128
|
+
|
|
129
|
+
return useQuery<{ data: { agents: Partial<AgentWithStatus>[] } }>({
|
|
130
|
+
queryKey: ['agents'],
|
|
131
|
+
queryFn: async () => {
|
|
132
|
+
const result = await elizaClient.agents.listAgents();
|
|
133
|
+
// Map the API agents to client format
|
|
134
|
+
const mappedAgents = result.agents.map((agent) => mapApiAgentToClient(agent));
|
|
135
|
+
return { data: { agents: mappedAgents } };
|
|
136
|
+
},
|
|
137
|
+
staleTime: STALE_TIMES.FREQUENT, // Use shorter stale time for real-time data
|
|
138
|
+
// Use more frequent polling for real-time updates
|
|
139
|
+
refetchInterval: !network.isOffline ? STALE_TIMES.FREQUENT : false,
|
|
140
|
+
// Disable polling when the tab is not active
|
|
141
|
+
refetchIntervalInBackground: false,
|
|
142
|
+
// Configure based on network conditions
|
|
143
|
+
...(!network.isOffline &&
|
|
144
|
+
network.effectiveType === 'slow-2g' && {
|
|
145
|
+
refetchInterval: STALE_TIMES.STANDARD, // Poll less frequently on slow connections
|
|
146
|
+
}),
|
|
147
|
+
// Allow overriding any options
|
|
148
|
+
...options,
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
// Hook for fetching a specific agent with smart polling
|
|
153
|
+
/**
|
|
154
|
+
* Custom hook to fetch agent data based on the provided agentId.
|
|
155
|
+
* @param {UUID | undefined | null} agentId - The ID of the agent to fetch data for.
|
|
156
|
+
* @param {Object} options - Additional options to configure the query.
|
|
157
|
+
* @returns {QueryResult} The result of the query containing agent data.
|
|
158
|
+
*/
|
|
159
|
+
export function useAgent(agentId: UUID | undefined | null, options = {}) {
|
|
160
|
+
const network = useNetworkStatus();
|
|
161
|
+
|
|
162
|
+
return useQuery<{ data: AgentWithStatus }>({
|
|
163
|
+
queryKey: ['agent', agentId],
|
|
164
|
+
queryFn: async () => {
|
|
165
|
+
if (!agentId) throw new Error('Agent ID is required');
|
|
166
|
+
const result = await elizaClient.agents.getAgent(agentId);
|
|
167
|
+
return { data: mapApiAgentToClient(result) };
|
|
168
|
+
},
|
|
169
|
+
staleTime: STALE_TIMES.FREQUENT, // Use shorter stale time for real-time data
|
|
170
|
+
enabled: Boolean(agentId),
|
|
171
|
+
// Use more frequent polling for real-time updates
|
|
172
|
+
refetchInterval: !network.isOffline && Boolean(agentId) ? STALE_TIMES.FREQUENT : false,
|
|
173
|
+
// Disable polling when the tab is not active
|
|
174
|
+
refetchIntervalInBackground: false,
|
|
175
|
+
// Configure based on network conditions
|
|
176
|
+
...(!network.isOffline &&
|
|
177
|
+
network.effectiveType === 'slow-2g' && {
|
|
178
|
+
refetchInterval: STALE_TIMES.STANDARD, // Poll less frequently on slow connections
|
|
179
|
+
}),
|
|
180
|
+
// Allow overriding any options
|
|
181
|
+
...options,
|
|
182
|
+
});
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
// Hook for starting an agent with optimistic updates
|
|
186
|
+
/**
|
|
187
|
+
* Custom hook to start an agent by calling the API with the provided agent ID.
|
|
188
|
+
*
|
|
189
|
+
* @returns {MutationFunction<UUID, unknown>} The useMutation hook for starting an agent.
|
|
190
|
+
*/
|
|
191
|
+
export function useStartAgent() {
|
|
192
|
+
const queryClient = useQueryClient();
|
|
193
|
+
const { toast } = useToast();
|
|
194
|
+
|
|
195
|
+
return useMutation<{ data: { id: UUID; name: string; status: string } }, Error, UUID>({
|
|
196
|
+
mutationFn: async (agentId: UUID) => {
|
|
197
|
+
try {
|
|
198
|
+
const result = await elizaClient.agents.startAgent(agentId);
|
|
199
|
+
return { data: { id: agentId, name: 'Agent', status: result.status } };
|
|
200
|
+
} catch (error) {
|
|
201
|
+
// Use the centralized error handler, but preserve specific agent logic
|
|
202
|
+
if (error instanceof Error) {
|
|
203
|
+
if (error.message.includes('already running')) {
|
|
204
|
+
throw new Error('Agent is already running.');
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
throw error;
|
|
208
|
+
}
|
|
209
|
+
},
|
|
210
|
+
onMutate: async (_agentId) => {
|
|
211
|
+
// Optimistically update UI to show agent is starting
|
|
212
|
+
toast({
|
|
213
|
+
title: 'Starting Agent',
|
|
214
|
+
description: 'Initializing agent...',
|
|
215
|
+
});
|
|
216
|
+
|
|
217
|
+
// Return context for potential rollback
|
|
218
|
+
return {};
|
|
219
|
+
},
|
|
220
|
+
onSuccess: (response, agentId) => {
|
|
221
|
+
queryClient.invalidateQueries({ queryKey: ['agents'] });
|
|
222
|
+
queryClient.invalidateQueries({ queryKey: ['agent', agentId] });
|
|
223
|
+
|
|
224
|
+
toast({
|
|
225
|
+
title: 'Agent Started',
|
|
226
|
+
description: `${response?.data?.name || 'Agent'} is now running`,
|
|
227
|
+
});
|
|
228
|
+
},
|
|
229
|
+
onError: (error) => {
|
|
230
|
+
// Handle specific error cases
|
|
231
|
+
const errorMessage = error instanceof Error ? error.message : 'Failed to start agent';
|
|
232
|
+
|
|
233
|
+
toast({
|
|
234
|
+
title: 'Error Starting Agent',
|
|
235
|
+
description: `${errorMessage}. Please try again.`,
|
|
236
|
+
variant: 'destructive',
|
|
237
|
+
});
|
|
238
|
+
},
|
|
239
|
+
});
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
// Hook for stopping an agent with optimistic updates
|
|
243
|
+
/**
|
|
244
|
+
* Custom hook to stop an agent by calling the API and updating the UI optimistically.
|
|
245
|
+
*
|
|
246
|
+
* @returns {UseMutationResult} - Object containing the mutation function and its handlers.
|
|
247
|
+
*/
|
|
248
|
+
export function useStopAgent() {
|
|
249
|
+
const queryClient = useQueryClient();
|
|
250
|
+
const { toast } = useToast();
|
|
251
|
+
|
|
252
|
+
return useMutation<{ data: { message: string } }, Error, UUID>({
|
|
253
|
+
mutationFn: async (agentId: UUID) => {
|
|
254
|
+
const result = await elizaClient.agents.stopAgent(agentId);
|
|
255
|
+
return { data: { message: `Agent ${result.status}` } };
|
|
256
|
+
},
|
|
257
|
+
onMutate: async (agentId) => {
|
|
258
|
+
// Optimistically update the UI
|
|
259
|
+
// Get the agent data from the cache
|
|
260
|
+
const agent = queryClient.getQueryData<Agent>(['agent', agentId]);
|
|
261
|
+
|
|
262
|
+
if (agent) {
|
|
263
|
+
toast({
|
|
264
|
+
title: 'Stopping Agent',
|
|
265
|
+
description: `Stopping ${agent.name}...`,
|
|
266
|
+
});
|
|
267
|
+
}
|
|
268
|
+
},
|
|
269
|
+
onSuccess: (response, agentId) => {
|
|
270
|
+
// Immediately invalidate the queries for fresh data
|
|
271
|
+
queryClient.invalidateQueries({ queryKey: ['agents'] });
|
|
272
|
+
queryClient.invalidateQueries({ queryKey: ['agent', agentId] });
|
|
273
|
+
|
|
274
|
+
toast({
|
|
275
|
+
title: 'Agent Stopped',
|
|
276
|
+
description: response?.data?.message || 'The agent has been successfully stopped',
|
|
277
|
+
});
|
|
278
|
+
},
|
|
279
|
+
onError: (error, agentId) => {
|
|
280
|
+
// Force invalidate on error
|
|
281
|
+
queryClient.invalidateQueries({ queryKey: ['agents'] });
|
|
282
|
+
queryClient.invalidateQueries({ queryKey: ['agent', agentId] });
|
|
283
|
+
|
|
284
|
+
toast({
|
|
285
|
+
title: 'Error',
|
|
286
|
+
description: error instanceof Error ? error.message : 'Failed to stop agent',
|
|
287
|
+
variant: 'destructive',
|
|
288
|
+
});
|
|
289
|
+
},
|
|
290
|
+
});
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
// Type for UI message list items
|
|
294
|
+
export type UiMessage = Content & {
|
|
295
|
+
id: UUID; // Message ID
|
|
296
|
+
name: string; // Display name of sender (USER_NAME or agent name)
|
|
297
|
+
senderId: UUID; // Central ID of the sender
|
|
298
|
+
isAgent: boolean;
|
|
299
|
+
createdAt: number; // Timestamp ms
|
|
300
|
+
isLoading?: boolean;
|
|
301
|
+
channelId: UUID; // Central Channel ID
|
|
302
|
+
serverId?: UUID; // Server ID (optional in some contexts, but good for full context)
|
|
303
|
+
prompt?: string; // The LLM prompt used to generate this message (for agents)
|
|
304
|
+
// attachments and other Content props are inherited
|
|
305
|
+
};
|
|
306
|
+
|
|
307
|
+
/**
|
|
308
|
+
* Custom hook to manage fetching and loading messages for a specific channel.
|
|
309
|
+
* @param {UUID | undefined} channelId - The GLOBAL ID of the channel.
|
|
310
|
+
* @returns {{...
|
|
311
|
+
}} An object containing messages data, loading states, etc.
|
|
312
|
+
*/
|
|
313
|
+
export function useChannelMessages(
|
|
314
|
+
channelId: UUID | undefined, // Changed from UUID | null
|
|
315
|
+
initialServerId?: UUID | undefined // Changed from UUID (optional was already undefined)
|
|
316
|
+
): {
|
|
317
|
+
data: UiMessage[] | undefined;
|
|
318
|
+
isLoading: boolean;
|
|
319
|
+
isError: boolean;
|
|
320
|
+
error: unknown;
|
|
321
|
+
fetchNextPage: () => Promise<void>; // Simplified pagination trigger
|
|
322
|
+
hasNextPage: boolean;
|
|
323
|
+
isFetchingNextPage: boolean;
|
|
324
|
+
addMessage: (newMessage: UiMessage) => void;
|
|
325
|
+
updateMessage: (messageId: UUID, updates: Partial<UiMessage>) => void;
|
|
326
|
+
removeMessage: (messageId: UUID) => void;
|
|
327
|
+
clearMessages: () => void;
|
|
328
|
+
} {
|
|
329
|
+
const currentClientCentralId = getEntityId(); // Central ID of the currently logged-in user
|
|
330
|
+
|
|
331
|
+
// Using a more manual approach for pagination with getChannelMessages
|
|
332
|
+
const [messages, setMessages] = useState<UiMessage[]>([]);
|
|
333
|
+
const [oldestMessageTimestamp, setOldestMessageTimestamp] = useState<number | null>(null);
|
|
334
|
+
const [hasMoreMessages, setHasMoreMessages] = useState<boolean>(true);
|
|
335
|
+
const [internalIsLoading, setInternalIsLoading] = useState<boolean>(true); // Start true
|
|
336
|
+
const [internalIsError, setInternalIsError] = useState<boolean>(false);
|
|
337
|
+
const [internalError, setInternalError] = useState<unknown>(null);
|
|
338
|
+
const [isFetchingMore, setIsFetchingMore] = useState<boolean>(false);
|
|
339
|
+
|
|
340
|
+
const transformServerMessageToUiMessage = useCallback(
|
|
341
|
+
(sm: ServerMessage, serverIdToUse?: UUID): UiMessage => {
|
|
342
|
+
const isAgent = sm.authorId !== currentClientCentralId;
|
|
343
|
+
let timestamp = Date.now(); // Default to now
|
|
344
|
+
|
|
345
|
+
if (typeof sm.createdAt === 'number') {
|
|
346
|
+
timestamp = sm.createdAt;
|
|
347
|
+
} else if (typeof sm.createdAt === 'string') {
|
|
348
|
+
const parsedTs = Date.parse(sm.createdAt); // Try direct parse
|
|
349
|
+
if (!isNaN(parsedTs)) {
|
|
350
|
+
timestamp = parsedTs;
|
|
351
|
+
} else {
|
|
352
|
+
// If direct parse fails, try moment (if available and robust)
|
|
353
|
+
// For now, log a warning if it's an unparsable string not handled by Date.parse
|
|
354
|
+
clientLogger.warn(
|
|
355
|
+
'[transformServerMessageToUiMessage] createdAt string was not directly parsable by Date.parse():',
|
|
356
|
+
sm.createdAt,
|
|
357
|
+
'for message id:',
|
|
358
|
+
sm.id
|
|
359
|
+
);
|
|
360
|
+
// As a fallback, could try new Date(sm.createdAt).getTime(), but Date.parse is usually sufficient
|
|
361
|
+
// Defaulting to Date.now() if unparsable to avoid NaN
|
|
362
|
+
}
|
|
363
|
+
} else if (sm.createdAt) {
|
|
364
|
+
// If it's not a number or string, but exists (e.g. could be a Date object from some contexts)
|
|
365
|
+
// Attempt to convert. This is less likely if types are strict from server.
|
|
366
|
+
try {
|
|
367
|
+
const dateObjTimestamp = new Date(sm.createdAt as any).getTime();
|
|
368
|
+
if (!isNaN(dateObjTimestamp)) {
|
|
369
|
+
timestamp = dateObjTimestamp;
|
|
370
|
+
}
|
|
371
|
+
} catch (e) {
|
|
372
|
+
clientLogger.warn(
|
|
373
|
+
'[transformServerMessageToUiMessage] Could not process createdAt (unknown type):',
|
|
374
|
+
sm.createdAt,
|
|
375
|
+
'for message:',
|
|
376
|
+
sm.id
|
|
377
|
+
);
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
return {
|
|
382
|
+
id: sm.id,
|
|
383
|
+
text: sm.content,
|
|
384
|
+
name: isAgent
|
|
385
|
+
? sm.metadata?.agentName ||
|
|
386
|
+
sm.metadata?.authorDisplayName ||
|
|
387
|
+
sm.authorDisplayName ||
|
|
388
|
+
'Agent'
|
|
389
|
+
: USER_NAME,
|
|
390
|
+
senderId: sm.authorId,
|
|
391
|
+
isAgent: isAgent,
|
|
392
|
+
createdAt: timestamp,
|
|
393
|
+
attachments: sm.metadata?.attachments as any[],
|
|
394
|
+
thought: isAgent ? sm.metadata?.thought : undefined,
|
|
395
|
+
actions: isAgent ? sm.metadata?.actions : undefined,
|
|
396
|
+
channelId: sm.channelId,
|
|
397
|
+
serverId: serverIdToUse || sm.metadata?.serverId || sm.serverId || initialServerId,
|
|
398
|
+
source: sm.sourceType,
|
|
399
|
+
isLoading: false,
|
|
400
|
+
prompt: isAgent ? sm.metadata?.prompt : undefined,
|
|
401
|
+
};
|
|
402
|
+
},
|
|
403
|
+
[currentClientCentralId, initialServerId]
|
|
404
|
+
);
|
|
405
|
+
|
|
406
|
+
const fetchMessages = useCallback(
|
|
407
|
+
async (beforeTimestamp?: number) => {
|
|
408
|
+
if (!channelId) {
|
|
409
|
+
setMessages([]);
|
|
410
|
+
setInternalIsLoading(false);
|
|
411
|
+
return;
|
|
412
|
+
}
|
|
413
|
+
if (!beforeTimestamp) {
|
|
414
|
+
setInternalIsLoading(true); // Full load
|
|
415
|
+
} else {
|
|
416
|
+
setIsFetchingMore(true);
|
|
417
|
+
}
|
|
418
|
+
setInternalIsError(false);
|
|
419
|
+
setInternalError(null);
|
|
420
|
+
|
|
421
|
+
try {
|
|
422
|
+
const response = await elizaClient.messaging.getChannelMessages(channelId, {
|
|
423
|
+
limit: 30,
|
|
424
|
+
before: beforeTimestamp ? new Date(beforeTimestamp).toISOString() : undefined,
|
|
425
|
+
});
|
|
426
|
+
|
|
427
|
+
const newUiMessages = response.messages.map((msg) =>
|
|
428
|
+
mapApiMessageToUi(msg, initialServerId || msg.metadata?.serverId)
|
|
429
|
+
);
|
|
430
|
+
|
|
431
|
+
setMessages((prev) => {
|
|
432
|
+
const combined = beforeTimestamp ? [...newUiMessages, ...prev] : newUiMessages;
|
|
433
|
+
const uniqueMessages = Array.from(
|
|
434
|
+
new Map(combined.map((item) => [item.id, item])).values()
|
|
435
|
+
);
|
|
436
|
+
return uniqueMessages.sort((a, b) => a.createdAt - b.createdAt);
|
|
437
|
+
});
|
|
438
|
+
|
|
439
|
+
if (newUiMessages.length > 0) {
|
|
440
|
+
const oldestFetched = Math.min(...newUiMessages.map((m) => m.createdAt));
|
|
441
|
+
if (!beforeTimestamp || oldestFetched < (oldestMessageTimestamp || Infinity)) {
|
|
442
|
+
setOldestMessageTimestamp(oldestFetched);
|
|
443
|
+
}
|
|
444
|
+
}
|
|
445
|
+
setHasMoreMessages(newUiMessages.length >= 30);
|
|
446
|
+
} catch (err) {
|
|
447
|
+
setInternalIsError(true);
|
|
448
|
+
setInternalError(err);
|
|
449
|
+
clientLogger.error(`Failed to fetch messages for channel ${channelId}:`, err);
|
|
450
|
+
} finally {
|
|
451
|
+
setInternalIsLoading(false);
|
|
452
|
+
setIsFetchingMore(false);
|
|
453
|
+
}
|
|
454
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
455
|
+
},
|
|
456
|
+
[channelId, transformServerMessageToUiMessage, initialServerId]
|
|
457
|
+
); // Add initialServerId to deps
|
|
458
|
+
|
|
459
|
+
useEffect(() => {
|
|
460
|
+
// Initial fetch when channelId changes or becomes available
|
|
461
|
+
if (channelId) {
|
|
462
|
+
clientLogger.info(
|
|
463
|
+
`[useChannelMessages] ChannelId changed or became available: ${channelId}. Clearing messages and fetching initial set.`
|
|
464
|
+
);
|
|
465
|
+
setMessages([]); // Clear previous messages
|
|
466
|
+
setOldestMessageTimestamp(null);
|
|
467
|
+
setHasMoreMessages(true);
|
|
468
|
+
fetchMessages(); // This will set internalIsLoading to true
|
|
469
|
+
} else {
|
|
470
|
+
clientLogger.info('[useChannelMessages] ChannelId is undefined. Clearing messages.');
|
|
471
|
+
setMessages([]);
|
|
472
|
+
setOldestMessageTimestamp(null);
|
|
473
|
+
setHasMoreMessages(true);
|
|
474
|
+
setInternalIsLoading(false); // No channel, so not loading anything
|
|
475
|
+
}
|
|
476
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
477
|
+
}, [channelId, fetchMessages]); // fetchMessages is memoized with useCallback
|
|
478
|
+
|
|
479
|
+
const fetchNextPage = async () => {
|
|
480
|
+
if (hasMoreMessages && !isFetchingMore && oldestMessageTimestamp) {
|
|
481
|
+
await fetchMessages(oldestMessageTimestamp - 1); // -1 to avoid fetching the same last message
|
|
482
|
+
}
|
|
483
|
+
};
|
|
484
|
+
|
|
485
|
+
// Add method to manually add/update messages from external sources (e.g., WebSocket)
|
|
486
|
+
const addMessage = useCallback((newMessage: UiMessage) => {
|
|
487
|
+
setMessages((prev) => {
|
|
488
|
+
// Check if message already exists
|
|
489
|
+
const existingIndex = prev.findIndex((m) => m.id === newMessage.id);
|
|
490
|
+
|
|
491
|
+
if (existingIndex >= 0) {
|
|
492
|
+
// Update existing message
|
|
493
|
+
const updated = [...prev];
|
|
494
|
+
updated[existingIndex] = newMessage;
|
|
495
|
+
return updated.sort((a, b) => a.createdAt - b.createdAt);
|
|
496
|
+
} else {
|
|
497
|
+
// Add new message
|
|
498
|
+
return [...prev, newMessage].sort((a, b) => a.createdAt - b.createdAt);
|
|
499
|
+
}
|
|
500
|
+
});
|
|
501
|
+
}, []);
|
|
502
|
+
|
|
503
|
+
// Add method to update a message by ID
|
|
504
|
+
const updateMessage = useCallback((messageId: UUID, updates: Partial<UiMessage>) => {
|
|
505
|
+
setMessages((prev) => {
|
|
506
|
+
return prev.map((m) => {
|
|
507
|
+
if (m.id === messageId) {
|
|
508
|
+
return { ...m, ...updates };
|
|
509
|
+
}
|
|
510
|
+
return m;
|
|
511
|
+
});
|
|
512
|
+
});
|
|
513
|
+
}, []);
|
|
514
|
+
|
|
515
|
+
// Add method to remove a message by ID
|
|
516
|
+
const removeMessage = useCallback((messageId: UUID) => {
|
|
517
|
+
setMessages((prev) => prev.filter((m) => m.id !== messageId));
|
|
518
|
+
}, []);
|
|
519
|
+
|
|
520
|
+
// Add method to clear all messages
|
|
521
|
+
const clearMessages = useCallback(() => {
|
|
522
|
+
setMessages([]);
|
|
523
|
+
setOldestMessageTimestamp(null);
|
|
524
|
+
setHasMoreMessages(true);
|
|
525
|
+
}, []);
|
|
526
|
+
|
|
527
|
+
// This hook now manages its own state for messages
|
|
528
|
+
// To integrate with React Query for caching of initial load or background updates:
|
|
529
|
+
// One could use useInfiniteQuery, but given the manual state management already here for append/prepend,
|
|
530
|
+
// this simpler useState + manual fetch approach is retained from the original structure of useMessages.
|
|
531
|
+
// For full React Query benefits, `useInfiniteQuery` would be the way to go.
|
|
532
|
+
|
|
533
|
+
return {
|
|
534
|
+
data: messages,
|
|
535
|
+
isLoading: internalIsLoading && messages.length === 0, // True only on initial load
|
|
536
|
+
isError: internalIsError,
|
|
537
|
+
error: internalError,
|
|
538
|
+
fetchNextPage,
|
|
539
|
+
hasNextPage: hasMoreMessages,
|
|
540
|
+
isFetchingNextPage: isFetchingMore,
|
|
541
|
+
addMessage,
|
|
542
|
+
updateMessage,
|
|
543
|
+
removeMessage,
|
|
544
|
+
clearMessages,
|
|
545
|
+
};
|
|
546
|
+
}
|
|
547
|
+
|
|
548
|
+
export function useGroupChannelMessages(channelId: UUID | null, initialServerId?: UUID) {
|
|
549
|
+
// This hook now becomes an alias or a slightly specialized version of useChannelMessages
|
|
550
|
+
// if group-specific logic (like different source filtering) isn't handled here.
|
|
551
|
+
// For now, it can directly use useChannelMessages.
|
|
552
|
+
return useChannelMessages(channelId ?? undefined, initialServerId);
|
|
553
|
+
}
|
|
554
|
+
|
|
555
|
+
// Hook for fetching agent actions
|
|
556
|
+
/**
|
|
557
|
+
* Custom hook to fetch agent actions for a specific agent and room.
|
|
558
|
+
* @param {UUID} agentId - The ID of the agent.
|
|
559
|
+
* @param {UUID} roomId - The ID of the room.
|
|
560
|
+
* @param {string[]} excludeTypes - Optional array of types to exclude from results.
|
|
561
|
+
* @returns {QueryResult} The result of the query containing agent actions.
|
|
562
|
+
*/
|
|
563
|
+
export function useAgentActions(agentId: UUID, roomId?: UUID, excludeTypes?: string[]) {
|
|
564
|
+
return useQuery({
|
|
565
|
+
queryKey: ['agentActions', agentId, roomId, excludeTypes],
|
|
566
|
+
queryFn: async () => {
|
|
567
|
+
const response = await elizaClient.agents.getAgentLogs(agentId, {
|
|
568
|
+
limit: 50,
|
|
569
|
+
});
|
|
570
|
+
// Map the API logs to client format
|
|
571
|
+
return response ? response.map(mapApiLogToClient) : [];
|
|
572
|
+
},
|
|
573
|
+
refetchInterval: 1000,
|
|
574
|
+
staleTime: 1000,
|
|
575
|
+
});
|
|
576
|
+
}
|
|
577
|
+
|
|
578
|
+
/**
|
|
579
|
+
* Hook to delete an agent log/action.
|
|
580
|
+
* @returns {UseMutationResult} - Object containing the mutation function and its handlers.
|
|
581
|
+
*/
|
|
582
|
+
export function useDeleteLog() {
|
|
583
|
+
const queryClient = useQueryClient();
|
|
584
|
+
const { toast } = useToast();
|
|
585
|
+
|
|
586
|
+
return useMutation({
|
|
587
|
+
mutationFn: async ({ agentId, logId }: { agentId: UUID; logId: UUID }) => {
|
|
588
|
+
await elizaClient.agents.deleteAgentLog(agentId, logId);
|
|
589
|
+
return { agentId, logId };
|
|
590
|
+
},
|
|
591
|
+
|
|
592
|
+
onMutate: async ({ agentId, logId }) => {
|
|
593
|
+
// Optimistically update the UI by removing the log from the cache
|
|
594
|
+
const previousLogs = queryClient.getQueryData(['agentActions', agentId]);
|
|
595
|
+
|
|
596
|
+
// Update cache if we have the data
|
|
597
|
+
if (previousLogs) {
|
|
598
|
+
queryClient.setQueryData(['agentActions', agentId], (oldData: any) =>
|
|
599
|
+
oldData.filter((log: any) => log.id !== logId)
|
|
600
|
+
);
|
|
601
|
+
}
|
|
602
|
+
|
|
603
|
+
return { previousLogs, agentId, logId };
|
|
604
|
+
},
|
|
605
|
+
|
|
606
|
+
onSuccess: (_, { agentId }) => {
|
|
607
|
+
// Invalidate relevant queries to refetch the latest data
|
|
608
|
+
queryClient.invalidateQueries({ queryKey: ['agentActions', agentId] });
|
|
609
|
+
|
|
610
|
+
toast({
|
|
611
|
+
title: 'Log Deleted',
|
|
612
|
+
description: 'The log entry has been successfully removed',
|
|
613
|
+
});
|
|
614
|
+
},
|
|
615
|
+
|
|
616
|
+
onError: (error, { agentId }, context) => {
|
|
617
|
+
// Revert the optimistic update on error
|
|
618
|
+
if (context?.previousLogs) {
|
|
619
|
+
queryClient.setQueryData(['agentActions', agentId], context.previousLogs);
|
|
620
|
+
}
|
|
621
|
+
|
|
622
|
+
toast({
|
|
623
|
+
title: 'Error',
|
|
624
|
+
description: error instanceof Error ? error.message : 'Failed to delete log',
|
|
625
|
+
variant: 'destructive',
|
|
626
|
+
});
|
|
627
|
+
|
|
628
|
+
// Force invalidate on error to ensure data is fresh
|
|
629
|
+
queryClient.invalidateQueries({ queryKey: ['agentActions', agentId] });
|
|
630
|
+
},
|
|
631
|
+
});
|
|
632
|
+
}
|
|
633
|
+
|
|
634
|
+
/**
|
|
635
|
+
* Fetches memories for a specific agent, optionally filtered by channel
|
|
636
|
+
*/
|
|
637
|
+
export function useAgentMemories(
|
|
638
|
+
agentId: UUID,
|
|
639
|
+
tableName?: string,
|
|
640
|
+
channelId?: UUID, // Changed from roomId to channelId
|
|
641
|
+
includeEmbedding = false
|
|
642
|
+
) {
|
|
643
|
+
const queryKey = channelId
|
|
644
|
+
? ['agents', agentId, 'channels', channelId, 'memories', tableName, includeEmbedding] // Updated query key
|
|
645
|
+
: ['agents', agentId, 'memories', tableName, includeEmbedding];
|
|
646
|
+
|
|
647
|
+
return useQuery({
|
|
648
|
+
queryKey,
|
|
649
|
+
queryFn: async () => {
|
|
650
|
+
const params: any = {
|
|
651
|
+
tableName,
|
|
652
|
+
includeEmbedding,
|
|
653
|
+
};
|
|
654
|
+
const result = channelId
|
|
655
|
+
? await elizaClient.memory.getRoomMemories(agentId, channelId, params)
|
|
656
|
+
: await elizaClient.memory.getAgentMemories(agentId, params);
|
|
657
|
+
console.log('Agent memories result:', {
|
|
658
|
+
agentId,
|
|
659
|
+
tableName,
|
|
660
|
+
includeEmbedding,
|
|
661
|
+
channelId,
|
|
662
|
+
result,
|
|
663
|
+
dataLength: result.memories?.length,
|
|
664
|
+
firstMemory: result.memories?.[0],
|
|
665
|
+
hasEmbeddings: (result.memories || []).some((m: any) => m.embedding?.length > 0),
|
|
666
|
+
});
|
|
667
|
+
// Map the API memories to client format
|
|
668
|
+
const memories = result.memories || [];
|
|
669
|
+
return memories.map(mapApiMemoryToClient);
|
|
670
|
+
},
|
|
671
|
+
enabled: Boolean(agentId && tableName),
|
|
672
|
+
staleTime: 1000,
|
|
673
|
+
refetchInterval: 10 * 1000,
|
|
674
|
+
});
|
|
675
|
+
}
|
|
676
|
+
|
|
677
|
+
/**
|
|
678
|
+
* Provides a mutation hook to delete a specific memory entry for an agent.
|
|
679
|
+
*
|
|
680
|
+
* On success, invalidates related agent and room memory queries to ensure data consistency.
|
|
681
|
+
*/
|
|
682
|
+
export function useDeleteMemory() {
|
|
683
|
+
const queryClient = useQueryClient();
|
|
684
|
+
|
|
685
|
+
return useMutation({
|
|
686
|
+
mutationFn: async ({ agentId, memoryId }: { agentId: UUID; memoryId: UUID }) => {
|
|
687
|
+
await elizaClient.memory.deleteMemory(agentId, memoryId);
|
|
688
|
+
return { agentId, memoryId };
|
|
689
|
+
},
|
|
690
|
+
onSuccess: (data) => {
|
|
691
|
+
// Invalidate relevant queries to trigger refetch
|
|
692
|
+
queryClient.invalidateQueries({
|
|
693
|
+
queryKey: ['agents', data.agentId, 'memories'],
|
|
694
|
+
});
|
|
695
|
+
|
|
696
|
+
// Also invalidate room-specific memories
|
|
697
|
+
queryClient.invalidateQueries({
|
|
698
|
+
queryKey: ['agents', data.agentId, 'rooms'],
|
|
699
|
+
predicate: (query) => query.queryKey.length > 3 && query.queryKey[4] === 'memories',
|
|
700
|
+
});
|
|
701
|
+
},
|
|
702
|
+
});
|
|
703
|
+
}
|
|
704
|
+
|
|
705
|
+
/**
|
|
706
|
+
* Hook for deleting all memories associated with a specific agent in a given room.
|
|
707
|
+
*
|
|
708
|
+
* @returns A mutation object for triggering the deletion and tracking its state.
|
|
709
|
+
*/
|
|
710
|
+
export function useDeleteAllMemories() {
|
|
711
|
+
const queryClient = useQueryClient();
|
|
712
|
+
|
|
713
|
+
return useMutation({
|
|
714
|
+
mutationFn: async ({ agentId, roomId }: { agentId: UUID; roomId: UUID }) => {
|
|
715
|
+
await elizaClient.memory.clearRoomMemories(agentId, roomId);
|
|
716
|
+
return { agentId };
|
|
717
|
+
},
|
|
718
|
+
onSuccess: (data) => {
|
|
719
|
+
// Invalidate relevant queries to trigger refetch
|
|
720
|
+
queryClient.invalidateQueries({
|
|
721
|
+
queryKey: ['agents', data.agentId, 'memories'],
|
|
722
|
+
});
|
|
723
|
+
},
|
|
724
|
+
});
|
|
725
|
+
}
|
|
726
|
+
|
|
727
|
+
/**
|
|
728
|
+
* Updates a specific memory entry for an agent.
|
|
729
|
+
*
|
|
730
|
+
* Triggers cache invalidation for related agent and room memories, as well as messages, to ensure data consistency. Displays a toast notification on success or error.
|
|
731
|
+
*
|
|
732
|
+
* @returns A mutation object for updating an agent's memory entry.
|
|
733
|
+
*/
|
|
734
|
+
export function useUpdateMemory() {
|
|
735
|
+
const queryClient = useQueryClient();
|
|
736
|
+
const { toast } = useToast();
|
|
737
|
+
|
|
738
|
+
return useMutation({
|
|
739
|
+
mutationFn: async ({
|
|
740
|
+
agentId,
|
|
741
|
+
memoryId,
|
|
742
|
+
memoryData,
|
|
743
|
+
}: {
|
|
744
|
+
agentId: UUID;
|
|
745
|
+
memoryId: UUID;
|
|
746
|
+
memoryData: Partial<Memory>;
|
|
747
|
+
}) => {
|
|
748
|
+
const result = await elizaClient.memory.updateMemory(agentId, memoryId, memoryData);
|
|
749
|
+
return { agentId, memoryId, result };
|
|
750
|
+
},
|
|
751
|
+
|
|
752
|
+
onSuccess: (data) => {
|
|
753
|
+
// Invalidate relevant queries to trigger refetch
|
|
754
|
+
queryClient.invalidateQueries({
|
|
755
|
+
queryKey: ['agents', data.agentId, 'memories'],
|
|
756
|
+
});
|
|
757
|
+
|
|
758
|
+
// Also invalidate room-specific memories if we have roomId in the memory data
|
|
759
|
+
if (data.result?.roomId) {
|
|
760
|
+
queryClient.invalidateQueries({
|
|
761
|
+
queryKey: ['agents', data.agentId, 'rooms', data.result.roomId, 'memories'],
|
|
762
|
+
});
|
|
763
|
+
} else {
|
|
764
|
+
// Otherwise invalidate all room memories for this agent
|
|
765
|
+
queryClient.invalidateQueries({
|
|
766
|
+
queryKey: ['agents', data.agentId, 'rooms'],
|
|
767
|
+
predicate: (query) => query.queryKey.length > 3 && query.queryKey[4] === 'memories',
|
|
768
|
+
});
|
|
769
|
+
}
|
|
770
|
+
|
|
771
|
+
// Also invalidate regular messages queries
|
|
772
|
+
if (data.result?.roomId) {
|
|
773
|
+
queryClient.invalidateQueries({
|
|
774
|
+
queryKey: ['messages', data.agentId, data.result.roomId],
|
|
775
|
+
});
|
|
776
|
+
}
|
|
777
|
+
|
|
778
|
+
toast({
|
|
779
|
+
title: 'Memory Updated',
|
|
780
|
+
description: 'The memory has been successfully updated',
|
|
781
|
+
});
|
|
782
|
+
},
|
|
783
|
+
|
|
784
|
+
onError: (error) => {
|
|
785
|
+
toast({
|
|
786
|
+
title: 'Error',
|
|
787
|
+
description: error instanceof Error ? error.message : 'Failed to update memory',
|
|
788
|
+
variant: 'destructive',
|
|
789
|
+
});
|
|
790
|
+
},
|
|
791
|
+
});
|
|
792
|
+
}
|
|
793
|
+
|
|
794
|
+
export function useDeleteGroupMemory() {
|
|
795
|
+
const queryClient = useQueryClient();
|
|
796
|
+
|
|
797
|
+
return useMutation({
|
|
798
|
+
mutationFn: async ({ serverId, memoryId }: { serverId: UUID; memoryId: UUID }) => {
|
|
799
|
+
await elizaClient.messaging.deleteMessage(serverId, memoryId);
|
|
800
|
+
return { serverId };
|
|
801
|
+
},
|
|
802
|
+
onSuccess: ({ serverId }) => {
|
|
803
|
+
queryClient.invalidateQueries({ queryKey: ['groupmessages', serverId] });
|
|
804
|
+
},
|
|
805
|
+
});
|
|
806
|
+
}
|
|
807
|
+
|
|
808
|
+
export function useClearGroupChat() {
|
|
809
|
+
const queryClient = useQueryClient();
|
|
810
|
+
|
|
811
|
+
return useMutation({
|
|
812
|
+
mutationFn: async (serverId: UUID) => {
|
|
813
|
+
await elizaClient.messaging.clearChannelHistory(serverId);
|
|
814
|
+
return { serverId };
|
|
815
|
+
},
|
|
816
|
+
onSuccess: ({ serverId }) => {
|
|
817
|
+
queryClient.invalidateQueries({ queryKey: ['groupmessages', serverId] });
|
|
818
|
+
},
|
|
819
|
+
});
|
|
820
|
+
}
|
|
821
|
+
|
|
822
|
+
// REMOVED: useRooms - Client should use channels, not rooms
|
|
823
|
+
// Rooms are an agent-only abstraction
|
|
824
|
+
|
|
825
|
+
// Hook for fetching agent panels (public GET routes)
|
|
826
|
+
/**
|
|
827
|
+
* Custom hook to fetch public GET routes (panels) for a specific agent.
|
|
828
|
+
* @param {UUID | undefined | null} agentId - The ID of the agent.
|
|
829
|
+
* @param {object} options - Optional TanStack Query options.
|
|
830
|
+
* @returns {QueryResult} The result of the query containing agent panels.
|
|
831
|
+
*/
|
|
832
|
+
export type AgentPanel = {
|
|
833
|
+
id: string;
|
|
834
|
+
name: string;
|
|
835
|
+
url: string;
|
|
836
|
+
type: string;
|
|
837
|
+
};
|
|
838
|
+
|
|
839
|
+
export function useAgentPanels(agentId: UUID | undefined | null, options = {}) {
|
|
840
|
+
const network = useNetworkStatus();
|
|
841
|
+
|
|
842
|
+
return useQuery<{
|
|
843
|
+
success: boolean;
|
|
844
|
+
data: AgentPanel[];
|
|
845
|
+
error?: { code: string; message: string; details?: string };
|
|
846
|
+
}>({
|
|
847
|
+
queryKey: ['agentPanels', agentId],
|
|
848
|
+
queryFn: async () => {
|
|
849
|
+
if (!agentId) throw new Error('Agent ID required');
|
|
850
|
+
const result = await elizaClient.agents.getAgentPanels(agentId);
|
|
851
|
+
return { success: true, data: result.panels };
|
|
852
|
+
},
|
|
853
|
+
enabled: Boolean(agentId),
|
|
854
|
+
staleTime: STALE_TIMES.STANDARD, // Panels are unlikely to change very frequently
|
|
855
|
+
refetchInterval: !network.isOffline && Boolean(agentId) ? STALE_TIMES.RARE : false,
|
|
856
|
+
refetchIntervalInBackground: false,
|
|
857
|
+
...(!network.isOffline &&
|
|
858
|
+
network.effectiveType === 'slow-2g' && {
|
|
859
|
+
refetchInterval: STALE_TIMES.NEVER, // Or even disable for slow connections
|
|
860
|
+
}),
|
|
861
|
+
...options,
|
|
862
|
+
});
|
|
863
|
+
}
|
|
864
|
+
|
|
865
|
+
/**
|
|
866
|
+
* Custom hook that combines useAgents with individual useAgent calls for detailed data
|
|
867
|
+
* @returns {AgentsWithDetailsResult} Combined query results with both list and detailed data
|
|
868
|
+
*/
|
|
869
|
+
interface AgentsWithDetailsResult {
|
|
870
|
+
data: {
|
|
871
|
+
agents: Agent[];
|
|
872
|
+
};
|
|
873
|
+
isLoading: boolean;
|
|
874
|
+
isError: boolean;
|
|
875
|
+
error: unknown;
|
|
876
|
+
}
|
|
877
|
+
|
|
878
|
+
/**
|
|
879
|
+
* Fetches a list of agents with detailed information for each agent in parallel.
|
|
880
|
+
*
|
|
881
|
+
* Combines the agent list from {@link useAgents} with individual agent detail queries using `useQueries`, aggregating loading and error states. Polling intervals adapt to network conditions.
|
|
882
|
+
*
|
|
883
|
+
* @returns An object containing detailed agent data, loading and error states, and any encountered error.
|
|
884
|
+
*/
|
|
885
|
+
export function useAgentsWithDetails(): AgentsWithDetailsResult {
|
|
886
|
+
const network = useNetworkStatus();
|
|
887
|
+
const { data: agentsData, isLoading: isAgentsLoading } = useAgents();
|
|
888
|
+
const agentIds = agentsData?.data?.agents?.map((agent) => agent.id as UUID) || [];
|
|
889
|
+
|
|
890
|
+
// Use useQueries for parallel fetching
|
|
891
|
+
const agentQueries = useQueries<UseQueryResult<{ data: Agent }, Error>[]>({
|
|
892
|
+
queries: agentIds.map((id) => ({
|
|
893
|
+
queryKey: ['agent', id] as const,
|
|
894
|
+
queryFn: async () => {
|
|
895
|
+
const result = await elizaClient.agents.getAgent(id);
|
|
896
|
+
return { data: result };
|
|
897
|
+
},
|
|
898
|
+
staleTime: STALE_TIMES.FREQUENT,
|
|
899
|
+
enabled: Boolean(id),
|
|
900
|
+
refetchInterval: !network.isOffline && Boolean(id) ? STALE_TIMES.FREQUENT : false,
|
|
901
|
+
refetchIntervalInBackground: false,
|
|
902
|
+
...(!network.isOffline &&
|
|
903
|
+
network.effectiveType === 'slow-2g' && {
|
|
904
|
+
refetchInterval: STALE_TIMES.STANDARD,
|
|
905
|
+
}),
|
|
906
|
+
})),
|
|
907
|
+
});
|
|
908
|
+
|
|
909
|
+
// Safely check loading and error states
|
|
910
|
+
const isLoading = isAgentsLoading || agentQueries.some((query) => query.isLoading);
|
|
911
|
+
const isError = agentQueries.some((query) => query.isError);
|
|
912
|
+
const error = agentQueries.find((query) => query.error)?.error;
|
|
913
|
+
|
|
914
|
+
// Safely collect agent details
|
|
915
|
+
const detailedAgents = agentQueries
|
|
916
|
+
.filter((query): query is UseQueryResult<{ data: Agent }, Error> & { data: { data: Agent } } =>
|
|
917
|
+
Boolean(query.data?.data)
|
|
918
|
+
)
|
|
919
|
+
.map((query) => query.data.data);
|
|
920
|
+
|
|
921
|
+
return {
|
|
922
|
+
data: {
|
|
923
|
+
agents: detailedAgents,
|
|
924
|
+
},
|
|
925
|
+
isLoading,
|
|
926
|
+
isError,
|
|
927
|
+
error,
|
|
928
|
+
};
|
|
929
|
+
}
|
|
930
|
+
|
|
931
|
+
// --- Hooks for Admin/Debug (Agent-Perspective Data) ---
|
|
932
|
+
export function useAgentInternalActions(
|
|
933
|
+
agentId: UUID | null,
|
|
934
|
+
agentPerspectiveRoomId?: UUID | null
|
|
935
|
+
) {
|
|
936
|
+
return useQuery<AgentLog[], Error>({
|
|
937
|
+
queryKey: ['agentInternalActions', agentId, agentPerspectiveRoomId],
|
|
938
|
+
queryFn: async () => {
|
|
939
|
+
if (!agentId) return []; // Or throw error, depending on desired behavior for null agentId
|
|
940
|
+
const response = await elizaClient.agents.getAgentLogs(agentId, {
|
|
941
|
+
limit: 50,
|
|
942
|
+
});
|
|
943
|
+
// Map the API logs to client format
|
|
944
|
+
return response ? response.map(mapApiLogToClient) : [];
|
|
945
|
+
},
|
|
946
|
+
enabled: !!agentId, // Only enable if agentId is present
|
|
947
|
+
staleTime: STALE_TIMES.FREQUENT,
|
|
948
|
+
refetchInterval: 5000,
|
|
949
|
+
});
|
|
950
|
+
}
|
|
951
|
+
|
|
952
|
+
export function useDeleteAgentInternalLog() {
|
|
953
|
+
const queryClient = useQueryClient();
|
|
954
|
+
const { toast } = useToast();
|
|
955
|
+
return useMutation<void, Error, { agentId: UUID; logId: UUID }>({
|
|
956
|
+
mutationFn: async ({ agentId, logId }: { agentId: UUID; logId: UUID }) => {
|
|
957
|
+
await elizaClient.agents.deleteAgentLog(agentId, logId);
|
|
958
|
+
},
|
|
959
|
+
onSuccess: (_, { agentId }) => {
|
|
960
|
+
queryClient.invalidateQueries({ queryKey: ['agentInternalActions', agentId] });
|
|
961
|
+
queryClient.invalidateQueries({
|
|
962
|
+
queryKey: ['agentInternalActions', agentId, undefined],
|
|
963
|
+
exact: false,
|
|
964
|
+
});
|
|
965
|
+
toast({ title: 'Log Deleted', description: 'The agent log entry has been removed' });
|
|
966
|
+
},
|
|
967
|
+
onError: (error) => {
|
|
968
|
+
toast({
|
|
969
|
+
title: 'Error Deleting Log',
|
|
970
|
+
description: error instanceof Error ? error.message : 'Failed to delete agent log',
|
|
971
|
+
variant: 'destructive',
|
|
972
|
+
});
|
|
973
|
+
},
|
|
974
|
+
});
|
|
975
|
+
}
|
|
976
|
+
|
|
977
|
+
export function useAgentInternalMemories(
|
|
978
|
+
agentId: UUID | null,
|
|
979
|
+
agentPerspectiveRoomId: UUID | null,
|
|
980
|
+
tableName: string = 'messages',
|
|
981
|
+
includeEmbedding = false
|
|
982
|
+
) {
|
|
983
|
+
return useQuery<CoreMemory[], Error>({
|
|
984
|
+
queryKey: [
|
|
985
|
+
'agentInternalMemories',
|
|
986
|
+
agentId,
|
|
987
|
+
agentPerspectiveRoomId,
|
|
988
|
+
tableName,
|
|
989
|
+
includeEmbedding,
|
|
990
|
+
],
|
|
991
|
+
queryFn: async () => {
|
|
992
|
+
if (!agentId || !agentPerspectiveRoomId) return Promise.resolve([]);
|
|
993
|
+
const response = await elizaClient.memory.getAgentInternalMemories(
|
|
994
|
+
agentId,
|
|
995
|
+
agentPerspectiveRoomId,
|
|
996
|
+
includeEmbedding
|
|
997
|
+
);
|
|
998
|
+
return response.data || [];
|
|
999
|
+
},
|
|
1000
|
+
enabled: !!agentId && !!agentPerspectiveRoomId,
|
|
1001
|
+
staleTime: STALE_TIMES.STANDARD,
|
|
1002
|
+
});
|
|
1003
|
+
}
|
|
1004
|
+
|
|
1005
|
+
export function useDeleteAgentInternalMemory() {
|
|
1006
|
+
const queryClient = useQueryClient();
|
|
1007
|
+
const { toast } = useToast();
|
|
1008
|
+
return useMutation<{ agentId: UUID; memoryId: UUID }, Error, { agentId: UUID; memoryId: UUID }>({
|
|
1009
|
+
mutationFn: async ({ agentId, memoryId }: { agentId: UUID; memoryId: UUID }) => {
|
|
1010
|
+
await elizaClient.memory.deleteAgentInternalMemory(agentId, memoryId);
|
|
1011
|
+
return { agentId, memoryId };
|
|
1012
|
+
},
|
|
1013
|
+
onSuccess: (_data, variables) => {
|
|
1014
|
+
toast({
|
|
1015
|
+
title: 'Memory Deleted',
|
|
1016
|
+
description: `Agent memory ${variables.memoryId} removed.`,
|
|
1017
|
+
});
|
|
1018
|
+
queryClient.invalidateQueries({ queryKey: ['agentInternalMemories', variables.agentId] });
|
|
1019
|
+
// More specific invalidation if needed:
|
|
1020
|
+
// queryClient.invalidateQueries({ queryKey: ['agentInternalMemories', variables.agentId, variables.memoryData?.roomId] });
|
|
1021
|
+
},
|
|
1022
|
+
onError: (error) => {
|
|
1023
|
+
toast({
|
|
1024
|
+
title: 'Error Deleting Memory',
|
|
1025
|
+
description: error instanceof Error ? error.message : 'Failed to delete agent memory',
|
|
1026
|
+
variant: 'destructive',
|
|
1027
|
+
});
|
|
1028
|
+
},
|
|
1029
|
+
});
|
|
1030
|
+
}
|
|
1031
|
+
|
|
1032
|
+
export function useDeleteAllAgentInternalMemories() {
|
|
1033
|
+
const queryClient = useQueryClient();
|
|
1034
|
+
const { toast } = useToast();
|
|
1035
|
+
return useMutation<
|
|
1036
|
+
{ agentId: UUID; agentPerspectiveRoomId: UUID },
|
|
1037
|
+
Error,
|
|
1038
|
+
{ agentId: UUID; agentPerspectiveRoomId: UUID }
|
|
1039
|
+
>({
|
|
1040
|
+
mutationFn: async ({ agentId, agentPerspectiveRoomId }) => {
|
|
1041
|
+
await elizaClient.memory.deleteAllAgentInternalMemories(agentId, agentPerspectiveRoomId);
|
|
1042
|
+
return { agentId, agentPerspectiveRoomId };
|
|
1043
|
+
},
|
|
1044
|
+
onSuccess: (_data, variables) => {
|
|
1045
|
+
toast({
|
|
1046
|
+
title: 'All Memories Deleted',
|
|
1047
|
+
description: `All memories for agent in room perspective ${variables.agentPerspectiveRoomId} cleared.`,
|
|
1048
|
+
});
|
|
1049
|
+
queryClient.invalidateQueries({
|
|
1050
|
+
queryKey: ['agentInternalMemories', variables.agentId, variables.agentPerspectiveRoomId],
|
|
1051
|
+
});
|
|
1052
|
+
},
|
|
1053
|
+
onError: (error) => {
|
|
1054
|
+
toast({
|
|
1055
|
+
title: 'Error Clearing Memories',
|
|
1056
|
+
description: error instanceof Error ? error.message : 'Failed to clear agent memories',
|
|
1057
|
+
variant: 'destructive',
|
|
1058
|
+
});
|
|
1059
|
+
},
|
|
1060
|
+
});
|
|
1061
|
+
}
|
|
1062
|
+
|
|
1063
|
+
export function useUpdateAgentInternalMemory() {
|
|
1064
|
+
const queryClient = useQueryClient();
|
|
1065
|
+
const { toast } = useToast();
|
|
1066
|
+
return useMutation<
|
|
1067
|
+
{
|
|
1068
|
+
agentId: UUID;
|
|
1069
|
+
memoryId: string;
|
|
1070
|
+
response: { success: boolean; data: { id: UUID; message: string } };
|
|
1071
|
+
},
|
|
1072
|
+
Error,
|
|
1073
|
+
{ agentId: UUID; memoryId: UUID; memoryData: Partial<CoreMemory> }
|
|
1074
|
+
>({
|
|
1075
|
+
mutationFn: async ({ agentId, memoryId, memoryData }) => {
|
|
1076
|
+
const response = await elizaClient.memory.updateAgentInternalMemory(
|
|
1077
|
+
agentId,
|
|
1078
|
+
memoryId,
|
|
1079
|
+
memoryData
|
|
1080
|
+
);
|
|
1081
|
+
return { agentId, memoryId, response };
|
|
1082
|
+
},
|
|
1083
|
+
onSuccess: (_data, variables) => {
|
|
1084
|
+
toast({
|
|
1085
|
+
title: 'Memory Updated',
|
|
1086
|
+
description: `Agent memory ${variables.memoryId} updated.`,
|
|
1087
|
+
});
|
|
1088
|
+
queryClient.invalidateQueries({ queryKey: ['agentInternalMemories', variables.agentId] });
|
|
1089
|
+
},
|
|
1090
|
+
onError: (error) => {
|
|
1091
|
+
toast({
|
|
1092
|
+
title: 'Error Updating Memory',
|
|
1093
|
+
description: error instanceof Error ? error.message : 'Failed to update agent memory',
|
|
1094
|
+
variant: 'destructive',
|
|
1095
|
+
});
|
|
1096
|
+
},
|
|
1097
|
+
});
|
|
1098
|
+
}
|
|
1099
|
+
|
|
1100
|
+
// --- Hooks for Servers and Channels (GUI Navigation) ---
|
|
1101
|
+
export function useServers(options = {}) {
|
|
1102
|
+
const network = useNetworkStatus();
|
|
1103
|
+
return useQuery<{ data: { servers: ClientMessageServer[] } }>({
|
|
1104
|
+
queryKey: ['servers'],
|
|
1105
|
+
queryFn: async () => {
|
|
1106
|
+
const result = await elizaClient.messaging.listServers();
|
|
1107
|
+
return { data: { servers: mapApiServersToClient(result.servers) } };
|
|
1108
|
+
},
|
|
1109
|
+
staleTime: STALE_TIMES.RARE,
|
|
1110
|
+
refetchInterval: !network.isOffline ? STALE_TIMES.RARE : false,
|
|
1111
|
+
...options,
|
|
1112
|
+
});
|
|
1113
|
+
}
|
|
1114
|
+
|
|
1115
|
+
export function useChannels(serverId: UUID | undefined, options = {}) {
|
|
1116
|
+
const network = useNetworkStatus();
|
|
1117
|
+
return useQuery<{ data: { channels: ClientMessageChannel[] } }>({
|
|
1118
|
+
queryKey: ['channels', serverId],
|
|
1119
|
+
queryFn: async () => {
|
|
1120
|
+
if (!serverId) return Promise.resolve({ data: { channels: [] } });
|
|
1121
|
+
const result = await elizaClient.messaging.getServerChannels(serverId);
|
|
1122
|
+
return { data: { channels: mapApiChannelsToClient(result.channels) } };
|
|
1123
|
+
},
|
|
1124
|
+
enabled: !!serverId,
|
|
1125
|
+
staleTime: STALE_TIMES.STANDARD,
|
|
1126
|
+
refetchInterval: !network.isOffline && !!serverId ? STALE_TIMES.STANDARD : false,
|
|
1127
|
+
...options,
|
|
1128
|
+
});
|
|
1129
|
+
}
|
|
1130
|
+
|
|
1131
|
+
export function useChannelDetails(channelId: UUID | undefined, options = {}) {
|
|
1132
|
+
// Allow undefined
|
|
1133
|
+
const network = useNetworkStatus();
|
|
1134
|
+
return useQuery<{ success: boolean; data: ClientMessageChannel | null }>({
|
|
1135
|
+
queryKey: ['channelDetails', channelId],
|
|
1136
|
+
queryFn: async () => {
|
|
1137
|
+
if (!channelId) return Promise.resolve({ success: true, data: null });
|
|
1138
|
+
const result = await elizaClient.messaging.getChannelDetails(channelId);
|
|
1139
|
+
return { success: true, data: mapApiChannelToClient(result) };
|
|
1140
|
+
},
|
|
1141
|
+
enabled: !!channelId,
|
|
1142
|
+
staleTime: STALE_TIMES.STANDARD,
|
|
1143
|
+
refetchInterval: !network.isOffline && !!channelId ? STALE_TIMES.RARE : false,
|
|
1144
|
+
...options,
|
|
1145
|
+
});
|
|
1146
|
+
}
|
|
1147
|
+
|
|
1148
|
+
export function useChannelParticipants(channelId: UUID | undefined, options = {}) {
|
|
1149
|
+
// Allow undefined
|
|
1150
|
+
const network = useNetworkStatus();
|
|
1151
|
+
return useQuery<{ success: boolean; data: UUID[] }>({
|
|
1152
|
+
queryKey: ['channelParticipants', channelId],
|
|
1153
|
+
queryFn: async () => {
|
|
1154
|
+
if (!channelId) return Promise.resolve({ success: true, data: [] });
|
|
1155
|
+
try {
|
|
1156
|
+
const result = await elizaClient.messaging.getChannelParticipants(channelId);
|
|
1157
|
+
|
|
1158
|
+
// Handle different possible response formats
|
|
1159
|
+
let participants = [];
|
|
1160
|
+
if (result && Array.isArray(result.participants)) {
|
|
1161
|
+
participants = result.participants.map((participant) => participant.userId);
|
|
1162
|
+
} else if (result && Array.isArray(result)) {
|
|
1163
|
+
// If result is directly an array
|
|
1164
|
+
participants = result.map(
|
|
1165
|
+
(participant) => participant.userId || participant.id || participant
|
|
1166
|
+
);
|
|
1167
|
+
}
|
|
1168
|
+
return { success: true, data: participants };
|
|
1169
|
+
} catch (error) {
|
|
1170
|
+
console.error('[useChannelParticipants] Error:', error);
|
|
1171
|
+
return { success: false, data: [] };
|
|
1172
|
+
}
|
|
1173
|
+
},
|
|
1174
|
+
enabled: !!channelId,
|
|
1175
|
+
staleTime: STALE_TIMES.STANDARD,
|
|
1176
|
+
refetchInterval: !network.isOffline && !!channelId ? STALE_TIMES.FREQUENT : false,
|
|
1177
|
+
...options,
|
|
1178
|
+
});
|
|
1179
|
+
}
|
|
1180
|
+
|
|
1181
|
+
export function useDeleteChannelMessage() {
|
|
1182
|
+
const queryClient = useQueryClient();
|
|
1183
|
+
const { toast } = useToast();
|
|
1184
|
+
return useMutation<
|
|
1185
|
+
{ channelId: UUID; messageId: UUID },
|
|
1186
|
+
Error,
|
|
1187
|
+
{ channelId: UUID; messageId: UUID }
|
|
1188
|
+
>({
|
|
1189
|
+
mutationFn: async ({ channelId, messageId }) => {
|
|
1190
|
+
await elizaClient.messaging.deleteMessage(channelId, messageId);
|
|
1191
|
+
return { channelId, messageId };
|
|
1192
|
+
},
|
|
1193
|
+
onSuccess: (_data, variables) => {
|
|
1194
|
+
toast({
|
|
1195
|
+
title: 'Message Deleted',
|
|
1196
|
+
description: 'Message removed successfully.',
|
|
1197
|
+
});
|
|
1198
|
+
},
|
|
1199
|
+
onError: (error) => {
|
|
1200
|
+
toast({
|
|
1201
|
+
title: 'Error Deleting Message',
|
|
1202
|
+
description: error instanceof Error ? error.message : 'Failed to delete message',
|
|
1203
|
+
variant: 'destructive',
|
|
1204
|
+
});
|
|
1205
|
+
},
|
|
1206
|
+
});
|
|
1207
|
+
}
|
|
1208
|
+
|
|
1209
|
+
export function useClearChannelMessages() {
|
|
1210
|
+
const queryClient = useQueryClient();
|
|
1211
|
+
const { toast } = useToast();
|
|
1212
|
+
return useMutation<{ channelId: UUID }, Error, UUID>({
|
|
1213
|
+
mutationFn: async (channelId: UUID) => {
|
|
1214
|
+
await elizaClient.messaging.clearChannelHistory(channelId);
|
|
1215
|
+
return { channelId };
|
|
1216
|
+
},
|
|
1217
|
+
onSuccess: (_data, variables_channelId) => {
|
|
1218
|
+
toast({
|
|
1219
|
+
title: 'Channel Cleared',
|
|
1220
|
+
description: `All messages in channel ${variables_channelId} cleared.`,
|
|
1221
|
+
});
|
|
1222
|
+
queryClient.invalidateQueries({ queryKey: ['messages', variables_channelId] });
|
|
1223
|
+
queryClient.setQueryData(['messages', variables_channelId], () => []);
|
|
1224
|
+
},
|
|
1225
|
+
onError: (error) => {
|
|
1226
|
+
toast({
|
|
1227
|
+
title: 'Error Clearing Channel',
|
|
1228
|
+
description: error instanceof Error ? error.message : 'Failed to clear messages',
|
|
1229
|
+
variant: 'destructive',
|
|
1230
|
+
});
|
|
1231
|
+
},
|
|
1232
|
+
});
|
|
1233
|
+
}
|
|
1234
|
+
|
|
1235
|
+
export function useDeleteChannel() {
|
|
1236
|
+
const queryClient = useQueryClient();
|
|
1237
|
+
const { toast } = useToast();
|
|
1238
|
+
const navigate = useNavigate();
|
|
1239
|
+
|
|
1240
|
+
return useMutation<void, Error, { channelId: UUID; serverId: UUID }>({
|
|
1241
|
+
mutationFn: async ({ channelId }) => {
|
|
1242
|
+
await elizaClient.messaging.deleteChannel(channelId);
|
|
1243
|
+
},
|
|
1244
|
+
onSuccess: (_data, variables) => {
|
|
1245
|
+
toast({
|
|
1246
|
+
title: 'Group Deleted',
|
|
1247
|
+
description: 'The group has been successfully deleted.',
|
|
1248
|
+
});
|
|
1249
|
+
// Invalidate channel queries
|
|
1250
|
+
queryClient.invalidateQueries({ queryKey: ['channels', variables.serverId] });
|
|
1251
|
+
queryClient.invalidateQueries({ queryKey: ['channels'] });
|
|
1252
|
+
// Navigate back to home
|
|
1253
|
+
navigate('/');
|
|
1254
|
+
},
|
|
1255
|
+
onError: (error) => {
|
|
1256
|
+
toast({
|
|
1257
|
+
title: 'Error Deleting Group',
|
|
1258
|
+
description: error instanceof Error ? error.message : 'Failed to delete group',
|
|
1259
|
+
variant: 'destructive',
|
|
1260
|
+
});
|
|
1261
|
+
},
|
|
1262
|
+
});
|
|
1263
|
+
}
|