@newfold/wp-module-ai-chat 1.0.1 → 1.0.2

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 (54) hide show
  1. package/build/index-rtl.css +1 -0
  2. package/build/index.asset.php +1 -0
  3. package/build/index.css +1 -0
  4. package/build/index.js +8 -0
  5. package/package.json +11 -9
  6. package/src/components/chat/ChatHeader.jsx +0 -63
  7. package/src/components/chat/ChatHistoryDropdown.jsx +0 -182
  8. package/src/components/chat/ChatHistoryList.jsx +0 -257
  9. package/src/components/chat/ChatInput.jsx +0 -157
  10. package/src/components/chat/ChatMessage.jsx +0 -157
  11. package/src/components/chat/ChatMessages.jsx +0 -137
  12. package/src/components/chat/WelcomeScreen.jsx +0 -115
  13. package/src/components/icons/CloseIcon.jsx +0 -27
  14. package/src/components/icons/SparklesOutlineIcon.jsx +0 -30
  15. package/src/components/icons/index.js +0 -5
  16. package/src/components/ui/AILogo.jsx +0 -47
  17. package/src/components/ui/BluBetaHeading.jsx +0 -18
  18. package/src/components/ui/ErrorAlert.jsx +0 -30
  19. package/src/components/ui/HeaderBar.jsx +0 -34
  20. package/src/components/ui/SuggestionButton.jsx +0 -28
  21. package/src/components/ui/ToolExecutionList.jsx +0 -264
  22. package/src/components/ui/TypingIndicator.jsx +0 -268
  23. package/src/constants/nfdAgents/input.js +0 -13
  24. package/src/constants/nfdAgents/storageKeys.js +0 -102
  25. package/src/constants/nfdAgents/typingStatus.js +0 -40
  26. package/src/constants/nfdAgents/websocket.js +0 -44
  27. package/src/hooks/useAIChat.js +0 -432
  28. package/src/hooks/useNfdAgentsWebSocket.js +0 -964
  29. package/src/index.js +0 -66
  30. package/src/services/mcpClient.js +0 -433
  31. package/src/services/openaiClient.js +0 -416
  32. package/src/styles/_branding.scss +0 -151
  33. package/src/styles/_history.scss +0 -180
  34. package/src/styles/_input.scss +0 -170
  35. package/src/styles/_messages.scss +0 -272
  36. package/src/styles/_mixins.scss +0 -21
  37. package/src/styles/_typing-indicator.scss +0 -162
  38. package/src/styles/_ui.scss +0 -173
  39. package/src/styles/_vars.scss +0 -103
  40. package/src/styles/_welcome.scss +0 -81
  41. package/src/styles/app.scss +0 -10
  42. package/src/utils/helpers.js +0 -75
  43. package/src/utils/markdownParser.js +0 -319
  44. package/src/utils/nfdAgents/archiveConversation.js +0 -82
  45. package/src/utils/nfdAgents/chatHistoryList.js +0 -130
  46. package/src/utils/nfdAgents/configFetcher.js +0 -137
  47. package/src/utils/nfdAgents/greeting.js +0 -55
  48. package/src/utils/nfdAgents/jwtUtils.js +0 -59
  49. package/src/utils/nfdAgents/messageHandler.js +0 -328
  50. package/src/utils/nfdAgents/storage.js +0 -112
  51. package/src/utils/nfdAgents/typingIndicatorToolDisplay.js +0 -180
  52. package/src/utils/nfdAgents/url.js +0 -101
  53. package/src/utils/restApi.js +0 -87
  54. package/src/utils/sanitizeHtml.js +0 -94
