@meetsmore-oss/use-ai-client 1.12.1 → 1.13.1

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/dist/index.d.ts CHANGED
@@ -353,9 +353,8 @@ declare class UseAIClient {
353
353
  private serverUrl;
354
354
  private socket;
355
355
  private eventHandlers;
356
- private reconnectAttempts;
357
- private maxReconnectAttempts;
358
356
  private reconnectDelay;
357
+ private reconnectDelayMax;
359
358
  private _threadId;
360
359
  private _tools;
361
360
  private _messages;
@@ -1022,6 +1021,19 @@ interface UseToolSystemReturn {
1022
1021
  */
1023
1022
  declare function useToolSystem({ clientRef, buildState, }: UseToolSystemOptions): UseToolSystemReturn;
1024
1023
 
1024
+ /**
1025
+ * How the chat textarea should treat the Enter key.
1026
+ *
1027
+ * - `'enter'` — Enter submits the message, Shift+Enter inserts a newline.
1028
+ * Typical desktop behavior.
1029
+ * - `'mod-enter'` — Enter inserts a newline. Cmd/Ctrl+Enter submits the message.
1030
+ * Recommended on mobile, where soft keyboards lack modifier
1031
+ * keys and the user is expected to tap the send button.
1032
+ * ("mod" follows the CodeMirror/ProseMirror convention of
1033
+ * meaning Cmd on macOS and Ctrl elsewhere.)
1034
+ */
1035
+ type SubmitMode = 'enter' | 'mod-enter';
1036
+
1025
1037
  /**
1026
1038
  * Options for programmatically sending a message via sendMessage().
1027
1039
  */
@@ -1158,6 +1170,8 @@ declare const defaultStrings: {
1158
1170
  API_OVERLOADED: string;
1159
1171
  /** Error when rate limited */
1160
1172
  RATE_LIMITED: string;
1173
+ /** Error when the connection was lost while a response was being generated */
1174
+ CONNECTION_LOST: string;
1161
1175
  /** Error for unknown/unexpected errors */
1162
1176
  UNKNOWN_ERROR: string;
1163
1177
  };
@@ -1558,6 +1572,32 @@ interface UseAIProviderProps extends UseAIConfig {
1558
1572
  * ```
1559
1573
  */
1560
1574
  onOpenChange?: (isOpen: boolean) => void;
1575
+ /**
1576
+ * How the built-in chat panel should treat the Enter key.
1577
+ *
1578
+ * - `'enter'` (default): Enter submits, Shift+Enter inserts a newline.
1579
+ * Typical desktop behavior.
1580
+ * - `'mod-enter'`: Enter inserts a newline. Cmd/Ctrl+Enter submits.
1581
+ * Recommended for mobile/touch devices where soft keyboards lack modifier
1582
+ * keys and the user is expected to tap the send button.
1583
+ *
1584
+ * Can be overridden per-instance via the `submitMode` prop on `<UseAIChat>`.
1585
+ *
1586
+ * @default 'enter'
1587
+ *
1588
+ * @example
1589
+ * ```tsx
1590
+ * import { isMobileApp } from '@/lib/is-mobile';
1591
+ *
1592
+ * <UseAIProvider
1593
+ * serverUrl="wss://your-server.com"
1594
+ * submitMode={isMobileApp() ? 'mod-enter' : 'enter'}
1595
+ * >
1596
+ * <App />
1597
+ * </UseAIProvider>
1598
+ * ```
1599
+ */
1600
+ submitMode?: SubmitMode;
1561
1601
  }
1562
1602
  /**
1563
1603
  * Provider component that manages AI client connection and tool registration.
@@ -1587,7 +1627,7 @@ interface UseAIProviderProps extends UseAIConfig {
1587
1627
  * }
1588
1628
  * ```
1589
1629
  */
1590
- declare function UseAIProvider({ serverUrl, children, systemPrompt, CustomButton, CustomChat, chatRepository, forwardedPropsProvider, fileUploadConfig: fileUploadConfigProp, commandRepository, renderChat, theme: customTheme, strings: customStrings, visibleAgentIds, onOpenChange, }: UseAIProviderProps): react_jsx_runtime.JSX.Element;
1630
+ declare function UseAIProvider({ serverUrl, children, systemPrompt, CustomButton, CustomChat, chatRepository, forwardedPropsProvider, fileUploadConfig: fileUploadConfigProp, commandRepository, renderChat, theme: customTheme, strings: customStrings, visibleAgentIds, onOpenChange, submitMode, }: UseAIProviderProps): react_jsx_runtime.JSX.Element;
1591
1631
  /**
1592
1632
  * Hook to access the UseAI context.
1593
1633
  * When used outside a UseAIProvider, returns a no-op context and logs a warning.
@@ -1652,12 +1692,26 @@ interface UseAIChatPanelProps {
1652
1692
  onApproveToolCall?: () => void;
1653
1693
  /** Callback to reject all pending tool calls */
1654
1694
  onRejectToolCall?: (reason?: string) => void;
1695
+ /**
1696
+ * How the textarea should treat the Enter key.
1697
+ *
1698
+ * - `'enter'` (default): Enter submits, Shift+Enter inserts a newline. Suitable
1699
+ * for desktop.
1700
+ * - `'mod-enter'`: Enter inserts a newline. Cmd/Ctrl+Enter submits. Recommended
1701
+ * for mobile, where soft keyboards lack modifier keys and the user is
1702
+ * expected to tap the send button.
1703
+ *
1704
+ * IME composition is always respected regardless of mode.
1705
+ *
1706
+ * @default 'enter'
1707
+ */
1708
+ submitMode?: SubmitMode;
1655
1709
  }
1656
1710
  /**
1657
1711
  * Chat panel content - fills its container.
1658
1712
  * Use directly for embedded mode, or wrap with UseAIFloatingChatWrapper for floating mode.
1659
1713
  */
1660
- declare function UseAIChatPanel({ onSendMessage, messages, loading, connected, streamingText, streamingReasoning, currentChatId, onNewChat, onLoadChat, onDeleteChat, onListChats, onGetChat, suggestions, availableAgents, defaultAgent, selectedAgent, onAgentChange, fileUploadConfig, fileProcessing, commands, onSaveCommand, onRenameCommand, onDeleteCommand, closeButton, executingTool, feedbackEnabled, onFeedback, pendingApprovals, onApproveToolCall, onRejectToolCall, }: UseAIChatPanelProps): react_jsx_runtime.JSX.Element;
1714
+ declare function UseAIChatPanel({ onSendMessage, messages, loading, connected, streamingText, streamingReasoning, currentChatId, onNewChat, onLoadChat, onDeleteChat, onListChats, onGetChat, suggestions, availableAgents, defaultAgent, selectedAgent, onAgentChange, fileUploadConfig, fileProcessing, commands, onSaveCommand, onRenameCommand, onDeleteCommand, closeButton, executingTool, feedbackEnabled, onFeedback, pendingApprovals, onApproveToolCall, onRejectToolCall, submitMode, }: UseAIChatPanelProps): react_jsx_runtime.JSX.Element;
1661
1715
 
1662
1716
  /**
1663
1717
  * Props for the floating chat wrapper.
@@ -1696,6 +1750,14 @@ interface UseAIChatProps {
1696
1750
  * When false (default), renders inline filling its container.
1697
1751
  */
1698
1752
  floating?: boolean;
1753
+ /**
1754
+ * How the chat textarea should treat the Enter key. Overrides the value
1755
+ * provided on `UseAIProvider` for this instance.
1756
+ *
1757
+ * - `'enter'` (default): Enter submits, Shift+Enter inserts a newline.
1758
+ * - `'mod-enter'`: Enter inserts a newline. Cmd/Ctrl+Enter submits.
1759
+ */
1760
+ submitMode?: SubmitMode;
1699
1761
  }
1700
1762
  /**
1701
1763
  * Standalone chat component that can be placed anywhere within UseAIProvider.
@@ -1722,7 +1784,7 @@ interface UseAIChatProps {
1722
1784
  * </UseAIProvider>
1723
1785
  * ```
1724
1786
  */
1725
- declare function UseAIChat({ floating }: UseAIChatProps): react_jsx_runtime.JSX.Element;
1787
+ declare function UseAIChat({ floating, submitMode }: UseAIChatProps): react_jsx_runtime.JSX.Element;
1726
1788
 
1727
1789
  /**
1728
1790
  * LocalStorage-based implementation of ChatRepository.
@@ -2334,6 +2396,12 @@ interface UseServerEventsReturn {
2334
2396
  * (currentToolCalls, currentMessageContent).
2335
2397
  */
2336
2398
  handleServerEvent: (client: UseAIClient, event: AGUIEvent) => Promise<void>;
2399
+ /**
2400
+ * Handles a connection loss. When a disconnect occurs mid-run the server
2401
+ * session is destroyed and the run cannot resume, so we surface an error
2402
+ * message and clear in-flight UI state. No-op when no run is active.
2403
+ */
2404
+ handleDisconnect: () => void;
2337
2405
  }
2338
2406
  /**
2339
2407
  * Hook that owns all server event handling state and logic.
@@ -2469,4 +2537,4 @@ interface UseDropdownStateOptions {
2469
2537
  */
2470
2538
  declare function useDropdownState(options?: UseDropdownStateOptions): UseDropdownStateReturn;
2471
2539
 
2472
- export { type AgentContextValue, type Chat, type ChatContextValue, type ChatMetadata, type ChatPanelProps, type ChatRepository, CloseButton, type CommandContextValue, type CommandRepository, type CreateChatOptions, type CreateCommandOptions, DEFAULT_MAX_FILE_SIZE, type DefinedTool, type DropZoneProps, EmbedFileUploadBackend, type ExecutingToolDisplay, type FileAttachment, type FileProcessingState, type FileProcessingStatus, type FileTransformer, type FileTransformerContext, type FileTransformerMap, type FileUploadBackend, type FileUploadConfig, type FloatingButtonProps, type InlineSaveProps, type ListChatsOptions, type ListCommandsOptions, LocalStorageChatRepository, LocalStorageCommandRepository, type Message, type PendingToolApproval, type PersistedContentPart, type PersistedFileContent, type PersistedFileMetadata, type PersistedMessage, type PersistedMessageContent, type PersistedTextContent, type ProcessAttachmentsConfig, type PromptsContextValue, type RegisterToolsOptions, type SavedCommand, type SendMessageOptions, type ToolExecutionContext, type ToolOptions, type ToolRegistryContextValue, type ToolsDefinition, type TriggerWorkflowOptions, UseAIChat, UseAIChatPanel, type UseAIChatPanelProps, type UseAIChatPanelStrings, type UseAIChatPanelTheme, type UseAIChatProps, UseAIClient, type UseAIConfig, type UseAIContextValue, UseAIFloatingButton, UseAIFloatingChatWrapper, type UseAIOptions, UseAIProvider, type UseAIProviderProps, type UseAIResult, type UseAIStrings, type UseAITheme, type UseAIWorkflowResult, type UseAgentSelectionOptions, type UseAgentSelectionReturn, type UseChatManagementOptions, type UseChatManagementReturn, type UseCommandManagementOptions, type UseCommandManagementReturn, type UseDropdownStateOptions, type UseDropdownStateReturn, type UseFeedbackOptions, type UseFeedbackReturn, type UseFileUploadOptions, type UseFileUploadReturn, type UseMessageQueueOptions, type UseMessageQueueReturn, type UsePromptStateOptions, type UsePromptStateReturn, type UseServerEventsOptions, type UseServerEventsReturn, type UseSlashCommandsOptions, type UseSlashCommandsReturn, type UseToolSystemOptions, type UseToolSystemReturn, type WorkflowProgress, clearTransformationCache, convertToolsToDefinitions, defaultStrings, defaultTheme, defineTool, executeDefinedTool, findTransformerPattern, generateChatId, generateCommandId, generateMessageId, matchesMimeType, processAttachments, useAI, useAIContext, useAIWorkflow, useAgentSelection, useChatManagement, useCommandManagement, useDropdownState, useFeedback, useFileUpload, useMessageQueue, usePromptState, useServerEvents, useSlashCommands, useStableTools, useStrings, useTheme, useToolSystem, validateCommandName };
2540
+ export { type AgentContextValue, type Chat, type ChatContextValue, type ChatMetadata, type ChatPanelProps, type ChatRepository, CloseButton, type CommandContextValue, type CommandRepository, type CreateChatOptions, type CreateCommandOptions, DEFAULT_MAX_FILE_SIZE, type DefinedTool, type DropZoneProps, EmbedFileUploadBackend, type ExecutingToolDisplay, type FileAttachment, type FileProcessingState, type FileProcessingStatus, type FileTransformer, type FileTransformerContext, type FileTransformerMap, type FileUploadBackend, type FileUploadConfig, type FloatingButtonProps, type InlineSaveProps, type ListChatsOptions, type ListCommandsOptions, LocalStorageChatRepository, LocalStorageCommandRepository, type Message, type PendingToolApproval, type PersistedContentPart, type PersistedFileContent, type PersistedFileMetadata, type PersistedMessage, type PersistedMessageContent, type PersistedTextContent, type ProcessAttachmentsConfig, type PromptsContextValue, type RegisterToolsOptions, type SavedCommand, type SendMessageOptions, type SubmitMode, type ToolExecutionContext, type ToolOptions, type ToolRegistryContextValue, type ToolsDefinition, type TriggerWorkflowOptions, UseAIChat, UseAIChatPanel, type UseAIChatPanelProps, type UseAIChatPanelStrings, type UseAIChatPanelTheme, type UseAIChatProps, UseAIClient, type UseAIConfig, type UseAIContextValue, UseAIFloatingButton, UseAIFloatingChatWrapper, type UseAIOptions, UseAIProvider, type UseAIProviderProps, type UseAIResult, type UseAIStrings, type UseAITheme, type UseAIWorkflowResult, type UseAgentSelectionOptions, type UseAgentSelectionReturn, type UseChatManagementOptions, type UseChatManagementReturn, type UseCommandManagementOptions, type UseCommandManagementReturn, type UseDropdownStateOptions, type UseDropdownStateReturn, type UseFeedbackOptions, type UseFeedbackReturn, type UseFileUploadOptions, type UseFileUploadReturn, type UseMessageQueueOptions, type UseMessageQueueReturn, type UsePromptStateOptions, type UsePromptStateReturn, type UseServerEventsOptions, type UseServerEventsReturn, type UseSlashCommandsOptions, type UseSlashCommandsReturn, type UseToolSystemOptions, type UseToolSystemReturn, type WorkflowProgress, clearTransformationCache, convertToolsToDefinitions, defaultStrings, defaultTheme, defineTool, executeDefinedTool, findTransformerPattern, generateChatId, generateCommandId, generateMessageId, matchesMimeType, processAttachments, useAI, useAIContext, useAIWorkflow, useAgentSelection, useChatManagement, useCommandManagement, useDropdownState, useFeedback, useFileUpload, useMessageQueue, usePromptState, useServerEvents, useSlashCommands, useStableTools, useStrings, useTheme, useToolSystem, validateCommandName };
package/dist/index.js CHANGED
@@ -94,6 +94,8 @@ var defaultStrings = {
94
94
  API_OVERLOADED: "The AI service is currently experiencing high demand. Please try again in a moment.",
95
95
  /** Error when rate limited */
96
96
  RATE_LIMITED: "Too many requests. Please wait a moment before trying again.",
97
+ /** Error when the connection was lost while a response was being generated */
98
+ CONNECTION_LOST: "The connection was lost. Please send your message again.",
97
99
  /** Error for unknown/unexpected errors */
98
100
  UNKNOWN_ERROR: "An unexpected error occurred. Please try again."
99
101
  },