@@ -1,44 +0,0 @@
1
- /**
2
- * NFD Agents WebSocket Configuration
3
- *
4
- * Constants for WebSocket connection, reconnection, typing indicator timeout,
5
- * and JWT refresh. Storage key construction lives in storageKeys.js (getChatHistoryStorageKeys).
6
- */
7
-
8
- /** WebSocket close code: authentication failed (e.g. expired token). */
9
- export const WS_CLOSE_AUTH_FAILED = 4000;
10
-
11
- /** WebSocket close code: missing authentication token. */
12
- export const WS_CLOSE_MISSING_TOKEN = 4001;
13
-
14
- /** Buffer (ms) before JWT exp at which to proactively refresh (e.g. 5 min). */
15
- const FIVE_MINUTES_MS = 5 * 60 * 1000;
16
-
17
- /** Never schedule proactive refresh sooner than this (ms). */
18
- const FIVE_MINUTES_MIN_DELAY_MS = 5 * 60 * 1000;
19
-
20
- /** Don't schedule another proactive refresh sooner than this after the last one (ms). */
21
- const ONE_HOUR_MS = 60 * 60 * 1000;
22
-
23
- /** Don't clear config + reset attempts on 4000/4001 more than once per this window (ms). */
24
- const AUTH_REFRESH_COOLDOWN_MS = 3 * 60 * 1000;
25
-
26
- /** Consider token expired if exp is within this many ms from now (pre-connect check). */
27
- const JWT_EXPIRED_BUFFER_MS = 60 * 1000;
28
-
29
- /** When proactive refresh fires during an in-flight reply, reschedule after this many ms. */
30
- const JWT_PROACTIVE_REFRESH_DEFER_MS = 30 * 1000;
31
-
32
- export const NFD_AGENTS_WEBSOCKET = {
33
- MAX_RECONNECT_ATTEMPTS: 5,
34
- RECONNECT_DELAY: 1000, // Base delay between reconnect attempts (ms)
35
- TYPING_TIMEOUT: 60000, // Hide typing indicator if no response within this time (ms)
36
- WS_CLOSE_AUTH_FAILED,
37
- WS_CLOSE_MISSING_TOKEN,
38
- JWT_REFRESH_BUFFER_MS: FIVE_MINUTES_MS,
39
- JWT_REFRESH_MIN_DELAY_MS: FIVE_MINUTES_MIN_DELAY_MS,
40
- JWT_PROACTIVE_REFRESH_COOLDOWN_MS: ONE_HOUR_MS,
41
- AUTH_REFRESH_COOLDOWN_MS,
42
- JWT_EXPIRED_BUFFER_MS,
43
- JWT_PROACTIVE_REFRESH_DEFER_MS,
44
- };
@@ -1,432 +0,0 @@
1
- /**
2
- * WordPress dependencies
3
- */
4
- import { useState, useCallback, useRef, useEffect } from "@wordpress/element";
5
-
6
- /**
7
- * Internal dependencies
8
- */
9
- import { generateSessionId } from "../utils/helpers";
10
-
11
- /**
12
- * Chat status enumeration
13
- */
14
- export const CHAT_STATUS = {
15
- IDLE: "idle",
16
- RECEIVED: "received",
17
- GENERATING: "generating",
18
- TOOL_CALL: "tool_call",
19
- SUMMARIZING: "summarizing",
20
- COMPLETED: "completed",
21
- FAILED: "failed",
22
- };
23
-
24
- /**
25
- * Default system prompt
26
- */
27
- const DEFAULT_SYSTEM_PROMPT = `You are a helpful AI assistant. Be concise and helpful in your responses.`;
28
-
29
- /**
30
- * useAIChat Hook
31
- *
32
- * A configurable React hook for managing AI chat conversations.
33
- * Provides extension points for tool handling and message processing.
34
- *
35
- * @param {Object} options - Hook configuration options
36
- * @param {Object} options.mcpClient - MCP client instance for tool execution
37
- * @param {Object} options.openaiClient - OpenAI client instance for chat completions
38
- * @param {string} options.systemPrompt - System prompt for the AI
39
- * @param {Function} options.onToolCall - Callback before tool execution (can intercept)
40
- * @param {Function} options.onToolResult - Callback after tool execution (for glue code)
41
- * @param {Function} options.onMessageComplete - Callback when a message is complete
42
- * @param {Function} options.onError - Callback for errors
43
- * @param {boolean} options.autoInitialize - Auto-initialize MCP client (default: true)
44
- * @return {Object} Chat state and controls
45
- */
46
- export const useAIChat = ({
47
- mcpClient = null,
48
- openaiClient = null,
49
- systemPrompt = DEFAULT_SYSTEM_PROMPT,
50
- onToolCall = null,
51
- onToolResult = null,
52
- onMessageComplete = null,
53
- onError = null,
54
- autoInitialize = true,
55
- } = {}) => {
56
- // Chat state
57
- const [messages, setMessages] = useState([]);
58
- const [isLoading, setIsLoading] = useState(false);
59
- const [error, setError] = useState(null);
60
- const [status, setStatus] = useState(CHAT_STATUS.IDLE);
61
- const [sessionId, setSessionId] = useState(() => generateSessionId());
62
-
63
- // Tool execution state
64
- const [activeToolCall, setActiveToolCall] = useState(null);
65
- const [executedTools, setExecutedTools] = useState([]);
66
- const [pendingTools, setPendingTools] = useState([]);
67
- const [toolProgress, setToolProgress] = useState(null);
68
-
69
- // MCP state
70
- const [mcpConnected, setMcpConnected] = useState(false);
71
- const [mcpTools, setMcpTools] = useState([]);
72
-
73
- // Refs
74
- const isProcessingRef = useRef(false);
75
- const abortControllerRef = useRef(null);
76
-
77
- /**
78
- * Initialize MCP client
79
- */
80
- const initializeMCP = useCallback(async () => {
81
- if (!mcpClient) {
82
- return false;
83
- }
84
-
85
- try {
86
- if (!mcpClient.isConnected()) {
87
- await mcpClient.connect();
88
- }
89
- await mcpClient.initialize();
90
- setMcpTools(mcpClient.getTools());
91
- setMcpConnected(true);
92
- return true;
93
- } catch (err) {
94
- // eslint-disable-next-line no-console
95
- console.error("Failed to initialize MCP:", err);
96
- setError(`Failed to initialize MCP: ${err.message}`);
97
- onError?.(err);
98
- return false;
99
- }
100
- }, [mcpClient, onError]);
101
-
102
- /**
103
- * Auto-initialize MCP on mount
104
- */
105
- useEffect(() => {
106
- if (autoInitialize && mcpClient) {
107
- initializeMCP();
108
- }
109
-
110
- // Copy ref to variable for cleanup function
111
- const abortController = abortControllerRef.current;
112
-
113
- return () => {
114
- if (abortController) {
115
- abortController.abort();
116
- }
117
- };
118
- }, [autoInitialize, mcpClient, initializeMCP]);
119
-
120
- /**
121
- * Create a new message object
122
- */
123
- const createMessage = useCallback((role, content, extras = {}) => {
124
- return {
125
- id: `${role}-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`,
126
- role,
127
- type: role,
128
- content,
129
- timestamp: new Date(),
130
- ...extras,
131
- };
132
- }, []);
133
-
134
- /**
135
- * Execute a single tool call
136
- *
137
- * The onToolCall callback can return an object with { intercepted: true, result: {...} }
138
- * to handle the tool call locally instead of calling MCP.
139
- */
140
- const executeTool = useCallback(
141
- async (toolCall) => {
142
- if (!mcpClient) {
143
- throw new Error("MCP client not available");
144
- }
145
-
146
- const { name, arguments: args } = toolCall;
147
-
148
- // Call onToolCall - it can optionally intercept and return a result
149
- if (onToolCall) {
150
- const interceptResult = await onToolCall(toolCall);
151
-
152
- // If the callback intercepted the call, use its result
153
- if (interceptResult && interceptResult.intercepted) {
154
- const result = interceptResult.result || {
155
- content: [],
156
- isError: false,
157
- };
158
-
159
- // Notify after execution (for glue code to react to changes)
160
- if (onToolResult) {
161
- await onToolResult(toolCall, result);
162
- }
163
-
164
- return {
165
- id: toolCall.id,
166
- name,
167
- result,
168
- isError: result.isError || false,
169
- hasChanges: result.hasChanges || false,
170
- undoData: interceptResult.undoData,
171
- };
172
- }
173
- }
174
-
175
- try {
176
- const result = await mcpClient.callTool(name, args);
177
-
178
- // Notify after execution (for glue code to react to changes)
179
- if (onToolResult) {
180
- await onToolResult(toolCall, result);
181
- }
182
-
183
- return {
184
- id: toolCall.id,
185
- name,
186
- result,
187
- isError: result.isError || false,
188
- };
189
- } catch (err) {
190
- return {
191
- id: toolCall.id,
192
- name,
193
- error: err.message,
194
- isError: true,
195
- };
196
- }
197
- },
198
- [mcpClient, onToolCall, onToolResult]
199
- );
200
-
201
- /**
202
- * Execute all tool calls
203
- */
204
- const executeToolCalls = useCallback(
205
- async (toolCalls) => {
206
- const results = [];
207
- setPendingTools(toolCalls.slice(1));
208
-
209
- for (let i = 0; i < toolCalls.length; i++) {
210
- const toolCall = toolCalls[i];
211
-
212
- setActiveToolCall({
213
- ...toolCall,
214
- index: i + 1,
215
- total: toolCalls.length,
216
- });
217
-
218
- const result = await executeTool(toolCall);
219
- results.push(result);
220
-
221
- setExecutedTools((prev) => [...prev, { ...toolCall, ...result }]);
222
- setPendingTools(toolCalls.slice(i + 2));
223
- }
224
-
225
- setActiveToolCall(null);
226
- setPendingTools([]);
227
- return results;
228
- },
229
- [executeTool]
230
- );
231
-
232
- /**
233
- * Send a message and get AI response
234
- */
235
- const sendMessage = useCallback(
236
- async (userMessage) => {
237
- if (!openaiClient) {
238
- setError("OpenAI client not configured");
239
- return;
240
- }
241
-
242
- if (isProcessingRef.current) {
243
- return;
244
- }
245
-
246
- isProcessingRef.current = true;
247
- setIsLoading(true);
248
- setError(null);
249
- setStatus(CHAT_STATUS.RECEIVED);
250
- setExecutedTools([]);
251
-
252
- // Add user message
253
- const userMsg = createMessage("user", userMessage);
254
- setMessages((prev) => [...prev, userMsg]);
255
-
256
- try {
257
- // Build conversation history
258
- const conversationHistory = [
259
- { role: "system", content: systemPrompt },
260
- ...messages.map((msg) => ({
261
- role: msg.role === "user" ? "user" : "assistant",
262
- content: msg.content,
263
- toolCalls: msg.toolCalls,
264
- toolResults: msg.toolResults,
265
- })),
266
- { role: "user", content: userMessage },
267
- ];
268
-
269
- // Get tools in OpenAI format
270
- const tools = mcpConnected && mcpClient ? mcpClient.getToolsForOpenAI() : [];
271
-
272
- setStatus(CHAT_STATUS.GENERATING);
273
-
274
- let response = null;
275
- let allToolResults = [];
276
-
277
- // Streaming completion
278
- await openaiClient.createStreamingCompletion(
279
- {
280
- messages: openaiClient.convertMessagesToOpenAI(conversationHistory),
281
- tools: tools.length > 0 ? tools : undefined,
282
- tool_choice: tools.length > 0 ? "auto" : undefined,
283
- },
284
- // onChunk - streaming content updates could be added here
285
- () => {},
286
- // onComplete
287
- async (fullMessage, toolCalls) => {
288
- if (toolCalls && toolCalls.length > 0) {
289
- setStatus(CHAT_STATUS.TOOL_CALL);
290
-
291
- // Execute tool calls
292
- const toolResults = await executeToolCalls(toolCalls);
293
- allToolResults = toolResults;
294
-
295
- // Continue conversation with tool results
296
- setStatus(CHAT_STATUS.SUMMARIZING);
297
-
298
- const followUpHistory = [
299
- ...conversationHistory,
300
- {
301
- role: "assistant",
302
- content: fullMessage || null,
303
- toolCalls,
304
- toolResults: toolResults.map((r) => ({
305
- id: r.id,
306
- result: r.result,
307
- error: r.error,
308
- })),
309
- },
310
- ];
311
-
312
- // Get follow-up response
313
- const followUp = await openaiClient.createChatCompletion({
314
- messages: openaiClient.convertMessagesToOpenAI(followUpHistory),
315
- });
316
-
317
- response = followUp.choices?.[0]?.message?.content || "";
318
- } else {
319
- response = fullMessage;
320
- }
321
- },
322
- // onError
323
- (err) => {
324
- throw err;
325
- }
326
- );
327
-
328
- // Add assistant message
329
- if (response) {
330
- const assistantMsg = createMessage("assistant", response, {
331
- toolCalls: allToolResults.length > 0 ? allToolResults : undefined,
332
- executedTools: allToolResults.length > 0 ? allToolResults : undefined,
333
- });
334
- setMessages((prev) => [...prev, assistantMsg]);
335
- onMessageComplete?.(assistantMsg);
336
- }
337
-
338
- setStatus(CHAT_STATUS.COMPLETED);
339
- } catch (err) {
340
- // eslint-disable-next-line no-console
341
- console.error("Chat error:", err);
342
- setError(err.message);
343
- setStatus(CHAT_STATUS.FAILED);
344
- onError?.(err);
345
- } finally {
346
- isProcessingRef.current = false;
347
- setIsLoading(false);
348
- setActiveToolCall(null);
349
- setExecutedTools([]);
350
- setPendingTools([]);
351
- }
352
- },
353
- [
354
- openaiClient,
355
- mcpClient,
356
- mcpConnected,
357
- messages,
358
- systemPrompt,
359
- createMessage,
360
- executeToolCalls,
361
- onMessageComplete,
362
- onError,
363
- ]
364
- );
365
-
366
- /**
367
- * Stop the current request
368
- */
369
- const stopRequest = useCallback(() => {
370
- if (abortControllerRef.current) {
371
- abortControllerRef.current.abort();
372
- }
373
- isProcessingRef.current = false;
374
- setIsLoading(false);
375
- setStatus(CHAT_STATUS.IDLE);
376
- setActiveToolCall(null);
377
- }, []);
378
-
379
- /**
380
- * Clear conversation history
381
- */
382
- const clearMessages = useCallback(() => {
383
- setMessages([]);
384
- setError(null);
385
- setStatus(CHAT_STATUS.IDLE);
386
- setSessionId(generateSessionId());
387
- }, []);
388
-
389
- /**
390
- * Add a message programmatically
391
- */
392
- const addMessage = useCallback(
393
- (role, content, extras = {}) => {
394
- const msg = createMessage(role, content, extras);
395
- setMessages((prev) => [...prev, msg]);
396
- return msg;
397
- },
398
- [createMessage]
399
- );
400
-
401
- return {
402
- // State
403
- messages,
404
- isLoading,
405
- error,
406
- status,
407
- sessionId,
408
-
409
- // Tool execution state
410
- activeToolCall,
411
- executedTools,
412
- pendingTools,
413
- toolProgress,
414
-
415
- // MCP state
416
- mcpConnected,
417
- mcpTools,
418
-
419
- // Actions
420
- sendMessage,
421
- stopRequest,
422
- clearMessages,
423
- addMessage,
424
- initializeMCP,
425
-
426
- // Setters for advanced usage
427
- setError,
428
- setToolProgress,
429
- };
430
- };
431
-
432
- export default useAIChat;