@@ -354,6 +356,17 @@ function mergeAssistantMessagesForDisplay(messages) {
354
356
  return result;
355
357
  }
356
358
 
359
+ // src/utils/keyboard.ts
360
+ function shouldSubmitOnEnter(e, mode) {
361
+ if (e.key !== "Enter" || e.nativeEvent.isComposing || e.keyCode === 229) {
362
+ return false;
363
+ }
364
+ if (mode === "enter") {
365
+ return !e.shiftKey;
366
+ }
367
+ return e.metaKey || e.ctrlKey;
368
+ }
369
+
357
370
  // src/components/MarkdownContent.tsx
358
371
  import ReactMarkdown from "react-markdown";
359
372
  import remarkGfm from "remark-gfm";
@@ -2176,7 +2189,8 @@ function UseAIChatPanel({
2176
2189
  onFeedback,
2177
2190
  pendingApprovals = [],
2178
2191
  onApproveToolCall,
2179
- onRejectToolCall
2192
+ onRejectToolCall,
2193
+ submitMode = "enter"
2180
2194
  }) {
2181
2195
  const strings = useStrings();
2182
2196
  const theme = useTheme();
@@ -2251,7 +2265,7 @@ function UseAIChatPanel({
2251
2265
  if (slashCommands.handleKeyDown(e)) {
2252
2266
  return;
2253
2267
  }
2254
- if (e.key === "Enter" && !e.shiftKey && !e.nativeEvent.isComposing && !(e.keyCode === 229)) {
2268
+ if (shouldSubmitOnEnter(e, submitMode)) {
2255
2269
  e.preventDefault();
2256
2270
  handleSend();
2257
2271
  }
@@ -3271,9 +3285,10 @@ function useChatUIContext() {
3271
3285
  }
3272
3286
  return context;
3273
3287
  }
3274
- function UseAIChat({ floating = false }) {
3288
+ function UseAIChat({ floating = false, submitMode }) {
3275
3289
  const ctx = useChatUIContext();
3276
3290
  const chatPanelProps = {
3291
+ submitMode: submitMode ?? ctx.submitMode,
3277
3292
  onSendMessage: ctx.sendMessage,
3278
3293
  messages: ctx.messages,
3279
3294
  loading: ctx.loading,
@@ -3338,9 +3353,12 @@ var UseAIClient = class {
3338
3353
  }
3339
3354
  socket = null;
3340
3355
  eventHandlers = /* @__PURE__ */ new Map();
3341
- reconnectAttempts = 0;
3342
- maxReconnectAttempts = 5;
3356
+ // Reconnect indefinitely so clients recover after extended outages (mobile
3357
+ // app backgrounded long enough for server pingTimeout, airplane mode, etc.).
3358
+ // Socket.IO applies exponential backoff capped at reconnectionDelayMax,
3359
+ // so steady-state retry frequency is ~one attempt per 10s.
3343
3360
  reconnectDelay = 1e3;
3361
+ reconnectDelayMax = 1e4;
3344
3362
  // Session state
3345
3363
  _threadId = null;
3346
3364
  _tools = [];
@@ -3378,14 +3396,14 @@ var UseAIClient = class {
3378
3396
  this.socket = io(this.serverUrl, {
3379
3397
  transports: ["polling", "websocket"],
3380
3398
  reconnection: true,
3381
- reconnectionAttempts: this.maxReconnectAttempts,
3399
+ reconnectionAttempts: Infinity,
3382
3400
  reconnectionDelay: this.reconnectDelay,
3401
+ reconnectionDelayMax: this.reconnectDelayMax,
3383
3402
  withCredentials: true
3384
3403
  });
3385
3404
  this.socket.on("connect", () => {
3386
3405
  console.log("[UseAI] Connected to server");
3387
3406
  console.log("[UseAI] Transport:", this.socket?.io?.engine?.transport?.name);
3388
- this.reconnectAttempts = 0;
3389
3407
  const engine = this.socket?.io?.engine;
3390
3408
  if (engine) {
3391
3409
  engine.on("upgrade", (transport) => {
@@ -3555,6 +3573,7 @@ var UseAIClient = class {
3555
3573
  this._currentAssistantMessage = null;
3556
3574
  this._currentAssistantToolCalls = [];
3557
3575
  this._pendingToolResults = [];
3576
+ this._currentReasoningBlockText = "";
3558
3577
  }
3559
3578
  }
3560
3579
  this.eventHandlers.forEach((handler) => handler(event));
@@ -5211,6 +5230,8 @@ function useServerEvents({
5211
5230
  const [streamingText, setStreamingText] = useState13("");
5212
5231
  const [streamingReasoning, setStreamingReasoning] = useState13("");
5213
5232
  const streamingChatIdRef = useRef10(null);
5233
+ const loadingRef = useRef10(loading);
5234
+ loadingRef.current = loading;
5214
5235
  const messageCountAtRunStartRef = useRef10(0);
5215
5236
  const hasTextFromPriorStepRef = useRef10(false);
5216
5237
  const [executingToolRaw, setExecutingTool] = useState13(null);
@@ -5306,6 +5327,17 @@ function useServerEvents({
5306
5327
  setLoading(false);
5307
5328
  }
5308
5329
  }, []);
5330
+ const handleDisconnect = useCallback11(() => {
5331
+ if (!loadingRef.current) return;
5332
+ const strs = stringsRef.current;
5333
+ const message = strs.errors[ErrorCode.CONNECTION_LOST] || strs.errors[ErrorCode.UNKNOWN_ERROR];
5334
+ saveAIResponseRef.current(message, "error");
5335
+ setStreamingText("");
5336
+ setStreamingReasoning("");
5337
+ streamingChatIdRef.current = null;
5338
+ setExecutingTool(null);
5339
+ setLoading(false);
5340
+ }, []);
5309
5341
  const executingTool = executingToolRaw ? {
5310
5342
  displayText: executingToolRaw.title ?? executingToolFallbackRef.current ?? strings.toolExecution.fallbackMessages[0]
5311
5343
  } : null;
@@ -5317,7 +5349,8 @@ function useServerEvents({
5317
5349
  executingTool,
5318
5350
  streamingChatIdRef,
5319
5351
  streamingReasoning,
5320
- handleServerEvent
5352
+ handleServerEvent,
5353
+ handleDisconnect
5321
5354
  };
5322
5355
  }
5323
5356
 
@@ -5483,7 +5516,8 @@ function UseAIProvider({
5483
5516
  theme: customTheme,
5484
5517
  strings: customStrings,
5485
5518
  visibleAgentIds,
5486
- onOpenChange
5519
+ onOpenChange,
5520
+ submitMode = "enter"
5487
5521
  }) {
5488
5522
  const fileUploadConfig = fileUploadConfigProp === false ? void 0 : fileUploadConfigProp ?? DEFAULT_FILE_UPLOAD_CONFIG;
5489
5523
  const theme = { ...defaultTheme, ...customTheme };
@@ -5542,12 +5576,17 @@ function UseAIProvider({
5542
5576
  } = useCommandManagement({ repository: commandRepository });
5543
5577
  const handleServerEventRef = useRef12(serverEvents.handleServerEvent);
5544
5578
  handleServerEventRef.current = serverEvents.handleServerEvent;
5579
+ const handleDisconnectRef = useRef12(serverEvents.handleDisconnect);
5580
+ handleDisconnectRef.current = serverEvents.handleDisconnect;
5545
5581
  useEffect11(() => {
5546
5582
  console.log("[UseAIProvider] Initializing client with serverUrl:", serverUrl);
5547
5583
  const client = new UseAIClient(serverUrl);
5548
5584
  const unsubscribeConnection = client.onConnectionStateChange((isConnected) => {
5549
5585
  console.log("[UseAIProvider] Connection state changed:", isConnected);
5550
5586
  setConnected(isConnected);
5587
+ if (!isConnected) {
5588
+ handleDisconnectRef.current();
5589
+ }
5551
5590
  });
5552
5591
  console.log("[UseAIProvider] Connecting...");
5553
5592
  client.connect();
@@ -5735,7 +5774,8 @@ function UseAIProvider({
5735
5774
  feedback: {
5736
5775
  enabled: feedback.enabled,
5737
5776
  submit: feedback.submitFeedback
5738
- }
5777
+ },
5778
+ submitMode
5739
5779
  };
5740
5780
  const isUIDisabled = CustomButton === null || CustomChat === null;
5741
5781
  const ButtonComponent = isUIDisabled ? null : CustomButton || UseAIFloatingButton;
@@ -5768,7 +5808,8 @@ function UseAIProvider({
5768
5808
  onFeedback: feedback.submitFeedback,
5769
5809
  pendingApprovals: toolSystem.pendingApprovals,
5770
5810
  onApproveToolCall: toolSystem.pendingApprovals.length > 0 ? toolSystem.approveAll : void 0,
5771
- onRejectToolCall: toolSystem.pendingApprovals.length > 0 ? toolSystem.rejectAll : void 0
5811
+ onRejectToolCall: toolSystem.pendingApprovals.length > 0 ? toolSystem.rejectAll : void 0,
5812
+ submitMode
5772
5813
  };
5773
5814
  const renderDefaultChat = () => {
5774
5815
  if (isUIDisabled) return null;